1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
;;;; Name: encode.lisp
;;;; Purpose: cl-base64 encoding routines
;;;; Programmer: Kevin M. Rosenberg
;;;; Date Started: Dec 2002
;;;;
;;;; $Id$
;;;;
;;;; This file implements the Base64 transfer encoding algorithm as
;;;; defined in RFC 1521 by Borensten & Freed, September 1993.
;;;; See: http://www.ietf.org/rfc/rfc1521.txt
;;;;
;;;; Based on initial public domain code by Juri Pakaste <juri@iki.fi>
;;;;
;;;; Copyright 2002-2003 Kevin M. Rosenberg
;;;; Permission to use with BSD-style license included in the COPYING file
;;;; *************************************************************************
(in-package #:cl-base64)
(declaim (inline whitespace-p))
(defun whitespace-p (c)
"Returns T for a whitespace character."
(or (char= c #\Newline) (char= c #\Linefeed)
(char= c #\Return) (char= c #\Space)
(char= c #\Tab)))
;;; Decoding
#+ignore
(defmacro def-base64-stream-to-* (output-type)
`(defun ,(intern (concatenate 'string (symbol-name :base64-stream-to-)
(symbol-name output-type)))
(input &key (uri nil)
,@(when (eq output-type :stream)
'(stream)))
,(concatenate 'string "Decode base64 stream to " (string-downcase
(symbol-name output-type)))
(declare (stream input)
(optimize (speed 3) (space 0) (safety 0)))
(let ((pad (if uri *uri-pad-char* *pad-char*))
(decode-table (if uri *uri-decode-table* *decode-table*)))
(declare (type decode-table decode-table)
(type character pad))
(let (,@(case output-type
(:string
'((result (make-string (* 3 (truncate (length string) 4))))))
(:usb8-array
'((result (make-array (* 3 (truncate (length string) 4))
:element-type '(unsigned-byte 8)
:fill-pointer nil
:adjustable nil)))))
(ridx 0))
(declare ,@(case output-type
(:string
'((simple-string result)))
(:usb8-array
'((type (simple-array (unsigned-byte 8) (*)) result))))
(fixnum ridx))
(do* ((bitstore 0)
(bitcount 0)
(char (read-char stream nil #\null)
(read-char stream nil #\null)))
((eq char #\null)
,(case output-type
(:stream
'stream)
((:string :usb8-array)
'result)
;; ((:stream :string)
;; '(subseq result 0 ridx))))
))
(declare (fixnum bitstore bitcount)
(character char))
(let ((svalue (aref decode-table (the fixnum (char-code char)))))
(declare (fixnum svalue))
(cond
((>= svalue 0)
(setf bitstore (logior
(the fixnum (ash bitstore 6))
svalue))
(incf bitcount 6)
(when (>= bitcount 8)
(decf bitcount 8)
(let ((ovalue (the fixnum
(logand
(the fixnum
(ash bitstore
(the fixnum (- bitcount))))
#xFF))))
(declare (fixnum ovalue))
,(case output-type
(:string
'(setf (char result ridx) (code-char ovalue)))
(:usb8-array
'(setf (aref result ridx) ovalue))
(:stream
'(write-char (code-char ovalue) stream)))
(incf ridx)
(setf bitstore (the fixnum (logand bitstore #xFF))))))
((char= char pad)
;; Could add checks to make sure padding is correct
;; Currently, padding is ignored
)
((whitespace-p char)
;; Ignore whitespace
)
((minusp svalue)
(warn "Bad character ~W in base64 decode" char))
)))))))
;;(def-base64-stream-to-* :string)
;;(def-base64-stream-to-* :stream)
;;(def-base64-stream-to-* :usb8-array)
(defmacro def-base64-string-to-* (output-type)
`(defun ,(intern (concatenate 'string (symbol-name :base64-string-to-)
(symbol-name output-type)))
(input &key (uri nil)
,@(when (eq output-type :stream)
'(stream)))
,(concatenate 'string "Decode base64 string to " (string-downcase
(symbol-name output-type)))
(declare (string input)
(optimize (speed 3) (safety 0) (space 0)))
(let ((pad (if uri *uri-pad-char* *pad-char*))
(decode-table (if uri *uri-decode-table* *decode-table*)))
(declare (type decode-table decode-table)
(type character pad))
(let (,@(case output-type
(:string
'((result (make-string (* 3 (truncate (length input) 4))))))
(:usb8-array
'((result (make-array (* 3 (truncate (length input) 4))
:element-type '(unsigned-byte 8)
:fill-pointer nil
:adjustable nil)))))
(ridx 0))
(declare ,@(case output-type
(:string
'((simple-string result)))
(:usb8-array
'((type (simple-array (unsigned-byte 8) (*)) result))))
(fixnum ridx))
(loop
for char of-type character across input
for svalue of-type fixnum = (aref decode-table
(the fixnum (char-code char)))
with bitstore of-type fixnum = 0
with bitcount of-type fixnum = 0
do
(cond
((>= svalue 0)
(setf bitstore (logior
(the fixnum (ash bitstore 6))
svalue))
(incf bitcount 6)
(when (>= bitcount 8)
(decf bitcount 8)
(let ((ovalue (the fixnum
(logand
(the fixnum
(ash bitstore
(the fixnum (- bitcount))))
#xFF))))
(declare (fixnum ovalue))
,(case output-type
(:string
'(setf (char result ridx) (code-char ovalue)))
(:usb8-array
'(setf (aref result ridx) ovalue))
(:stream
'(write-char (code-char ovalue) stream)))
(incf ridx)
(setf bitstore (the fixnum (logand bitstore #xFF))))))
((char= char pad)
;; Could add checks to make sure padding is correct
;; Currently, padding is ignored
)
((whitespace-p char)
;; Ignore whitespace
)
((minusp svalue)
(warn "Bad character ~W in base64 decode" char))
))
,(case output-type
(:stream
'stream)
((:usb8-array :string)
'(subseq result 0 ridx)))))))
(def-base64-string-to-* :string)
(def-base64-string-to-* :stream)
(def-base64-string-to-* :usb8-array)
;; input-mode can be :string or :stream
;; input-format can be :character or :usb8
(defun base64-string-to-integer (string &key (uri nil))
"Decodes a base64 string to an integer"
(declare (string string)
(optimize (speed 3) (safety 0) (space 0)))
(let ((pad (if uri *uri-pad-char* *pad-char*))
(decode-table (if uri *uri-decode-table* *decode-table*)))
(declare (type decode-table decode-table)
(character pad))
(let ((value 0))
(declare (integer value))
(loop
for char of-type character across string
for svalue of-type fixnum =
(aref decode-table (the fixnum (char-code char)))
do
(cond
((>= svalue 0)
(setq value (+ svalue (ash value 6))))
((char= char pad)
(setq value (ash value -2)))
((whitespace-p char)
; ignore whitespace
)
((minusp svalue)
(warn "Bad character ~W in base64 decode" char))))
value)))
(defun base64-stream-to-integer (stream &key (uri nil))
"Decodes a base64 string to an integer"
(declare (stream stream)
(optimize (speed 3) (space 0) (safety 0)))
(let ((pad (if uri *uri-pad-char* *pad-char*))
(decode-table (if uri *uri-decode-table* *decode-table*)))
(declare (type decode-table decode-table)
(character pad))
(do* ((value 0)
(char (read-char stream nil #\null)
(read-char stream nil #\null)))
((eq char #\null)
value)
(declare (integer value)
(character char))
(let ((svalue (aref decode-table (the fixnum (char-code char)))))
(declare (fixnum svalue))
(cond
((>= svalue 0)
(setq value (+ svalue (ash value 6))))
((char= char pad)
(setq value (ash value -2)))
((whitespace-p char) ; ignore whitespace
)
((minusp svalue)
(warn "Bad character ~W in base64 decode" char)))))))
|