{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.


  $Log$


   Rev 1.18    27.08.2004 22:03:20  Andreas Hausladen
 Optimized encoders
 speed optimization ("const" for string parameters)


   Rev 1.17    7/23/04 7:00:14 PM  RLebeau
 Added extra exception handling to DecodeString() and Encode()


   Rev 1.16    2004.06.14 9:23:06 PM  czhower
 Bug fix.


   Rev 1.15    22/05/2004 12:05:20  CCostelloe
 Bug fix


   Rev 1.14    2004.05.20 1:39:20 PM  czhower
 Last of the IdStream updates


   Rev 1.13    2004.05.20 11:37:08 AM  czhower
 IdStreamVCL


   Rev 1.12    2004.05.20 11:13:10 AM  czhower
 More IdStream conversions


   Rev 1.11    2004.05.19 3:06:48 PM  czhower
 IdStream / .NET fix


   Rev 1.10    2004.02.03 5:44:56 PM  czhower
 Name changes


   Rev 1.9    1/27/2004 3:58:16 PM  SPerry
 StringStream ->IdStringStream


   Rev 1.8    27/1/2004 1:57:58 PM  SGrobety
 Additional bug fix


   Rev 1.6    11/10/2003 7:39:22 PM  BGooijen
 Did all todo's ( TStream to TIdStream mainly )


   Rev 1.5    2003.10.02 10:52:48 PM  czhower
 .Net


   Rev 1.4    2003.06.24 12:02:08 AM  czhower
 Coders now decode properly again.


   Rev 1.3    2003.06.13 6:57:08 PM  czhower
 Speed improvement


   Rev 1.2    2003.06.13 3:41:18 PM  czhower
 Optimizaitions.


   Rev 1.1    2003.06.13 2:24:06 PM  czhower
 Speed improvement


   Rev 1.0    11/14/2002 02:14:30 PM  JPMugaas
}
unit IdCoder;

interface

{$i IdCompilerDefines.inc}

uses
  Classes,
  IdBaseComponent,
  IdGlobal;

type
  TIdEncoder = class(TIdBaseComponent)
  public
    function Encode(const AIn: string; AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ): string; overload;
    procedure Encode(const AIn: string; ADestStrings: TStrings; AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ); overload;
    procedure Encode(const AIn: string; ADestStream: TStream; AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ); overload;

    function Encode(ASrcStream: TStream; const ABytes: Integer = -1): string; overload;
    procedure Encode(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); overload;
    procedure Encode(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); overload; virtual; abstract;

    class function EncodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ): string; overload;
    class procedure EncodeString(const AIn: string; ADestStrings: TStrings;
      AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ); overload;
    class procedure EncodeString(const AIn: string; ADestStream: TStream;
      AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
      ); overload;

    class function EncodeBytes(const ABytes: TIdBytes): string; overload;
    class procedure EncodeBytes(const ABytes: TIdBytes; ADestStrings: TStrings); overload;
    class procedure EncodeBytes(const ABytes: TIdBytes; ADestStream: TStream); overload;

    class function EncodeStream(ASrcStream: TStream; const ABytes: Integer = -1): string; overload;
    class procedure EncodeStream(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); overload;
    class procedure EncodeStream(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); overload;
  end;

  TIdEncoderClass = class of TIdEncoder;

  TIdDecoder = class(TIdBaseComponent)
  protected
    FStream: TStream;
  public
    procedure DecodeBegin(ADestStream: TStream); virtual;
    procedure DecodeEnd; virtual;

    procedure Decode(const AIn: string); overload;
    procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); overload; virtual; abstract;

    class function DecodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil
      {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
      ): string;
    class function DecodeBytes(const AIn: string): TIdBytes;
    class procedure DecodeStream(const AIn: string; ADestStream: TStream);
  end;

  TIdDecoderClass = class of TIdDecoder;

implementation

uses
  {$IFDEF DOTNET}
  IdStreamNET,
  {$ELSE}
  IdStreamVCL,
  {$ENDIF}
  IdGlobalProtocols, SysUtils;

{ TIdDecoder }

procedure TIdDecoder.DecodeBegin(ADestStream: TStream);
begin
  FStream := ADestStream;
end;

procedure TIdDecoder.DecodeEnd;
begin
  FStream := nil;
end;

procedure TIdDecoder.Decode(const AIn: string);
var
  LStream: TMemoryStream;
  LEncoding: IIdTextEncoding;
begin
  LStream := TMemoryStream.Create;
  try
    LEncoding := IndyTextEncoding_8Bit;
    WriteStringToStream(LStream, AIn, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
    LEncoding := nil;
    LStream.Position := 0;
    Decode(LStream);
  finally
    LStream.Free;
  end;
end;

class function TIdDecoder.DecodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
  ): string;
