r3746: *** empty log message ***
[cl-base64.git] / encode.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          encode.lisp
6 ;;;; Purpose:       cl-base64 encoding routines
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Dec 2002
9 ;;;;
10 ;;;; $Id: encode.lisp,v 1.1 2003/01/12 20:25:26 kevin Exp $
11 ;;;;
12 ;;;; This file implements the Base64 transfer encoding algorithm as
13 ;;;; defined in RFC 1521 by Borensten & Freed, September 1993.
14 ;;;; See: http://www.ietf.org/rfc/rfc1521.txt
15 ;;;;
16 ;;;; Based on initial public domain code by Juri Pakaste <juri@iki.fi>
17 ;;;;
18 ;;;; Copyright 2002-2003 Kevin M. Rosenberg
19 ;;;; Permission to use with BSD-style license included in the COPYING file
20 ;;;; *************************************************************************
21
22 (declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))
23
24 ;;;; Extended by Kevin M. Rosenberg <kevin@rosenberg.net>:
25 ;;;;   - .asd file
26 ;;;;   - numerous speed optimizations
27 ;;;;   - conversion to and from integers
28 ;;;;   - Renamed functions now that supporting integer conversions
29 ;;;;   - URI-compatible encoding using :uri key
30 ;;;;
31 ;;;; $Id: encode.lisp,v 1.1 2003/01/12 20:25:26 kevin Exp $
32
33 (in-package #:cl-base64)
34
35
36 (defun round-next-multiple (x n)
37   "Round x up to the next highest multiple of n."
38   (declare (fixnum n)
39            (optimize (speed 3)))
40   (let ((remainder (mod x n)))
41     (declare (fixnum remainder))
42     (if (zerop remainder)
43         x
44         (the fixnum (+ x (the fixnum (- n remainder)))))))
45
46 (defun string-to-base64 (string &key (uri nil) (columns 0) (stream nil))
47   "Encode a string array to base64. If columns is > 0, designates
48 maximum number of columns in a line and the string will be terminated
49 with a #\Newline."
50   (declare (string string)
51            (fixnum columns)
52            (optimize (speed 3)))
53   (let ((pad (if uri *uri-pad-char* *pad-char*))
54         (encode-table (if uri *uri-encode-table* *encode-table*)))
55     (declare (simple-string encode-table)
56              (character pad))
57     (let* ((string-length (length string))
58            (complete-group-count (truncate string-length 3))
59            (remainder (nth-value 1 (truncate string-length 3)))
60            (padded-length (* 4 (truncate (+ string-length 2) 3)))
61            (num-lines (if (plusp columns)
62                           (truncate (+ padded-length (1- columns)) columns)
63                           0))
64            (num-breaks (if (plusp num-lines)
65                            (1- num-lines)
66                            0))
67            (strlen (if stream
68                        0
69                        (+ padded-length num-breaks)))
70            (result (make-string strlen))
71            (col (if (plusp columns)
72                     0
73                     (1+ padded-length)))
74            (ioutput 0))
75       (declare (fixnum string-length padded-length col ioutput)
76                (simple-string result))
77       (labels ((output-char (ch)
78                  (if (= col columns)
79                      (progn
80                        (if stream
81                            (write-char #\Newline stream)
82                            (progn
83                              (setf (schar result ioutput) #\Newline)
84                              (incf ioutput)))
85                        (setq col 1))
86                      (incf col))
87                  (if stream
88                      (write-char ch stream)
89                      (progn
90                        (setf (schar result ioutput) ch)
91                        (incf ioutput))))
92              (output-group (svalue chars)
93                (declare (fixnum svalue chars))
94                (output-char
95                 (schar encode-table
96                        (the fixnum
97                          (logand #x3f
98                                  (the fixnum (ash svalue -18))))))
99                (output-char
100                 (schar encode-table
101                        (the fixnum
102                          (logand #x3f
103                                  (the fixnum (ash svalue -12))))))
104                (if (> chars 2)
105                    (output-char
106                     (schar encode-table
107                            (the fixnum
108                              (logand #x3f
109                                      (the fixnum (ash svalue -6))))))
110                  (output-char pad))
111                (if (> chars 3)
112                    (output-char
113                     (schar encode-table
114                            (the fixnum
115                              (logand #x3f svalue))))
116                  (output-char pad))))
117         (do ((igroup 0 (1+ igroup))
118              (isource 0 (+ isource 3)))
119             ((= igroup complete-group-count)
120              (cond
121                ((= remainder 2)
122                 (output-group
123                  (the fixnum
124                    (+
125                     (the fixnum
126                       (ash (char-code (the character
127                                         (char string isource))) 16))
128                     (the fixnum
129                       (ash (char-code (the character
130                                         (char string (1+ isource)))) 8))))
131                  3))
132                ((= remainder 1)
133                 (output-group
134                  (the fixnum
135                    (ash (char-code (the character (char string isource))) 16))
136                  2)))
137              result)
138           (declare (fixnum igroup isource))
139           (output-group 
140            (the fixnum
141              (+
142               (the fixnum
143                 (ash (char-code (the character
144                                   (char string isource))) 16))
145               (the fixnum
146                 (ash (char-code (the character (char string (1+ isource)))) 8))
147               (the fixnum
148                 (char-code (the character (char string (+ 2 isource)))))))
149            4))))))
150   
151 (defun integer-to-base64 (input &key (uri nil) (columns 0) (stream nil))
152   (if stream
153       (integer-to-base64-stream input stream :uri uri :columns columns)
154       (integer-to-base64-string input :uri uri :columns columns)))
155
156 (defun integer-to-base64-string (input &key (uri nil) (columns 0))
157   "Encode an integer to base64 format."
158   (declare (integer input)
159            (fixnum columns)
160            (optimize (speed 3)))
161   (let ((pad (if uri *uri-pad-char* *pad-char*))
162         (encode-table (if uri *uri-encode-table* *encode-table*)))
163     (declare (simple-string encode-table)
164              (character pad))
165     (let* ((input-bits (integer-length input))
166            (byte-bits (round-next-multiple input-bits 8))
167            (padded-bits (round-next-multiple byte-bits 6))
168            (remainder-padding (mod padded-bits 24))
169            (padding-bits (if (zerop remainder-padding)
170                              0
171                              (- 24 remainder-padding)))
172            (padding-chars (/ padding-bits 6))
173            (padded-length (/ (+ padded-bits padding-bits) 6))
174            (last-line-len (if (plusp columns)
175                               (- padded-length (* columns
176                                                   (truncate
177                                                    padded-length columns)))
178                               0))
179            (num-lines (if (plusp columns)
180                           (truncate (+ padded-length (1- columns)) columns)
181                           0))
182            (num-breaks (if (plusp num-lines)
183                            (1- num-lines)
184                            0))
185            (strlen (+ padded-length num-breaks))
186            (last-char (1- strlen))
187            (str (make-string strlen))
188            (col (if (zerop last-line-len)
189                      columns
190                     last-line-len)))
191       (declare (fixnum padded-length num-lines col last-char
192                        padding-chars last-line-len))
193       (unless (plusp columns)
194         (setq col -1)) ;; set to flag to optimize in loop
195       
196       (dotimes (i padding-chars)
197         (declare (fixnum i))
198         (setf (schar str (the fixnum (- last-char i))) pad))
199
200       (do* ((strpos (- last-char padding-chars) (1- strpos))
201             (int (ash input (/ padding-bits 3))))
202            ((minusp strpos)
203             str)
204         (declare (fixnum strpos) (integer int))
205         (cond
206           ((zerop col)
207            (setf (schar str strpos) #\Newline)
208            (setq col columns))
209           (t
210            (setf (schar str strpos)
211                  (schar encode-table (the fixnum (logand int #x3f))))
212            (setq int (ash int -6))
213            (decf col)))))))
214
215 (defun integer-to-base64-stream (input stream &key (uri nil) (columns 0))
216   "Encode an integer to base64 format."
217   (declare (integer input)
218            (fixnum columns)
219            (optimize (speed 3)))
220   (let ((pad (if uri *uri-pad-char* *pad-char*))
221         (encode-table (if uri *uri-encode-table* *encode-table*)))
222     (declare (simple-string encode-table)
223              (character pad))
224     (let* ((input-bits (integer-length input))
225            (byte-bits (round-next-multiple input-bits 8))
226            (padded-bits (round-next-multiple byte-bits 6))
227            (remainder-padding (mod padded-bits 24))
228            (padding-bits (if (zerop remainder-padding)
229                              0
230                              (- 24 remainder-padding)))
231            (padding-chars (/ padding-bits 6))
232            (padded-length (/ (+ padded-bits padding-bits) 6))
233            (strlen padded-length)
234            (nonpad-chars (- strlen padding-chars))
235            (last-nonpad-char (1- nonpad-chars))
236            (str (make-string strlen)))
237       (declare (fixnum padded-length last-nonpad-char))
238       (do* ((strpos 0 (1+ strpos))
239             (int (ash input (/ padding-bits 3)) (ash int -6))
240             (6bit-value (logand int #x3f) (logand int #x3f)))
241            ((= strpos nonpad-chars)
242             (let ((col 0))
243               (declare (fixnum col))
244               (dotimes (i nonpad-chars)
245                 (declare (fixnum i))
246                 (write-char (schar str i) stream)
247                 (when (plusp columns)
248                   (incf col)
249                   (when (= col columns)
250                     (write-char #\Newline stream)
251                     (setq col 0))))
252               (dotimes (ipad padding-chars)
253                 (declare (fixnum ipad))
254                 (write-char pad stream)
255                 (when (plusp columns)
256                   (incf col)
257                   (when (= col columns)
258                     (write-char #\Newline stream)
259                     (setq col 0)))))
260             stream)
261         (declare (fixnum 6bit-value strpos)
262                  (integer int))
263         (setf (schar str (- last-nonpad-char strpos))
264               (schar encode-table 6bit-value))
265         ))))
266