{ $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:  11922: IdCoreGlobal.pas
{
{   Rev 1.68    5/12/2003 9:11:00 AM  GGrieve
{ Add WriteStringToStream
}
{
{   Rev 1.67    5/12/2003 12:32:48 AM  GGrieve
{ fix DotNet warnings
}
{
{   Rev 1.66    22/11/2003 12:03:02 AM  GGrieve
{ fix IdMultiPathFormData.pas implementation
}
{
{   Rev 1.65    11/15/2003 1:15:36 PM  VVassiliev
{ Move AppendByte from IdDNSCommon to IdCoreGlobal
}
{
{   Rev 1.64    10/28/2003 8:43:48 PM  BGooijen
{ compiles, and removed call to setstring
}
{
{   Rev 1.63    2003.10.24 10:44:50 AM  czhower
{ IdStream implementation, bug fixes.
}
{
{   Rev 1.62    10/18/2003 4:53:18 PM  BGooijen
{ Added ToHex
}
{
{   Rev 1.61    2003.10.17 6:17:24 PM  czhower
{ Some parts moved to stream
}
{
    Rev 1.60    10/15/2003 8:28:16 PM  DSiders
  Added localization comments.
}
{
{   Rev 1.59    2003.10.14 9:27:12 PM  czhower
{ Fixed compile erorr with missing )
}
{
{   Rev 1.58    10/14/2003 3:31:04 PM  SPerry
{ Modified ByteToHex() and IPv4ToHex
}
{
{   Rev 1.57    10/13/2003 5:06:46 PM  BGooijen
{ Removed local constant IdOctalDigits in favor of the unit constant. - attempt
{ 2
}
{
    Rev 1.56    10/13/2003 10:07:12 AM  DSiders
  Reverted prior change; local constant for IdOctalDigits is restored.
}
{
    Rev 1.55    10/12/2003 11:55:42 AM  DSiders
  Removed local constant IdOctalDigits in favor of the unit constant.
}
{
{   Rev 1.54    2003.10.11 5:47:22 PM  czhower
{ -VCL fixes for servers
{ -Chain suport for servers (Super core)
{ -Scheduler upgrades
{ -Full yarn support
}
{
{   Rev 1.53    10/8/2003 10:14:34 PM  GGrieve
{ add WriteStringToStream
}
{
{   Rev 1.52    10/8/2003 9:55:30 PM  GGrieve
{ Add IdDelete
}
{
{   Rev 1.51    10/7/2003 11:33:30 PM  GGrieve
{ Fix ReadStringFromStream
}
{
{   Rev 1.50    10/7/2003 10:07:30 PM  GGrieve
{ Get IdHTTP compiling for DotNet
}
{
{   Rev 1.49    6/10/2003 5:48:48 PM  SGrobety
{ DotNet updates
}
{
{   Rev 1.48    10/5/2003 12:26:46 PM  BGooijen
{ changed parameter names at some places
}
{
{   Rev 1.47    10/4/2003 7:08:26 PM  BGooijen
{ added some conversion routines type->TIdBytes->type, and fixed existing ones
}
{
{   Rev 1.46    10/4/2003 3:53:40 PM  BGooijen
{ added some ToBytes functions
}
{
{   Rev 1.45    04/10/2003 13:38:28  HHariri
{ Write(Integer) support
}
{
{   Rev 1.44    10/3/2003 10:44:54 PM  BGooijen
{ Added WriteBytesToStream
}
{
{   Rev 1.43    2003.10.02 8:29:14 PM  czhower
{ Changed names of byte conversion routines to be more readily understood and
{ not to conflict with already in use ones.
}
{
{   Rev 1.42    10/2/2003 5:15:16 PM  BGooijen
{ Added Grahame's functions
}
{
{   Rev 1.41    10/1/2003 8:02:20 PM  BGooijen
{ Removed some ifdefs and improved code
}
{
{   Rev 1.40    2003.10.01 9:10:58 PM  czhower
{ .Net
}
{
{   Rev 1.39    2003.10.01 2:46:36 PM  czhower
{ .Net
}
{
{   Rev 1.38    2003.10.01 2:30:36 PM  czhower
{ .Net
}
{
{   Rev 1.37    2003.10.01 12:30:02 PM  czhower
{ .Net
}
{
{   Rev 1.35    2003.10.01 1:12:32 AM  czhower
{ .Net
}
{
{   Rev 1.34    2003.09.30 7:37:14 PM  czhower
{ Typo fix.
}
{
{   Rev 1.33    30/9/2003 3:58:08 PM  SGrobety
{ More .net updates
}
{
{   Rev 1.31    2003.09.30 3:19:30 PM  czhower
{ Updates for .net
}
{
{   Rev 1.30    2003.09.30 1:22:54 PM  czhower
{ Stack split for DotNet
}
{
{   Rev 1.29    2003.09.30 12:09:36 PM  czhower
{ DotNet changes.
}
{
{   Rev 1.28    2003.09.30 10:36:02 AM  czhower
{ Moved stack creation to IdStack
{ Added DotNet stack.
}
{
{   Rev 1.27    9/29/2003 03:03:28 PM  JPMugaas
{ Changed CIL to DOTNET.
}
{
{   Rev 1.26    9/28/2003 04:22:00 PM  JPMugaas
{ IFDEF'ed out MemoryPos in NET because that will not work there.
}
{
{   Rev 1.25    9/26/03 11:20:50 AM  RLebeau
{ Updated defines used with SetThreadName() to allow it to work under BCB6.
}
{
{   Rev 1.24    9/24/2003 11:42:42 PM  JPMugaas
{ Minor changes to help compile under NET
}
{
{   Rev 1.23    2003.09.20 10:25:42 AM  czhower
{ Added comment and chaned for D6 compat.
}
{
{   Rev 1.22    9/18/2003 07:43:12 PM  JPMugaas
{ Moved GetThreadHandle to IdGlobals so the ThreadComponent can be in this
{ package.
}
{
{   Rev 1.21    9/8/2003 11:44:38 AM  JPMugaas
{ Fix for problem that was introduced in an optimization.
}
{
{   Rev 1.20    2003.08.19 1:54:34 PM  czhower
{ Removed warning
}
{
{   Rev 1.19    11/8/2003 6:25:44 PM  SGrobety
{ IPv4ToDWord: Added overflow checking disabling ($Q+) and changed "* 256"  by
{ "SHL 8".
}
{
{   Rev 1.18    2003.07.08 2:41:42 PM  czhower
{ This time I saved the file before checking in.
}
{
{   Rev 1.16    7/1/2003 03:39:38 PM  JPMugaas
{ Started numeric IP function API calls for more efficiency.
}
{
{   Rev 1.15    2003.07.01 3:49:56 PM  czhower
{ Added SetThreadName
}
{
    Rev 1.14    7/1/2003 12:03:56 AM  BGooijen
  Added functions to switch between IPv6 addresses in string and in
  TIdIPv6Address form
}
{
{   Rev 1.13    6/30/2003 06:33:58 AM  JPMugaas
{ Fix for range check error.
}
{
{   Rev 1.12    6/27/2003 04:43:30 PM  JPMugaas
{ Made IPv4ToDWord overload that returns a flag for an error message.
{ Moved MakeCanonicalIPv4Address code into IPv4ToDWord because most of that
{ simply reduces IPv4 addresses into a DWord.  That also should make the
{ function more useful in reducing various alternative forms of IPv4 addresses
{ down to DWords.
}
{
{   Rev 1.11    6/27/2003 01:19:38 PM  JPMugaas
{ Added MakeCanonicalIPv4Address for converting various IPv4 address forms
{ (mentioned at http://www.pc-help.org/obscure.htm) into a standard dotted IP
{ address.  Hopefully, we should soon support octal and hexidecimal addresses.
}
{
{   Rev 1.9    6/27/2003 04:36:08 AM  JPMugaas
{ Function for converting DWord to IP adcdress.
}
{
{   Rev 1.8    6/26/2003 07:54:38 PM  JPMugaas
{ Routines for converting standard dotted IPv4 addresses into dword,
{ hexidecimal, and octal forms.
}
{
    Rev 1.7    5/11/2003 11:57:06 AM  BGooijen
  Added RaiseLastOSError
}
{
{   Rev 1.6    4/28/2003 03:19:00 PM  JPMugaas
{ Made a function for obtaining the services file FQN.  That's in case
{ something else besides IdPorts needs it.
}
{
{   Rev 1.5    2003.04.16 10:06:42 PM  czhower
{ Moved DebugOutput to IdCoreGlobal
}
{
{   Rev 1.4    12/29/2002 2:15:30 PM  JPMugaas
{ GetCurrentThreadHandle function created as per Bas's instructions.  Moved
{ THandle to IdCoreGlobal for this function.
}
{
{   Rev 1.3    12-15-2002 17:02:58  BGooijen
{ Added comments to TIdExtList
}
{
{   Rev 1.2    12-15-2002 16:45:42  BGooijen
{ Added TIdList
}
{
{   Rev 1.1    29/11/2002 10:08:50 AM  SGrobety    Version: 1.1
{ Changed GetTickCount to use high-performance timer if available under windows
}
{
{   Rev 1.0    21/11/2002 12:36:18 PM  SGrobety    Version: Indy 10
}
{
{   Rev 1.0    11/13/2002 08:41:24 AM  JPMugaas
}
unit IdCoreGlobal;

{$I IdCompilerDefines.inc}

interface

uses
  {$IFDEF MSWINDOWS} Windows, {$ENDIF}
  {$IFDEF DOTNET}
  System.net, System.net.Sockets, System.Diagnostics,
  Windows,
  {$ENDIF}
  Classes,
  IdException,
  SyncObjs, SysUtils;

const
  {This is the only unit with references to OS specific units and IFDEFs. NO OTHER units
  are permitted to do so except .pas files which are counterparts to dfm/xfm files, and only for
  support of that.}

  //We make the version things an INC so that they can be managed independantly
  //by the package builder.
  {$I IdVers.inc}

  LF = #10;
  CR = #13;
  EOL = CR + LF;
  //
  CHAR0 = #0;
  BACKSPACE = #8;

  TAB = #9;
  CHAR32 = #32;

  //Timeout values
  IdTimeoutDefault = -1;
  IdTimeoutInfinite = -2;
  //Fetch Defaults
  IdFetchDelimDefault = ' ';    {Do not Localize}
  IdFetchDeleteDefault = true;
  IdFetchCaseSensitiveDefault = true;

const
  WhiteSpace = [#0..#12, #14..' ']; {do not localize}

const
  IdHexDigits: array [0..15] of AnsiChar = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');    {Do not Localize}
  IdOctalDigits: array [0..7] of AnsiChar = ('0','1','2','3','4','5','6','7');    {Do not Localize}
  HEXPREFIX = '0x';  {Do not translate}

type
  TIdLocalEvent = class(TEvent)
  public
    constructor Create(const AInitialState: Boolean = False;
     const AManualReset: Boolean = False); reintroduce;
    function WaitForEver: TWaitResult; overload;
  end;

  //This usually is a property editor exception
  EIdCorruptServicesFile = class(EIdException);

  TIdBytes = {$IFDEF DOTNET} TBytes; {$ELSE} array of Byte; {$ENDIF}
  TIdPort = Integer;

  {This way instead of a boolean for future expansion of other actions}
  TIdMaxLineAction = (maException, maSplit);
  TIdOSType = (otUnknown, otLinux, otWindows, otDotNet);
  //This is for IPv6 support when merged into the core
  TIdIPVersion = (Id_IPv4, Id_IPv6);

  {$IFDEF LINUX}
  TIdPID = Integer;
  TIdThreadPriority = -20..19;
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  TIdPID = LongWord;
  TIdThreadPriority = TThreadPriority;
  {$ENDIF}
  {$IFDEF DOTNET}
  TIdPID = LongWord;
  TIdThreadPriority = TThreadPriority;
  {$ENDIF}

  {$IFDEF LINUX}
    {$IFNDEF VCL6ORABOVE}
  THandle = LongWord; //D6.System
    {$ENDIF}
  {$ENDIF}
  {$IFDEF MSWINDOWS}
    {$IFNDEF VCL6ORABOVE}
  THandle = Windows.THandle;
    {$ENDIF}
  {$ENDIF}
  {$IFDEF DOTNET}
  THandle = LongWord;
  {$ENDIF}

  {$IFDEF DOTNET}
  TPosProc = function(const substr, str: WideString): Integer;
  {$ELSE}
  TPosProc = function(const Substr, S: string): Integer;
  {$ENDIF}
  TIdReuseSocket = (rsOSDependent, rsTrue, rsFalse);

{$IFNDEF VCL6ORABOVE}
  TIdExtList=class(TList) // We use this hack-class, because TList has no .assign on Delphi 5.
  public		   // Do NOT add DataMembers to this class !!!
    procedure Assign(AList: TList);
  end;
{$ELSE}
  TIdExtList=class(TList);
{$ENDIF}

  {$IFNDEF DOTNET}
  TSeekOrigin = word;
  {$ENDIF}
  // TIdBaseStream is defined here to allow TIdMultiPartFormData to be defined
  // without any $IFDEFs in the unit IdMultiPartFormData - in accordance with Indy Coding rules
  TIdBaseStream = class (TStream)
  protected
    function IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; virtual; abstract;
    function IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; virtual; abstract;
    function IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; virtual; abstract;
    procedure IdSetSize(ASize : Int64); virtual; abstract;
    {$IFDEF DOTNET}
    procedure SetSize(ASize : Int64); override;
    {$ELSE}
    procedure SetSize(ASize : Integer); override;
    {$ENDIF}
  public
    {$IFDEF DOTNET}
    function Read(var VBuffer: array of Byte; AOffset, ACount: Longint): Longint; override;
    function Write(const ABuffer: array of Byte; AOffset, ACount: Longint): Longint; override;
    function Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override;
    {$ELSE}
    function Read(var VBuffer; ACount: Longint): Longint; override;
    function Write(const ABuffer; ACount: Longint): Longint; override;
    function Seek(AOffset: Longint; AOrigin: Word): Longint; override;
    {$ENDIF}
  end;

const
  {$IFDEF Linux}
  GOSType = otLinux;
  GPathDelim = '/'; {do not localize}
  INFINITE = LongWord($FFFFFFFF);     { Infinite timeout }

  // approximate values, its finer grained on Linux
  tpIdle = 19;
  tpLowest = 12;
  tpLower = 6;
  tpNormal = 0;
  tpHigher = -7;
  tpHighest = -13;
  tpTimeCritical = -20;
  {$ENDIF}

  {$IFDEF MSWINDOWS}
  GOSType = otWindows;
  GPathDelim = '\'; {do not localize}
  Infinite = Windows.INFINITE; { redeclare here for use elsewhere without using Windows.pas }  // cls modified 1/23/2002
  {$ENDIF}

  {$IFDEF DOTNET}
  GOSType = otDotNet;
  GPathDelim = '\'; {do not localize}
//  Infinite = ?; { redeclare here for use elsewhere without using Windows.pas }  // cls modified 1/23/2002
  {$ENDIF}

  // S.G. 4/9/2002: IP version general switch for defaults
  {$IFDEF IdIPv6}
  ID_DEFAULT_IP_VERSION = Id_IPv6;
  {$ELSE}
  ID_DEFAULT_IP_VERSION = Id_IPv4;
  {$ENDIF}

  {$IFNDEF VCL6ORABOVE}
  //Only D6 & Kylix have this constant
  sLineBreak = EOL;
  {$ENDIF}

//The power constants are for processing IP addresses
//They are powers of 255.
const
  POWER_1 = $000000FF;
  POWER_2 = $0000FFFF;
  POWER_3 = $00FFFFFF;
  POWER_4 = $FFFFFFFF;

// To and From Bytes conversion routines
function ToBytes(const AValue: string): TIdBytes; overload;
function ToBytes(const AValue: Integer): TIdBytes; overload;
function ToBytes(const AValue: Short): TIdBytes; overload;
function ToBytes(const AValue: Cardinal): TIdBytes; overload;

function BytesToCardinal(const AValue: TIdBytes): Cardinal;
function ToHex(const AValue: TIdBytes): AnsiString; overload;
function ToHex(const AValue: Array of LongWord): AnsiString; overload; // for IdHash
function BytesToShort(const AValue: TIdBytes): Short;
function BytesToInteger(const AValue: TIdBytes): Integer;

// TIdBytes utilities
function BytesToString(const ABytes: TIdBytes): string; overload;
procedure AppendBytes(var VBytes: TIdBytes; AAdd: TIdBytes);
procedure AppendByte(var VBytes: TIdBytes; AByte: byte);

// Common Streaming routines
function ReadStringFromStream(AStream : TStream; ASize : integer):String;
procedure WriteStringToStream(AStream : TStream; AStr :String);

function ByteToHex(const AByte : Byte): string;
function ByteToOctal(const AByte : Byte): String;
procedure CopyTIdBytes(const ASource:TIdBytes;const ASourceIndex:integer;
    const ADest:TIdBytes;const ADestIndex:integer;const ALength:integer);
procedure CopyTIdByteArray(const ASource: Array of Byte;const ASourceIndex:integer;
    var ADest : Array of Byte; const ADestIndex:integer;const ALength:integer);
function CurrentProcessId: TIdPID;
procedure DebugOutput(const AText: string);
function Fetch(var AInput: string; const ADelim: string = IdFetchDelimDefault;
  const ADelete: Boolean = IdFetchDeleteDefault;
  const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String;
function FetchCaseInsensitive(var AInput: string; const ADelim: string = IdFetchDelimDefault;
    const ADelete: Boolean = IdFetchDeleteDefault) : string;
function GetCurrentThreadHandle : THandle;
function GetThreadHandle(AThread: TThread): THandle;
function GetTickCount: Cardinal;   //IdICMP uses this
  //required because GetTickCount will wrap
function GetTickDiff(const AOldTickCount, ANewTickCount : Cardinal):Cardinal; //IdICMP uses it
procedure IdDelete(var s : string; AOffset, ACount : integer);
function IdPorts: TList;
function iif(ATest: Boolean; const ATrue: Integer; const AFalse: Integer): Integer; overload;
function iif(ATest: Boolean; const ATrue: string;  const AFalse: string): string; overload;
function iif(ATest: Boolean; const ATrue: Boolean; const AFalse: Boolean): Boolean; overload;
function IncludeTrailingSlash(const APath: string): string;
{$IFNDEF DOTNET}
function InMainThread: boolean;
function IsCurrentThread(AThread: TThread): boolean;
{$ENDIF}
function IPv4ToDWord(const AIPAddress : String) : Cardinal; overload;
function IPv4ToDWord(const AIPAddress : String; var VErr : Boolean) : Cardinal; overload;
function IPv4ToHex(const AIPAddress : String; const ASDotted : Boolean=False) : String;
function IPv4ToOctal(const AIPAddress : String) : String;

function IsHexidecimal(AChar: Char): Boolean; overload;
function IsHexidecimal(const AString: string): Boolean; overload;
function IsNumeric(AChar: Char): Boolean; overload;
function IsNumeric(const AString: string): Boolean; overload;
function IsOctal(AChar: Char): Boolean; overload;
function IsOctal(const AString: string): Boolean; overload;
function MakeCanonicalIPv4Address(const AAddr: string): string;
function MakeCanonicalIPv6Address(const AAddr: string): string;
function MakeDWordIntoIPv4Address(const ADWord : Cardinal) : String;
function Max(AValueOne,AValueTwo: Integer): Integer;
{$IFNDEF DOTNET}
function MemoryPos(const ASubStr: String; MemBuff: PChar; MemorySize: Integer): Integer;
{$ENDIF}
function Min(AValueOne, AValueTwo : Integer): Integer;
function PosIdx (const ASubStr,AStr: AnsiString; AStartPos: Cardinal=0): Cardinal;//For "ignoreCase" use AnsiUpperCase
{$IFNDEF DOTNET}
{$ENDIF}
function ServicesFilePath : String;
procedure SetThreadPriority(AThread: TThread; const APriority: TIdThreadPriority; const APolicy: Integer = -MaxInt);
procedure SetThreadName(const AName: string);
procedure Sleep(ATime: cardinal);
//in Integer(Strings.Objects[i]) - column position in AData
procedure SplitColumnsNoTrim(const AData: String; AStrings: TStrings; const ADelim: String=' ');    {Do not Localize}
procedure SplitColumns(const AData: String; AStrings: TStrings; const ADelim: String=' ');    {Do not Localize}
{$IFNDEF DOTNET}
{$ENDIF}
procedure ToDo;

var
  IndyPos: TPosProc = nil;
  {$IFNDEF VCL6ORABOVE}
    {$IFDEF MSWINDOWS}
  RaiseLastOSError:procedure = SysUtils.RaiseLastWin32Error;
    {$ELSE}
  RaiseLastOSError:procedure = SysUtils.RaiseLastOSError;
    {$ENDIF}
  {$ELSE}
  RaiseLastOSError:procedure = SysUtils.RaiseLastOSError;
  {$ENDIF}

implementation

uses
  {$IFDEF LINUX} Libc, {$ENDIF}
  IdCoreResourceStrings;

var
  GIdPorts: TList;

//IPv4 address conversion
function ByteToHex(const AByte: Byte): String;
begin
  Result := IdHexDigits[AByte shr 4] + IdHexDigits[AByte and $F];
end;

function ToHex(const AValue: TIdBytes): AnsiString;
var
  i: Integer;
begin
  SetLength(Result,Length(AValue)*2);
  for i:=0 to Length(AValue)-1 do begin
    Result[i*2+1]:=IdHexDigits[AValue[i] shr 4];
    Result[i*2+2]:=IdHexDigits[AValue[i] and $F];
  end;//for   
end;

{$IFNDEF DOTNET}
function ToHex(const AValue: Array of LongWord): AnsiString;
var
  P: PChar;
  i: Integer;
begin
  P:=PChar(@AValue);
  SetString(Result,NIL,Length(AValue)*4*2);//40
  for i:=0 to Length(AValue)*4-1 do begin
    Result[i*2+1]:=IdHexDigits[ord(P[i]) shr 4];
    Result[i*2+2]:=IdHexDigits[ord(P[i]) and $F];
  end;//for
end;
{$ELSE}
function ToHex(const AValue: Array of LongWord): AnsiString;
var
  i: Integer;
begin
  for i:=0 to Length(AValue)-1 do begin
    Result:=Result+ToHex(ToBytes(AValue[i]));
  end;//for
end;
{$ENDIF}

function IPv4ToHex(const AIPAddress: String; const ASDotted: Boolean = False): String;
var
  i: Integer;
  LBuf, LTmp: string;
begin
  LBuf := Trim(AIPAddress);
  Result := HEXPREFIX;

  for i := 0 to 3 do begin
    LTmp := ByteToHex(StrToIntDef(Fetch(LBuf, '.', True), 0));

    if ASDotted then begin
      Result := Result + '.' + HEXPREFIX + LTmp;
    end else begin
	    Result := Result + LTmp;
    end;
  end;
end;

function OctalToInt64(const AValue: string): Int64;
var
  i: Integer;
begin
  Result := 0;
  for i := 1 to Length(AValue) do
  begin
    Result := (Result shl 3) + StrToIntDef(copy(AValue, i, 1), 0);
  end;
end;

function ByteToOctal(const AByte: Byte): String;
begin
  Result := IdOctalDigits[(AByte shr 6) and $7] +
            IdOctalDigits[(AByte shr 3) and $7] +
            IdOctalDigits[AByte and $7];

  if Result[1] <> '0' then
  begin
    Result := '0' + Result;
  end;
end;

function IPv4ToOctal(const AIPAddress: String): String;
var
  i: Integer;
  LBuf: String;
begin
  LBuf := Trim(AIPAddress);
  Result := ByteToOctal(StrToIntDef(Fetch(LBuf, '.', True), 0));
  for i := 0 to 2 do
  begin
    Result := Result + '.' + ByteToOctal(StrToIntDef(Fetch(LBuf, '.', True), 0));
  end;
end;

procedure CopyTIdBytes(const ASource:TIdBytes;const ASourceIndex:integer;
    const ADest:TIdBytes;const ADestIndex:integer;const ALength:integer);
begin
  {$IFDEF DOTNET}
  System.Array.Copy(ASource,ASourceIndex,ADest,ADestIndex,ALength);
  {$ELSE}
  move(ASource[ASourceIndex], ADest[ADestIndex], ALength);
  {$ENDIF}
end;

procedure CopyTIdByteArray(const ASource: Array of Byte;const ASourceIndex:integer;
    var ADest : Array of Byte; const ADestIndex:integer;const ALength:integer);
begin
  {$IFDEF DOTNET}
  System.Array.Copy(ASource,ASourceIndex,ADest,ADestIndex,ALength);
  {$ELSE}
  move(ASource[ASourceIndex], ADest[ADestIndex], ALength);
  {$ENDIF}
end;

procedure DebugOutput(const AText: string);
begin
  {$IFDEF LINUX}
  __write(stderr, AText, Length(AText));
  __write(stderr, EOL, Length(EOL));
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  OutputDebugString(PChar(AText));
  {$ENDIF}
  {$IFDEF DOTNET}
   System.Diagnostics.Debug.WriteLine(AText);
  {$ENDIF}
end;

{$IFNDEF DotNet}
function GetCurrentThreadHandle : THandle;
begin
  Result := GetCurrentThreadID;
end;
{$ELSE}
function GetCurrentThreadHandle : THandle;
begin
  // SG: I'm not sure if this return the handle of the dotnet thread or the handle of the application domain itself (or even if there is a difference)
  Result := AppDomain.GetCurrentThreadId;
end;
{$ENDIF}

function CurrentProcessId: TIdPID;
begin
  {$IFDEF LINUX}
  Result := getpid;
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  Result := GetCurrentProcessID;
  {$ENDIF}
  {$IFDEF DOTNET}
  Result := System.Diagnostics.Process.GetCurrentProcess.ID;
  {$ENDIF}
end;

function Fetch(var AInput: string; const ADelim: string = IdFetchDelimDefault;
 const ADelete: Boolean = IdFetchDeleteDefault;
 const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String;
var
  LPos: integer;
begin
  if ACaseSensitive then begin
    if ADelim = #0 then begin
      // AnsiPos does not work with #0
      LPos := Pos(ADelim, AInput);
    end else begin
      LPos := IndyPos(ADelim, AInput);
    end;
    if LPos = 0 then begin
      Result := AInput;
      if ADelete then begin
        AInput := '';    {Do not Localize}
      end;
    end
    else begin
      Result := Copy(AInput, 1, LPos - 1);
      if ADelete then begin
        //slower Delete(AInput, 1, LPos + Length(ADelim) - 1);
        AInput:=Copy(AInput, LPos + Length(ADelim), MaxInt);
      end;
    end;
  end else begin
    Result := FetchCaseInsensitive(AInput, ADelim, ADelete);
  end;
end;

function FetchCaseInsensitive(var AInput: string; const ADelim: string = IdFetchDelimDefault;
 const ADelete: Boolean = IdFetchDeleteDefault): String;
var
  LPos: integer;
begin
  if ADelim = #0 then begin
    // AnsiPos does not work with #0
    LPos := Pos(ADelim, AInput);
  end else begin
    //? may be AnsiUpperCase?
    LPos := IndyPos(UpperCase(ADelim), UpperCase(AInput));
  end;
  if LPos = 0 then begin
    Result := AInput;
    if ADelete then begin
      AInput := '';    {Do not Localize}
    end;
  end else begin
    Result := Copy(AInput, 1, LPos - 1);
    if ADelete then begin
      //This is faster than Delete(AInput, 1, LPos + Length(ADelim) - 1);
      AInput := Copy(AInput, LPos + Length(ADelim), MaxInt);
    end;
  end;
end;

function GetThreadHandle(AThread : TThread) : THandle;
begin
  {$IFDEF LINUX}
  Result := AThread.ThreadID;
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  Result := AThread.Handle;
  {$ENDIF}
  {$IFDEF DOTNET}
  Result := 0;
  {$ENDIF}
end;

{$IFDEF LINUX}
function GetTickCount: Cardinal;
var
  tv: timeval;
begin
  gettimeofday(tv, nil);
  {$RANGECHECKS OFF}
  Result := int64(tv.tv_sec) * 1000 + tv.tv_usec div 1000;
  {
    I've implemented this correctly for now. I'll argue for using
    an int64 internally, since apparently quite some functionality
    (throttle, etc etc) depends on it, and this value may wrap
    at any point in time.
    For Windows: Uptime > 72 hours isn't really that rare any more,
    For Linux: no control over when this wraps.

    IdEcho has code to circumvent the wrap, but its not very good
    to have code for that at all spots where it might be relevant.

  }
end;
{$ENDIF}

{$IFDEF MSWINDOWS}
// S.G. 27/11/2002: Changed to use high-performance counters as per suggested
// S.G. 27/11/2002: by David B. Ferguson (david.mcs@ns.sympatico.ca)
function GetTickCount: Cardinal;
var
  nTime, freq: Int64;
begin
  if Windows.QueryPerformanceFrequency(freq) then
    if Windows.QueryPerformanceCounter(nTime) then
       result:=Trunc(nTime/Freq*1000)
    else
       result:= Windows.GetTickCount
  else
    result:= Windows.GetTickCount;
end;
{$ENDIF}

{$IFDEF DOTNET}
function GetTickCount: Cardinal;
var
  nTime, freq: Int64;
begin
  if Windows.QueryPerformanceFrequency(freq) then
    if Windows.QueryPerformanceCounter(nTime) then
       result:=Trunc(nTime/Freq*1000)
    else
       result:= Windows.GetTickCount
  else
    result:= Windows.GetTickCount;
end;
{$ENDIF}

function GetTickDiff(const AOldTickCount, ANewTickCount : Cardinal):Cardinal;
begin
  {This is just in case the TickCount rolled back to zero}
    if ANewTickCount >= AOldTickCount then begin
      Result := ANewTickCount - AOldTickCount;
    end else begin
      Result := High(Cardinal) - AOldTickCount + ANewTickCount;
    end;
end;

function ServicesFilePath : String;
var sLocation : String;
begin
    {$IFDEF LINUX}
    sLocation := '/etc/';  // assume Berkeley standard placement   {do not localize}
    {$ENDIF}
    {$IFDEF MSWINDOWS}
    SetLength(sLocation, MAX_PATH);
    SetLength(sLocation, GetWindowsDirectory(pchar(sLocation), MAX_PATH));
    sLocation := IncludeTrailingSlash(sLocation);
    if Win32Platform = VER_PLATFORM_WIN32_NT then begin
      sLocation := sLocation + 'system32\drivers\etc\'; {do not localize}
    end;
    {$ENDIF}
  Result := sLocation + 'services'; {do not localize}
end;

// IdPorts returns a list of defined ports in /etc/services
function IdPorts: TList;
var
  s: String;
  idx, i, iPrev, iPosSlash: integer;
  sl: TStringList;
begin
  if GIdPorts = nil then
  begin
    GIdPorts := TList.Create;
    sl := TStringList.Create;
    try
      sl.LoadFromFile(ServicesFilePath);  {do not localize}
      iPrev := 0;
      for idx := 0 to sl.Count - 1 do
      begin
        s := sl[idx];
        iPosSlash := IndyPos('/', s);   {do not localize}
        if (iPosSlash > 0) and (not (IndyPos('#', s) in [1..iPosSlash])) then {do not localize}
        begin // presumably found a port number that isn't commented    {Do not Localize}
          i := iPosSlash;
          repeat
            dec(i);
            if i = 0 then begin
              raise EIdCorruptServicesFile.CreateFmt(RSCorruptServicesFile, [ServicesFilePath]); {do not localize}
            end;
                                                            
          until s[i] in WhiteSpace;
          i := StrToInt(Copy(s, i+1, iPosSlash-i-1));
          if i <> iPrev then begin
            GIdPorts.Add(TObject(i));
          end;
          iPrev := i;
        end;
      end;
    finally
      sl.Free;
    end;
  end;
  Result := GIdPorts;
end;

function iif(ATest: Boolean; const ATrue: Integer; const AFalse: Integer): Integer;
begin
  if ATest then begin
    Result := ATrue;
  end else begin
    Result := AFalse;
  end;
end;

function iif(ATest: Boolean; const ATrue: string; const AFalse: string): string;
begin
  if ATest then begin
    Result := ATrue;
  end else begin
    Result := AFalse;
  end;
end;

function iif(ATest: Boolean; const ATrue: Boolean; const AFalse: Boolean): Boolean;
begin
  if ATest then begin
    Result := ATrue;
  end else begin
    Result := AFalse;
  end;
end;

function IncludeTrailingSlash(const APath: string): string;
begin
  {for some odd reason, the IFDEF's were not working in Delphi 4
  so as a workaround and to ensure some code is actually compiled into
  the procedure, I use a series of $elses}
  {$IFDEF VCL5O}
  Result := IncludeTrailingBackSlash(APath);
  {$ELSE}
    {$IFDEF VCL6ORABOVE}
    Result :=  IncludeTrailingPathDelimiter(APath);
    {$ELSE}
    Result := APath;
    if not IsPathDelimiter(Result, Length(Result)) then begin
      Result := Result + GPathDelim;
    end;
    {$ENDIF}
  {$ENDIF}
end;

{$IFNDEF DotNet}
function InMainThread: boolean;
begin
  Result := GetCurrentThreadID = MainThreadID;
end;

function IsCurrentThread(AThread: TThread): boolean;
begin
  Result := AThread.ThreadID = GetCurrentThreadID;
end;
{$ENDIF}

//convert a dword into an IPv4 address in dotted form
function MakeDWordIntoIPv4Address(const ADWord: Cardinal): String;
begin
  Result := IntToStr((ADWord shr 24) and $FF) + '.';
  Result := Result + IntToStr((ADWord shr 16) and $FF) + '.';
  Result := Result + IntToStr((ADWord shr 8) and $FF) + '.';
  Result := Result + IntToStr(ADWord and $FF);
end;

function IsOctal(AChar: Char): Boolean; overload;
begin
  Result := (AChar >= '0') and (AChar <= '7') {Do not Localize}
end;

function IsOctal(const AString: string): Boolean; overload;
var
  i : Integer;
begin
  Result := True;
  for i := 1 to Length(AString) do
  begin
    if IsOctal(AString[i])=False then
    begin
      Result := False;
    end;
  end;
end;

function IsHexidecimal(AChar: Char): Boolean; overload;
begin
  Result := ((AChar >= '0') and (AChar <= '9')) {Do not Localize}
   or ((AChar >= 'A') and (AChar <= 'F')) {Do not Localize}
   or ((AChar >= 'a') and (AChar <= 'f')); {Do not Localize}
end;

function IsHexidecimal(const AString: string): Boolean; overload;
var i : Integer;
begin
  Result := True;
  for i := 1 to Length(AString) do
  begin
    if IsHexidecimal(AString[i])=False then
    begin
      Result := False;
    end;
  end;
end;

{$HINTS OFF}
function IsNumeric(const AString: string): Boolean;
var
  LCode: Integer;
  LVoid: Int64;
begin
  Val(AString, LVoid, LCode);
  Result := LCode = 0;
end;
{$HINTS ON}

function IsNumeric(AChar: Char): Boolean;
begin
  // Do not use IsCharAlpha or IsCharAlphaNumeric - they are Win32 routines
  Result := (AChar >= '0') and (AChar <= '9'); {Do not Localize}
end;

{
This is an adaptation of the StrToInt64 routine in SysUtils.
We had to adapt it to work with Int64 because the one with Integers
can not deal with anything greater than MaxInt and IP addresses are
always $0-$FFFFFFFF (unsigned)
}
function StrToInt64Def(const S: string; Default: Integer): Int64;
var
  E: Integer;
begin
  Val(S, Result, E);
  if E <> 0 then
  begin
    Result := Default;
  end;
end;

{$IFNDEF DotNet}
function IPv4MakeCardInRange(const AInt : Int64; const A256Power : Integer) : Cardinal;
//Note that this function is only for stripping off some extra bits
//from an address that might appear in some spam E-Mails.
begin
  case A256Power of
    4 : Result := (AInt and POWER_4);
    3 : Result := (AInt and POWER_3);
    2 : Result := (AInt and POWER_2);
  else
    Result := Lo(AInt and POWER_1);
  end;
end;
{$ENDIF}

function IPv4ToDWord(const AIPAddress : String) : Cardinal; overload;
var LErr : Boolean;
begin
  Result := IPv4ToDWord(AIPAddress,LErr);
end;

{$IFDEF DotNet}
function IPv4ToDWord(const AIPAddress : String; var VErr : Boolean) : Cardinal; overload;
var
  AIPaddr: IPAddress;
begin
  VErr := true;
  result := 0;
  AIPaddr := System.Net.IPAddress.Parse(AIPAddress);
  try
    try
      if AIPaddr.AddressFamily = Addressfamily.InterNetwork then
      begin
        result := AIPaddr.Address;
        VErr := false;
      end;
    except
      VErr := true;
    end;
  finally
    AIPaddr.free;
  end;
end;
{$ELSE}
function IPv4ToDWord(const AIPAddress : String; var VErr : Boolean) : Cardinal; overload;
var
  LBuf, LBuf2 : String;
  L256Power : Integer;
  LParts : Integer; //how many parts should we process at a time
begin
  // S.G. 11/8/2003: Added overflow checking disabling and change multiplys by SHLs.
  // Locally disable overflow checking so we can safely use SHL and SHR
  {$ifopt Q+} // detect previous setting
  {$define _QPlusWasEnabled}
  {$Q-}
  {$endif}
  VErr := True;
  L256Power := 4;
  LBuf2 := AIPAddress;
  Result := 0;
  repeat
    LBuf := Fetch(LBuf2,'.');
    if LBuf = '' then
    begin
      break;
    end;
    //We do things this way because we have to treat
    //IP address parts differently than a whole number
    //and sometimes, there can be missing periods.
    if (LBuf2='') and (L256Power > 1) then
    begin
      LParts := L256Power;
      Result := Result shl (L256Power SHL 3);
//      Result := Result shl ((L256Power - 1) SHL 8);
    end
    else
    begin
      LParts := 1;
      result := result SHL 8;
    end;
    if (Copy(LBuf,1,2)=HEXPREFIX) then
    begin
      //this is a hexideciaml number
      if IsHexidecimal(Copy(LBuf,3,MaxInt))=False then
      begin
        Exit;
      end
      else
      begin
        Result :=  Result + IPv4MakeCardInRange (StrToInt64Def(LBuf,0), LParts);
      end;
    end
    else
    begin
      if IsNumeric(LBuf) then
      begin
        if (LBuf[1]='0') and IsOctal(LBuf) then
        begin
          //this is octal
          Result := Result + IPv4MakeCardInRange(OctalToInt64(LBuf),LParts);
        end
        else
        begin
          //this must be a decimal
          Result :=  Result + IPv4MakeCardInRange(StrToInt64Def(LBuf,0), LParts);
        end;
      end
      else
      begin
        //There was an error meaning an invalid IP address
        Exit;
      end;
    end;
    Dec(L256Power);
  until False;
  VErr := False;
  // Restore overflow checking
  {$ifdef _QPlusWasEnabled} // detect previous setting
  {$undef _QPlusWasEnabled}
  {$Q-}
  {$endif}
end;
{$ENDIF}

function MakeCanonicalIPv4Address(const AAddr: string): string;
var LErr : Boolean;
  LIP : Cardinal;
begin
  LIP := IPv4ToDWord(AAddr,LErr);
  if LErr then begin
    Result := '';
  end else begin
    Result := MakeDWordIntoIPv4Address(LIP);
  end;
end;

function MakeCanonicalIPv6Address(const AAddr: string): string;
// return an empty string if the address is invalid,
// for easy checking if its an address or not.
var
  p, i: integer;
  dots, colons: integer;
  colonpos: array[1..8] of integer;
  dotpos: array[1..3] of integer;
  LAddr: string;
  num: integer;
  haddoublecolon: boolean;
  fillzeros: integer;
begin
  Result := ''; // error
  LAddr := AAddr;
  if Length(LAddr) = 0 then exit;

  if LAddr[1] = ':' then begin
    LAddr := '0'+LAddr;
  end;
  if LAddr[Length(LAddr)] = ':' then begin
    LAddr := LAddr + '0';
  end;
  dots := 0;
  colons := 0;
  for p := 1 to Length(LAddr) do begin
    case LAddr[p] of
      '.' : begin
              inc(dots);
              if dots < 4 then begin
                dotpos[dots] := p;
              end else begin
                exit; // error in address
              end;
            end;
      ':' : begin
              inc(colons);
              if colons < 8 then begin
                colonpos[colons] := p;
              end else begin
                exit; // error in address
              end;
            end;
      'a'..'f',
      'A'..'F': if dots>0 then exit;
        // allow only decimal stuff within dotted portion, ignore otherwise
      '0'..'9': ; // do nothing
      else exit; // error in address
    end; // case
  end; // for
  if not (dots in [0,3]) then begin
    exit; // you have to write 0 or 3 dots...
  end;
  if dots = 3 then begin
    if not (colons in [2..6]) then begin
      exit; // must not have 7 colons if we have dots
    end;
    if colonpos[colons] > dotpos[1] then begin
      exit; // x:x:x.x:x:x is not valid
    end;
  end else begin
    if not (colons in [2..7]) then begin
      exit; // must at least have two colons
    end;
  end;

  // now start :-)
  num := StrToIntDef('$'+Copy(LAddr, 1, colonpos[1]-1), -1);
  if (num<0) or (num>65535) then begin
    exit; // huh? odd number...
  end;
  Result := IntToHex(num,1)+':';

  haddoublecolon := false;
  for p := 2 to colons do begin
    if colonpos[p-1] = colonpos[p]-1 then begin
      if haddoublecolon then begin
        Result := '';
        exit; // only a single double-dot allowed!
      end;
      haddoublecolon := true;
      fillzeros := 8 - colons;
      if dots>0 then dec(fillzeros,2);
      for i := 1 to fillzeros do begin
        Result := Result + '0:'; {do not localize}
      end;
    end else begin
      num := StrToIntDef('$'+Copy(LAddr, colonpos[p-1]+1, colonpos[p]-colonpos[p-1]-1), -1);
      if (num<0) or (num>65535) then begin
        Result := '';
        exit; // huh? odd number...
      end;
      Result := Result + IntToHex(num,1)+':';
    end;
  end; // end of colon separated part

  if dots = 0 then begin
    num := StrToIntDef('$'+Copy(LAddr, colonpos[colons]+1, MaxInt), -1);
    if (num<0) or (num>65535) then begin
      Result := '';
      exit; // huh? odd number...
    end;
    Result := Result + IntToHex(num,1)+':';
  end;

  if dots > 0 then begin
    num := StrToIntDef(Copy(LAddr, colonpos[colons]+1, dotpos[1]-colonpos[colons]-1),-1);
    if (num < 0) or (num>255) then begin
      Result := '';
      exit;
    end;
    Result := Result + IntToHex(num, 2);
    num := StrToIntDef(Copy(LAddr, dotpos[1]+1, dotpos[2]-dotpos[1]-1),-1);
    if (num < 0) or (num>255) then begin
      Result := '';
      exit;
    end;
    Result := Result + IntToHex(num, 2)+':';

    num := StrToIntDef(Copy(LAddr, dotpos[2]+1, dotpos[3]-dotpos[2]-1),-1);
    if (num < 0) or (num>255) then begin
      Result := '';
      exit;
    end;
    Result := Result + IntToHex(num, 2);
    num := StrToIntDef(Copy(LAddr, dotpos[3]+1, 3), -1);
    if (num < 0) or (num>255) then begin
      Result := '';
      exit;
    end;
    Result := Result + IntToHex(num, 2)+':';
  end;
  SetLength(Result, Length(Result)-1);
end;

function Max(AValueOne,AValueTwo: Integer): Integer;
begin
  if AValueOne < AValueTwo then
  begin
    Result := AValueTwo
  end //if AValueOne < AValueTwo then
  else
  begin
    Result := AValueOne;
  end; //else..if AValueOne < AValueTwo then
end;

{$IFNDEF DOTNET}
function MemoryPos(const ASubStr: String; MemBuff: PChar; MemorySize: Integer): Integer;
var
  LSearchLength: Integer;
  LS1: Integer;
  LChar: Char;
  LPS,LPM: PChar;
begin
  LSearchLength := Length(ASubStr);
  if (LSearchLength = 0) or (LSearchLength > MemorySize) then begin
    Result := 0;
    Exit;
  end;

  LChar := PChar(Pointer(ASubStr))^; //first char
  LPS:=PChar(Pointer(ASubStr))+1;//tail string
  LPM:=MemBuff;
  LS1:=LSearchLength-1;
  LSearchLength := MemorySize-LS1;//MemorySize-LS+1
  if LS1=0 then begin //optimization for freq used LF
    while LSearchLength>0 do begin
      if LPM^= LChar then begin
        Result:=LPM-MemBuff+1;
        EXIT;
      end;
      inc(LPM);
      dec(LSearchLength);
    end;//while
  end else begin
    while LSearchLength>0 do begin
      if LPM^= LChar then begin
        inc(LPM);
        if CompareMem(LPM,LPS,LS1) then begin
          Result:=LPM-MemBuff;
          EXIT;
        end;
      end
      else begin
        inc(LPM);
      end;
      dec(LSearchLength);
    end;//while
  end;//if OneChar
  Result:=0;
End;
{$ENDIF}

function Min(AValueOne, AValueTwo : Integer): Integer;
begin
  If AValueOne > AValueTwo then
  begin
    Result := AValueTwo
  end //If AValueOne > AValueTwo then
  else
  begin
    Result := AValueOne;
  end; //..If AValueOne > AValueTwo then
end;

Function  PosIdx (const ASubStr,AStr: AnsiString; AStartPos: Cardinal): Cardinal;
{$IFDEF DotNet}
var
  SearchString: AnsiString;
begin
  SearchString := Copy(AStr, AStartPos, Length(AStr));
  result := pos(ASubStr, SearchString);
end;
{$else}
var
  lpSubStr,lpS: PChar;
  LenSubStr,LenS: Integer;
  LChar: Char;
Begin
  LenSubStr:=Length(ASubStr);
  LenS:=Length(AStr);

  if (LenSubStr=0) or (LenSubStr>LenS) then begin
    Result:=0;//not found
    EXIT;
  end;//if

  lpSubStr:=Pointer(ASubStr);
  lpS:=Pointer(AStr);
  if AStartPos>0 then begin
    lpS:=lpS+AStartPos-1;
    LenS:=LenS+1-Integer(AStartPos);
  end;//if

  LChar :=lpSubStr[0];//first char
  lpSubStr:=lpSubStr  +1;//next char
  LenSubStr:=LenSubStr-1;//len w/o first char

  LenS:=LenS-LenSubStr; //Length(S)-Length(SubStr) +1(!) MUST BE >0
  if LenS<=0 then begin
    Result:=0;
    EXIT;
  end;//if

  while LenS>0 do begin
    if lpS^= LChar then begin
      inc(lpS);
      if CompareMem(lpS,lpSubStr,LenSubStr) then begin
        Result:=lpS-Pointer(AStr);//+1 already here
        EXIT;
      end;
    end
    else begin
      inc(lpS);
    end;
    dec(LenS);
  end;
  Result:=0;
end;
{$ENDIF}

function SBPos(const Substr, S: string): Integer;
// Necessary because of "Compiler magic"
begin
  Result := Pos(Substr, S);
end;

procedure SetThreadPriority(AThread: TThread; const APriority: TIdThreadPriority; const APolicy: Integer = -MaxInt);
begin
  {$IFDEF LINUX}
  // Linux only allows root to adjust thread priorities, so we just ingnore this call in Linux?
  // actually, why not allow it if root
  // and also allow setting *down* threadpriority (anyone can do that)
  // note that priority is called "niceness" and positive is lower priority
  if (getpriority(PRIO_PROCESS, 0) < APriority) or (geteuid = 0) then begin
    setpriority(PRIO_PROCESS, 0, APriority);
  end;
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  AThread.Priority := APriority;
  {$ENDIF}
end;

procedure Sleep(ATime: cardinal);
{$IFDEF LINUX}
var
  LTime: TTimeVal;
begin
  // what if the user just calls sleep? without doing anything...
  // cannot use GStack.WSSelectRead(nil, ATime)
  // since no readsocketlist exists to get the fdset
  LTime.tv_sec := ATime div 1000;
  LTime.tv_usec := (ATime mod 1000) * 1000;
  Libc.Select(0, nil, nil, nil, @LTime);
end;
{$ENDIF}
{$IFDEF MSWINDOWS}
begin
  Windows.Sleep(ATime);
end;
{$ENDIF}
{$IFDEF DOTNET}
begin
  Sleep(ATime);
end;
{$ENDIF}

procedure SplitColumnsNoTrim(const AData: String; AStrings: TStrings; const ADelim: String=' ');    {Do not Localize}
var
  i: Integer;
  LDelim: Integer; //delim len
  LLeft: String;
  LLastPos: Integer;
begin
  Assert(Assigned(AStrings));
  AStrings.Clear;
  LDelim := Length(ADelim);
  LLastPos := 1;

  i := Pos(ADelim, AData);
  while I > 0 do begin
    LLeft:= Copy(AData, LLastPos, I-LLastPos); //'abc d' len:=i(=4)-1    {Do not Localize}
    if LLeft > '' then begin    {Do not Localize}
      {$Ifdef DotNet}
      AStrings.AddObject(LLeft,TObject(LLastPos));
      {$else}
      AStrings.AddObject(LLeft,Pointer(LLastPos));
      {$endif}
    end;
    LLastPos := I + LDelim; //first char after Delim
    i := PosIdx (ADelim, AData, LLastPos);
  end;
  if LLastPos <= Length(AData) then begin
    {$IfDef DotNet}
    AStrings.AddObject(Copy(AData,LLastPos,MaxInt), TObject(LLastPos));
    {$else}
    AStrings.AddObject(Copy(AData,LLastPos,MaxInt), Pointer(LLastPos));
    {$endif}
  end;
end;
{$IFNDEF DotNet}
{$ENDIF}


{$IFDEF ALLOW_NAMED_THREADS}
procedure SetThreadName(const AName: string);
type
  TThreadNameInfo = record
	RecType: LongWord;  // Must be 0x1000
	Name: PChar;        // Pointer to name (in user address space)
	ThreadID: LongWord; // Thread ID (-1 indicates caller thread)
	Flags: LongWord;    // Reserved for future use. Must be zero
  end;
var
  LThreadNameInfo: TThreadNameInfo;
begin
  with LThreadNameInfo do begin
	RecType := $1000;
	Name := PChar(AName);
	ThreadID := $FFFFFFFF;
	Flags := 0;
  end;
  try
	// This is a wierdo Windows way to pass the info in
	RaiseException($406D1388, 0, SizeOf(LThreadNameInfo) div SizeOf(LongWord)
	 , PDWord(@LThreadNameInfo));
  except end;
end;
{$ELSE}
procedure SetThreadName(const AName: string);
begin
end;
{$ENDIF}

procedure SplitColumns(const AData: String; AStrings: TStrings; const ADelim: String=' ');    {Do not Localize}
var
  i: Integer;
  LData: String;
  LDelim: Integer; //delim len
  LLeft: String;
  LLastPos: Integer;
  LLeadingSpaceCnt: Integer;
Begin
  Assert(Assigned(AStrings));
  AStrings.Clear;
  LDelim := Length(ADelim);
  LLastPos := 1;
  LData := Trim(AData);

  LLeadingSpaceCnt := 0;
  if Length(LData)>0 then begin //if Not WhiteStr
    while AData[LLeadingSpaceCnt+1]<=' ' do inc(LLeadingSpaceCnt);
  end
  else begin
    EXIT;
  end;

  i := Pos(ADelim, LData);
  while I > 0 do begin
    LLeft:= Copy(LData, LLastPos, I-LLastPos); //'abc d' len:=i(=4)-1    {Do not Localize}
    if LLeft > '' then begin    {Do not Localize}
      {$IFDEF DotNet}
      AStrings.AddObject(Trim(LLeft),TObject(LLastPos + LLeadingSpaceCnt));
      {$ELSE}
      AStrings.AddObject(Trim(LLeft),Pointer(LLastPos + LLeadingSpaceCnt));
      {$ENDIF}
    end;
    LLastPos := I + LDelim; //first char after Delim
    i := PosIdx (ADelim, LData, LLastPos);
  end;//while found
  if LLastPos <= Length(LData) then begin
    {$IFDEF DotNet}
    AStrings.AddObject(Trim( Copy(LData,LLastPos,MaxInt) ), TObject(LLastPos+LLeadingSpaceCnt));
    {$ELSE}
    AStrings.AddObject(Trim( Copy(LData,LLastPos,MaxInt) ), Pointer(LLastPos+LLeadingSpaceCnt));
    {$ENDIF}
  end;
end;
{$IFNDEF DotNet}
{$ENDIF}

{ TIdLocalEvent }

constructor TIdLocalEvent.Create(const AInitialState: Boolean = False; const AManualReset: Boolean = False);
begin
  inherited Create(nil, AManualReset, AInitialState, '');    {Do not Localize}
end;

function TIdLocalEvent.WaitForEver: TWaitResult;
begin
  Result := WaitFor(Infinite);
end;

{ TIdList }

{$IFNDEF VCL6ORABOVE}
procedure TIdExtList.Assign(AList: TList);
var a:integer;
begin
  Clear;
  Capacity := AList.Capacity;
  for a := 0 to AList.Count - 1 do
    Add( AList.Items[a] );
end;
{$ENDIF}

procedure ToDo;
begin
  raise EIdException.Create('To do item undone.'); {do not localize}
end;

function ToBytes(const AValue: string): TIdBytes; overload;
begin
   {$IFDEF DotNet}
   Result := AnsiEncoding.GetBytes(AValue);
   {$ELSE}
   SetLength(Result, Length(AValue));
   if Length(AValue) > 0 then
     begin
     move(AValue[1], result[0], length(AValue));
     end;
   {$ENDIF}
end;

function ToBytes(const AValue: Integer): TIdBytes; overload;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.GetBytes(AValue);
  {$ELSE}
  SetLength(Result, 4);
  Move(AValue, Result[0], 4);
  {$ENDIF}
end;

function ToBytes(const AValue: Cardinal): TIdBytes; overload;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.GetBytes(AValue);
  {$ELSE}
  SetLength(Result, 4);
  Move(AValue, Result[0], 4);
  {$ENDIF}
end;

function ToBytes(const AValue: Short): TIdBytes; overload;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.GetBytes(AValue);
  {$ELSE}
  SetLength(Result, 2);
  Move(AValue, Result[0], 2);
  {$ENDIF}
end;

function BytesToInteger(const AValue: TIdBytes): Integer;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.ToInt32(AValue,0);
  {$ELSE}
  Move(AValue[0], Result, 4);
  {$ENDIF}
end;

function BytesToShort(const AValue: TIdBytes): Short;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.ToInt16(AValue,0);
  {$ELSE}
  Move(AValue[0], Result, 2);
  {$ENDIF}
end;

function BytesToCardinal(const AValue: TIdBytes): Cardinal;
begin
  {$IFDEF DotNet}
  Result := System.BitConverter.ToUInt32(AValue,0);
  {$ELSE}
  Move(AValue[0], Result, 4);
  {$ENDIF}
end;

function BytesToString(const ABytes : TIdBytes):String; overload;
begin
   {$IFDEF DotNet}
   Result := AnsiEncoding.GetString(ABytes);
   {$ELSE}
   SetLength(Result, Length(ABytes));
   if Length(ABytes) > 0 then begin
     Move(ABytes[0], Result[1], Length(ABytes));
   end;
   {$ENDIF}
end;

function ReadStringFromStream(AStream : TStream; ASize : integer):String;
{$IFDEF DOTNET}
var
  LBytes : TIdBytes;
{$ENDIF}
begin
  if ASize = 0 then
    begin
    result := '';
    end
  else
    begin
    {$IFDEF DOTNET}
    SetLength(LBytes, ASize);
    AStream.Read(LBytes, 0, ASize);
    result := BytesToString(LBytes);
    {$ELSE}
    SetLength(result, AStream.Size);
    AStream.Read(result[1], AStream.Size);
    {$ENDIF}
    end;
end;

procedure WriteStringToStream(AStream : TStream; AStr :String);
{$IFDEF DOTNET}
var
  LBytes : TIdBytes;
{$ENDIF}
begin
  if AStr <> '' then
    begin
    {$IFDEF DOTNET}
    LBytes := ToBytes(AStr);
    AStream.Write(LBytes, 0, Length(LBytes));
    {$ELSE}
    AStream.Write(AStr[1], length(AStr));
    {$ENDIF}
    end;
end;

//    function IdRead(var Buffer: TIdByteArray; Offset, Count: Longint): Longint; virtual; abstract;
//    function IdWrite(const Buffer: TIdByteArray; Offset, Count: Longint): Longint; virtual; abstract;
//    function IdSeek(const Offset: Int64; Origin: TSeekOrigin): Int64; virtual; abstract;

{$IFDEF DOTNET}
function TIdBaseStream.Read(var VBuffer: array of Byte; AOffset, ACount: Longint): Longint;
var
  LBytes : TIdBytes;
begin
  // this is a silly work around really, but array of Byte and TIdByte aren't
  // interchangable in a var parameter, though really they *should be*
  SetLength(LBytes, ACount - AOffset);
  result := IdRead(LBytes, 0, ACount - AOffset);
  CopyTIdByteArray(LBytes, 0, VBuffer, AOffset, result);
end;

function TIdBaseStream.Write(const ABuffer: array of Byte; AOffset, ACount: Longint): Longint;
begin
  result := IdWrite(ABuffer, AOffset, ACount);
end;

function TIdBaseStream.Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64;
begin
  result := IdSeek(AOffset, AOrigin);
end;

procedure TIdBaseStream.SetSize(ASize : Int64);
begin
  IdSetSize(ASize);
end;

{$ELSE}
procedure TIdBaseStream.SetSize(ASize : Integer); 
begin
  IdSetSize(ASize);
end;

function TIdBaseStream.Read(var VBuffer; ACount: Longint): Longint;
var
  LBytes : TIdBytes;
begin
  SetLength(LBytes, ACount);
  result := IdRead(LBytes, 0, ACount);
  if result > 0 then
    begin
    move(LBytes[0], VBuffer, result);
    end;
end;

function TIdBaseStream.Write(const ABuffer; ACount: Longint): Longint;
var
  LBytes : TIdBytes;
begin
  if ACount > 0 then
    begin
    SetLength(LBytes, ACount);
    move(ABuffer, LBytes[0], ACount);
    end;
  result := IdWrite(LBytes, 0, ACount);
end;

function TIdBaseStream.Seek(AOffset: Longint; AOrigin: Word): Longint;
begin
  result := IdSeek(AOffset, AOrigin);
end;
{$ENDIF}

procedure AppendBytes(var VBytes : TIdBytes; AAdd : TIdBytes);
var
  LOldLen : integer;
begin
  LOldLen := Length(VBytes);
  SetLength(VBytes, LOldLen + Length(AAdd));
  CopyTIdBytes(AAdd, 0, VBytes, LOldLen, Length(AAdd));
end;

procedure AppendByte(var VBytes: TIdBytes; AByte: byte);
var
  LOldLen : integer;
begin
  LOldLen := Length(VBytes);
  SetLength(VBytes, LOldLen + 1);
  VBytes[High(VBytes)] := AByte;
end;

procedure IdDelete(var s : string; AOffset, ACount : integer);
begin
  {$IFDEF DOTNET}
  Borland.Delphi.System.Delete(s, AOffset, ACount);
  {$else}
  System.Delete(s, AOffset, ACount);
  {$endif}
end;

initialization
  // AnsiPos does not handle strings with #0 and is also very slow compared to Pos
  {$IFDEF DOTNET}
  IndyPos := SBPos;
  {$ELSE}
  if LeadBytes = [] then begin
    IndyPos := SBPos;
  end else begin
    IndyPos := AnsiPos;
  end;
  {$ENDIF}
finalization
  FreeAndNil(GIdPorts);
end.

