Subversion Repositories decoder

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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