
//////////////////////////////////////////////////
//  DB Access Components
//  Copyright © 1998-2007 Core Lab. All right reserved.
//  Mem Data
//  Created:            06.11.03
//////////////////////////////////////////////////

{$IFNDEF CLR}

{$I Dac.inc}

unit MemUtils;
{$ENDIF}
interface
uses
  Classes, SysUtils, {$IFDEF VER6P}Variants, {$ENDIF}
{$IFDEF HAVE_COMPRESS_INTERNAL}
  ZLib, ZLibConst,
{$ENDIF}
{$IFDEF LINUX}
{$ELSE}
  Windows,
{$ENDIF}
{$IFDEF CLR}
  System.Xml,
  System.Runtime.InteropServices;
{$ELSE}
  CLRClasses;
{$ENDIF}

{$IFNDEF VER6P}
const
  varShortInt = $0010; { vt_i1          }
  varWord     = $0012; { vt_ui2         }
  varLongWord = $0013; { vt_ui4         }
  varInt64    = $0014; { vt_i8          }

type
  TVarType = Word;
{$ENDIF}

{$IFDEF CLR}
const
  wsAttribute: System.Xml.WriteState = System.Xml.WriteState.Attribute;
  wsClosed: System.Xml.WriteState = System.Xml.WriteState.Closed;
  wsContent: System.Xml.WriteState = System.Xml.WriteState.Content;
  wsElement: System.Xml.WriteState = System.Xml.WriteState.Element;
  wsStart: System.Xml.WriteState = System.Xml.WriteState.Start;

  fmtNone: System.Xml.Formatting = System.Xml.Formatting.None;
  fmtIndented: System.Xml.Formatting = System.Xml.Formatting.Indented;

  ntNone: System.Xml.XmlNodeType = System.Xml.XmlNodeType.None;
  ntElement: System.Xml.XmlNodeType = System.Xml.XmlNodeType.Element;
  ntAttribute: System.Xml.XmlNodeType = System.Xml.XmlNodeType.Attribute;
  ntEndElement: System.Xml.XmlNodeType = System.Xml.XmlNodeType.EndElement;
  ntComment: System.Xml.XmlNodeType = System.Xml.XmlNodeType.Comment;
  ntDeclaration: System.Xml.XmlNodeType = System.Xml.XmlNodeType.XmlDeclaration;
  ntDocumentType: System.Xml.XmlNodeType = System.Xml.XmlNodeType.DocumentType;
  ntText: System.Xml.XmlNodeType = System.Xml.XmlNodeType.Text;
{$ENDIF}

type
{$IFNDEF CLR}
  TValueArr = PChar;
{$ELSE}
  TValueArr = TBytes;

  PChar = string;
  PWideChar = string;

  POleVariant = IntPtr;

const
  { TStream seek origins compatibility aliases }
  soFromBeginning = soBeginning;
  soFromCurrent = soCurrent;
  soFromEnd = soEnd;
{$ENDIF}

{$IFNDEF VER7P}
const
  MSecsPerSec   = 1000;
{$ENDIF}

{ CLR compatibility routines }
{$IFDEF CLR}
  function CompareMem(P1, P2: IntPtr; Length: integer): boolean;
{$ENDIF}
  function CompareGuid(const g1, g2: TGuid): boolean;
  function TimeStampToDateTime(const ATimeStamp: TTimeStamp): TDateTime;

  function VarEqual(const Value1, Value2: variant): boolean;
  procedure OleVarClear(pValue: POleVariant);
  function GetOleVariant(pValue: POleVariant): OleVariant;
  procedure SetOleVariant(pValue: POleVariant; const Value: OleVariant);

  procedure CopyBuffer(Source, Dest: IntPtr; Count: cardinal);
  procedure CopyBufferAnsi(const Source: string; Dest: IntPtr; Count{Bytes (#0 included)}: cardinal);
  procedure CopyBufferUni(const Source: WideString; Dest: IntPtr; Count{Bytes (#0 included)}: cardinal);

  procedure FillChar(X: IntPtr; Count: integer; Value: byte);
  procedure FillStr(var S: string; Count: integer; Value: char);
  procedure ArrayCopy(sourceArray: TBytes; sourceIndex: integer; destinationArray: TBytes; destinationIndex: integer; length: integer);
  function AllocGCHandle(Obj: {$IFDEF CLR}TObject{$ELSE}pointer{$ENDIF}; Pinned: boolean = False): IntPtr;
  function GetGCHandleTarget(Handle: IntPtr): TObject;
  function GetAddrOfPinnedObject(Handle: IntPtr): IntPtr;
  procedure FreeGCHandle(Handle: IntPtr);

  function AllocString(var S: string; Length: integer): IntPtr;
  procedure FreeString(P: IntPtr);
  function AllocOrdinal(var Obj: IntPtr): IntPtr; overload;
  function AllocOrdinal(var Obj: shortint): IntPtr; overload;
  function AllocOrdinal(var Obj: byte): IntPtr; overload;
  function AllocOrdinal(var Obj: word): IntPtr; overload;
  function AllocOrdinal(var Obj: integer): IntPtr; overload;
  function AllocOrdinal(var Obj: cardinal): IntPtr; overload;
  function OrdinalToPtr(var Obj: double): IntPtr; overload;
  function OrdinalToPtr(var Obj: byte): IntPtr; overload;
  function OrdinalToPtr(var Obj: smallint): IntPtr; overload;
  function OrdinalToPtr(var Obj: integer): IntPtr; overload;
  function OrdinalToPtr(var Obj: int64): IntPtr; overload;
  function OrdinalToPtr(var Obj: cardinal): IntPtr; overload;
  function OrdinalToPtr(var Obj: word): IntPtr; overload;
  function OrdinalToPtr(var Obj: IntPtr): IntPtr; overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: shortint); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: byte); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: smallint); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: word); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: integer); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: int64); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: cardinal); overload;
  procedure PtrToOrdinal(P: IntPtr; var Obj: IntPtr); overload;
  procedure FreeOrdinal(P: IntPtr);
{ PChar and PWideChar routines }
{$IFDEF CLR}
  procedure StrCopy(Dest: IntPtr; const Source: IntPtr);
  function StrComp(const Str1: IntPtr; const Str2: IntPtr): integer;
  procedure StrLCopy(Dest: IntPtr; const Source: IntPtr; MaxLen{Chars}: Cardinal); 
  function StrLen(const Str: IntPtr): Cardinal;

  function AnsiUpperCase(const S: string): string;
  function AnsiCompareText(const S1, S2: string): integer;
  function AnsiCompareStr(const S1, S2: string): integer;
  function AnsiSameText(const S1, S2: string): Boolean;
{$ELSE}
  function AnsiStrCompS(S1, S2: PChar): Integer; // SORT_STRINGSORT
  function AnsiStrICompS(S1, S2: PChar): Integer; // SORT_STRINGSORT
{$ENDIF}
  function AnsiCompareTextS(const S1, S2: string): integer; // SORT_STRINGSORT
  function AnsiCompareStrS(const S1, S2: string): integer; // SORT_STRINGSORT
  function Utf8ToWs(const Dest: TValueArr; DestIdx: Cardinal; MaxDestBytes{w/wo #0}: Cardinal;
    const Source: TValueArr; SourceIdx, SourceBytes: Cardinal;
    const AddNull: boolean): Cardinal{bytes w/wo #0};

  function StrCopyW(Dest: IntPtr; const Source: IntPtr): IntPtr;
  procedure StrLCopyW(Dest: IntPtr; const Source: IntPtr; MaxLen{WideChars}: Cardinal);
  function StrLenW(const Str: IntPtr): Cardinal;
  procedure StrTrim(const Str: IntPtr; Len: integer = -1);
  procedure StrTrimW(const Str: IntPtr; Len: integer = -1);
  function AnsiStrLCompWS(const S1, S2: WideString; MaxLen: Cardinal): Integer; // SORT_STRINGSORT
  function AnsiStrLICompWS(const S1, S2: WideString; MaxLen: Cardinal): Integer; // SORT_STRINGSORT
  function AnsiStrCompWS(const S1, S2: WideString): Integer; // SORT_STRINGSORT
  function AnsiStrICompWS(const S1, S2: WideString): Integer; // SORT_STRINGSORT

  function IsClass(Obj: TObject; AClass: TClass): boolean;

type
{$IFDEF CLR}
  TDAList = class(TObject)
    FList: array of TObject;
    FCount: Integer;
    FCapacity: Integer;
  protected
    function Get(Index: Integer): TObject;
    procedure Grow; virtual;
    procedure Put(Index: Integer; Item: TObject);
    procedure SetCapacity(NewCapacity: Integer);

  public
    constructor Create;
    destructor Destroy; override;
    function Add(Item: TObject): Integer;
    procedure Clear; virtual;
    procedure Delete(Index: Integer);
    function IndexOf(Item: TObject): Integer;
    function Last: TObject;
    function Remove(Item: TObject): Integer;
    procedure Sort(Compare: TListSortCompare);

    property Capacity: Integer read FCapacity write SetCapacity;
    property Count: Integer read FCount;
    property Items[Index: Integer]: TObject read Get write Put; default;

  end;
{$ELSE}
  TDAList = TList;
{$ENDIF}

{$IFNDEF CLR}
function TryEncodeDate(Year, Month, Day: Word; var Date: TDateTime): Boolean;
function TryEncodeTime(Hour, Min, Sec, MSec: Word; var Time: TDateTime): Boolean;
function TryEncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond,
  AMilliSecond: Word; out AValue: TDateTime): Boolean;
function EncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond,
  AMilliSecond: Word): TDateTime;
{$ENDIF}

{$IFNDEF VER6P}
  function BoolToStr(const Value: boolean; UseBoolStrs: Boolean = False): string;
  function TryStrToBool(const S: string; out Value: Boolean): Boolean;
  function StrToBool(const S: string): Boolean;
  procedure DecodeDateTime(const AValue: TDateTime; out AYear, AMonth, ADay,
    AHour, AMinute, ASecond, AMilliSecond: Word);
  function WideUpperCase(const S: WideString): WideString;
  function VarToWideStr(const V: Variant): WideString;
{$ENDIF}

{$IFDEF VER6}
function StrToBool(const S: string): Boolean;
function TryStrToBool(const S: string; out Value: Boolean): Boolean;
{$ENDIF}

  function Reverse4(Value: cardinal): cardinal;
{$IFNDEF CLR}
  procedure Reverse8(pValue: IntPtr);
{$ENDIF}

{$IFDEF VER8}
type
  UTF8String = AnsiString deprecated;

function UTF8Encode(const WS: WideString): UTF8String; deprecated;
function UTF8Decode(const S: UTF8String): WideString; deprecated;
{$ENDIF}

{$IFDEF HAVE_COMPRESS}
const
  MIN_COMPRESS_LENGTH = 50; // Don't compress small bl.

  procedure CheckZLib;
  procedure DoCompress(dest: IntPtr; destLen: IntPtr; const source: IntPtr; sourceLen: longint);
  procedure DoUncompress(dest: IntPtr; destlen: IntPtr; source: IntPtr; sourceLne: longint);

type
  TCompressProc = function(dest: IntPtr; destLen: IntPtr; const source: IntPtr; sourceLen: longint): longint; {$IFDEF LINUX}cdecl;{$ENDIF}
  TUncompressProc = function(dest: IntPtr; destlen: IntPtr; source: IntPtr; sourceLne: longint): longint; {$IFDEF LINUX}cdecl;{$ENDIF}

var
  CompressProc: TCompressProc;
  UncompressProc: TUncompressProc;

{$ENDIF}

{$IFDEF MSWINDOWS}
var
  IsWin9x: boolean;
{$ENDIF}

implementation

{$IFDEF CLR}
uses
  ActiveX, System.Text,
  RTLConsts;
{$ELSE}
uses
{$IFDEF VER6}
  SysConst,
{$ENDIF}
  DAConsts;
{$ENDIF}

{$IFNDEF CLR}
  {$IFDEF MSWINDOWS}
    {$DEFINE SORT_STRINGSORT}
  {$ENDIF}
{$ENDIF}

{$IFDEF CLR}
[DllImport('kernel32.dll')]
procedure CopyMemory(Dest, Source: IntPtr; Count: cardinal); external;

[DllImport('kernel32.dll')]
function lstrcpy(lpString1, lpString2: IntPtr): IntPtr; external;

[DllImport('kernel32.dll')]
function lstrcpyn(lpString1, lpString2: IntPtr; iMaxLength: Integer): IntPtr; external;

[DllImport('kernel32.dll')]
function lstrcmp(lpString1, lpString2: IntPtr): integer; external;

[DllImport('kernel32.dll')]
function lstrlen(lpString: IntPtr): Integer; external;

