📄 idcoder3to4.pas
字号:
{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence }
{ Team Coherence is Copyright 2002 by Quality Software Components }
{ }
{ For further information / comments, visit our WEB site at }
{ http://www.TeamCoherence.com }
{**********************************************************************}
{}
{ $Log: 13754: IdCoder3to4.pas
{
{ Rev 1.30 15.09.2004 22:38:22 Andreas Hausladen
{ Added "Delphi 7.1 compiler warning bug" fix code
}
{
{ Rev 1.29 27.08.2004 22:03:22 Andreas Hausladen
{ Optimized encoders
{ speed optimization ("const" for string parameters)
}
{
{ Rev 1.28 7/8/04 5:09:04 PM RLebeau
{ Updated Encode() to remove use of local TIdBytes variable
}
{
{ Rev 1.27 2004.05.20 1:39:20 PM czhower
{ Last of the IdStream updates
}
{
{ Rev 1.26 2004.05.20 11:37:08 AM czhower
{ IdStreamVCL
}
{
{ Rev 1.25 2004.05.20 11:13:12 AM czhower
{ More IdStream conversions
}
{
{ Rev 1.24 2004.05.19 3:06:54 PM czhower
{ IdStream / .NET fix
}
{
{ Rev 1.23 2004.03.12 7:54:18 PM czhower
{ Removed old commented out code.
}
{
{ Rev 1.22 11/03/2004 22:36:14 CCostelloe
{ Bug fix (1 to 3 spurious extra characters at the end of UUE encoded messages,
{ see comment starting CC3.
}
{
{ Rev 1.21 2004.02.03 5:44:56 PM czhower
{ Name changes
}
{
{ Rev 1.20 28/1/2004 6:22:16 PM SGrobety
{ Removed base 64 encoding stream length check is stream size was provided
}
{
{ Rev 1.19 16/01/2004 17:47:48 CCostelloe
{ Restructured slightly to allow IdCoderBinHex4 reuse some of its code
}
{
{ Rev 1.18 02/01/2004 20:59:28 CCostelloe
{ Fixed bugs to get ported code to work in Delphi 7 (changes marked CC2)
}
{
{ Rev 1.17 11/10/2003 7:54:14 PM BGooijen
{ Did all todo's ( TStream to TIdStream mainly )
}
{
{ Rev 1.16 2003.10.24 10:43:02 AM czhower
{ TIdSTream to dos
}
{
{ Rev 1.15 22/10/2003 12:25:36 HHariri
{ Stephanes changes
}
{
Rev 1.14 10/16/2003 11:10:18 PM DSiders
Added localization comments, whitespace.
}
{
{ Rev 1.13 2003.10.11 10:00:12 PM czhower
{ Compiles again
}
{
{ Rev 1.12 10/5/2003 4:31:02 PM GGrieve
{ use ToBytes for Cardinal to Bytes conversion
}
{
{ Rev 1.11 10/4/2003 9:12:18 PM GGrieve
{ DotNet
}
{
{ Rev 1.10 2003.06.24 12:02:10 AM czhower
{ Coders now decode properly again.
}
{
{ Rev 1.9 2003.06.23 10:53:16 PM czhower
{ Removed unused overriden methods.
}
{
{ Rev 1.8 2003.06.13 6:57:10 PM czhower
{ Speed improvement
}
{
{ Rev 1.7 2003.06.13 3:41:18 PM czhower
{ Optimizaitions.
}
{
{ Rev 1.6 2003.06.13 2:24:08 PM czhower
{ Speed improvement
}
{
{ Rev 1.5 10/6/2003 5:37:02 PM SGrobety
{ Bug fix in decoders.
}
{
{ Rev 1.4 6/6/2003 4:50:30 PM SGrobety
{ Reworked the 3to4decoder for performance and stability.
{ Note that encoders haven't been touched. Will come later. Another problem:
{ input is ALWAYS a string. Should be a TStream.
{
{ 1/ Fix: added filtering for #13,#10 and #32 to the decoding mechanism.
{ 2/ Optimization: Speed the decoding by a factor 7-10 AND added filtering ;)
{ Could still do better by using a pointer and a stiding window by a factor 2-3.
{ 3/ Improvement: instead of writing everything to the output stream, there is
{ an internal buffer of 4k. It should speed things up when working on large
{ data (no large chunk of memory pre-allocated while keeping a decent perf by
{ not requiring every byte to be written separately).
}
{
{ Rev 1.3 28/05/2003 10:06:56 CCostelloe
{ StripCRLFs changes stripped out at the request of Chad
}
{
{ Rev 1.2 20/05/2003 02:01:00 CCostelloe
}
{
{ Rev 1.1 20/05/2003 01:44:12 CCostelloe
{ Bug fix: decoder code altered to ensure that any CRLFs inserted by an MTA are
{ removed
}
{
{ Rev 1.0 11/14/2002 02:14:36 PM JPMugaas
}
unit IdCoder3to4;
interface
uses
Classes,
IdCoder, IdGlobal, IdStreamRandomAccess;
type
TIdDecodeTable = array[1..127] of Byte;
TIdEncoder3to4 = class(TIdEncoder)
protected
FCodingTable: string;
FFillChar: Char;
function EncodeIdBytes(ABuffer: TIdBytes): TIdBytes;
public
function Encode(ASrcStream: TIdStreamRandomAccess;
const ABytes: Integer = MaxInt): string; override;
//procedure EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: TIdBytes);
published
property CodingTable: string read FCodingTable;
property FillChar: Char read FFillChar write FFillChar;
end;
TIdEncoder3to4Class = class of TIdEncoder3to4;
TIdDecoder4to3 = class(TIdDecoder)
protected
FCodingTable: string;
FDecodeTable: TIdDecodeTable;
FFillChar: Char;
function InternalDecode(const LIn: TIdBytes; const AStartPos: Integer = 1; const ABytes: Integer = -1): TIdBytes;
public
class procedure ConstructDecodeTable(const ACodingTable: string;
var ADecodeArray: TIdDecodeTable);
procedure Decode(const AIn: string; const AStartPos: Integer = 1;
const ABytes: Integer = -1); override;
published
property FillChar: Char read FFillChar write FFillChar;
end;
implementation
uses
IdException, IdResourceStrings,
SysUtils;
{ TIdDecoder4to3 }
class procedure TIdDecoder4to3.ConstructDecodeTable(const ACodingTable: string;
var ADecodeArray: TIdDecodeTable);
var
i: integer;
begin
//TODO: See if we can find an efficient way, or maybe an option to see if the requested
//decode char is valid, that is it returns a 255 from the DecodeTable, or at maybe
//check its presence in the encode table.
for i := Low(ADecodeArray) to High(ADecodeArray) do begin
ADecodeArray[i] := 255;
end;
for i := 1 to Length(ACodingTable) do begin
ADecodeArray[Ord(ACodingTable[i])] := i - 1;
end;
end;
procedure TIdDecoder4to3.Decode(const AIn: string; const AStartPos: Integer = 1; const ABytes: Integer = -1);
var
LIn : TIdBytes;
LOut: TIdBytes;
begin
if AIn <> '' then begin
SetLength(LIn, 0); // Delphi 7.1 first edition warning bug
SetLength(LOut, 0); // Delphi 7.1 first edition warning bug
LIn := ToBytes(AIn); // if in dotnet, convert to serialisable format
LOut := InternalDecode(LIn, AStartPos, ABytes);
// Write out data to stream
FStream.Write(LOut);
end;
end;
function TIdDecoder4to3.InternalDecode(const LIn: TIdBytes; const AStartPos: Integer = 1; const ABytes: Integer = -1): TIdBytes;
const
LInBytesLen = 4;
var
LEmptyBytes: Integer;
LInBytes: array[0..LInBytesLen - 1] of Byte;
LWorkBytes: TIdBytes;
LOutPos: Integer;
LOutSize: Integer;
LInLimit: Integer;
LInPos: Integer;
LWhole : Cardinal;
LFillChar: Char; // local copy of FFillChar
begin
LFillChar := FillChar;
SetLength(LWorkBytes, 4);
//TODO: Change output to a TMemoryStream
LEmptyBytes := 0;
// Presize output buffer
//CC2, bugfix: was LOutPos := 1;
LOutPos := 0;
if ABytes = -1 then begin
//LOutSize := (Length(AIn) div 4) * 3;
LOutSize := (Length(LIn) div 4) * 3;
end else begin
// Need to make sure we have space as we always write out 3 and then trim
// because it requires less checking in the loop
if ABytes mod 3 > 0 then begin
LOutSize := (ABytes div 3) * 3 + 3;
end else begin
LOutSize := ABytes;
end;
end;
SetLength(Result, LOutSize);
//
LInPos := AStartPos;
// +1 because LInPos is 1 based
LInLimit := Length(LIn) - LInBytesLen + 1;
while LInPos <= LInLimit do begin
// Read 4 bytes in for processing
//CC2 bugfix: was CopyTIdBytes(LIn, LInPos, LInBytes, 0, LInBytesLen);
//CopyTIdBytes(LIn, LInPos-1, LInBytes, 0, LInBytesLen);
// Faster than CopyTIdBytes
LInBytes[0] := LIn[LInPos - 1];
LInBytes[1] := LIn[LInPos - 1 + 1];
LInBytes[2] := LIn[LInPos - 1 + 2];
LInBytes[3] := LIn[LInPos - 1 + 3];
// Inc pointer
Inc(LInPos, LInBytesLen);
// Reduce to 3 bytes
LWhole :=
(FDecodeTable[LInBytes[0]] shl 18)
or (FDecodeTable[LInBytes[1]] shl 12)
or (FDecodeTable[LInBytes[2]] shl 6)
or FDecodeTable[LInBytes[3]];
ToBytesF(LWorkBytes, LWhole);
//TODO: Temp - Change the above to reconstruct in our order if possible
// Then we can call a move on all 3 bytes
Result[LOutPos] := LWorkBytes[2];
Result[LOutPos + 1] := LWorkBytes[1];
Result[LOutPos + 2] := LWorkBytes[0];
Inc(LOutPos, 3);
// If we dont know how many bytes we need to watch for fill chars. MIME
// is this way.
//
// In best case, the end is not before the end of the input, but the input
// may be right padded with spaces, or even contain the EOL chars.
//
// Because of this we watch for early ends beyond what we originally
// estimated.
if ABytes = -1 then begin
// Must check 3 before 4, if 3 is FillChar, 4 will also be FillChar
if LInBytes[2] = ord(LFillChar) then begin
LEmptyBytes := 2;
Break;
end else if LInBytes[3] = ord(LFillChar) then begin
LEmptyBytes := 1;
Break;
end;
// But with 00E's, we have a length signal for each line so we know
end else if LOutPos > ABytes then begin
LEmptyBytes := LOutPos - ABytes;
Break;
end;
end;
if LEmptyBytes > 0 then
SetLength(Result, LOutSize - LEmptyBytes);
end;
{ TIdEncoder3to4 }
function TIdEncoder3to4.Encode(ASrcStream: TIdStreamRandomAccess; const ABytes: Integer = MaxInt): string;
//TODO: Make this more efficient. Profile it to test, but maybe make single
// calls to ReadBuffer then pull from memory
var
LBuffer : TIdBytes;
LBufSize : Integer;
begin
//CC2: generated "never used" hint: LIn3 := 0;
// SG 28.01.04: removed that check: it's only there to "optimize" the output strin
// SG 28.01.04: and creates more trouble than it solves.
// if (ABytes <> MaxInt) and ((ABytes mod 3) > 0) then begin
// raise EIdException.Create(RSUnevenSizeInEncodeStream);
// end;
// No no - this will read the whole thing into memory and what if its MBs?
// need to load it in smaller buffered chunks MaxInt is WAY too big....
LBufSize := Min(ASrcStream.Size - ASrcStream.Position, ABytes);
if LBufSize > 0 then begin
SetLength(LBuffer, LBufSize);
ASrcStream.ReadBytes(LBuffer, LBufSize);
Result := BytesToString(EncodeIdBytes(LBuffer));
end else begin
Result := '';
end;
end;
function TIdEncoder3to4.EncodeIdBytes(ABuffer: TIdBytes): TIdBytes;
var
LOutSize: Integer;
LLen : integer;
LPos : Integer;
LBufSize : Integer;
LBufDataLen: Integer;
LIn1, LIn2, LIn3: Byte;
LSize : Integer;
LUnit: array[0..3] of Byte; // TIdBytes;
begin
LBufSize := Length(ABuffer);
LOutSize := ((LBufSize + 2) div 3) * 4;
SetLength(Result, LOutSize); // we know that the string will grow by 4/3 adjusted to 3 boundary
//SetLength(LUnit, 4);
LLen := 0;
LPos := 0;
// S.G. 21/10/2003: Copy the relevant bytes into the temporary buffer.
// S.G. 21/10/2003: Record the data length and force exit loop when necessary
while (LPos <= LBufSize) do
begin
LBufDataLen := LBufSize - LPos;
if LBufDataLen > 3 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos+1];
LIn3 := ABuffer[LPos+2];
LSize := 3;
inc(LPos, 3);
end
else
begin
if LBufDataLen > 2 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos+1];
LIn3 := ABuffer[LPos+2];
LSize := 3;
LPos := LBufSize+1; // Make sure we break at end of loop
end
else
begin
if LBufDataLen > 1 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos+1];
LIn3 := 0;
LSize := 2;
LPos := LBufSize+1; // Make sure we break at end of loop
end
else
begin
LIn1 := ABuffer[LPos];
LIn2 := 0;
LIn3 := 0;
LSize := 1;
LPos := LBufSize+1; // Make sure we break at end of loop
end;
end;
end;
//EncodeUnit(LIn1, LIn2, LIn3, LUnit);
// inline
LUnit[0] := Ord(FCodingTable[((LIn1 shr 2) and 63) + 1]);
LUnit[1] := Ord(FCodingTable[(((LIn1 shl 4) or (LIn2 shr 4)) and 63) + 1]);
LUnit[2] := Ord(FCodingTable[(((LIn2 shl 2) or (LIn3 shr 6)) and 63) + 1]);
LUnit[3] := Ord(FCodingTable[(Ord(LIn3) and 63) + 1]);
assert(LLen + 4 <= length(Result),
'TIdEncoder3to4.Encode: Calculated length exceeded (expected '+ {do not localize}
inttostr(4 * trunc((LBufSize + 2)/3)) +
', about to go '+ {do not localize}
inttostr(LLen + 4) +
' at offset ' + {do not localize}
inttostr(LPos) +
' of '+ {do not localize}
inttostr(LBufSize));
//CopyTIdBytes(LUnit, 0, Result, LLen, 4);
Result[LLen] := LUnit[0];
Result[LLen + 1] := LUnit[1];
Result[LLen + 2] := LUnit[2];
Result[LLen + 3] := LUnit[3];
inc(LLen, 4);
if LSize < 3 then begin
Result[LLen-1] := ord(FillChar);
if LSize = 1 then begin
Result[LLen-2] := ord(FillChar);
end;
end;
end;
assert(LLen = (4 * trunc((LBufSize + 2)/3)),
'TIdEncoder3to4.Encode: Calculated length not met (expected ' + {do not localize}
inttostr(4 * trunc((LBufSize + 2)/3)) +
', finished at ' + {do not localize}
inttostr(LLen + 4) +
', Bufsize = ' + {do not localize}
inttostr(LBufSize));
end;
(*procedure TIdEncoder3to4.EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: TIdBytes);
begin
SetLength(VOut, 4);
VOut[0] := Ord(FCodingTable[((AIn1 shr 2) and 63) + 1]);
VOut[1] := Ord(FCodingTable[(((AIn1 shl 4) or (AIn2 shr 4)) and 63) + 1]);
VOut[2] := Ord(FCodingTable[(((AIn2 shl 2) or (AIn3 shr 6)) and 63) + 1]);
VOut[3] := Ord(FCodingTable[(Ord(AIn3) and 63) + 1]);
end;*)
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -