|
1 "{ Package: 'stx:libbasic2' }" |
|
2 |
|
3 ObjectCoder subclass:#Base64Coder |
|
4 instanceVariableNames:'buffer bits charCount atEnd' |
|
5 classVariableNames:'Base64Mapping Base64ReverseMapping' |
|
6 poolDictionaries:'' |
|
7 category:'System-Storage' |
|
8 ! |
|
9 |
|
10 !Base64Coder class methodsFor:'documentation'! |
|
11 |
|
12 documentation |
|
13 " |
|
14 Instances of this class perform Base64 en- and decoding as defined in RFC 2045 |
|
15 3 bytes are mapped to 4 characters, representing 6 bits each. |
|
16 |
|
17 [author:] |
|
18 Stefan Vogel |
|
19 |
|
20 [see also:] |
|
21 |
|
22 [instance variables:] |
|
23 buffer SmallInteger Up to 24 bits of data |
|
24 bits SmallInteger Number of valid bits in buffer |
|
25 charCount SmallInteger Number of characters since last cr |
|
26 atEnd Boolean true if end of Base64 string reached |
|
27 |
|
28 [class variables:] |
|
29 Base64Mapping String Mapping from bytes (with 6 valid bits) |
|
30 to Base64 characters |
|
31 Base64ReverseMapping Array Mapping from Base64 characters to 6-bit-Bytes |
|
32 " |
|
33 ! |
|
34 |
|
35 examples |
|
36 " |
|
37 |
|
38 [exBegin] |
|
39 |coder| |
|
40 |
|
41 coder := Base64Coder on:'' writeStream. |
|
42 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]. |
|
43 coder flush. |
|
44 coder contents inspect. |
|
45 coder reset. |
|
46 coder nextPut:254. |
|
47 coder contents inspect. |
|
48 [exEnd] |
|
49 |
|
50 [exBegin] |
|
51 |coder coder1| |
|
52 |
|
53 coder := Base64Coder on:'' writeStream. |
|
54 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]. |
|
55 coder flush. |
|
56 |
|
57 coder1 := Base64Coder on:coder contents readStream. |
|
58 [coder1 atEnd] whileFalse:[ |
|
59 Transcript show:coder1 next |
|
60 ]. |
|
61 Transcript cr. |
|
62 [exEnd] |
|
63 " |
|
64 ! ! |
|
65 |
|
66 !Base64Coder class methodsFor:'initialization'! |
|
67 |
|
68 initialize |
|
69 "initialize class variables" |
|
70 |
|
71 "64 characters representing the 6-bit values from 0-63 and one pad character" |
|
72 Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='. |
|
73 Base64ReverseMapping := ByteArray new:256 withAll:255. |
|
74 Base64Mapping keysAndValuesDo:[:idx :char| |
|
75 Base64ReverseMapping at:char asciiValue put:idx-1. |
|
76 ]. |
|
77 |
|
78 " |
|
79 self initialize |
|
80 " |
|
81 ! ! |
|
82 |
|
83 !Base64Coder class methodsFor:'instance creation'! |
|
84 |
|
85 new |
|
86 ^ self basicNew initialize |
|
87 ! ! |
|
88 |
|
89 !Base64Coder methodsFor:'encoding'! |
|
90 |
|
91 encodeObject:anObject with:aParameter |
|
92 "not defined. Use nextPut or nextPutAll:. |
|
93 Could encode the printString here" |
|
94 |
|
95 ^ self shouldNotImplement |
|
96 ! |
|
97 |
|
98 next |
|
99 "answer the next decoded byte" |
|
100 |
|
101 |b shift| |
|
102 |
|
103 bits == 0 ifTrue:[ |
|
104 "no more bits in buffer |
|
105 if end has been reached, raise signal" |
|
106 atEnd ifTrue:[ |
|
107 ^ stream endOfStreamSignal raiseRequest. |
|
108 ]. |
|
109 |
|
110 "read next 4 characters each representing 6 bits" |
|
111 buffer := 0. |
|
112 [ |
|
113 "read next valid Base64 character, skip invalid characters" |
|
114 [ |
|
115 b := Base64ReverseMapping at:stream next asciiValue. |
|
116 ] doWhile:[b == 255]. |
|
117 |
|
118 b == 64 ifTrue:[ |
|
119 "got #=, end of Base64 string has been reached" |
|
120 atEnd := true. |
|
121 bits == 12 ifTrue:[ |
|
122 "data has been padded to 12, skip 4 bits" |
|
123 shift := -4. |
|
124 ] ifFalse:[ |
|
125 "data has been padded to 18, skip 2 bits" |
|
126 shift := -2. |
|
127 ]. |
|
128 bits := bits + shift. |
|
129 buffer := buffer bitShift:shift. |
|
130 ] ifFalse:[ |
|
131 "got valid Base64 character, append to buffer" |
|
132 buffer := (buffer bitShift:6) bitOr:b. |
|
133 bits := bits + 6. |
|
134 ]. |
|
135 ] doWhile:[bits ~~ 24 and:[atEnd not]]. |
|
136 ]. |
|
137 |
|
138 b := (buffer bitShift:(8 - bits)) bitAnd:16rFF. |
|
139 bits := bits - 8. |
|
140 |
|
141 ^ b. |
|
142 ! |
|
143 |
|
144 nextPut:aByte |
|
145 "encode aByte on the output stream" |
|
146 |
|
147 |b1 b2 b3 b4| |
|
148 |
|
149 buffer := (buffer bitShift:8) bitOr:aByte. |
|
150 bits := bits + 8. |
|
151 bits == 24 ifTrue:[ |
|
152 b4 := buffer bitAnd:16r3F. |
|
153 b3 := (buffer bitShift:-6) bitAnd:16r3F. |
|
154 b2 := (buffer bitShift:-12) bitAnd:16r3F. |
|
155 b1 := (buffer bitShift:-18) bitAnd:16r3F. |
|
156 buffer := bits := 0. |
|
157 stream nextPut:(Base64Mapping at:b1+1); |
|
158 nextPut:(Base64Mapping at:b2+1); |
|
159 nextPut:(Base64Mapping at:b3+1); |
|
160 nextPut:(Base64Mapping at:b4+1). |
|
161 |
|
162 "RFC 2045 says: max 76 characters in one line" |
|
163 charCount >= 68 ifTrue:[ |
|
164 stream cr. |
|
165 charCount := 0. |
|
166 ] ifFalse:[ |
|
167 charCount := charCount + 4. |
|
168 ] |
|
169 ]. |
|
170 ! ! |
|
171 |
|
172 !Base64Coder methodsFor:'initialization'! |
|
173 |
|
174 initialize |
|
175 |
|
176 buffer := bits := charCount := 0. |
|
177 atEnd := false. |
|
178 ! ! |
|
179 |
|
180 !Base64Coder methodsFor:'misc'! |
|
181 |
|
182 flush |
|
183 "flush the remaining bits of buffer. |
|
184 The number of bits in buffer is not a multiple of 6, so we pad |
|
185 the buffer and signal that padding has been done via $= characters." |
|
186 |
|
187 |b1 b2 b3 b4| |
|
188 |
|
189 bits == 0 ifTrue:[ |
|
190 "buffer is empty, nothing to do" |
|
191 ^ self. |
|
192 ]. |
|
193 |
|
194 bits == 8 ifTrue:[ |
|
195 buffer := buffer bitShift:4. |
|
196 b4 := b3 := 64. "pad with '=='" |
|
197 b1 := (buffer bitShift:-6) bitAnd:16r3F. |
|
198 b2 := buffer bitAnd:16r3F. |
|
199 ] ifFalse:[bits = 16 ifTrue:[ |
|
200 buffer := buffer bitShift:2. |
|
201 b4 := 64. "pad with '='" |
|
202 b3 := buffer bitAnd:16r3F. |
|
203 b2 := (buffer bitShift:-6) bitAnd:16r3F. |
|
204 b1 := (buffer bitShift:-12) bitAnd:16r3F. |
|
205 ]]. |
|
206 bits := buffer := 0. |
|
207 |
|
208 stream nextPut:(Base64Mapping at:b1+1); |
|
209 nextPut:(Base64Mapping at:b2+1); |
|
210 nextPut:(Base64Mapping at:b3+1); |
|
211 nextPut:(Base64Mapping at:b4+1). |
|
212 ! |
|
213 |
|
214 reset |
|
215 "reset to initial state" |
|
216 |
|
217 super reset. |
|
218 buffer := bits := charCount := 0. |
|
219 atEnd := false. |
|
220 ! ! |
|
221 |
|
222 !Base64Coder methodsFor:'queries'! |
|
223 |
|
224 atEnd |
|
225 "answer true, if no more bytes can be read" |
|
226 |
|
227 ^ atEnd or:[bits == 0 and:[stream atEnd]] |
|
228 ! ! |
|
229 |
|
230 !Base64Coder class methodsFor:'documentation'! |
|
231 |
|
232 version |
|
233 ^ '$Header: /cvs/stx/stx/libbasic2/Base64Coder.st,v 1.1 2002-03-18 10:55:48 stefan Exp $' |
|
234 ! ! |
|
235 Base64Coder initialize! |