[DllImport('kernel32.dll')]
procedure FillMemory(Destination: IntPtr; Length: DWORD; Fill: Byte); external;

function CompareMem(P1, P2: IntPtr; Length: integer): boolean;
var
  i: integer;
begin
  Result := False;
  for i := 0 to Length - 1 do begin
    if Marshal.ReadByte(P1) <> Marshal.ReadByte(P2) then
      Exit;
    P1 := IntPtr(integer(P1) + 1);
    P2 := IntPtr(integer(P2) + 1);
  end;
  Result := True;
end;
function CompareGuid(const g1, g2: TGuid): boolean;
begin
  Result := g1 = g2;
end;
{$ELSE}
function CompareGuid(const g1, g2: TGuid): boolean;
begin
  Result := CompareMem(@g1, @g2, SizeOf(TGuid));
end;
{$ENDIF}

function TimeStampToDateTime(const ATimeStamp: TTimeStamp): TDateTime;
  procedure ValidateTimeStamp(const ATimeStamp: TTimeStamp);
  begin
    if (ATimeStamp.Time < 0) or (ATimeStamp.Date <= 0) then
      raise EConvertError.Create(Format('''%d.%d'' is not a valid timestamp', [ATimeStamp.Date, ATimeStamp.Time]));
  end;
begin
  ValidateTimeStamp(ATimeStamp);
  Result := ATimeStamp.Date - DateDelta;
{$IFNDEF CLR}
  if Result < 0 then
    Result := Result - (ATimeStamp.Time / MSecsPerDay)
  else
{$ENDIF}
    Result := Result + (ATimeStamp.Time / MSecsPerDay);
end;

// bug in D8 in compare strings as variant type
function VarEqual(const Value1, Value2: variant): boolean;
var
{$IFDEF CLR}
  va_old, va_new: TBytes;
  i: integer;
{$ELSE}
  va_old, va_new: PVarArray;
  va_data_old, va_data_new: IntPtr;
{$ENDIF}
begin
{$IFDEF CLR}
  if (Value1 <> nil) and (Value2 <> nil) and
    (integer(Convert.GetTypeCode(Value1)) = 18) and (integer(Convert.GetTypeCode(Value2)) = 18)
  then
    Result := System.String.CompareOrdinal(string(Value1), string(Value2)) = 0
  else
{$ELSE}
  // prevent comparing as AnsiString
  if (VarType(Value1) = 8) and ((VarType(Value2) = 8) or (VarType(Value2) = 256)) or
    (VarType(Value2) = 8) and ((VarType(Value1) = 8) or (VarType(Value1) = 256))
  then
    Result := WideString(Value1) = WideString(Value2)
  else
{$ENDIF}
  if (VarType(Value1) = varNull) and (VarType(Value2) = varNull) then
    Result := True
  else
  if (VarType(Value1) = varNull) or (VarType(Value2) = varNull) or
    (VarType(Value2) <> VarType(Value2)) then
     Result := False
  else
  if (VarType(Value1) = varArray + varByte) or
    (VarType(Value2) = varArray + varByte) then begin
    {$IFDEF CLR}
      va_old := Value1;
      va_new := Value2;
      if (va_old = nil) and (va_new = nil) then
        Result := True
      else
        if (va_old = nil) or (va_new = nil) or
          (Length(va_old) <> Length(va_new)) then
          Result := False
        else begin
          Result := True;
          for i := Low(va_old) to High(va_old) do
            if va_old[i] <> va_new[i] then begin
              Result := False;
              Break;
            end;
        end;
    {$ELSE}
      va_old := TVarData(Value1).VArray;
      va_new := TVarData(Value2).VArray;
      if (va_old = nil) and (va_new = nil) then
        Result := True
      else
        if (va_old = nil) or (va_new = nil) or
          (va_old.Bounds[0].ElementCount <> va_new.Bounds[0].ElementCount) then
          Result := False
        else begin
          va_data_old := va_old.Data;
          va_data_new := va_new.Data;
          if (va_data_old = nil) and (va_data_new = nil) then
            Result := True
          else
            if (va_data_old = nil) or (va_data_new = nil) then
              Result := False
            else
              Result := CompareMem(va_data_old, va_data_new, va_old.Bounds[0].ElementCount);
        end;
    {$ENDIF}
  end
  else
    Result := Value1 = Value2;
end;

procedure CopyBuffer(Source, Dest: IntPtr; Count: cardinal);
begin
{$IFDEF CLR}
  CopyMemory(Dest, Source, Count);
{$ELSE}
  Move(Source^, Dest^, Count);
{$ENDIF}
end;

procedure CopyBufferAnsi(const Source: string; Dest: IntPtr; Count{Bytes (#0 included)}: cardinal);
{$IFDEF CLR}
var
  buf: TBytes;
  CountInt: integer; // To prevent CLR compiler error
begin
  SetLength(buf, Count);
  CountInt := Convert.ToInt32(Count); // To prevent CLR compiler error
  Encoding.Default.GetBytes(Source, 0, CountInt - 1{#0}, buf, 0);
  buf[CountInt - 1] := 0;
  Marshal.Copy(buf, 0, Dest, CountInt);
end;
{$ELSE}
begin
  CopyBuffer(PChar(Source), Dest, Count);
end;
{$ENDIF}

procedure CopyBufferUni(const Source: WideString; Dest: IntPtr; Count{Bytes (#0#0 included)}: cardinal);
{$IFDEF CLR}
var
  buf: TBytes;
  CountInt: integer; // To prevent CLR compiler error
begin
  SetLength(buf, Count);
  CountInt := Convert.ToInt32(Count); // To prevent CLR compiler error
  Encoding.Unicode.GetBytes(Source, 0, (CountInt - 1{#0}) shr 1, buf, 0);
  buf[CountInt - 1] := 0;
  buf[CountInt - 2] := 0;
  Marshal.Copy(buf, 0, Dest, CountInt);
end;
{$ELSE}
begin
  CopyBuffer(PWideChar(Source), Dest, Count);
end;
{$ENDIF}

procedure FillChar(X: IntPtr; Count: integer; Value: byte);
begin
{$IFDEF CLR}
  FillMemory(X, Count, Value);
{$ELSE}
  System.FillChar(X^, Count, Value);
{$ENDIF}
end;

procedure FillStr(var S: string; Count: integer; Value: char);
begin
{$IFDEF CLR}
  S := System.String.Create(Value, Count);
{$ELSE}
  SetLength(S, Count);
  FillChar(PChar(S), Count, byte(Value));
{$ENDIF}
end;

procedure ArrayCopy(sourceArray: TBytes; sourceIndex: integer; destinationArray: TBytes; destinationIndex: integer; length: integer);
begin
{$IFDEF CLR}
  System.Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
{$ELSE}
  System.Move(sourceArray[sourceIndex], destinationArray[destinationIndex], length);
{$ENDIF}
end;

function AllocGCHandle(Obj: {$IFDEF CLR}TObject{$ELSE}pointer{$ENDIF};
   Pinned: boolean = False): IntPtr;
begin
{$IFDEF CLR}
  if Pinned then
    Result := IntPtr(GCHandle.Alloc(Obj, GCHandleType.Pinned))
  else
    Result := IntPtr(GCHandle.Alloc(Obj, GCHandleType.Normal));
{$ELSE}
  Result := Obj;
{$ENDIF}
end;

function GetGCHandleTarget(Handle: IntPtr): TObject;
begin
{$IFDEF CLR}
  if Handle = nil then
    Result := nil
  else
    Result := GCHandle(Handle).Target;
{$ELSE}
  Result := Handle;
{$ENDIF}
end;

function GetAddrOfPinnedObject(Handle: IntPtr): IntPtr;
begin
{$IFDEF CLR}
  Result := GCHandle(Handle).AddrOfPinnedObject;
{$ELSE}
  Result := Handle;
{$ENDIF}
end;

procedure FreeGCHandle(Handle: IntPtr);
begin
{$IFDEF CLR}
  GCHandle(Handle).Free;
{$ELSE}
{$ENDIF}
end;

function AllocString(var S: string; Length: integer): IntPtr;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(Length + 1);
  {$ELSE}
    SetLength(S, Length);
    Result := PChar(S);
  {$ENDIF}
end;

procedure FreeString(P: IntPtr);
begin
  {$IFDEF CLR}
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

function AllocOrdinal(var Obj: shortint): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(shortint));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function AllocOrdinal(var Obj: byte): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(byte));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function AllocOrdinal(var Obj: word): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(word));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function AllocOrdinal(var Obj: integer): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(integer));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function AllocOrdinal(var Obj: cardinal): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(integer));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function AllocOrdinal(var Obj: IntPtr): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(IntPtr));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: double): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(Int64));
    Marshal.WriteInt64(Result, BitConverter.DoubleToInt64Bits(Obj));
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: byte): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(byte));
    Marshal.WriteByte(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: smallint): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(smallint));
    Marshal.WriteInt16(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: integer): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(integer));
    Marshal.WriteInt32(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: int64): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(int64));
    Marshal.WriteInt64(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: cardinal): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(cardinal));
    Marshal.WriteInt32(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: word): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(word));
    Marshal.WriteInt16(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

function OrdinalToPtr(var Obj: IntPtr): IntPtr; overload;
begin
  {$IFDEF CLR}
    Result := Marshal.AllocHGlobal(sizeof(integer));
    Marshal.WriteIntPtr(Result, Obj);
  {$ELSE}
    Result := @Obj;
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: shortint); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadByte(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: byte); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadByte(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: smallint); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadInt16(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: word); overload;
begin
  {$IFDEF CLR}
    Obj := Word(Marshal.ReadInt32(P));
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: integer); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadInt32(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: int64); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadInt64(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: cardinal); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadInt32(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure PtrToOrdinal(P: IntPtr; var Obj: IntPtr); overload;
begin
  {$IFDEF CLR}
    Obj := Marshal.ReadIntPtr(P);
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

procedure FreeOrdinal(P: IntPtr);
begin
  {$IFDEF CLR}
    Marshal.FreeHGlobal(P);
  {$ELSE}
  {$ENDIF}
end;

{$IFDEF CLR}
procedure StrCopy(Dest: IntPtr; const Source: IntPtr);
begin
  Win32Check(lstrcpy(Dest, Source) <> nil);
end;

procedure StrLCopy(Dest: IntPtr; const Source: IntPtr; MaxLen{Chars}: Cardinal);
begin
  Win32Check(lstrcpyn(Dest, Source, Integer(MaxLen + 1)) <> nil);
end;

function StrComp(const Str1: IntPtr; const Str2: IntPtr): integer;
begin
  Result := lstrcmp(Str1, Str2);
end;

function StrLen(const Str: IntPtr): Cardinal;
begin
  Result := lstrlen(Str);
end;

function StrLenW(const Str: IntPtr): Cardinal;
var
  s: WideString;
begin
  s := Marshal.PtrToStringUni(Str);
  Result := s.Length;
end;

procedure StrTrim(const Str: IntPtr; Len: integer = -1);
var
  i: integer;
  v: byte;
begin
  if Len = - 1 then // Detect length
    Len := StrLen(Str);

  i := Integer(Str) + Len - 1;

  while True do begin
    v := Marshal.ReadByte(IntPtr(i));
    if ((v <> 32 {Byte(' ')}) and (v <> 0{Byte(#0)})) or (i < Integer(Str)) then
      Exit;
    Marshal.WriteByte(IntPtr(i), Byte(#0));
    Dec(i);
  end;
end;

procedure StrTrimW(const Str: IntPtr; Len: integer = -1);
var
  i: integer;
  v: smallint;
begin
  if Len = - 1 then // Detect length
    Len := StrLenW(Str);

  i := Integer(Str) + (Len - 1) shl 1;

  while True do begin
    v := Marshal.ReadInt16(IntPtr(i));
    if ((v <> 32 {SmallInt(' ')}) and (v <> 0{SmallInt(#0)})) or (i < Integer(Str)) then
      Exit;
    Marshal.WriteInt16(IntPtr(i), SmallInt(#0));
    Dec(i);
    Dec(i);
  end;
end;

{$ENDIF}

function StrCopyW(Dest: IntPtr; const Source: IntPtr): IntPtr;
{$IFDEF CLR}
var
  Buf: smallint;
  i: integer;
begin
  i := 0;
  repeat
    Buf := Marshal.ReadInt16(Source, i);
    Marshal.WriteInt16(Dest, i, Buf);
    Inc(i, 2);
  until Buf = 0;
{$ELSE}
asm
        PUSH    EDI
        PUSH    ESI
        MOV     ESI,EAX
        MOV     EDI,EDX
        MOV     ECX,0FFFFFFFFH
        XOR     AX,AX
        REPNE   SCASW
        NOT     ECX
        SHL     ECX, 1  // Size := Len * sizeof(WideChar)
        MOV     EDI,ESI
        MOV     ESI,EDX
        MOV     EDX,ECX
        MOV     EAX,EDI
        SHR     ECX,2
        REP     MOVSD
        MOV     ECX,EDX
        AND     ECX,3
        REP     MOVSB
        POP     ESI
        POP     EDI
{$ENDIF}
end;

procedure StrLCopyW(Dest: IntPtr; const Source: IntPtr; MaxLen{WideChars}: Cardinal);
{$IFDEF CLR}
var
  Buf: smallint;
  i: cardinal;
begin
  i := 0;
  Buf := 0;
  while i < MaxLen * 2 do begin
    Buf := Marshal.ReadInt16(Source, i);
    Marshal.WriteInt16(Dest, i, Buf);
    if Buf = 0 then
      Break;
    Inc(i, 2);
  end;
  if Buf <> 0 then
    Marshal.WriteInt16(Dest, i, 0);
{$ELSE}
var
  pwc: PWideChar;
begin
  pwc := Source;
  while (pwc^ <> #0) and (pwc < PWideChar(Source) + MaxLen) do begin
    PWideChar(Dest)^ := pwc^;
    Inc(PWideChar(Dest));
    Inc(pwc);
  end;
  PWideChar(Dest)^ := #0;
{$ENDIF}
end;

{$IFNDEF CLR}
function StrLenW(const Str: IntPtr): Cardinal; assembler;
asm
        MOV     EDX,EDI
        MOV     EDI,EAX
        MOV     ECX,0FFFFFFFFH
        XOR     AX,AX
        REPNE   SCASW
        MOV     EAX,0FFFFFFFEH
        SUB     EAX,ECX
        MOV     EDI,EDX
end;

procedure StrTrim(const Str: IntPtr; Len: integer = -1);
var
  pc: PChar;
begin
  if Len = - 1 then // Detect length
    Len := StrLen(Str);

  pc := PChar(Str) + Len - 1;

  while ((pc^ = ' ') or (pc^ = #0)) and (pc >= Str) do begin
    pc^ := #0;
    Dec(pc);
  end;
end;

procedure StrTrimW(const Str: IntPtr; Len: integer = -1);
var
  pwc: PWideChar;
begin
  if Len = - 1 then // Detect length
    Len := StrLenW(Str);

  pwc := PWideChar(Str) + Len - 1;

  while ((pwc^ = ' ') or (pwc^ = #0)) and (pwc >= Str) do begin
    PWideChar(pwc)^ := #0;
    Dec(pwc);
  end;
end;
{$ENDIF}

function AnsiStrLCompWS(const S1, S2: WideString; MaxLen: Cardinal): Integer;
begin
{$IFDEF MSWINDOWS}
  Assert(not IsWin9x, 'Unicode support on Win9x');
{$IFDEF CLR}
  Result := CompareStringW(LOCALE_USER_DEFAULT, SORT_STRINGSORT, S1, MaxLen, S2, MaxLen) - 2;
{$ELSE}
  Result := CompareStringW(LOCALE_USER_DEFAULT, SORT_STRINGSORT, PWideChar(S1), MaxLen,
    PWideChar(S2), MaxLen) - 2;
{$ENDIF}
{$ELSE}
  Result := 0;
  Assert(False);
{$ENDIF}
end;

function AnsiStrLICompWS(const S1, S2: WideString; MaxLen: Cardinal): Integer;
begin
{$IFDEF MSWINDOWS}
  Assert(not IsWin9x, 'Unicode support on Win9x');
{$IFDEF CLR}
  Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT,
    S1, MaxLen, S2, MaxLen) - 2;
{$ELSE}
  Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT,
    PWideChar(S1), MaxLen, PWideChar(S2), MaxLen) - 2;
{$ENDIF}
{$ELSE}
  Result := 0;
  Assert(False);
{$ENDIF}
end;

function AnsiStrCompWS(const S1, S2: WideString): Integer;
begin
{$IFDEF MSWINDOWS}
  Assert(not IsWin9x, 'Unicode support on Win9x');
{$IFDEF CLR}
  Result := CompareStringW(LOCALE_USER_DEFAULT, SORT_STRINGSORT, S1, -1, S2, -1) - 2;
{$ELSE}
  Result := CompareStringW(LOCALE_USER_DEFAULT, SORT_STRINGSORT, PWideChar(S1), -1,
    PWideChar(S2), -1) - 2;
{$ENDIF}
{$ELSE}
  Result := 0;
  Assert(False);
{$ENDIF}
end;

function AnsiStrICompWS(const S1, S2: WideString): Integer;
begin
{$IFDEF MSWINDOWS}
  Assert(not IsWin9x, 'Unicode support on Win9x');
{$IFDEF CLR}
  Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT, S1, -1,
    S2, -1) - 2;
{$ELSE}
  Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT, PWideChar(S1),
    -1, PWideChar(S2), -1) - 2;
{$ENDIF}
{$ELSE}
  Result := 0;
  Assert(False);
{$ENDIF}
end;

function IsClass(Obj: TObject; AClass: TClass): boolean;

  function IsClassByName(Obj: TObject; AClass: TClass): boolean;
  var
    ParentClass: TClass;
  begin
    Result := False;
    ParentClass := Obj.ClassType;
    while ParentClass <> nil do begin
      Result := ParentClass.ClassName = AClass.ClassName;
      if not Result then
        ParentClass := ParentClass.ClassParent
      else
        Break;
    end;
  end;

begin
  if IsLibrary then
    Result := IsClassByName(Obj, AClass)
  else
    Result := Obj is AClass;
end;

{$IFDEF CLR}
function AnsiUpperCase(const S: string): string;
begin
  if S <> nil then
    Result := System.String(S).ToUpper
  else
    Result := '';
end;

function AnsiCompareText(const S1, S2: string): integer;
begin
  Result := System.String.Compare(S1, S2, True);
end;

function AnsiCompareStr(const S1, S2: string): integer;
begin
  Result := System.String.Compare(S1, S2, False);
end;

function AnsiSameText(const S1, S2: string): Boolean;
begin
  Result := System.String.Compare(S1, S2, True) = 0;
end;

{$ELSE}

function AnsiStrCompS(S1, S2: PChar): Integer; // SORT_STRINGSORT
begin
{$IFDEF SORT_STRINGSORT}
  Result := CompareString(LOCALE_USER_DEFAULT, SORT_STRINGSORT, S1, -1, S2, -1) - 2;
{$ELSE}
  Result := AnsiStrComp(S1, S2);
{$ENDIF}
end;

function AnsiStrICompS(S1, S2: PChar): Integer; // SORT_STRINGSORT
begin
{$IFDEF SORT_STRINGSORT}
  Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT, S1, -1,
    S2, -1) - 2;
{$ELSE}
  Result := AnsiStrIComp(S1, S2);
{$ENDIF}
end;

{$ENDIF}

function AnsiCompareTextS(const S1, S2: string): integer; // SORT_STRINGSORT
begin
{$IFDEF SORT_STRINGSORT}
  Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE + SORT_STRINGSORT, PChar(S1),
    Length(S1), PChar(S2), Length(S2)) - 2;
{$ELSE}
  Result := AnsiCompareText(S1, S2);
{$ENDIF}
end;

function AnsiCompareStrS(const S1, S2: string): integer; // SORT_STRINGSORT
begin
{$IFDEF SORT_STRINGSORT}
  Result := CompareString(LOCALE_USER_DEFAULT, SORT_STRINGSORT, PChar(S1), Length(S1),
    PChar(S2), Length(S2)) - 2;
{$ELSE}
  Result := AnsiCompareStr(S1, S2);
{$ENDIF}
end;

// Convert Utf8 buffer to WideString buffer with or without null terminator.
// Nearly copied from System.Utf8ToUnicode
function Utf8ToWs(const Dest: TValueArr; DestIdx: Cardinal; MaxDestBytes{w/wo #0}: Cardinal;
  const Source: TValueArr; SourceIdx, SourceBytes: Cardinal;
  const AddNull: boolean): Cardinal{bytes w/wo #0};
var
  i: Cardinal;
  c: Byte;
  wc: Cardinal;
begin
  Assert(Source <> nil, 'Utf8ToWs: Source is nil');
  Assert(Dest <> nil, 'Utf8ToWs: Destination is nil');
{$IFDEF CLR}
  Assert(Integer(DestIdx + MaxDestBytes) <= Length(Dest), 'Utf8ToWs: DestIdx = ' + IntToStr(DestIdx) + ', MaxDestBytes = ' + IntToStr(MaxDestBytes) + ', Length(Dest) = ' + IntToStr(Length(Dest)));
  Assert(Integer(SourceIdx + SourceBytes) <= Length(Source), 'Utf8ToWs: SourceIdx = ' + IntToStr(SourceIdx) + ', SourceBytes = ' + IntToStr(SourceBytes) + ', Length(Source) = ' + IntToStr(Length(Source)));
{$ENDIF}
  Result := 0;
  i := SourceIdx;
  while i < SourceIdx + SourceBytes do
  begin
    wc := Cardinal(Source[Integer(i)]);
    if wc = 0 then   //zero terminator
      break;
    Inc(i);
    if (wc and $80) <> 0 then
    begin
      Assert(i < SourceIdx + SourceBytes, 'Utf8ToWs: Incomplete multibyte char');
      wc := wc and $3F;
      if (wc and $20) <> 0 then
      begin
        c := Byte(Source[Integer(i)]);
        Inc(i);
        Assert((c and $C0) = $80, 'Utf8ToWs: Malformed trail byte or out of range char');
        Assert(i < SourceIdx + SourceBytes, 'Utf8ToWs: Incomplete multibyte char');
        wc := (wc shl 6) or (c and $3F);
      end;
      c := Byte(Source[Integer(i)]);
      Inc(i);
      Assert((c and $C0) = $80, 'Utf8ToWs: Malformed trail byte');
      wc := (wc shl 6) or (c and $3F);
    end;

    if not (Result + 1 < MaxDestBytes) then
      Break;
  {$IFDEF CLR}
    Dest[Integer(Result + DestIdx)] := Byte(wc);
    Dest[Integer(Result + DestIdx + 1)] := Byte(wc shr 8);
  {$ELSE}
    Cardinal(Pointer(Cardinal(Dest) + DestIdx + Result)^) := wc;
  {$ENDIF}
    Inc(Result, SizeOf(WideChar));
  end;

  if AddNull and (MaxDestBytes > 0) then begin
    if Result >= MaxDestBytes then
      Result := MaxDestBytes - SizeOf(WideChar);
  {$IFDEF CLR}
    Dest[Integer(Result + DestIdx)] := 0;
    Dest[Integer(Result + DestIdx + 1)] := 0;
  {$ELSE}
    Marshal.WriteInt16(Dest, Integer(DestIdx + Result), 0);
  {$ENDIF}
    Inc(Result, SizeOf(WideChar));
  end;
end;

{$IFDEF CLR}
{ TDAList }
constructor TDAList.Create;
begin
  inherited Create;

  SetCapacity(10);
end;

destructor TDAList.Destroy;
begin
  Clear;
  
  inherited;
end;

procedure TDAList.Clear;
begin
  FCount := 0;
  SetCapacity(0);
end;

procedure TDAList.Delete(Index: Integer);
begin
  if (Index < 0) or (Index >= FCount) then
    TList.Error({$IFNDEF CLR}@{$ENDIF}SListIndexError, Index);
  Dec(FCount);
  if Index < FCount then
  {$IFDEF CLR}
    System.Array.Copy(FList, Index + 1, FList, Index, FCount - Index);
  {$ELSE}//TODO
    System.Move(FList[Index + 1], FList[Index],
      (FCount - Index) * SizeOf(Pointer));
  {$ENDIF}
end;

function TDAList.IndexOf(Item: TObject): Integer;
begin
  Result := 0;
  while (Result < FCount) and (FList[Result] <> Item) do
    Inc(Result);
  if Result = FCount then
    Result := -1;
end;

function TDAList.Last: TObject;
begin
  Result := Get(FCount - 1);
end;

function TDAList.Remove(Item: TObject): Integer;
begin
  Result := IndexOf(Item);
  if Result >= 0 then
    Delete(Result);
end;

procedure QuickSort(var SortList: array of TObject; L, R: Integer;
  SCompare: TListSortCompare);
var
  I, J: Integer;
  P, T: TObject;
begin
  repeat
    I := L;
    J := R;
    P := SortList[(L + R) shr 1];
    repeat
      while SCompare(SortList[I], P) < 0 do
        Inc(I);
      while SCompare(SortList[J], P) > 0 do
        Dec(J);
      if I <= J then
      begin
        T := SortList[I];
        SortList[I] := SortList[J];
        SortList[J] := T;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSort(SortList, L, J, SCompare);
    L := I;
  until I >= R;
end;

procedure TDAList.Sort(Compare: TListSortCompare);
begin
  if (FList <> nil) and (Count > 0) then
    QuickSort(FList, 0, Count - 1, Compare);
end;

function TDAList.Add(Item: TObject): Integer;
begin
  Result := FCount;
  if Result = FCapacity then
    Grow;
  FList[Result] := Item;
  Inc(FCount);
end;

function TDAList.Get(Index: Integer): TObject;
begin
  if (Index < 0) or (Index >= FCount) then
    TList.Error({$IFNDEF CLR}@{$ENDIF}SListIndexError, Index);
  Result := FList[Index];
end;

procedure TDAList.Grow;
var
  Delta: Integer;
begin
  if FCapacity > 64 then
    Delta := FCapacity div 4
  else
    if FCapacity > 8 then
      Delta := 16
    else
      Delta := 4;
  SetCapacity(FCapacity + Delta);
end;

procedure TDAList.Put(Index: Integer; Item: TObject);
begin
  if (Index < 0) or (Index >= FCount) then
    TList.Error({$IFNDEF CLR}@{$ENDIF}SListIndexError, Index);
  FList[Index] := Item;
end;

procedure TDAList.SetCapacity(NewCapacity: Integer);
begin
  if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then
    TList.Error({$IFNDEF CLR}@{$ENDIF}SListCapacityError, NewCapacity);
  if NewCapacity <> FCapacity then
  begin
    SetLength(FList, NewCapacity);
    FCapacity := NewCapacity;
  end;
end;
{$ENDIF}

{$IFDEF CLR}
const
  ole32    = 'ole32.dll';
  oleaut32 = 'oleaut32.dll';

// Copied from ActiveX.pas (d7)
const
  STGM_READ             = $00000000;

[DllImport(oleaut32, CharSet = CharSet.Ansi, SetLastError = True, EntryPoint = 'VariantClear')]
function VariantClear(Value: POleVariant): HResult; external;

procedure OleVarClear(pValue: POleVariant);
begin
  SetOleVariant(pValue, Unassigned);
end;

function GetOleVariant(pValue: POleVariant): OleVariant;
begin
  Result := OleVariant(Marshal.GetObjectForNativeVariant(pValue));
//  Result := OleVariant(Marshal.PtrToStructure(pValue, TypeOf(OleVariant)));
end;

procedure SetOleVariant(pValue: POleVariant; const Value: OleVariant);
begin
  Marshal.GetNativeVariantForObject(TObject(Value), pValue);
end;

{$ELSE}
procedure OleVarClear(pValue: POleVariant);
begin
  pValue^ := Unassigned;
end;

function GetOleVariant(pValue: POleVariant): OleVariant;
begin
  Result := pValue^;
end;

procedure SetOleVariant(pValue: POleVariant; const Value: OleVariant);
begin
  pValue^ := Value;
end;

{$ENDIF}


{$IFNDEF VER6P}
function BoolToStr(const Value: boolean; UseBoolStrs: Boolean = False): string;
const
  cSimpleBoolStrs: array [boolean] of String = ('0', '-1');
begin
  if UseBoolStrs then
  begin
    if Value then
      Result := 'True'
    else
      Result := 'False';
  end
  else
    Result := cSimpleBoolStrs[Value];
end;

function TryStrToBool(const S: string; out Value: Boolean): Boolean;
begin
  Result := True;
  if SameText(s, 'True') or SameText(s, 'Yes') or SameText(s, '1') then
    Value := True
  else
  if SameText(s, 'False') or SameText(s, 'No') or SameText(s, '0') then
    Value := False
  else
    Result := False;
end;

function StrToBool(const S: string): Boolean;
begin
  if not TryStrToBool(S, Result) then
    raise EConvertError.Create('InvalidBoolean - ' + S);
end;

type
  PWordBool = ^WordBool;

procedure DecodeDateTime(const AValue: TDateTime; out AYear, AMonth, ADay,
  AHour, AMinute, ASecond, AMilliSecond: Word);
begin
  DecodeDate(AValue, AYear, AMonth, ADay);
  DecodeTime(AValue, AHour, AMinute, ASecond, AMilliSecond);
end;

function WideUpperCase(const S: WideString): WideString;
var
  Len: Integer;
begin
  Len := Length(S);
  SetString(Result, PWideChar(S), Len);
  if Len > 0 then CharUpperBuffW(Pointer(Result), Len);
end;

function VarToWideStr(const V: Variant): WideString;
begin
  if not VarIsNull(V) then
    Result := V
  else
    Result := '';;
end;
{$ENDIF}

{$IFDEF VER6}
procedure ConvertErrorFmt(ResString: PResStringRec; const Args: array of const); local;
begin
  raise EConvertError.CreateResFmt(ResString, Args);
end;

function StrToBool(const S: string): Boolean;
begin
  if not TryStrToBool(S, Result) then
    ConvertErrorFmt(@SInvalidBoolean, [S]);
end;

procedure VerifyBoolStrArray;
begin
  if Length(TrueBoolStrs) = 0 then
  begin
    SetLength(TrueBoolStrs, 1);
    TrueBoolStrs[0] := DefaultTrueBoolStr;
  end;
  if Length(FalseBoolStrs) = 0 then
  begin
    SetLength(FalseBoolStrs, 1);
    FalseBoolStrs[0] := DefaultFalseBoolStr;
  end;
end;

function TryStrToBool(const S: string; out Value: Boolean): Boolean;
  function CompareWith(const aArray: array of string): Boolean;
  var
    I: Integer;
  begin
    Result := False;
    for I := Low(aArray) to High(aArray) do
      if AnsiSameText(S, aArray[I]) then
      begin
        Result := True;
        Break;
      end;
  end;
var
  LResult: Extended;
begin
  Result := TryStrToFloat(S, LResult);
  if Result then
    Value := LResult <> 0
  else
  begin
    VerifyBoolStrArray;
    Result := CompareWith(TrueBoolStrs);
    if Result then
      Value := True
    else
    begin
      Result := CompareWith(FalseBoolStrs);
      if Result then
        Value := False;
    end;
  end;
end;
{$ENDIF}

{$IFNDEF CLR}
function TryEncodeDate(Year, Month, Day: Word; var Date: TDateTime): Boolean;
var
  I: Integer;
  DayTable: PDayTable;
begin
  Result := False;
  DayTable := @MonthDays[IsLeapYear(Year)];
  if (Year >= 1) and (Year <= 9999) and (Month >= 1) and (Month <= 12) and
    (Day >= 1) and (Day <= DayTable^[Month]) then
  begin
    for I := 1 to Month - 1 do Inc(Day, DayTable^[I]);
    I := Year - 1;
    Date := I * 365 + I div 4 - I div 100 + I div 400 + Day - DateDelta;
    Result := True;
  end;
end;

function TryEncodeTime(Hour, Min, Sec, MSec: Word; var Time: TDateTime): Boolean;
begin
  Result := False;
  if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  begin
    Time := (Hour * 3600000 + Min * 60000 + Sec * 1000 + MSec) / MSecsPerDay;
    Result := True;
  end;
end;

function TryEncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond,
  AMilliSecond: Word; out AValue: TDateTime): Boolean;
var
  LTime: TDateTime;
begin
  Result := TryEncodeDate(AYear, AMonth, ADay, AValue);
  if Result then
  begin
    Result := TryEncodeTime(AHour, AMinute, ASecond, AMilliSecond, LTime);
    if Result then
      if AValue > 0 then
        AValue := AValue + LTime
      else
        AValue := AValue - LTime;
  end;
end;

function EncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond,
  AMilliSecond: Word): TDateTime;
begin
  if not TryEncodeDateTime(AYear, AMonth, ADay,
                           AHour, AMinute, ASecond, AMilliSecond, Result) then
    raise EConvertError.Create(SDateEncodeError);
end;
{$ENDIF}

function Reverse4(Value: cardinal): cardinal;
begin
   Result := Cardinal((byte(Value) shl 24) or (byte(Value shr 8) shl 16)
           or (byte(Value shr 16) shl 8) or byte(Value shr 24));
end;

{$IFNDEF CLR}
procedure Reverse8(pValue: IntPtr);
var
  FirstByte: PByte;
  LastByte: PByte;
  TmpValue: Byte;
  i: integer;
begin
  FirstByte := PByte(pValue);
  LastByte := FirstByte;
  Inc(LastByte, SizeOf(Int64) - 1);
  for i := 0 to 3 do begin
    TmpValue := LastByte^;
    LastByte^ := FirstByte^;
    FirstByte^ := TmpValue;
    Inc(FirstByte);
    Dec(LastByte);
  end;
end;
{$ENDIF}

{$IFDEF VER8}
function UTF8Encode(const WS: WideString): UTF8String;
begin
  Result := UTF8String(System.Array(nil));
  if Assigned(WS) then
    Result := System.Text.Encoding.UTF8.GetBytes(WS);
end;

function UTF8Decode(const S: UTF8String): WideString;
begin
  Result := WideString(System.String(nil));
  if Assigned(S) then
    Result := System.Text.Encoding.UTF8.GetString(TBytes(S), 0, High(TBytes(S)) + 1);
end;
{$ENDIF}

{$IFDEF HAVE_COMPRESS}
procedure CheckZLib;
begin
  if not Assigned(CompressProc) then
    raise Exception.Create(SCompressorNotLinked);
  if not Assigned(UncompressProc) then
    raise Exception.Create(SUncompressorNotLinked);
end;

procedure DoCompress(dest: IntPtr; destLen: IntPtr; const source: IntPtr; sourceLen: longint);
begin
  Assert(Assigned(CompressProc), SCompressorNotLinked);
  CompressProc(dest, destLen, source, sourceLen)
end;

procedure DoUncompress(dest: IntPtr; destlen: IntPtr; source: IntPtr; sourceLne: longint);
begin
  Assert(Assigned(UncompressProc), SUncompressorNotLinked);
  UncompressProc(dest, destLen, source, sourceLne)
end;

{$IFDEF HAVE_COMPRESS_INTERNAL}
function CCheck(code: Integer): Integer;
begin
  Result := code;
  if code < 0 then
    raise ECompressionError.Create(sError);
end;

function DCheck(code: Integer): Integer;
begin
  Result := code;
  if code < 0 then
    raise EDecompressionError.Create(sError);
end;

function compress(dest: IntPtr; destLen: IntPtr; const source: IntPtr; sourceLen: longint): longint;
var
  strm: TZStreamRec;
begin
  FillChar(@strm, sizeof(strm), 0);
  strm.zalloc := zlibAllocMem;
  strm.zfree := zlibFreeMem;
  strm.next_in := source;
  strm.avail_in := sourceLen;
  strm.next_out := dest;
  strm.avail_out := Integer(destLen^);
  CCheck(deflateInit_(strm, Z_DEFAULT_COMPRESSION{Z_BEST_COMPRESSION}, zlib_version, sizeof(strm)));
  try
    Result := CCheck(deflate(strm, Z_FINISH));
    if Result <> Z_STREAM_END then
      raise EZlibError.CreateRes(@sTargetBufferTooSmall);
  finally
    CCheck(deflateEnd(strm));
  end;
  Integer(destLen^) := strm.total_out;
end;

function uncompress(dest: IntPtr; destlen: IntPtr; source: IntPtr; sourceLne: longint): longint;
var
  strm: TZStreamRec;
begin
  FillChar(@strm, sizeof(strm), 0);
  strm.zalloc := zlibAllocMem;
  strm.zfree := zlibFreeMem;
  strm.next_in := source;
  strm.avail_in := sourcelne;
  strm.next_out := dest;
  strm.avail_out := Integer(destlen^);
  DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
  try
    Result := DCheck(inflate(strm, Z_FINISH));
    if Result <> Z_STREAM_END then
      raise EZlibError.CreateRes(@sTargetBufferTooSmall);
  finally
    DCheck(inflateEnd(strm));
  end;
end;
{$ENDIF}
{$ENDIF}

{$IFDEF MSWINDOWS}
var
  lpVersionInformation: TOSVersionInfo;

initialization
  lpVersionInformation.dwOSVersionInfoSize := sizeof(lpVersionInformation);
{$IFDEF VER6P}
  {$WARN SYMBOL_PLATFORM OFF}
{$ENDIF}
{$IFDEF CLR}
  IsWin9x := False;
{$ELSE}
  Win32Check(GetVersionEx(lpVersionInformation));
  IsWin9x := lpVersionInformation.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
{$ENDIF}
{$ENDIF}

{$IFDEF HAVE_COMPRESS_INTERNAL}
  CompressProc := compress;
  UncompressProc := uncompress;
{$ENDIF}
end.
