Author: Daniel Wischnewski
This article gives you two routines allowing for fast encoding and decoding in and
out of the MIME format.
Answer:
I have written the following unit to replace the INDY TIdEncoderMIME and
TIdDecoderMIME components. This "Codec" is used in Email-Software, primarely.
First reason was the lack of speed of these components. Second reason was, that
they are components and therefore require the VCL - an heavy extra load on
otherwise nonVCL systems.
Both routines are written in Assembler and overloaded by different versions
allowing for easy access.
I am sure, some of your are able to increase the speed even some more. Please let
me know. Thanks.
I have introduced a compiler switch to switch between a fast decoding mode and a
more secure decoding mode. It is up to you, which you like to use. I recomend using
the more secure mode, especially if the user is required to work with the encoded
data.
1
2 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3 *
4 * Unit Name : uBase64Codec
5 * Author : Daniel Wischnewski
6 * Copyright : Copyright © 2001-2003 by gate(n)etwork GmbH. All Rights Reserved.
7 * Creator : Daniel Wischnewski
8 * Contact : Daniel Wischnewski (e-mail: delphi3000(at)wischnewski.tv);
9 *
10 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
11
12 // * * * License * * *
13 //
14 // The contents of this file are used with permission, subject to the Mozilla
15 // Public License Version 1.1 (the "License"); you may not use this file except
16 // in compliance with the License. You may obtain a copy of the License at
17 //
18 // http://www.mozilla.org/MPL/MPL-1.1.html
19 //
20 // Software distributed under the License is distributed on an "AS IS" basis,
21 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
22 // the specific language governing rights and limitations under the License.
23 //
24
25 // * * * My Wish * * *
26 //
27 // If you come to use this unit for your work, I would like to know about it.
28 // Drop me an e-mail and let me know how it worked out for you. If you wish, you
29 // can send me a copy of your work. No obligations!
30 // My e-mail address: delphi3000(at)wischnewski.tv
31 //
32
33 // * * * History * * *
34 //
35 // Version 1.0 (Oct-10 2002)
36 // first published on Delphi-PRAXiS (www.delphipraxis.net)
37 //
38 // Version 1.1 (May-13 2003)
39 // introduced a compiler switch (SpeedDecode) to switch between a faster
40 // decoding variant (prior version) and a litte less fast, but secure variant
41 // to work around bad formatted data (decoding only!)
42 //
43
44 unit Base64;
45
46 interface
47
48 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49 // !! THE COMPILER SWITCH MAY BE USED TO ADJUST THE BEHAVIOR !!
50 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
51
52 // enable "SpeedDecode"
53 // the switch to gain speed while decoding the message, however, the codec
54 // will raise different exceptions/access violations or invalid output if
55 // the incoming data is invalid or missized.
56
57 // disable "SpeedDecode"
58 // the switch to enable a data check, that will scan the data to decode to
59 // be valid. This method is to be used if you cannot guarantee to validity
60 // of the data to be decoded.
61
62 {.DEFINE SpeedDecode}
63
64 {$IFNDEF SpeedDecode}
65 {$DEFINE ValidityCheck}
66 {$ENDIF}
67
68 uses SysUtils;
69
70 // codiert einen String in die zugehörige Base64-Darstellung
71 function Base64Encode(const InText: AnsiString): AnsiString; overload;
72 // decodiert die Base64-Darstellung eines Strings in den zugehörigen String
73 function Base64Decode(const InText: AnsiString): AnsiString; overload;
74
75 // bestimmt die Größe der Base64-Darstellung
76 function CalcEncodedSize(InSize: Cardinal): Cardinal;
77 // bestimmt die Größe der binären Darstellung
78 function CalcDecodedSize(const InBuffer; InSize: Cardinal): Cardinal;
79
80 // codiert einen Buffer in die zugehörige Base64-Darstellung
81 procedure Base64Encode(const InBuffer; InSize: Cardinal; var OutBuffer); overload;
82 register;
83 // decodiert die Base64-Darstellung in einen Buffer
84 {$IFDEF SpeedDecode}
85 procedure Base64Decode(const InBuffer; InSize: Cardinal; var OutBuffer); overload;
86 register;
87 {$ENDIF}
88 {$IFDEF ValidityCheck}
89 function Base64Decode(const InBuffer; InSize: Cardinal; var OutBuffer): Boolean;
90 overload; register;
91 {$ENDIF}
92
93 // codiert einen String in die zugehörige Base64-Darstellung
94 procedure Base64Encode(const InText: PAnsiChar; var OutText: PAnsiChar); overload;
95 // decodiert die Base64-Darstellung eines Strings in den zugehörigen String
96 procedure Base64Decode(const InText: PAnsiChar; var OutText: PAnsiChar); overload;
97
98 // codiert einen String in die zugehörige Base64-Darstellung
99 procedure Base64Encode(const InText: AnsiString; var OutText: AnsiString); overload;
100 // decodiert die Base64-Darstellung eines Strings in den zugehörigen String
101 procedure Base64Decode(const InText: AnsiString; var OutText: AnsiString); overload;
102
103 implementation
104
105 const
106 cBase64Codec: array[0..63] of AnsiChar =
107 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
108 Base64Filler = '=';
109
110 function Base64Encode(const InText: string): string; overload;
111 begin
112 Base64Encode(InText, Result);
113 end;
114
115 function Base64Decode(const InText: string): string; overload;
116 begin
117 Base64Decode(InText, Result);
118 end;
119
120 function CalcEncodedSize(InSize: Cardinal): Cardinal;
121 begin
122 // no buffers passed along, calculate outbuffer size needed
123 Result := (InSize div 3) shl 2;
124 if ((InSize mod 3) > 0) then
125 Inc(Result, 4);
126 end;
127
128 function CalcDecodedSize(const InBuffer; InSize: Cardinal): Cardinal;
129 type
130 BA = array of Byte;
131 begin
132 Result := 0;
133 if InSize = 0 then
134 Exit;
135 if InSize mod 4 <> 0 then
136 Exit;
137 Result := InSize div 4 * 3;
138 if (BA(InBuffer)[InSize - 2] = Ord(Base64Filler)) then
139 Dec(Result, 2)
140 else if BA(InBuffer)[InSize - 1] = Ord(Base64Filler) then
141 Dec(Result);
142 end;
143
144 procedure Base64Encode(const InBuffer; InSize: Cardinal; var OutBuffer
145 ); register;
146 var
147 ByThrees, LeftOver: Cardinal;
148 // reset in- and outbytes positions
149 asm
150 // load addresses for source and destination
151 // PBYTE(InBuffer);
152 mov ESI, [EAX]
153 // PBYTE(OutBuffer);
154 mov EDI, [ECX]
155 // ByThrees := InSize div 3;
156 // LeftOver := InSize mod 3;
157 // load InSize (stored in EBX)
158 mov EAX, EBX
159 // load 3
160 mov ECX, $03
161 // clear upper 32 bits
162 xor EDX, EDX
163 // divide by ECX
164 div ECX
165 // save result
166 mov ByThrees, EAX
167 // save remainder
168 mov LeftOver, EDX
169 // load addresses
170 lea ECX, cBase64Codec[0]
171 // while I < ByThrees do
172 // begin
173 xor EAX, EAX
174 xor EBX, EBX
175 xor EDX, EDX
176 cmp ByThrees, 0
177 jz @@LeftOver
178 @@LoopStart:
179 // load the first two bytes of the source triplet
180 LODSW
181 // write Bits 0..5 to destination
182 mov BL, AL
183 shr BL, 2
184 mov DL, BYTE PTR [ECX + EBX]
185 // save the Bits 12..15 for later use [1]
186 mov BH, AH
187 and BH, $0F
188 // save Bits 6..11
189 rol AX, 4
190 and AX, $3F
191 mov DH, BYTE PTR [ECX + EAX]
192 mov AX, DX
193 // store the first two bytes of the destination quadruple
194 STOSW
195 // laod last byte (Bits 16..23) of the source triplet
196 LODSB
197 // extend bits 12..15 [1] with Bits 16..17 and save them
198 mov BL, AL
199 shr BX, 6
200 mov DL, BYTE PTR [ECX + EBX]
201 // save bits 18..23
202 and AL, $3F
203 xor AH, AH
204 mov DH, BYTE PTR [ECX + EAX]
205 mov AX, DX
206 // store the last two bytes of the destination quadruple
207 STOSW
208 dec ByThrees
209 jnz @@LoopStart
210 @@LeftOver:
211 // there are up to two more bytes to encode
212 cmp LeftOver, 0
213 jz @@Done
214 // clear result
215 xor EAX, EAX
216 xor EBX, EBX
217 xor EDX, EDX
218 // get left over 1
219 LODSB
220 // load the first six bits
221 shl AX, 6
222 mov BL, AH
223 // save them
224 mov DL, BYTE PTR [ECX + EBX]
225 // another byte ?
226 dec LeftOver
227 jz @@SaveOne
228 // save remaining two bits
229 shl AX, 2
230 and AH, $03
231 // get left over 2
232 LODSB
233 // load next 4 bits
234 shl AX, 4
235 mov BL, AH
236 // save all 6 bits
237 mov DH, BYTE PTR [ECX + EBX]
238 shl EDX, 16
239 // save last 4 bits
240 shr AL, 2
241 mov BL, AL
242 // save them
243 mov DL, BYTE PTR [ECX + EBX]
244 // load base 64 'no more data flag'
245 mov DH, Base64Filler
246 jmp @@WriteLast4
247 @@SaveOne:
248 // adjust the last two bits
249 shr AL, 2
250 mov BL, AL
251 // save them
252 mov DH, BYTE PTR [ECX + EBX]
253 shl EDX, 16
254 // load base 64 'no more data flags'
255 mov DH, Base64Filler
256 mov DL, Base64Filler
257 // ignore jump, as jump reference is next line !
258 // jmp @@WriteLast4
259 @@WriteLast4:
260 // load and adjust result
261 mov EAX, EDX
262 ror EAX, 16
263 // save it to destination
264 STOSD
265 @@Done:
266 end;
267
268 {$IFDEF SpeedDecode}
269
270 procedure Base64Decode(const InBuffer; InSize: Cardinal; var OutBuffer);
271 overload; register;
272 {$ENDIF}
273 {$IFDEF ValidityCheck}
274 function Base64Decode(const InBuffer; InSize: Cardinal; var OutBuffer):
275 Boolean; overload; register;
276 {$ENDIF}
277 const
278 {$IFDEF SpeedDecode}
279 cBase64Codec: array[0..127] of Byte =
280 {$ENDIF}
281 {$IFDEF ValidityCheck}
282 cBase64Codec: array[0..255] of Byte =
283 {$ENDIF}
284 (
285 $FF, $FF, $FF, $FF, $FF, {005>} $FF, $FF, $FF, $FF, $FF, // 000..009
286 $FF, $FF, $FF, $FF, $FF, {015>} $FF, $FF, $FF, $FF, $FF, // 010..019
287 $FF, $FF, $FF, $FF, $FF, {025>} $FF, $FF, $FF, $FF, $FF, // 020..029
288 $FF, $FF, $FF, $FF, $FF, {035>} $FF, $FF, $FF, $FF, $FF, // 030..039
289 $FF, $FF, $FF, $3E, $FF, {045>} $FF, $FF, $3F, $34, $35, // 040..049
290 $36, $37, $38, $39, $3A, {055>} $3B, $3C, $3D, $FF, $FF, // 050..059
291 $FF, $FF, $FF, $FF, $FF, {065>} $00, $01, $02, $03, $04, // 060..069
292 $05, $06, $07, $08, $09, {075>} $0A, $0B, $0C, $0D, $0E, // 070..079
293 $0F, $10, $11, $12, $13, {085>} $14, $15, $16, $17, $18, // 080..089
294 $19, $FF, $FF, $FF, $FF, {095>} $FF, $FF, $1A, $1B, $1C, // 090..099
295 $1D, $1E, $1F, $20, $21, {105>} $22, $23, $24, $25, $26, // 100..109
296 $27, $28, $29, $2A, $2B, {115>} $2C, $2D, $2E, $2F, $30, // 110..119
297 $31, $32, $33, $FF, $FF, {125>} $FF, $FF, $FF // 120..127
298
299 {$IFDEF ValidityCheck}
300 {125>}, $FF, $FF, // 128..129
301 $FF, $FF, $FF, $FF, $FF, {135>} $FF, $FF, $FF, $FF, $FF, // 130..139
302 $FF, $FF, $FF, $FF, $FF, {145>} $FF, $FF, $FF, $FF, $FF, // 140..149
303 $FF, $FF, $FF, $FF, $FF, {155>} $FF, $FF, $FF, $FF, $FF, // 150..159
304 $FF, $FF, $FF, $FF, $FF, {165>} $FF, $FF, $FF, $FF, $FF, // 160..169
305 $FF, $FF, $FF, $FF, $FF, {175>} $FF, $FF, $FF, $FF, $FF, // 170..179
306 $FF, $FF, $FF, $FF, $FF, {185>} $FF, $FF, $FF, $FF, $FF, // 180..189
307 $FF, $FF, $FF, $FF, $FF, {195>} $FF, $FF, $FF, $FF, $FF, // 190..199
308 $FF, $FF, $FF, $FF, $FF, {205>} $FF, $FF, $FF, $FF, $FF, // 200..209
309 $FF, $FF, $FF, $FF, $FF, {215>} $FF, $FF, $FF, $FF, $FF, // 210..219
310 $FF, $FF, $FF, $FF, $FF, {225>} $FF, $FF, $FF, $FF, $FF, // 220..229
311 $FF, $FF, $FF, $FF, $FF, {235>} $FF, $FF, $FF, $FF, $FF, // 230..239
312 $FF, $FF, $FF, $FF, $FF, {245>} $FF, $FF, $FF, $FF, $FF, // 240..249
313 $FF, $FF, $FF, $FF, $FF, {255>} $FF // 250..255
314 {$ENDIF}
315 );
316 asm
317 push EBX
318 mov ESI, [EAX]
319 mov EDI, [ECX]
320 {$IFDEF ValidityCheck}
321 mov EAX, InSize
322 and EAX, $03
323 cmp EAX, $00
324 jz @@DecodeStart
325 jmp @@ErrorDone
326 @@DecodeStart:
327 {$ENDIF}
328 mov EAX, InSize
329 shr EAX, 2
330 jz @@Done
331 lea ECX, cBase64Codec[0]
332 xor EBX, EBX
333 dec EAX
334 jz @@LeftOver
335 push EBP
336 mov EBP, EAX
337 @@LoopStart:
338 // load four bytes into EAX
339 LODSD
340 // save them to EDX as AX is used to store results
341 mov EDX, EAX
342 // get bits 0..5
343 mov BL, DL
344 // decode
345 mov AH, BYTE PTR [ECX + EBX]
346 {$IFDEF ValidityCheck}
347 // check valid code
348 cmp AH, $FF
349 jz @@ErrorDoneAndPopEBP
350 {$ENDIF}
351 // get bits 6..11
352 mov BL, DH
353 // decode
354 mov AL, BYTE PTR [ECX + EBX]
355 {$IFDEF ValidityCheck}
356 // check valid code
357 cmp AL, $FF
358 jz @@ErrorDoneAndPopEBP
359 {$ENDIF}
360 // align last 6 bits
361 shl AL, 2
362 // get first 8 bits
363 ror AX, 6
364 // store first byte
365 STOSB
366 // align remaining 4 bits
367 shr AX, 12
368 // get next two bytes from source quad
369 shr EDX, 16
370 // load bits 12..17
371 mov BL, DL
372 // decode
373 mov AH, BYTE PTR [ECX + EBX]
374 {$IFDEF ValidityCheck}
375 // check valid code
376 cmp AH, $FF
377 jz @@ErrorDoneAndPopEBP
378 {$ENDIF}
379 // align ...
380 shl AH, 2
381 // ... and adjust
382 rol AX, 4
383 // get last bits 18..23
384 mov BL, DH
385 // decord
386 mov BL, BYTE PTR [ECX + EBX]
387 {$IFDEF ValidityCheck}
388 // check valid code
389 cmp BL, $FF
390 jz @@ErrorDoneAndPopEBP
391 {$ENDIF}
392 // enter in destination word
393 or AH, BL
394 // and store to destination
395 STOSW
396 // more coming ?
397 dec EBP
398 jnz @@LoopStart
399 pop EBP
400 // no
401 // last four bytes are handled separately, as special checking is needed
402 // on the last two bytes (may be end of data signals '=' or '==')
403 @@LeftOver:
404 // get the last four bytes
405 LODSD
406 // save them to EDX as AX is used to store results
407 mov EDX, EAX
408 // get bits 0..5
409 mov BL, DL
410 // decode
411 mov AH, BYTE PTR [ECX + EBX]
412 {$IFDEF ValidityCheck}
413 // check valid code
414 cmp AH, $FF
415 jz @@ErrorDone
416 {$ENDIF}
417 // get bits 6..11
418 mov BL, DH
419 // decode
420 mov AL, BYTE PTR [ECX + EBX]
421 {$IFDEF ValidityCheck}
422 // check valid code
423 cmp AL, $FF
424 jz @@ErrorDone
425 {$ENDIF}
426 // align last 6 bits
427 shl AL, 2
428 // get first 8 bits
429 ror AX, 6
430 // store first byte
431 STOSB
432 // get next two bytes from source quad
433 shr EDX, 16
434 // check DL for "end of data signal"
435 cmp DL, Base64Filler
436 jz @@SuccessDone
437 // align remaining 4 bits
438 shr AX, 12
439 // load bits 12..17
440 mov BL, DL
441 // decode
442 mov AH, BYTE PTR [ECX + EBX]
443 {$IFDEF ValidityCheck}
444 // check valid code
445 cmp AH, $FF
446 jz @@ErrorDone
447 {$ENDIF}
448 // align ...
449 shl AH, 2
450 // ... and adjust
451 rol AX, 4
452 // store second byte
453 STOSB
454 // check DH for "end of data signal"
455 cmp DH, Base64Filler
456 jz @@SuccessDone
457 // get last bits 18..23
458 mov BL, DH
459 // decord
460 mov BL, BYTE PTR [ECX + EBX]
461 {$IFDEF ValidityCheck}
462 // check valid code
463 cmp BL, $FF
464 jz @@ErrorDone
465 {$ENDIF}
466 // enter in destination word
467 or AH, BL
468 // AH - AL for saving last byte
469 mov AL, AH
470 // store third byte
471 STOSB
472 @@SuccessDone:
473 {$IFDEF ValidityCheck}
474 mov Result, $01
475 jmp @@Done
476 @@ErrorDoneAndPopEBP:
477 pop EBP
478 @@ErrorDone:
479 mov Result, $00
480 {$ENDIF}
481 @@Done:
482 pop EBX
483 end;
484
485 procedure Base64Encode(const InText: PAnsiChar; var OutText: PAnsiChar);
486 var
487 InSize, OutSize: Cardinal;
488 begin
489 // get size of source
490 InSize := Length(InText);
491 // calculate size for destination
492 OutSize := CalcEncodedSize(InSize);
493 // reserve memory
494 OutText := StrAlloc(Succ(OutSize));
495 OutText[OutSize] := #0;
496 // encode !
497 Base64Encode(InText, InSize, OutText);
498 end;
499
500 procedure Base64Encode(const InText: AnsiString; var OutText: AnsiString);
501 overload;
502 var
503 InSize, OutSize: Cardinal;
504 PIn, POut: Pointer;
505 begin
506 // get size of source
507 InSize := Length(InText);
508 // calculate size for destination
509 OutSize := CalcEncodedSize(InSize);
510 // prepare string length to fit result data
511 SetLength(OutText, OutSize);
512 PIn := @InText[1];
513 POut := @OutText[1];
514 // encode !
515 Base64Encode(PIn, InSize, POut);
516 end;
517
518 procedure Base64Decode(const InText: PAnsiChar; var OutText: PAnsiChar);
519 overload;
520 var
521 InSize, OutSize: Cardinal;
522 begin
523 // get size of source
524 InSize := Length(InText);
525 // calculate size for destination
526 OutSize := CalcDecodedSize(InText, InSize);
527 // reserve memory
528 OutText := StrAlloc(Succ(OutSize));
529 OutText[OutSize] := #0;
530 // encode !
531 {$IFDEF SpeedDecode}
532 Base64Decode(InText, InSize, OutText);
533 {$ENDIF}
534 {$IFDEF ValidityCheck}
535 if not Base64Decode(InText, InSize, OutText) then
536 OutText[0] := #0;
537 {$ENDIF}
538 end;
539
540 procedure Base64Decode(const InText: AnsiString; var OutText: AnsiString);
541 overload;
542 var
543 InSize, OutSize: Cardinal;
544 PIn, POut: Pointer;
545 begin
546 // get size of source
547 InSize := Length(InText);
548 // calculate size for destination
549 PIn := @InText[1];
550 OutSize := CalcDecodedSize(PIn, InSize);
551 // prepare string length to fit result data
552 SetLength(OutText, OutSize);
553 FillChar(OutText[1], OutSize, '.');
554 POut := @OutText[1];
555 // encode !
556 {$IFDEF SpeedDecode}
557 Base64Decode(PIn, InSize, POut);
558 {$ENDIF}
559 {$IFDEF ValidityCheck}
560 if not Base64Decode(PIn, InSize, POut) then
561 SetLength(OutText, 0);
562 {$ENDIF}
563 end;
564
565 end.
|