var
  LStream: TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  try
    DecodeStream(AIn, LStream);
    LStream.Position := 0;
    EnsureEncoding(AByteEncoding, enc8Bit);
    Result := ReadStringFromStream(LStream, -1, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
  finally
    LStream.Free;
  end;
end;

class function TIdDecoder.DecodeBytes(const AIn: string): TIdBytes;
var
  LStream: TMemoryStream;
begin
  Result := nil;
  LStream := TMemoryStream.Create;
  try
    DecodeStream(AIn, LStream);
    LStream.Position := 0;
    ReadTIdBytesFromStream(LStream, Result, -1);
  finally
    FreeAndNil(LStream);
  end;
end;

class procedure TIdDecoder.DecodeStream(const AIn: string; ADestStream: TStream);
var
  LDecoder: TIdDecoder;
begin
  LDecoder := Create(nil);
  try
    LDecoder.DecodeBegin(ADestStream);
    try
      LDecoder.Decode(AIn);
    finally
      LDecoder.DecodeEnd;
    end;
  finally
    LDecoder.Free;
  end;
end;

{ TIdEncoder }

function TIdEncoder.Encode(const AIn: string; AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  ): string;
var
  LStream: TMemoryStream;
begin
  if AIn <> '' then begin
    LStream := TMemoryStream.Create;
    try
      EnsureEncoding(AByteEncoding, enc8Bit);
      WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
      LStream.Position := 0;
      Result := Encode(LStream);
    finally
      FreeAndNil(LStream);
    end;
  end else begin
    Result := '';
  end;
end;

procedure TIdEncoder.Encode(const AIn: string; ADestStrings: TStrings;
  AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  );
var
  LStream: TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  try
    EnsureEncoding(AByteEncoding, enc8Bit);
    WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
    LStream.Position := 0;
    Encode(LStream, ADestStrings);
  finally
    FreeAndNil(LStream);
  end;
end;

procedure TIdEncoder.Encode(const AIn: string; ADestStream: TStream;
  AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  );
var
  LStream: TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  try
    EnsureEncoding(AByteEncoding, enc8Bit);
    WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
    LStream.Position := 0;
    Encode(LStream, ADestStream);
  finally
    FreeAndNil(LStream);
  end;
end;

function TIdEncoder.Encode(ASrcStream: TStream; const ABytes: Integer = -1) : string;
var
  LStream: TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  try
    Encode(ASrcStream, LStream, ABytes);
    LStream.Position := 0;
    Result := ReadStringFromStream(LStream, -1, IndyTextEncoding_8Bit);
  finally
    FreeAndNil(LStream);
  end;
end;

procedure TIdEncoder.Encode(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1);
var
  LStream: TMemoryStream;
begin
  ADestStrings.Clear;
  // TODO: provide an Encode() implementation that can save its output directly
  // to ADestStrings without having to waste memory encoding the data entirely
  // to memory first. In Delphi 2009+ in particular, TStrings.LoadFromStream()
  // wastes a lot of memory handling large streams...
  LStream := TMemoryStream.Create;
  try
    Encode(ASrcStream, LStream, ABytes);
    LStream.Position := 0;
    ADestStrings.LoadFromStream(LStream);
  finally
    FreeAndNil(LStream);
  end;
end;

class function TIdEncoder.EncodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  ): string;
var
  LEncoder: TIdEncoder;
begin
  LEncoder := Create(nil);
  try
    Result := LEncoder.Encode(AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  finally
    LEncoder.Free;
  end;
end;

class procedure TIdEncoder.EncodeString(const AIn: string; ADestStrings: TStrings;
  AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  );
var
  LEncoder: TIdEncoder;
begin
  LEncoder := Create(nil);
  try
    LEncoder.Encode(AIn, ADestStrings, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  finally
    LEncoder.Free;
  end;
end;

class procedure TIdEncoder.EncodeString(const AIn: string; ADestStream: TStream;
  AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  );
var
  LEncoder: TIdEncoder;
begin
  LEncoder := Create(nil);
  try
    LEncoder.Encode(AIn, ADestStream, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  finally
    LEncoder.Free;
  end;
end;

class function TIdEncoder.EncodeBytes(const ABytes: TIdBytes): string;
var
  LStream: TMemoryStream;
begin
  if ABytes <> nil then begin
    LStream := TMemoryStream.Create;
    try
      WriteTIdBytesToStream(LStream, ABytes);
      LStream.Position := 0;
      Result := EncodeStream(LStream);
    finally
      FreeAndNil(LStream);
    end;
  end else begin
    Result := '';
  end;
end;

class procedure TIdEncoder.EncodeBytes(const ABytes: TIdBytes; ADestStrings: TStrings);
var
  LStream: TMemoryStream;
begin
  if ABytes <> nil then begin
    LStream := TMemoryStream.Create;
    try
      WriteTIdBytesToStream(LStream, ABytes);
      LStream.Position := 0;
      EncodeStream(LStream, ADestStrings);
    finally
      FreeAndNil(LStream);
    end;
  end;
end;

class procedure TIdEncoder.EncodeBytes(const ABytes: TIdBytes; ADestStream: TStream);
var
  LStream: TMemoryStream;
begin
  if ABytes <> nil then begin
    LStream := TMemoryStream.Create;
    try
      WriteTIdBytesToStream(LStream, ABytes);
      LStream.Position := 0;
      EncodeStream(LStream, ADestStream);
    finally
      FreeAndNil(LStream);
    end;
  end;
end;

class function TIdEncoder.EncodeStream(ASrcStream: TStream; const ABytes: Integer = -1): string;
var
  LEncoder: TIdEncoder;
begin
  if ASrcStream <> nil then begin
    LEncoder := Create(nil);
    try
      Result := LEncoder.Encode(ASrcStream, ABytes);
    finally
      LEncoder.Free;
    end;
  end else begin
    Result := '';
  end;
end;

class procedure TIdEncoder.EncodeStream(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1);
var
  LEncoder: TIdEncoder;
begin
  if ASrcStream <> nil then begin
    LEncoder := Create(nil);
    try
      LEncoder.Encode(ASrcStream, ADestStrings, ABytes);
    finally
      LEncoder.Free;
    end;
  end;
end;

class procedure TIdEncoder.EncodeStream(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1);
var
  LEncoder: TIdEncoder;
begin
  if ASrcStream <> nil then begin
    LEncoder := Create(nil);
    try
      LEncoder.Encode(ASrcStream, ADestStream, ABytes);
    finally
      LEncoder.Free;
    end;
  end;
end;

end.


