author | Claus Gittinger <cg@exept.de> |
Mon, 04 Nov 2002 15:38:30 +0100 | |
changeset 1111 | 2a64f0fe418a |
parent 1084 | 9d51967037ad |
child 1122 | 9f6405dc3319 |
permissions | -rw-r--r-- |
1027 | 1 |
"{ Package: 'stx:libbasic2' }" |
2 |
||
3 |
ObjectCoder subclass:#Base64Coder |
|
1111 | 4 |
instanceVariableNames:'buffer bits charCount peekByte atEnd' |
1027 | 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 |
||
1063 | 89 |
!Base64Coder methodsFor:'decoding'! |
1027 | 90 |
|
1111 | 91 |
basicNext |
92 |
"answer the next decoded byte" |
|
93 |
||
94 |
|b| |
|
95 |
||
96 |
bits == 0 ifTrue:[ |
|
97 |
self fillBuffer. |
|
98 |
bits == 0 ifTrue:[ |
|
99 |
^ stream class endOfStreamSignal raiseRequest. |
|
100 |
] |
|
101 |
]. |
|
102 |
||
103 |
b := (buffer bitShift:(8 - bits)) bitAnd:16rFF. |
|
104 |
bits := bits - 8. |
|
105 |
||
106 |
^ b. |
|
107 |
! |
|
108 |
||
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
109 |
fillBuffer |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
110 |
"fill buffer with next 4 characters each representing 6 bits" |
1027 | 111 |
|
112 |
|b shift| |
|
113 |
||
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
114 |
buffer := 0. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
115 |
bits := 0. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
116 |
[ |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
117 |
"read next valid Base64 character, skip invalid characters" |
1027 | 118 |
[ |
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
119 |
b := stream next. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
120 |
b isNil ifTrue:[ "end of stream" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
121 |
atEnd := true. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
122 |
^ self. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
123 |
]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
124 |
b := Base64ReverseMapping at:b asciiValue. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
125 |
] doWhile:[b == 255]. |
1027 | 126 |
|
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
127 |
b == 64 ifTrue:[ |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
128 |
"got #=, end of Base64 string has been reached" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
129 |
atEnd := true. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
130 |
bits == 12 ifTrue:[ |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
131 |
"data has been padded to 12, skip 4 bits" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
132 |
shift := -4. |
1027 | 133 |
] ifFalse:[ |
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
134 |
"data has been padded to 18, skip 2 bits" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
135 |
shift := -2. |
1027 | 136 |
]. |
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
137 |
bits := bits + shift. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
138 |
buffer := buffer bitShift:shift. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
139 |
] ifFalse:[ |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
140 |
"got valid Base64 character, append to buffer" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
141 |
buffer := (buffer bitShift:6) bitOr:b. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
142 |
bits := bits + 6. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
143 |
]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
144 |
] doWhile:[bits ~~ 24 and:[atEnd not]]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
145 |
! |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
146 |
|
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
147 |
next |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
148 |
"answer the next decoded byte" |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
149 |
|
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
150 |
|b| |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
151 |
|
1111 | 152 |
peekByte notNil ifTrue:[ |
153 |
b := peekByte. |
|
154 |
peekByte := nil. |
|
155 |
^ b |
|
1027 | 156 |
]. |
1111 | 157 |
^ self basicNext. |
158 |
! |
|
1027 | 159 |
|
1111 | 160 |
peek |
161 |
"answer the next decoded byte" |
|
1027 | 162 |
|
1111 | 163 |
peekByte isNil ifTrue:[ |
164 |
peekByte := self basicNext. |
|
165 |
]. |
|
166 |
^ peekByte |
|
1063 | 167 |
! ! |
168 |
||
169 |
!Base64Coder methodsFor:'encoding'! |
|
170 |
||
171 |
encodeByteArray:aByteArray with:aParameter |
|
172 |
||
173 |
^ self nextPutAll:aByteArray; flush. |
|
174 |
||
175 |
" |
|
176 |
self encodingOf:#[1 2 3 4 5 6 255] |
|
177 |
" |
|
178 |
! |
|
179 |
||
180 |
encodeObject:anObject with:aParameter |
|
181 |
"not defined. Use nextPut or nextPutAll:. |
|
182 |
Could encode the printString here" |
|
183 |
||
184 |
^ self shouldNotImplement |
|
185 |
! |
|
186 |
||
187 |
encodeString:aString with:aParameter |
|
188 |
||
189 |
^ self nextPutAll:aString asByteArray; flush. |
|
190 |
||
191 |
" |
|
1111 | 192 |
|encoded decoded decoder| |
193 |
||
194 |
encoded := self encodingOf:'hello world'. |
|
195 |
decoded := #[] writeStream. |
|
196 |
decoder := Base64Coder on:encoded readStream. |
|
197 |
[decoder atEnd] whileFalse:[ |
|
198 |
decoded nextPut:(decoder next). |
|
199 |
]. |
|
200 |
decoded := decoded contents. |
|
201 |
decoded asString. |
|
1063 | 202 |
" |
1027 | 203 |
! |
204 |
||
205 |
nextPut:aByte |
|
206 |
"encode aByte on the output stream" |
|
207 |
||
208 |
|b1 b2 b3 b4| |
|
209 |
||
210 |
buffer := (buffer bitShift:8) bitOr:aByte. |
|
211 |
bits := bits + 8. |
|
212 |
bits == 24 ifTrue:[ |
|
213 |
b4 := buffer bitAnd:16r3F. |
|
214 |
b3 := (buffer bitShift:-6) bitAnd:16r3F. |
|
215 |
b2 := (buffer bitShift:-12) bitAnd:16r3F. |
|
216 |
b1 := (buffer bitShift:-18) bitAnd:16r3F. |
|
217 |
buffer := bits := 0. |
|
218 |
stream nextPut:(Base64Mapping at:b1+1); |
|
219 |
nextPut:(Base64Mapping at:b2+1); |
|
220 |
nextPut:(Base64Mapping at:b3+1); |
|
221 |
nextPut:(Base64Mapping at:b4+1). |
|
222 |
||
223 |
"RFC 2045 says: max 76 characters in one line" |
|
224 |
charCount >= 68 ifTrue:[ |
|
225 |
stream cr. |
|
226 |
charCount := 0. |
|
227 |
] ifFalse:[ |
|
228 |
charCount := charCount + 4. |
|
229 |
] |
|
230 |
]. |
|
231 |
! ! |
|
232 |
||
233 |
!Base64Coder methodsFor:'initialization'! |
|
234 |
||
1078 | 235 |
emptyWriteStream |
236 |
"answer an empty stream. We encode as string" |
|
237 |
||
238 |
^ WriteStream on:(String new:64) |
|
239 |
! |
|
240 |
||
1027 | 241 |
initialize |
242 |
||
243 |
buffer := bits := charCount := 0. |
|
244 |
atEnd := false. |
|
245 |
! ! |
|
246 |
||
247 |
!Base64Coder methodsFor:'misc'! |
|
248 |
||
249 |
flush |
|
250 |
"flush the remaining bits of buffer. |
|
251 |
The number of bits in buffer is not a multiple of 6, so we pad |
|
252 |
the buffer and signal that padding has been done via $= characters." |
|
253 |
||
254 |
|b1 b2 b3 b4| |
|
255 |
||
256 |
bits == 0 ifTrue:[ |
|
257 |
"buffer is empty, nothing to do" |
|
258 |
^ self. |
|
259 |
]. |
|
260 |
||
261 |
bits == 8 ifTrue:[ |
|
262 |
buffer := buffer bitShift:4. |
|
263 |
b4 := b3 := 64. "pad with '=='" |
|
264 |
b1 := (buffer bitShift:-6) bitAnd:16r3F. |
|
265 |
b2 := buffer bitAnd:16r3F. |
|
266 |
] ifFalse:[bits = 16 ifTrue:[ |
|
267 |
buffer := buffer bitShift:2. |
|
268 |
b4 := 64. "pad with '='" |
|
269 |
b3 := buffer bitAnd:16r3F. |
|
270 |
b2 := (buffer bitShift:-6) bitAnd:16r3F. |
|
271 |
b1 := (buffer bitShift:-12) bitAnd:16r3F. |
|
272 |
]]. |
|
273 |
bits := buffer := 0. |
|
274 |
||
275 |
stream nextPut:(Base64Mapping at:b1+1); |
|
276 |
nextPut:(Base64Mapping at:b2+1); |
|
277 |
nextPut:(Base64Mapping at:b3+1); |
|
278 |
nextPut:(Base64Mapping at:b4+1). |
|
279 |
! |
|
280 |
||
281 |
reset |
|
282 |
"reset to initial state" |
|
283 |
||
284 |
super reset. |
|
285 |
buffer := bits := charCount := 0. |
|
286 |
atEnd := false. |
|
287 |
! ! |
|
288 |
||
289 |
!Base64Coder methodsFor:'queries'! |
|
290 |
||
291 |
atEnd |
|
292 |
"answer true, if no more bytes can be read" |
|
293 |
||
1084
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
294 |
bits == 0 ifTrue:[ |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
295 |
atEnd ifTrue:[^ true]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
296 |
self fillBuffer. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
297 |
bits == 0 ifTrue:[^ true]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
298 |
]. |
9d51967037ad
Fix for whitespace and end of line and multiple of 4 characters
Stefan Vogel <sv@exept.de>
parents:
1078
diff
changeset
|
299 |
^ false. |
1111 | 300 |
! |
301 |
||
302 |
binary |
|
303 |
^ self |
|
304 |
! |
|
305 |
||
306 |
isStream |
|
307 |
^ true |
|
1063 | 308 |
! ! |
309 |
||
310 |
!Base64Coder methodsFor:'stream compatibility'! |
|
311 |
||
312 |
upToEnd |
|
313 |
"return a collection of the elements up-to the end. |
|
314 |
Return nil if the stream-end is reached before." |
|
315 |
||
316 |
|answerStream| |
|
317 |
||
318 |
answerStream := WriteStream on:(ByteArray new:128). |
|
319 |
[self atEnd] whileFalse:[ |
|
320 |
answerStream nextPut:self next |
|
321 |
]. |
|
322 |
||
323 |
^ answerStream contents |
|
1027 | 324 |
! ! |
325 |
||
326 |
!Base64Coder class methodsFor:'documentation'! |
|
327 |
||
328 |
version |
|
1111 | 329 |
^ '$Header: /cvs/stx/stx/libbasic2/Base64Coder.st,v 1.5 2002-11-04 14:38:30 cg Exp $' |
1027 | 330 |
! ! |
1111 | 331 |
|
1027 | 332 |
Base64Coder initialize! |