41 documentation |
41 documentation |
42 " |
42 " |
43 Instances of this class perform Base64 en- and decoding as defined in RFC 2045 |
43 Instances of this class perform Base64 en- and decoding as defined in RFC 2045 |
44 3 bytes are mapped to 4 characters, representing 6 bits each. |
44 3 bytes are mapped to 4 characters, representing 6 bits each. |
45 The encoded string consists only of characters from the set: |
45 The encoded string consists only of characters from the set: |
46 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' |
46 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' |
47 |
47 |
48 Notice: for URLs, a slightly different encoding is used, |
48 Notice: for URLs, a slightly different encoding is used, |
49 where instead of plus and slash, minus and underline are generated (see Base64UrlCoder). |
49 where instead of plus and slash, minus and underline are generated (see Base64UrlCoder). |
50 |
50 |
51 The main entry point API is: |
51 The main entry point API is: |
52 Base64Coder encode:aStringOrBytes |
52 Base64Coder encode:aStringOrBytes |
53 and |
53 and |
54 Base64Coder decode:aString |
54 Base64Coder decode:aString |
55 |
55 |
56 Typically, binary data is encoded as base64, |
56 Typically, binary data is encoded as base64, |
57 so the natural return value is a byte array. |
57 so the natural return value is a byte array. |
58 |
58 |
59 If the decoder should return a string, use |
59 If the decoder should return a string, use |
60 Base64Coder decodeAsString:aString. |
60 Base64Coder decodeAsString:aString. |
61 otherwise, a bytearray is returned from the decode: method. |
61 otherwise, a bytearray is returned from the decode: method. |
62 |
62 |
63 [author:] |
63 [author:] |
64 Stefan Vogel |
64 Stefan Vogel |
65 |
65 |
66 [see also:] |
66 [see also:] |
67 RFC https://tools.ietf.org/html/rfc4648 |
67 RFC https://tools.ietf.org/html/rfc4648 |
68 |
68 |
69 [instance variables:] |
69 [instance variables:] |
70 |
70 |
71 [class variables:] |
71 [class variables:] |
72 Base64Mapping String Mapping from bytes (with 6 valid bits) |
72 Base64Mapping String Mapping from bytes (with 6 valid bits) |
73 to Base64 characters |
73 to Base64 characters |
74 Base64ReverseMapping Array Mapping from Base64 characters to 6-bit-Bytes |
74 Base64ReverseMapping Array Mapping from Base64 characters to 6-bit-Bytes |
75 " |
75 " |
76 ! |
76 ! |
77 |
77 |
78 examples |
78 examples |
79 " |
79 " |
80 [exBegin] |
80 [exBegin] |
81 (Base64Coder encode:'queen%27s%20gambit') asString = 'cXVlZW4lMjdzJTIwZ2FtYml0' |
81 (Base64Coder encode:'queen%27s%20gambit') asString = 'cXVlZW4lMjdzJTIwZ2FtYml0' |
82 [exEnd] |
82 [exEnd] |
83 |
83 |
84 [exBegin] |
84 [exBegin] |
85 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString = 'queen%27s%20gambit' |
85 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString = 'queen%27s%20gambit' |
86 [exEnd] |
86 [exEnd] |
87 |
87 |
88 [exBegin] |
88 [exBegin] |
89 |data1 text data2| |
89 |data1 text data2| |
90 |
90 |
91 data1 := #[0 1 16r7F 16r80 16r81 16rFE 16rFF]. |
91 data1 := #[0 1 16r7F 16r80 16r81 16rFE 16rFF]. |
92 text := Base64Coder encode:data1. |
92 text := Base64Coder encode:data1. |
93 data2 := Base64Coder decode:text. |
93 data2 := Base64Coder decode:text. |
94 data2 |
94 data2 |
95 [exEnd] |
95 [exEnd] |
96 |
96 |
97 [exBegin] |
97 [exBegin] |
98 |coder| |
98 |coder| |
99 |
99 |
100 coder := Base64Coder on:'' writeStream. |
100 coder := Base64Coder on:'' writeStream. |
101 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]. |
101 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]. |
102 coder flush. |
102 coder flush. |
103 coder contents inspect. |
103 coder contents inspect. |
104 coder reset. |
104 coder reset. |
105 coder nextPut:254. |
105 coder nextPut:254. |
106 coder contents inspect. |
106 coder contents inspect. |
107 [exEnd] |
107 [exEnd] |
108 |
108 |
109 [exBegin] |
109 [exBegin] |
110 |coder decoder| |
110 |coder decoder| |
111 |
111 |
112 coder := Base64Coder on:'' writeStream. |
112 coder := Base64Coder on:'' writeStream. |
113 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]. |
113 coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]. |
114 coder flush. |
114 coder flush. |
116 decoder := Base64Coder on:(coder contents readStream). |
116 decoder := Base64Coder on:(coder contents readStream). |
117 [decoder atEnd] whileFalse:[ |
117 [decoder atEnd] whileFalse:[ |
118 Transcript show:decoder next |
118 Transcript show:decoder next |
119 ]. |
119 ]. |
120 Transcript cr. |
120 Transcript cr. |
121 [exEnd] |
121 [exEnd] |
122 [exBegin] |
122 [exBegin] |
123 |coder| |
123 |coder| |
124 |
124 |
125 coder := Base64Coder on:'' writeStream. |
125 coder := Base64Coder on:'' writeStream. |
126 coder nextPutAll:(0 to:200) asByteArray. |
126 coder nextPutAll:(0 to:200) asByteArray. |
127 coder flush. |
127 coder flush. |
128 |
128 |
129 Transcript showCR:(coder contents). |
129 Transcript showCR:(coder contents). |
130 [exEnd] |
130 [exEnd] |
131 [exBegin] |
131 [exBegin] |
132 |bytes| |
132 |bytes| |
133 |
133 |
134 bytes := ByteArray new:100000. |
134 bytes := ByteArray new:100000. |
135 Time millisecondsToRun:[ |
135 Time millisecondsToRun:[ |
136 100 timesRepeat:[ |
136 100 timesRepeat:[ |
137 Base64Coder encode:bytes. |
137 Base64Coder encode:bytes. |
138 ]. |
138 ]. |
139 ]. |
139 ]. |
140 [exEnd] |
140 [exEnd] |
141 [exBegin] |
141 [exBegin] |
142 |bytes encoded decoded| |
142 |bytes encoded decoded| |
143 |
143 |
144 bytes := #[0 0 0] copy. |
144 bytes := #[0 0 0] copy. |
145 0 to:255 do:[:b1 | |
145 0 to:255 do:[:b1 | |
146 Transcript showCR:b1. |
146 Transcript showCR:b1. |
147 bytes at:1 put:b1. |
147 bytes at:1 put:b1. |
148 0 to:255 do:[:b2 | |
148 0 to:255 do:[:b2 | |
149 bytes at:2 put:b2. |
149 bytes at:2 put:b2. |
150 0 to:255 do:[:b3 | |
150 0 to:255 do:[:b3 | |
151 bytes at:3 put:b3. |
151 bytes at:3 put:b3. |
152 encoded := Base64Coder encode:bytes. |
152 encoded := Base64Coder encode:bytes. |
153 decoded := Base64Coder decode:encoded. |
153 decoded := Base64Coder decode:encoded. |
154 self assert:(decoded = bytes). |
154 self assert:(decoded = bytes). |
155 ] |
155 ] |
156 ] |
156 ] |
157 ]. |
157 ]. |
158 [exEnd] |
158 [exEnd] |
159 " |
159 " |
160 ! ! |
160 ! ! |
161 |
161 |
162 !Base64Coder class methodsFor:'initialization'! |
162 !Base64Coder class methodsFor:'initialization'! |
163 |
163 |
164 initializeMappings |
164 initializeMappings |
165 "initialize class variables" |
165 "initialize class variables" |
166 |
166 |
167 Base64Mapping isNil ifTrue:[ |
167 Base64Mapping isNil ifTrue:[ |
168 "65 characters representing the 6-bit values from 0-63 and one pad character" |
168 "65 characters representing the 6-bit values from 0-63 and one pad character" |
169 Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='. |
169 Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='. |
170 Base64ReverseMapping := self reverseMappingFor:Base64Mapping. |
170 Base64ReverseMapping := self reverseMappingFor:Base64Mapping. |
171 ]. |
171 ]. |
172 |
172 |
173 " |
173 " |
174 Base64Mapping := nil. |
174 Base64Mapping := nil. |
175 self initializeMappings |
175 self initializeMappings |
212 a specially tuned version is provided here |
212 a specially tuned version is provided here |
213 for the common case of decoding a string. |
213 for the common case of decoding a string. |
214 This returns a string." |
214 This returns a string." |
215 |
215 |
216 encodedString isString ifTrue:[ |
216 encodedString isString ifTrue:[ |
217 ^ self fastDecodeString:encodedString asString:true |
217 ^ self fastDecodeString:encodedString asString:true |
218 ]. |
218 ]. |
219 ^ super decodeAsString:encodedString. |
219 ^ super decodeAsString:encodedString. |
220 |
220 |
221 "Created: / 21-03-2019 / 20:43:47 / Claus Gittinger" |
221 "Created: / 21-03-2019 / 20:43:47 / Claus Gittinger" |
222 "Modified: / 21-03-2019 / 22:10:24 / Claus Gittinger" |
222 "Modified: / 21-03-2019 / 22:10:24 / Claus Gittinger" |
223 ! |
223 ! |
224 |
224 |
225 encode:aStringOrStream |
225 encode:aStringOrStream |
226 "because base64 encoding is used heavily in some protocols, |
226 "because base64 encoding is used heavily in some protocols, |
227 a specially tuned version is provided here |
227 a specially tuned version is provided here |
228 for the common case of encoding a string. |
228 for the common case of encoding a string. |
229 A string is generated with an inserted |
229 A string is generated with an inserted |
230 newline after every 76 characters (see RFC 2045)" |
230 newline after every 76 characters (see RFC 2045)" |
231 |
231 |
232 (aStringOrStream isString or:[aStringOrStream isByteArray]) ifTrue:[ |
232 (aStringOrStream isString or:[aStringOrStream isByteArray]) ifTrue:[ |
233 ^ self fastEncode:aStringOrStream asString:true lineLimit:(self lineLimit) |
233 ^ self fastEncode:aStringOrStream asString:true lineLimit:(self lineLimit) |
234 ]. |
234 ]. |
235 ^ super encode:aStringOrStream. |
235 ^ super encode:aStringOrStream. |
236 |
236 |
237 "Created: / 21-03-2019 / 20:44:35 / Claus Gittinger" |
237 "Created: / 21-03-2019 / 20:44:35 / Claus Gittinger" |
238 "Modified: / 21-03-2019 / 22:33:37 / Claus Gittinger" |
238 "Modified: / 21-03-2019 / 22:33:37 / Claus Gittinger" |
239 ! |
239 ! |
249 " |
249 " |
250 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
250 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
251 |
251 |
252 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') => #[113 117 101 101 110 37 50 55 115 37 50 48 103 97 109 98 105 116] |
252 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') => #[113 117 101 101 110 37 50 55 115 37 50 48 103 97 109 98 105 116] |
253 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
253 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
254 (Base64Coder decodeAsString:'cXVlZW4lMjdzJTIwZ2FtYml0') => 'queen%27s%20gambit' |
254 (Base64Coder decodeAsString:'cXVlZW4lMjdzJTIwZ2FtYml0') => 'queen%27s%20gambit' |
255 |
255 |
256 (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
256 (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
257 " |
257 " |
258 |
258 |
259 "Created: / 30-09-2018 / 14:36:58 / Claus Gittinger" |
259 "Created: / 30-09-2018 / 14:36:58 / Claus Gittinger" |
260 "Modified (comment): / 21-03-2019 / 22:12:07 / Claus Gittinger" |
260 "Modified (comment): / 21-03-2019 / 22:12:07 / Claus Gittinger" |
261 ! |
261 ! |
262 |
262 |
263 fastDecodeString:aString asString:asStringBoolean |
263 fastDecodeString:aString asString:asStringBoolean |
264 "because base64 decoding is used heavily in some protocols, |
264 "because base64 decoding is used heavily in some protocols, |
265 a specially tuned version is provided here |
265 a specially tuned version is provided here |
266 for the common case of decoding a string. |
266 for the common case of decoding a string. |
267 If the argument is true, a string is returned; |
267 If the argument is true, a string is returned; |
268 otherwise, a bytearray" |
268 otherwise, a bytearray" |
269 |
269 |
270 |decoding revMapping| |
270 |decoding revMapping| |
271 |
271 |
272 revMapping := self reverseMapping. |
272 revMapping := self reverseMapping. |
273 revMapping isNil ifTrue:[ |
273 revMapping isNil ifTrue:[ |
274 self initializeMappings. |
274 self initializeMappings. |
275 revMapping := self reverseMapping. |
275 revMapping := self reverseMapping. |
276 ]. |
276 ]. |
277 %{ |
277 %{ |
278 // overallocate by 3 |
278 // overallocate by 3 |
279 # define N_QUICKBUFFER 512 |
279 # define N_QUICKBUFFER 512 |
280 if (__isStringLike(aString) |
280 if (__isStringLike(aString) |
281 && __isByteArray(revMapping)) { |
281 && __isByteArray(revMapping)) { |
282 unsigned char *_revMapping = __stringVal(revMapping); |
282 unsigned char *_revMapping = __stringVal(revMapping); |
283 int numInChars = __stringSize(aString); |
283 int numInChars = __stringSize(aString); |
284 char *in = __stringVal(aString); |
284 char *in = __stringVal(aString); |
285 unsigned char quickBuffer[N_QUICKBUFFER+3]; |
285 unsigned char quickBuffer[N_QUICKBUFFER+3]; |
286 unsigned char *buffer = quickBuffer; |
286 unsigned char *buffer = quickBuffer; |
287 int bufferSize = N_QUICKBUFFER; |
287 int bufferSize = N_QUICKBUFFER; |
288 int outLen = 0; |
288 int outLen = 0; |
289 int charBuffer = 0; |
289 int charBuffer = 0; |
290 int nBitsOut = 0; |
290 int nBitsOut = 0; |
291 int i; |
291 int i; |
292 |
292 |
293 for (i=0; i<numInChars; i++) { |
293 for (i=0; i<numInChars; i++) { |
294 char ch = in[i]; |
294 char ch = in[i]; |
295 int bits = -1; |
295 int bits = -1; |
296 |
296 |
297 if (ch <= 127) { |
297 if (ch <= 127) { |
298 bits = _revMapping[(ch-1) & 0x7F]; |
298 bits = _revMapping[(ch-1) & 0x7F]; |
299 } |
299 } |
300 |
300 |
301 if ((unsigned)bits <= 0x3F) { |
301 if ((unsigned)bits <= 0x3F) { |
302 charBuffer = (charBuffer << 6) | bits; |
302 charBuffer = (charBuffer << 6) | bits; |
303 nBitsOut += 6; |
303 nBitsOut += 6; |
304 if (nBitsOut == 24) { |
304 if (nBitsOut == 24) { |
305 if ((outLen + 3) > bufferSize) { |
305 if ((outLen + 3) > bufferSize) { |
306 if (buffer == quickBuffer) { |
306 if (buffer == quickBuffer) { |
307 // overallocate by 3 |
307 // overallocate by 3 |
308 buffer = (unsigned char *)malloc(bufferSize*2+3); |
308 buffer = (unsigned char *)malloc(bufferSize*2+3); |
309 memcpy(buffer, quickBuffer, bufferSize); |
309 memcpy(buffer, quickBuffer, bufferSize); |
310 } else { |
310 } else { |
311 buffer = (unsigned char *)realloc(buffer, bufferSize*2+3); |
311 buffer = (unsigned char *)realloc(buffer, bufferSize*2+3); |
312 } |
312 } |
313 bufferSize = bufferSize * 2; |
313 bufferSize = bufferSize * 2; |
314 } |
314 } |
315 buffer[outLen] = (charBuffer >> 16) & 0xFF; |
315 buffer[outLen] = (charBuffer >> 16) & 0xFF; |
316 buffer[outLen+1] = (charBuffer >> 8) & 0xFF; |
316 buffer[outLen+1] = (charBuffer >> 8) & 0xFF; |
317 buffer[outLen+2] = (charBuffer) & 0xFF; |
317 buffer[outLen+2] = (charBuffer) & 0xFF; |
318 outLen += 3; |
318 outLen += 3; |
319 charBuffer = nBitsOut = 0; |
319 charBuffer = nBitsOut = 0; |
320 } |
320 } |
321 } else { |
321 } else { |
322 if ((unsigned)bits == 0x40) { |
322 if ((unsigned)bits == 0x40) { |
323 // end mark |
323 // end mark |
324 // because of overallocation, there is no need to check for buffer-full condition here |
324 // because of overallocation, there is no need to check for buffer-full condition here |
325 if (nBitsOut == 12) { |
325 if (nBitsOut == 12) { |
326 // data has been padded to 12, skip 4 bits |
326 // data has been padded to 12, skip 4 bits |
327 // one more byte coming |
327 // one more byte coming |
328 charBuffer >>= 4; |
328 charBuffer >>= 4; |
329 nBitsOut -= 4; |
329 nBitsOut -= 4; |
330 buffer[outLen] = (charBuffer) & 0xFF; |
330 buffer[outLen] = (charBuffer) & 0xFF; |
331 outLen += 1; |
331 outLen += 1; |
332 } else if (nBitsOut == 18) { |
332 } else if (nBitsOut == 18) { |
333 // data has been padded to 18, skip 2 bits |
333 // data has been padded to 18, skip 2 bits |
334 charBuffer >>= 2; |
334 charBuffer >>= 2; |
335 nBitsOut -= 2; |
335 nBitsOut -= 2; |
336 buffer[outLen] = (charBuffer >> 8) & 0xFF; |
336 buffer[outLen] = (charBuffer >> 8) & 0xFF; |
337 buffer[outLen+1] = (charBuffer) & 0xFF; |
337 buffer[outLen+1] = (charBuffer) & 0xFF; |
338 outLen += 2; |
338 outLen += 2; |
339 } |
339 } |
340 } else { |
340 } else { |
341 // ignore |
341 // ignore |
342 } |
342 } |
343 } |
343 } |
344 } |
344 } |
345 |
345 |
346 if (asStringBoolean == true) { |
346 if (asStringBoolean == true) { |
347 decoding = __MKSTRING_L(buffer, outLen); |
347 decoding = __MKSTRING_L(buffer, outLen); |
348 } else { |
348 } else { |
349 decoding = __MKBYTEARRAY(buffer, outLen); |
349 decoding = __MKBYTEARRAY(buffer, outLen); |
350 } |
350 } |
351 if (buffer != quickBuffer) { |
351 if (buffer != quickBuffer) { |
352 free(buffer); |
352 free(buffer); |
353 } |
353 } |
354 RETURN(decoding); |
354 RETURN(decoding); |
355 } |
355 } |
356 %}. |
356 %}. |
357 decoding := super decode:aString. |
357 decoding := super decode:aString. |
358 asStringBoolean ifTrue:[ |
358 asStringBoolean ifTrue:[ |
359 ^ decoding asString |
359 ^ decoding asString |
360 ]. |
360 ]. |
361 ^ decoding |
361 ^ decoding |
362 |
362 |
363 " |
363 " |
364 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
364 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
365 |
365 |
366 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
366 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
367 (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
367 (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
423 "Modified (comment): / 21-03-2019 / 22:14:17 / Claus Gittinger" |
423 "Modified (comment): / 21-03-2019 / 22:14:17 / Claus Gittinger" |
424 ! |
424 ! |
425 |
425 |
426 fastEncode:aStringOrByteArray asString:asStringBoolean lineLimit:lineLimitOrNil |
426 fastEncode:aStringOrByteArray asString:asStringBoolean lineLimit:lineLimitOrNil |
427 "because base64 encoding is used heavily in some protocols, |
427 "because base64 encoding is used heavily in some protocols, |
428 a specially tuned version is provided here |
428 a specially tuned version is provided here |
429 for the common case of encoding a string. |
429 for the common case of encoding a string. |
430 If asStringBoolean is true, a string is generated; otherwise, a bytearray is returned. |
430 If asStringBoolean is true, a string is generated; otherwise, a bytearray is returned. |
431 If lineLimitOrNil is non-nil, a newline is inserted after every such number of characters" |
431 If lineLimitOrNil is non-nil, a newline is inserted after every such number of characters" |
432 |
432 |
433 |encoding mapping| |
433 |encoding mapping| |
434 |
434 |
435 mapping := self mapping. |
435 mapping := self mapping. |
436 mapping isNil ifTrue:[ |
436 mapping isNil ifTrue:[ |
437 self initializeMappings. |
437 self initializeMappings. |
438 mapping := self mapping. |
438 mapping := self mapping. |
439 ]. |
439 ]. |
440 %{ |
440 %{ |
441 // overallocate by 5 |
441 // overallocate by 5 |
442 # define N_QUICKBUFFER 512 |
442 # define N_QUICKBUFFER 512 |
443 int argIsString = __isStringLike(aStringOrByteArray); |
443 int argIsString = __isStringLike(aStringOrByteArray); |
444 |
444 |
445 if ((argIsString || __isByteArray(aStringOrByteArray)) |
445 if ((argIsString || __isByteArray(aStringOrByteArray)) |
446 && __isStringLike(mapping)) { |
446 && __isStringLike(mapping)) { |
447 unsigned char *__mapping = __stringVal(mapping);; |
447 unsigned char *__mapping = __stringVal(mapping); |
448 int numInChars; |
448 int numInChars; |
449 unsigned char *in; |
449 unsigned char *in; |
450 unsigned char quickBuffer[N_QUICKBUFFER+5]; |
450 unsigned char quickBuffer[N_QUICKBUFFER+5]; |
451 unsigned char *buffer = quickBuffer; |
451 unsigned char *buffer = quickBuffer; |
452 int bufferSize = N_QUICKBUFFER; |
452 int bufferSize = N_QUICKBUFFER; |
453 int outLen = 0; |
453 int outLen = 0; |
454 int nBitsOut = 0; |
454 int nBitsOut = 0; |
455 int i; |
455 int i; |
456 int numInCharsMinus3; |
456 int numInCharsMinus3; |
457 unsigned int lineLimit = ~0; |
457 unsigned int lineLimit = ~0; |
458 int lineLength = 0; |
458 int lineLength = 0; |
459 int restLength = 0; |
459 int restLength = 0; |
460 |
460 |
461 if (__isSmallInteger(lineLimitOrNil)) { |
461 if (__isSmallInteger(lineLimitOrNil)) { |
462 lineLimit = __intVal(lineLimitOrNil); |
462 lineLimit = __intVal(lineLimitOrNil); |
463 } |
463 } |
464 |
464 |
465 if (argIsString) { |
465 if (argIsString) { |
466 numInChars = __stringSize(aStringOrByteArray); |
466 numInChars = __stringSize(aStringOrByteArray); |
467 in = __stringVal(aStringOrByteArray); |
467 in = __stringVal(aStringOrByteArray); |
468 } else { |
468 } else { |
469 numInChars = __byteArraySize(aStringOrByteArray); |
469 numInChars = __byteArraySize(aStringOrByteArray); |
470 in = __byteArrayVal(aStringOrByteArray); |
470 in = __byteArrayVal(aStringOrByteArray); |
471 } |
471 } |
472 // fprintf(stderr, "%d\n", numInChars); |
472 // fprintf(stderr, "%d\n", numInChars); |
473 |
473 |
474 lineLength = 0; |
474 lineLength = 0; |
475 numInCharsMinus3 = numInChars-3; |
475 numInCharsMinus3 = numInChars-3; |
476 |
476 |
477 for (i=0; i<=numInCharsMinus3; i+=3) { |
477 for (i=0; i<=numInCharsMinus3; i+=3) { |
478 int charBuffer; |
478 int charBuffer; |
479 |
479 |
480 if (lineLength >= lineLimit) { |
480 if (lineLength >= lineLimit) { |
481 buffer[outLen++] = '\n'; |
481 buffer[outLen++] = '\n'; |
482 lineLength = 0; |
482 lineLength = 0; |
483 } |
483 } |
484 |
484 |
485 charBuffer = (in[i]) << 16; |
485 charBuffer = (in[i]) << 16; |
486 charBuffer |= ((in[i+1]) << 8); |
486 charBuffer |= ((in[i+1]) << 8); |
487 charBuffer |= (in[i+2]); |
487 charBuffer |= (in[i+2]); |
488 |
488 |
489 if ((outLen + 5) > bufferSize) { |
489 if ((outLen + 5) > bufferSize) { |
490 if (buffer == quickBuffer) { |
490 if (buffer == quickBuffer) { |
491 // overallocate by 5 |
491 // overallocate by 5 |
492 buffer = (unsigned char *)malloc(bufferSize*2+5); |
492 buffer = (unsigned char *)malloc(bufferSize*2+5); |
493 memcpy(buffer, quickBuffer, bufferSize); |
493 memcpy(buffer, quickBuffer, bufferSize); |
494 } else { |
494 } else { |
495 buffer = (unsigned char *)realloc(buffer, bufferSize*2+5); |
495 buffer = (unsigned char *)realloc(buffer, bufferSize*2+5); |
496 } |
496 } |
497 bufferSize = bufferSize * 2; |
497 bufferSize = bufferSize * 2; |
498 } |
498 } |
499 #ifdef __LSBFIRST__ |
499 #ifdef __LSBFIRST__ |
500 { |
500 { |
501 unsigned int out = __mapping[(charBuffer >> 18) & 0x3F]; |
501 unsigned int out = __mapping[(charBuffer >> 18) & 0x3F]; |
502 out |= (__mapping[(charBuffer >> 12) & 0x3F]) << 8; |
502 out |= (__mapping[(charBuffer >> 12) & 0x3F]) << 8; |
503 out |= (__mapping[(charBuffer >> 6) & 0x3F]) << 16; |
503 out |= (__mapping[(charBuffer >> 6) & 0x3F]) << 16; |
504 out |= (__mapping[(charBuffer) & 0x3F]) << 24; |
504 out |= (__mapping[(charBuffer) & 0x3F]) << 24; |
505 ((unsigned int*)(&buffer[outLen]))[0] = out; |
505 ((unsigned int*)(&buffer[outLen]))[0] = out; |
506 } |
506 } |
507 #else |
507 #else |
508 buffer[outLen] = __mapping[(charBuffer >> 18) & 0x3F]; |
508 buffer[outLen] = __mapping[(charBuffer >> 18) & 0x3F]; |
509 buffer[outLen+1] = __mapping[(charBuffer >> 12) & 0x3F]; |
509 buffer[outLen+1] = __mapping[(charBuffer >> 12) & 0x3F]; |
510 buffer[outLen+2] = __mapping[(charBuffer >> 6) & 0x3F]; |
510 buffer[outLen+2] = __mapping[(charBuffer >> 6) & 0x3F]; |
511 buffer[outLen+3] = __mapping[(charBuffer) & 0x3F]; |
511 buffer[outLen+3] = __mapping[(charBuffer) & 0x3F]; |
512 #endif |
512 #endif |
513 outLen += 4; |
513 outLen += 4; |
514 lineLength += 4; |
514 lineLength += 4; |
515 } |
515 } |
516 |
516 |
517 restLength = numInChars-i; |
517 restLength = numInChars-i; |
518 // fprintf(stderr, "rest: %d\n", restLength); |
518 // fprintf(stderr, "rest: %d\n", restLength); |
519 if (restLength) { |
519 if (restLength) { |
520 if (lineLength >= lineLimit) { |
520 if (lineLength >= lineLimit) { |
521 buffer[outLen++] = '\n'; |
521 buffer[outLen++] = '\n'; |
522 lineLength = 0; |
522 lineLength = 0; |
523 } |
523 } |
524 if (restLength == 1) { |
524 if (restLength == 1) { |
525 unsigned int charBuffer; |
525 unsigned int charBuffer; |
526 |
526 |
527 // pad with '==' |
527 // pad with '==' |
528 charBuffer = (in[i]) << 4; |
528 charBuffer = (in[i]) << 4; |
529 buffer[outLen] = __mapping[(charBuffer >> 6) & 0x3F]; |
529 buffer[outLen] = __mapping[(charBuffer >> 6) & 0x3F]; |
530 buffer[outLen+1] = __mapping[(charBuffer) & 0x3F]; |
530 buffer[outLen+1] = __mapping[(charBuffer) & 0x3F]; |
531 buffer[outLen+2] = __mapping[64]; |
531 buffer[outLen+2] = __mapping[64]; |
532 buffer[outLen+3] = __mapping[64]; |
532 buffer[outLen+3] = __mapping[64]; |
533 outLen += 4; |
533 outLen += 4; |
534 } else { |
534 } else { |
535 unsigned int charBuffer; |
535 unsigned int charBuffer; |
536 |
536 |
537 // restLength == 2 |
537 // restLength == 2 |
538 // pad with '=' |
538 // pad with '=' |
539 charBuffer = (in[i]) << 8; |
539 charBuffer = (in[i]) << 8; |
540 charBuffer |= (in[i+1]); |
540 charBuffer |= (in[i+1]); |
541 charBuffer <<= 2; |
541 charBuffer <<= 2; |
542 buffer[outLen] = __mapping[(charBuffer >> 12) & 0x3F]; |
542 buffer[outLen] = __mapping[(charBuffer >> 12) & 0x3F]; |
543 buffer[outLen+1] = __mapping[(charBuffer >> 6) & 0x3F]; |
543 buffer[outLen+1] = __mapping[(charBuffer >> 6) & 0x3F]; |
544 buffer[outLen+2] = __mapping[(charBuffer) & 0x3F]; |
544 buffer[outLen+2] = __mapping[(charBuffer) & 0x3F]; |
545 buffer[outLen+3] = __mapping[64]; |
545 buffer[outLen+3] = __mapping[64]; |
546 outLen += 4; |
546 outLen += 4; |
547 } |
547 } |
548 } |
548 } |
549 |
549 |
550 if (asStringBoolean == true) { |
550 if (asStringBoolean == true) { |
551 encoding = __MKSTRING_L(buffer, outLen); |
551 encoding = __MKSTRING_L(buffer, outLen); |
552 } else { |
552 } else { |
553 encoding = __MKBYTEARRAY(buffer, outLen); |
553 encoding = __MKBYTEARRAY(buffer, outLen); |
554 } |
554 } |
555 if (buffer != quickBuffer) { |
555 if (buffer != quickBuffer) { |
556 free(buffer); |
556 free(buffer); |
557 } |
557 } |
558 RETURN(encoding); |
558 RETURN(encoding); |
559 } |
559 } |
560 %}. |
560 %}. |
561 encoding := super encode:aStringOrByteArray with:lineLimitOrNil. |
561 encoding := super encode:aStringOrByteArray with:lineLimitOrNil. |
562 asStringBoolean ifTrue:[ |
562 asStringBoolean ifTrue:[ |
563 ^ encoding asString |
563 ^ encoding asString |
564 ]. |
564 ]. |
565 ^ encoding |
565 ^ encoding |
566 |
566 |
567 " |
567 " |
568 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
568 (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
569 (Base64Coder fastEncode:'queen%27s%20gambit' asString:true) => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
569 (Base64Coder fastEncode:'queen%27s%20gambit' asString:true) => 'cXVlZW4lMjdzJTIwZ2FtYml0' |
570 |
570 |
571 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
571 (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit' |
597 Base64Coder fastEncode:data asString:true lineLimit:nil. |
597 Base64Coder fastEncode:data asString:true lineLimit:nil. |
598 |
598 |
599 |data| |
599 |data| |
600 data := ByteArray new:1000. |
600 data := ByteArray new:1000. |
601 Time millisecondsToRun:[ |
601 Time millisecondsToRun:[ |
602 10000 timesRepeat:[ |
602 10000 timesRepeat:[ |
603 Base64Coder fastEncode:data. |
603 Base64Coder fastEncode:data. |
604 ] |
604 ] |
605 ] |
605 ] |
606 |
606 |
607 self assert:((Base64Coder fastEncode:'abc' asString:true) |
607 self assert:((Base64Coder fastEncode:'abc' asString:true) |
608 = 'abc' base64Encoded). |
608 = 'abc' base64Encoded). |
609 self assert:((Base64Coder fastEncode:'a' asString:true) |
609 self assert:((Base64Coder fastEncode:'a' asString:true) |
610 = 'a' base64Encoded). |
610 = 'a' base64Encoded). |
611 self assert:((Base64Coder fastEncode:'ab' asString:true) |
611 self assert:((Base64Coder fastEncode:'ab' asString:true) |
612 = 'ab' base64Encoded). |
612 = 'ab' base64Encoded). |
613 self assert:((Base64Coder fastEncode:'abcd' asString:true) |
613 self assert:((Base64Coder fastEncode:'abcd' asString:true) |
614 = 'abcd' base64Encoded). |
614 = 'abcd' base64Encoded). |
615 self assert:((Base64Coder fastEncode:'abcde' asString:true) |
615 self assert:((Base64Coder fastEncode:'abcde' asString:true) |
616 = 'abcde' base64Encoded). |
616 = 'abcde' base64Encoded). |
617 self assert:((Base64Coder fastEncode:'abcdef' asString:true) |
617 self assert:((Base64Coder fastEncode:'abcdef' asString:true) |
618 = 'abcdef' base64Encoded). |
618 = 'abcdef' base64Encoded). |
619 |
619 |
620 self assert:((Base64Coder fastEncode:#'parseMethod:onError:rememberNodes:nodeGenerationCallback:' asString:true) |
620 self assert:((Base64Coder fastEncode:#'parseMethod:onError:rememberNodes:nodeGenerationCallback:' asString:true) |
621 = #'parseMethod:onError:rememberNodes:nodeGenerationCallback:' base64Encoded). |
621 = #'parseMethod:onError:rememberNodes:nodeGenerationCallback:' base64Encoded). |
622 |
622 |
623 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true) |
623 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true) |
624 = '_INVOKESTATIC_R:' base64Encoded). |
624 = '_INVOKESTATIC_R:' base64Encoded). |
625 |
625 |
626 self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true) |
626 self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true) |
627 = #'_INVOKESTATIC_R:' base64Encoded) |
627 = #'_INVOKESTATIC_R:' base64Encoded) |
628 |
628 |
629 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true) |
629 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true) |
630 = (Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)). |
630 = (Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)). |
631 |
631 |
632 self assert:((#'_INVOKESTATIC_R:' base64Encoded) |
632 self assert:((#'_INVOKESTATIC_R:' base64Encoded) |
633 = ('_INVOKESTATIC_R:' base64Encoded)). |
633 = ('_INVOKESTATIC_R:' base64Encoded)). |
634 |
634 |
635 |
635 |
636 |
636 |
637 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true) |
637 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true) |
638 = '_INVOKESTATIC_R:_:' base64Encoded). |
638 = '_INVOKESTATIC_R:_:' base64Encoded). |
639 |
639 |
640 self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true) |
640 self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true) |
641 = #'_INVOKESTATIC_R:_:' base64Encoded) |
641 = #'_INVOKESTATIC_R:_:' base64Encoded) |
642 |
642 |
643 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true) |
643 self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true) |
644 = (Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)). |
644 = (Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)). |
645 |
645 |
646 self assert:((#'_INVOKESTATIC_R:_:' base64Encoded) |
646 self assert:((#'_INVOKESTATIC_R:_:' base64Encoded) |
647 = ('_INVOKESTATIC_R:_:' base64Encoded)). |
647 = ('_INVOKESTATIC_R:_:' base64Encoded)). |
648 |
648 |
649 Symbol allInstancesDo:[:each | |
649 Symbol allInstancesDo:[:each | |
650 self assert:((Base64Coder fastEncode:each asString:true) |
650 self assert:((Base64Coder fastEncode:each asString:true) |
651 = (Base64Coder encode:each with:nil)) |
651 = (Base64Coder encode:each with:nil)) |
652 ] |
652 ] |
653 " |
653 " |
654 |
654 |
655 "Created: / 21-03-2019 / 21:58:59 / Claus Gittinger" |
655 "Created: / 21-03-2019 / 21:58:59 / Claus Gittinger" |
656 "Modified (comment): / 21-03-2019 / 23:11:50 / Claus Gittinger" |
656 "Modified (comment): / 21-03-2019 / 23:11:50 / Claus Gittinger" |
657 ! ! |
657 ! ! |
660 |
660 |
661 nextPutByte:aByte |
661 nextPutByte:aByte |
662 "encode aByte on the output stream" |
662 "encode aByte on the output stream" |
663 |
663 |
664 |b1 "{ Class: SmallInteger }" |
664 |b1 "{ Class: SmallInteger }" |
665 b2 "{ Class: SmallInteger }" |
665 b2 "{ Class: SmallInteger }" |
666 b3 "{ Class: SmallInteger }" |
666 b3 "{ Class: SmallInteger }" |
667 b4 "{ Class: SmallInteger }" |
667 b4 "{ Class: SmallInteger }" |
668 bufferedBytes "{ Class: SmallInteger }" | |
668 bufferedBytes "{ Class: SmallInteger }" | |
669 |
669 |
670 buffer := (buffer bitShift:8) bitOr:aByte. |
670 buffer := (buffer bitShift:8) bitOr:aByte. |
671 bits := bits + 8. |
671 bits := bits + 8. |
672 bits == 24 ifTrue:[ |
672 bits == 24 ifTrue:[ |
673 "RFC 2045 says: max 76 characters in one line" |
673 "RFC 2045 says: max 76 characters in one line" |
674 (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[ |
674 (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[ |
675 stream cr. |
675 stream cr. |
676 charCount := 0. |
676 charCount := 0. |
677 ]. |
677 ]. |
678 bufferedBytes := buffer. |
678 bufferedBytes := buffer. |
679 |
679 |
680 b4 := bufferedBytes bitAnd:16r3F. |
680 b4 := bufferedBytes bitAnd:16r3F. |
681 b3 := (bufferedBytes bitShift:-6) bitAnd:16r3F. |
681 b3 := (bufferedBytes bitShift:-6) bitAnd:16r3F. |
682 b2 := (bufferedBytes bitShift:-12) bitAnd:16r3F. |
682 b2 := (bufferedBytes bitShift:-12) bitAnd:16r3F. |
683 b1 := (bufferedBytes bitShift:-18) bitAnd:16r3F. |
683 b1 := (bufferedBytes bitShift:-18) bitAnd:16r3F. |
684 buffer := bits := 0. |
684 buffer := bits := 0. |
685 |
685 |
686 stream nextPut:(mapping at:b1+1); |
686 stream nextPut:(mapping at:b1+1); |
687 nextPut:(mapping at:b2+1); |
687 nextPut:(mapping at:b2+1); |
688 nextPut:(mapping at:b3+1); |
688 nextPut:(mapping at:b3+1); |
689 nextPut:(mapping at:b4+1). |
689 nextPut:(mapping at:b4+1). |
690 |
690 |
691 charCount := charCount + 4. |
691 charCount := charCount + 4. |
692 ]. |
692 ]. |
693 |
693 |
694 "Modified: / 26-08-2017 / 12:35:17 / cg" |
694 "Modified: / 26-08-2017 / 12:35:17 / cg" |
695 "Modified: / 30-09-2018 / 15:15:14 / Claus Gittinger" |
695 "Modified: / 30-09-2018 / 15:15:14 / Claus Gittinger" |
696 ! ! |
696 ! ! |
697 |
697 |
698 !Base64Coder methodsFor:'misc'! |
698 !Base64Coder methodsFor:'misc'! |
699 |
699 |
700 flush |
700 flush |
701 "flush the remaining bits of buffer. |
701 "flush the remaining bits of buffer. |
702 The number of bits in buffer is not a multiple of 6, so we pad |
702 The number of bits in buffer is not a multiple of 6, so we pad |
703 the buffer and signal that padding has been done via $= characters." |
703 the buffer and signal that padding has been done via $= characters." |
704 |
704 |
705 |tempBuffer "{Class: SmallInteger}" |
705 |tempBuffer "{Class: SmallInteger}" |
706 b1 b2 b3 b4| |
706 b1 b2 b3 b4| |
707 |
707 |
708 bits == 0 ifTrue:[ |
708 bits == 0 ifTrue:[ |
709 "buffer is empty, nothing to do" |
709 "buffer is empty, nothing to do" |
710 ^ self. |
710 ^ self. |
711 ]. |
711 ]. |
712 |
712 |
713 tempBuffer := buffer. |
713 tempBuffer := buffer. |
714 bits == 8 ifTrue:[ |
714 bits == 8 ifTrue:[ |
715 tempBuffer := tempBuffer bitShift:4. |
715 tempBuffer := tempBuffer bitShift:4. |
716 b4 := b3 := 64. "pad with '=='" |
716 b4 := b3 := 64. "pad with '=='" |
717 b1 := (tempBuffer bitShift:-6) bitAnd:16r3F. |
717 b1 := (tempBuffer bitShift:-6) bitAnd:16r3F. |
718 b2 := tempBuffer bitAnd:16r3F. |
718 b2 := tempBuffer bitAnd:16r3F. |
719 ] ifFalse:[ |
719 ] ifFalse:[ |
720 bits = 16 ifTrue:[ |
720 bits = 16 ifTrue:[ |
721 tempBuffer := tempBuffer bitShift:2. |
721 tempBuffer := tempBuffer bitShift:2. |
722 b4 := 64. "pad with '='" |
722 b4 := 64. "pad with '='" |
723 b3 := tempBuffer bitAnd:16r3F. |
723 b3 := tempBuffer bitAnd:16r3F. |
724 b2 := (tempBuffer bitShift:-6) bitAnd:16r3F. |
724 b2 := (tempBuffer bitShift:-6) bitAnd:16r3F. |
725 b1 := (tempBuffer bitShift:-12) bitAnd:16r3F. |
725 b1 := (tempBuffer bitShift:-12) bitAnd:16r3F. |
726 ] |
726 ] |
727 ]. |
727 ]. |
728 bits := buffer := 0. |
728 bits := buffer := 0. |
729 |
729 |
730 "RFC 2045 says: max 76 characters in one line" |
730 "RFC 2045 says: max 76 characters in one line" |
731 (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[ |
731 (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[ |
732 stream cr. |
732 stream cr. |
733 charCount := 0. |
733 charCount := 0. |
734 ]. |
734 ]. |
735 |
735 |
736 stream nextPut:(mapping at:b1+1); |
736 stream nextPut:(mapping at:b1+1); |
737 nextPut:(mapping at:b2+1); |
737 nextPut:(mapping at:b2+1); |
738 nextPut:(mapping at:b3+1); |
738 nextPut:(mapping at:b3+1); |
739 nextPut:(mapping at:b4+1). |
739 nextPut:(mapping at:b4+1). |
740 charCount := charCount + 4. |
740 charCount := charCount + 4. |
741 |
741 |
742 "Modified: / 20-03-2019 / 21:22:39 / Claus Gittinger" |
742 "Modified: / 20-03-2019 / 21:22:39 / Claus Gittinger" |
743 ! ! |
743 ! ! |
744 |
744 |
746 |
746 |
747 fillBuffer |
747 fillBuffer |
748 "fill buffer with next 4 characters each representing 6 bits. |
748 "fill buffer with next 4 characters each representing 6 bits. |
749 Used when decoding." |
749 Used when decoding." |
750 |
750 |
751 |b |
751 |b |
752 tempBuffer "{Class: SmallInteger}" |
752 tempBuffer "{Class: SmallInteger}" |
753 _bits "{Class: SmallInteger}" | |
753 _bits "{Class: SmallInteger}" | |
754 |
754 |
755 tempBuffer := 0. |
755 tempBuffer := 0. |
756 _bits := 0. |
756 _bits := 0. |
757 |
757 |
758 [ |
758 [ |
759 "read next valid Base64 character, skip invalid characters" |
759 "read next valid Base64 character, skip invalid characters" |
760 b := 255. |
760 b := 255. |
761 [b == 255] whileTrue:[ |
761 [b == 255] whileTrue:[ |
762 b := stream next. |
762 b := stream next. |
763 b isNil ifTrue:[ "end of stream" |
763 b isNil ifTrue:[ "end of stream" |
764 b := 64. "simulate end-mark" |
764 b := 64. "simulate end-mark" |
765 ] ifFalse:[ |
765 ] ifFalse:[ |
766 b := reverseMapping at:b codePoint ifAbsent:255. |
766 b := reverseMapping at:b codePoint ifAbsent:255. |
767 ] |
767 ] |
768 ]. |
768 ]. |
769 |
769 |
770 b == 64 ifTrue:[ |
770 b == 64 ifTrue:[ |
771 "got $=, end of Base64 string has been reached" |
771 "got $=, end of Base64 string has been reached" |
772 atEnd := true. |
772 atEnd := true. |
773 _bits == 12 ifTrue:[ |
773 _bits == 12 ifTrue:[ |
774 "data has been padded to 12, skip 4 bits" |
774 "data has been padded to 12, skip 4 bits" |
775 tempBuffer := tempBuffer bitShift:-4. |
775 tempBuffer := tempBuffer bitShift:-4. |
776 _bits := _bits - 4. |
776 _bits := _bits - 4. |
777 ] ifFalse:[_bits == 18 ifTrue:[ |
777 ] ifFalse:[_bits == 18 ifTrue:[ |
778 "data has been padded to 18, skip 2 bits" |
778 "data has been padded to 18, skip 2 bits" |
779 tempBuffer := tempBuffer bitShift:-2. |
779 tempBuffer := tempBuffer bitShift:-2. |
780 _bits := _bits - 2. |
780 _bits := _bits - 2. |
781 ]]. |
781 ]]. |
782 ] ifFalse:[ |
782 ] ifFalse:[ |
783 "got valid Base64 character, append to buffer" |
783 "got valid Base64 character, append to buffer" |
784 tempBuffer := (tempBuffer bitShift:6) bitOr:b. |
784 tempBuffer := (tempBuffer bitShift:6) bitOr:b. |
785 _bits := _bits + 6. |
785 _bits := _bits + 6. |
786 ]. |
786 ]. |
787 (_bits == 24 or:[atEnd]) ifTrue:[ |
787 (_bits == 24 or:[atEnd]) ifTrue:[ |
788 bits := _bits. |
788 bits := _bits. |
789 buffer := tempBuffer. |
789 buffer := tempBuffer. |
790 ^ self. |
790 ^ self. |
791 ]. |
791 ]. |
792 ] loop. |
792 ] loop. |
793 |
793 |
794 "Modified: / 30-09-2018 / 15:16:19 / Claus Gittinger" |
794 "Modified: / 30-09-2018 / 15:16:19 / Claus Gittinger" |
795 ! ! |
795 ! ! |
796 |
796 |