IdCoder3to4.pas 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10091: IdCoder3to4.pas
  11. {
  12. { Rev 1.3 28/05/2003 01:14:32 CCostelloe
  13. { StripCRLFs changes reversed out at the request of Chad
  14. }
  15. {
  16. { Rev 1.2 20/05/2003 02:02:24 CCostelloe
  17. }
  18. {
  19. { Rev 1.1 20/05/2003 01:39:14 CCostelloe
  20. { Bug fix: decoder code altered to ensure that any CRLFs inserted by an MTA are
  21. { removed
  22. }
  23. {
  24. { Rev 1.0 2002.11.12 10:32:28 PM czhower
  25. }
  26. unit IdCoder3to4;
  27. interface
  28. uses
  29. Classes,
  30. IdCoder;
  31. type
  32. TIdDecodeTable = array[1..127] of Byte;
  33. TIdEncoder3to4 = class(TIdEncoder)
  34. protected
  35. FCodingTable: string;
  36. FFillChar: Char;
  37. public
  38. function Encode(ASrcStream: TStream;
  39. const ABytes: Integer = MaxInt): string; override;
  40. procedure EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: Cardinal);
  41. published
  42. property CodingTable: string read FCodingTable;
  43. property FillChar: Char read FFillChar write FFillChar;
  44. end;
  45. TIdEncoder3to4Class = class of TIdEncoder3to4;
  46. TIdDecoder4to3 = class(TIdDecoder)
  47. protected
  48. FDecodeTable: TIdDecodeTable;
  49. FFillChar: Char;
  50. public
  51. class procedure ConstructDecodeTable(const ACodingTable: string;
  52. var ADecodeArray: TIdDecodeTable);
  53. procedure DecodeToStream(AIn: string; ADest: TStream); override;
  54. procedure DecodeUnit(AIn: Cardinal; var VOut1, VOut2, VOut3: Byte);
  55. published
  56. property FillChar: Char read FFillChar write FFillChar;
  57. end;
  58. implementation
  59. uses
  60. IdException, IdGlobal, IdResourceStrings,
  61. SysUtils;
  62. { TIdDecoder4to3 }
  63. class procedure TIdDecoder4to3.ConstructDecodeTable(const ACodingTable: string;
  64. var ADecodeArray: TIdDecodeTable);
  65. var
  66. i: integer;
  67. begin
  68. //TODO: See if we can find an efficient way, or maybe an option to see if the requested
  69. //decode char is valid, that is it returns a 255 from the DecodeTable, or at maybe
  70. //check its presence in the encode table.
  71. for i := Low(ADecodeArray) to High(ADecodeArray) do begin
  72. ADecodeArray[i] := 255;
  73. end;
  74. for i := 1 to Length(ACodingTable) do begin
  75. ADecodeArray[Ord(ACodingTable[i])] := i - 1;
  76. end;
  77. end;
  78. procedure TIdDecoder4to3.DecodeToStream(AIn: string; ADest: TStream);
  79. type
  80. ThreeByteRec = record
  81. case Integer of
  82. 0: (Bytes : array[1..3] of Byte);
  83. 1: (ThreeChars : array[1..3] of Char);
  84. 2: (TwoChars : array[1..2] of Char; Waste1: Char);
  85. 3: (OneChar: Char; Waste2: array[1..2] of Char);
  86. end;
  87. var
  88. LBOut: ThreeByteRec;
  89. LOut: string;
  90. LUnit: TIdCardinalBytes;
  91. LInSize, LInPos : Integer;
  92. LOutBuf : String;
  93. begin
  94. if (Length(AIn) mod 4) > 0 then begin
  95. raise EIdException.Create(RSUnevenSizeInDecodeStream);
  96. end;
  97. LOutBuf := ''; {Do not Localize}
  98. LInSize := Length(AIn);
  99. LInPos := 1;
  100. LOut := ''; {Do not Localize}
  101. while LInPos <= LInSize do begin
  102. Move(AIn[LInPos], LUnit, SizeOf(LUnit));
  103. Inc(LInPos, SizeOf(LUnit));
  104. DecodeUnit(LUnit.Whole, LBOut.Bytes[1], LBOut.Bytes[2], LBOut.Bytes[3]);
  105. // Must check Byte3 before for, as if Byte3 is FillChar, Byte 4 will be
  106. // also be FillChar
  107. if Chr(LUnit.Byte3) = FillChar then begin
  108. LOut := LOut + LBOut.OneChar;
  109. end
  110. else
  111. begin
  112. if Chr(LUnit.Byte4) = FillChar then
  113. begin
  114. LOut := LOut + LBOut.TwoChars;
  115. end
  116. else
  117. begin
  118. LOut := LOut + LBout.ThreeChars;
  119. end;
  120. end;
  121. end;
  122. if LOut <> '' then begin
  123. ADest.WriteBuffer(LOut[1], Length(LOut));
  124. end;
  125. end;
  126. procedure TIdDecoder4to3.DecodeUnit(AIn: Cardinal; var VOut1, VOut2
  127. , VOut3: Byte);
  128. var
  129. LUnit: TIdCardinalBytes;
  130. begin
  131. LUnit.Whole := AIn;
  132. LUnit.Whole := (FDecodeTable[LUnit.Byte1] shl 18)
  133. or (FDecodeTable[LUnit.Byte2] shl 12) or (FDecodeTable[LUnit.Byte3] shl 6)
  134. or FDecodeTable[LUnit.Byte4];
  135. VOut1 := LUnit.Byte3;
  136. VOut2 := LUnit.Byte2;
  137. VOut3 := LUnit.Byte1;
  138. end;
  139. { TIdEncoder3to4 }
  140. function TIdEncoder3to4.Encode(ASrcStream: TStream; const ABytes: Integer = MaxInt): string;
  141. //TODO: Make this more efficient. Profile it to test, but maybe make single
  142. // calls to ReadBuffer then pull from memory
  143. var
  144. LBuffer : String;
  145. LSize : Integer;
  146. LLen : integer;
  147. LBufSize : Integer;
  148. LPos : Integer;
  149. LIn1, LIn2, LIn3: Byte;
  150. LUnit: TIdCardinalBytes;
  151. begin
  152. Result := ''; {Do not Localize}
  153. LIn3 := 0;
  154. if (ABytes <> MaxInt) and ((ABytes mod 3) > 0) then begin
  155. raise EIdException.Create(RSUnevenSizeInEncodeStream);
  156. end;
  157. // No no - this will read the whole thing into memory and what if its MBs?
  158. // need to load it in smaller buffered chunks MaxInt is WAY too big....
  159. LBufSize := ASrcStream.Size - ASrcStream.Position;
  160. if LBufSize > ABytes then begin
  161. LBufSize := ABytes;
  162. end;
  163. if LBufSize = 0 then begin
  164. Exit;
  165. end;
  166. SetLength(result, ((LBufSize+2) div 3) * 4); // we know that the string will grow by 4/3 adjusted to 3 boundary
  167. LLen := 0;
  168. SetLength(LBuffer, LBufSize);
  169. ASrcStream.ReadBuffer(LBuffer[1], LBufSize);
  170. LPos := 1;
  171. while (LPos <= LBufSize) do
  172. begin
  173. LIn1 := Byte(LBuffer[LPos]);
  174. Inc(LPos);
  175. if LPos <= LBufSize then
  176. begin
  177. LIn2 := Byte(LBuffer[LPos]);
  178. Inc(LPos);
  179. if LPos <= LBufSize then
  180. begin
  181. LIn3 := Byte(LBuffer[LPos]);
  182. Inc(LPos);
  183. LSize := 3;
  184. end
  185. else
  186. begin
  187. LIn3 := 0;
  188. LSize := 2;
  189. end;
  190. end
  191. else
  192. begin
  193. LIn2 := 0;
  194. LSize := 1;
  195. end;
  196. EncodeUnit(LIn1, LIn2, LIn3, LUnit.Whole);
  197. assert(LLen + 4 <= length(result), 'TIdEncoder3to4.Encode: Calculated length exceeded (expected '+inttostr(4 * trunc((LBufSize + 2)/3))+', about to go '+inttostr(LLen + 4)+' at offset '+inttostr(LPos)+' of '+inttostr(LBufSize));
  198. move(LUnit, result[LLen + 1], 4);
  199. inc(LLen, 4);
  200. if LSize < 3 then begin
  201. Result[LLen] := FillChar;
  202. if LSize = 1 then begin
  203. Result[LLen-1] := FillChar;
  204. end;
  205. end;
  206. end;
  207. assert(LLen = 4 * trunc((LBufSize + 2)/3), 'TIdEncoder3to4.Encode: Calculated length not met (expected '+inttostr(4 * trunc((LBufSize + 2)/3))+', finished at '+inttostr(LLen + 4)+', Bufsize = '+inttostr(LBufSize));
  208. end;
  209. procedure TIdEncoder3to4.EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: Cardinal);
  210. var
  211. LUnit: TIdCardinalBytes;
  212. begin
  213. LUnit.Byte1 := Ord(FCodingTable[((AIn1 SHR 2) and 63) + 1]);
  214. LUnit.Byte2 := Ord(FCodingTable[(((AIn1 SHL 4) or (AIn2 SHR 4)) and 63) + 1]);
  215. LUnit.Byte3 := Ord(FCodingTable[(((AIn2 SHL 2) or (AIn3 SHR 6)) and 63) + 1]);
  216. LUnit.Byte4 := Ord(FCodingTable[(Ord(AIn3) and 63) + 1]);
  217. VOut := LUnit.Whole;
  218. end;
  219. end.