
//////////////////////////////////////////////////
//  DB Access Components
//  Copyright © 1998-2014 Devart. All right reserved.
//  CRTypes
//////////////////////////////////////////////////

{$IFNDEF CLR}
{$IFNDEF EntityDAC}
{$I Dac.inc}
unit CRTypes;
{$ENDIF}
{$ENDIF}

interface

uses
{$IFDEF CLR}
  System.Xml, System.Text, Windows,
{$ENDIF}
  Classes, SysUtils;

{$IFNDEF VER6P}
type
  EVariantInvalidOpError = class(EVariantError);
  EVariantTypeCastError = class(EVariantError);
  EVariantBadVarTypeError = class(EVariantError);
  EVariantOverflowError = class(EVariantError);
  EVariantBadIndexError = class(EVariantError);
  EVariantArrayLockedError = class(EVariantError);
  EVariantNotImplError = class(EVariantError);
  EVariantOutOfMemoryError = class(EVariantError);
  EVariantInvalidArgError = class(EVariantError);
  EVariantUnexpectedError = class(EVariantError);

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

  MaxBCDPrecision = 18;
  MaxBCDScale = 4;

// These equate to Window's constants but are renamed to less OS dependent
  VAR_OK            = HRESULT($00000000); // = Windows.S_OK
  VAR_PARAMNOTFOUND = HRESULT($80020004); // = Windows.DISP_E_PARAMNOTFOUND
  VAR_TYPEMISMATCH  = HRESULT($80020005); // = Windows.DISP_E_TYPEMISMATCH
  VAR_BADVARTYPE    = HRESULT($80020008); // = Windows.DISP_E_BADVARTYPE
  VAR_EXCEPTION     = HRESULT($80020009); // = Windows.DISP_E_EXCEPTION
  VAR_OVERFLOW      = HRESULT($8002000A); // = Windows.DISP_E_OVERFLOW
  VAR_BADINDEX      = HRESULT($8002000B); // = Windows.DISP_E_BADINDEX
  VAR_ARRAYISLOCKED = HRESULT($8002000D); // = Windows.DISP_E_ARRAYISLOCKED
  VAR_NOTIMPL       = HRESULT($80004001); // = Windows.E_NOTIMPL
  VAR_OUTOFMEMORY   = HRESULT($8007000E); // = Windows.E_OUTOFMEMORY
  VAR_INVALIDARG    = HRESULT($80070057); // = Windows.E_INVALIDARG
  VAR_UNEXPECTED    = HRESULT($8000FFFF); // = Windows.E_UNEXPECTED

  SInvalidVarCast = 'Invalid variant type conversion';
  SVarBadType = 'Invalid variant type';
  SInvalidVarOp = 'Invalid variant operation';
  SVarOverflow = 'Variant overflow';
  SVarArrayBounds = 'Variant or safe array index out of bounds';
  SVarArrayLocked = 'Variant or safe array is locked';
  SVarNotImplemented = 'Operation not supported';
  SOutOfMemory = 'Out of memory';
  SVarInvalid = 'Invalid argument';
  SVarUnexpected = 'Unexpected variant error';
  SInvalidVarOpWithHResultWithPrefix = 'Invalid variant operation (%s%.8x)'#10'%s';
{$ENDIF}

{$IFNDEF VER7P}
const
  SecsPerMin    = 60;
  MSecsPerSec   = 1000;

{$IFDEF FPC}
  SVarArrayBounds = 'Variant or safe array index out of bounds';
  SVarArrayLocked = 'Variant or safe array is locked';
{$ENDIF}
  SVarArrayWithHResult = 'Unexpected variant or safe array error: %s%.8x';

type
  ESafeArrayError = class(Exception)
  private
    FErrorCode: HRESULT;
  public
    constructor CreateHResult(AResult: HRESULT; const AMessage: string = '');
    property ErrorCode: HRESULT read FErrorCode write FErrorCode;
  end;

  ESafeArrayCreateError = class(ESafeArrayError);
  ESafeArrayBoundsError = class(ESafeArrayError);
  ESafeArrayLockedError = class(ESafeArrayError);
{$ENDIF}

{$IFDEF CLR}
const
  soFromBeginning = soBeginning;
  soFromCurrent = soCurrent;
  soFromEnd = soEnd;

  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}
  Int    = Integer;
  Int16  = SmallInt;
  Int32  = Integer;
  UInt16 = Word;
  UInt32 = LongWord;
  SByte  = ShortInt;

  IntPtr = Pointer;
  MulticastDelegate = Pointer;
{$ENDIF}

{$IFNDEF VER16P}
{$IFDEF CLR}
  {$IFDEF VER11P}
  NativeInt = size_t;
  NativeUInt = size_t;
  {$ELSE}
  NativeInt = Integer;
  NativeUInt = Cardinal;
  {$ENDIF}
{$ELSE}
  {$IFDEF FPC}
  NativeInt = SizeInt;
  NativeUInt = SizeUInt;
  {$ELSE}
  NativeInt = Integer;
  NativeUInt = Cardinal;
  {$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFNDEF VER7P}
{$IFNDEF FPC}
  UInt64 = Int64;
{$ENDIF}
{$ENDIF}

{$IFDEF VER17P}
  TValueBuffer = TArray<Byte>;
{$ELSE}
  TValueBuffer = IntPtr;
{$ENDIF}

{$IFNDEF CLR}
{$IFDEF NEXTGEN}
  AnsiString = record
  private type
    TDisposer = class
    private
      // Use inline storage to avoid more allocation and indirection for few items.
      FInline: array[0..3] of Pointer;
      FOverflow: TArray<Pointer>;
      FCount: Integer;
      procedure AddDispose(P: Pointer);
      procedure Flush;
    public
      destructor Destroy; override;
    end;

  private
    FPtr: MarshaledAString;
    FLength: integer;
    FDisposer: TDisposer;

    procedure AddDispose(P: Pointer);
    function GetChars(Index: Integer): MarshaledAString; inline;
    procedure SetChars(Index: Integer; Value: MarshaledAString); inline;
    procedure SetPtr(Value: MarshaledAString);
  public
    procedure SetLength(NewLength: integer);
    function Length: integer; inline;

    class operator Equal(const Left, Right: AnsiString): Boolean;
    class operator NotEqual(const Left, Right: AnsiString): Boolean; inline;

    class operator Implicit(const Val: AnsiString): MarshaledAString;
    class operator Explicit(const Ptr: MarshaledAString): AnsiString;

    class operator Implicit(const Val: AnsiString): string;
    class operator Implicit(const Str: string): AnsiString;

    class operator Implicit(const Val: AnsiString): Variant;
    class operator Explicit(const v: Variant): AnsiString;

    class operator Implicit(const Val: AnsiString): TBytes;
    class operator Explicit(const b: TBytes): AnsiString;

    property Chars[index: Integer]: MarshaledAString read GetChars write SetChars; default;
    property Ptr: MarshaledAString read FPtr write SetPtr;
  end;

  WideString = string;

  AnsiChar = byte;
  PAnsiChar = MarshaledAString;
{$ENDIF NEXTGEN}

  TValueArr = PAnsiChar;

{$IFNDEF VER11P}
  TBytes = array of Byte;
{$ENDIF}
  PAChar = PAnsiChar;
  PWChar = PWideChar;

{$ELSE}
  TValueArr = TBytes;

  PChar = string;
  PAnsiChar = AnsiString;
  PWideChar = WideString;

  PAChar = IntPtr;
  PWChar = IntPtr;
  POleVariant = IntPtr;

  AnsiStringBuilder = StringBuilder;
  WideStringBuilder = StringBuilder;
{$ENDIF}

{$IFNDEF VER6P}
  TStringDynArray = array of string;
  TVarType = Word;

  // Copied from d7 SysUtils
  EOSError = class(Exception)
  public
    ErrorCode: longword;
  end;
{$ENDIF}

  TByteArr = TBytes;
  TIntArr = array of Integer;
  TWordArr = array of Word;

  TStringArray = array of string;

{$IFDEF CLR}
  LPSTR = IntPtr;
  LPWSTR = IntPtr;
{$ENDIF}

{$IFDEF NEXTGEN}
  LPSTR = IntPtr;
  LPWSTR = IntPtr;
{$ELSE}
{$IFNDEF MSWINDOWS}
  LPSTR = PAnsiChar;
  {$EXTERNALSYM LPSTR}
  LPWSTR = PWideChar;
  {$EXTERNALSYM LPWSTR}
{$ENDIF}
{$ENDIF}

{$IFDEF UTF8}
type
  UTF8String = type AnsiString;
{$ENDIF}

{$IFDEF CLR}
  TIntArray     = array of Longword;
{$ELSE}
  PInteger      = ^LongWord;
  TIntegerArray = array[0..1023 + 20] of LongWord;
  TIntArray     = ^TIntegerArray;
{$ENDIF}

  TIntValueItem = record
    Key: string;
    Value: Integer;
  end;
{$IFNDEF CLR}
  PIntValueItem = ^TIntValueItem;
{$ENDIF}

  TIntValueStringList = class
  private
    FCodeStrings: TList;
    FSorted: boolean;

    function GetKey(Index: integer): string;
    function GetValue(Index: integer): integer;
    function GetCount: Integer;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Assign(Source: TIntValueStringList);

    function Add(S: string; Code: Integer): integer;
    procedure Insert(Index: Integer; Key: string; Value: Integer);
    procedure Delete(Index: Integer);
    procedure Clear;

    function Find(const Key: string; var Index: Integer): Boolean;
    function IndexOf(Key: string): integer;

    procedure Sort;

    property Strings[Index: integer]: string read GetKey; default;
    property Values[Index: integer]: integer read GetValue;
    property Count: Integer read GetCount;
  end;

  TStrValueItem = record
    Key: string;
    Value: string;
  end;
{$IFNDEF CLR}
  PStrValueItem = ^TStrValueItem;
{$ENDIF}

  TStrValueStringList = class
  private
    FStrValueStrings: TList;
    FSorted: boolean;

    function GetKey(Index: integer): string;
    function GetValue(Index: integer): string;
    function GetCount: Integer;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Assign(Source: TStrValueStringList);

    function Add(Key: string; Value: string): integer;
    procedure Insert(Index: Integer; Key: string; Value: string);
    procedure Delete(Index: Integer);
    procedure Clear;

    function Find(const Key: string; var Index: Integer): Boolean;
    function IndexOf(Key: string): integer;

    procedure Sort;

    property Strings[Index: integer]: string read GetKey;
    property Values[Index: integer]: string read GetValue; default;
    property Count: Integer read GetCount;
  end;

implementation

{$IFDEF NEXTGEN}
uses
  Math;
{$ENDIF}

{$IFDEF NEXTGEN}
{ AnsiString.TDisposer }

destructor AnsiString.TDisposer.Destroy;
begin
  Flush;
  inherited;
end;

procedure AnsiString.TDisposer.AddDispose(P: Pointer);
var
  c: Integer;
begin
  if FCount < System.Length(FInline) then
    FInline[FCount] := P
  else begin
    c := FCount - System.Length(FInline);
    if c = System.Length(FOverflow) then begin
      if System.Length(FOverflow) < 4 then
        System.SetLength(FOverflow, 4)
      else
        System.SetLength(FOverflow, System.Length(FOverflow) * 2);
    end;
    FOverflow[c] := P;
  end;

  Inc(FCount);
end;

procedure AnsiString.TDisposer.Flush;
var
  i: Integer;
begin
  if FCount <= System.Length(FInline) then begin
    for i := 0 to FCount - 1 do
      System.FreeMem(FInline[i]);
  end
  else begin
    for i := 0 to System.Length(FInline) - 1 do
      System.FreeMem(FInline[i]);
    for i := 0 to FCount - System.Length(FInline) - 1 do
      System.FreeMem(FOverflow[i]);
  end;
  FCount := 0;
  System.SetLength(FOverflow, 0);
end;

{ AnsiString }

procedure AnsiString.AddDispose(P: Pointer);
begin
  if FDisposer = nil then
    FDisposer := TDisposer.Create;
  FDisposer.AddDispose(P);
end;

procedure AnsiString.SetLength(NewLength: integer);
var
  NewPtr: Pointer;
begin
  if NewLength <= 0 then begin
    FPtr := nil;
    FLength := 0;
    Exit;
  end;

  NewPtr := System.AllocMem(NewLength + 1);
  if (FDisposer <> nil) and (FPtr <> nil) then
    Move(FPtr^, NewPtr^, Min(FLength, NewLength));

  AddDispose(NewPtr);
  FPtr := NewPtr;
  FPtr[NewLength] := #0;
  FLength := NewLength;
end;

function AnsiString.Length: integer;
begin
  if (FDisposer <> nil) and (FPtr <> nil) then
    Result := FLength
  else
    Result := 0;
end;

class operator AnsiString.Equal(const Left, Right: AnsiString): Boolean;
var
  Len: integer;
  P1, P2: PAnsiChar;
begin
  Len := Left.Length;
  Result := Len = Right.Length;
  if Result then begin
    P1 := Left.FPtr;
    P2 := Right.Ptr;
    while Len > 0 do begin
      if P1^ <> P2^ then
        Exit(False);
      Inc(P1);
      Inc(P2);
      Dec(Len);
    end;
  end;
end;

class operator AnsiString.NotEqual(const Left, Right: AnsiString): Boolean;
begin
  Result := not (Left = Right);
end;

class operator AnsiString.Implicit(const Val: AnsiString): MarshaledAString;
begin
  if Val.FPtr = nil then
    Result := #0
  else
    Result := Val.FPtr;
end;

class operator AnsiString.Explicit(const Ptr: MarshaledAString): AnsiString;
begin
  if Result.FPtr <> Ptr then begin
    Result.FLength := System.Length(Ptr);

    if Result.FLength = 0 then
      Result.FPtr := nil
    else begin
      Result.FPtr := System.AllocMem(Result.FLength + 1);
      Result.AddDispose(Result.FPtr);

      Move(Ptr^, Result.FPtr^, Result.FLength);
      Result.FPtr[Result.FLength] := #0;
    end;
  end;
end;

class operator AnsiString.Implicit(const Val: AnsiString): string;
begin
  Result := string(Val.FPtr);
end;

class operator AnsiString.Implicit(const Str: string): AnsiString;
begin
  Result.FLength := LocaleCharsFromUnicode(DefaultSystemCodePage, 0, PWideChar(Str), System.Length(Str) + 1, nil, 0, nil, nil);
  if Result.FLength > 0 then begin
    Result.FPtr := System.AllocMem(Result.FLength);
    Result.AddDispose(Result.FPtr);
    LocaleCharsFromUnicode(DefaultSystemCodePage, 0, PWideChar(Str), System.Length(Str) + 1,
      Result.FPtr, Result.FLength, nil, nil);
    Dec(Result.FLength);
  end
  else
    Result.FPtr := nil;
end;

class operator AnsiString.Implicit(const Val: AnsiString): Variant;
begin
  Result := string(Val.FPtr);
end;

class operator AnsiString.Explicit(const v: Variant): AnsiString;
begin
  Result := AnsiString(string(v));
end;

class operator AnsiString.Implicit(const Val: AnsiString): TBytes;
var
  Len: integer;
begin
  Len := Val.Length;
  System.SetLength(Result, Len);
  if Len > 0 then
    Move(Val.FPtr^, Result[0], Len);
end;

class operator AnsiString.Explicit(const b: TBytes): AnsiString;
begin
  Result.FLength := System.Length(b);

  if Result.FLength = 0 then
    Result.FPtr := nil
  else begin
    Result.FPtr := System.AllocMem(Result.FLength + 1);
    Result.AddDispose(Result.FPtr);

    Move(b[0], Result.FPtr^, Result.FLength);
    Result.FPtr[Result.FLength] := #0;
  end;
end;

function AnsiString.GetChars(Index: Integer): MarshaledAString;
begin
  Result := @FPtr[Index - 1];
end;

procedure AnsiString.SetChars(Index: Integer; Value: MarshaledAString);
begin
  FPtr[Index - 1] := Value[0];
end;

procedure AnsiString.SetPtr(Value: MarshaledAString);
begin
  if FDisposer = nil then
    FDisposer := TDisposer.Create;

  FPtr := Value;
  FLength := System.Length(Value);
end;

{$ENDIF}

{$IFNDEF VER7P}
constructor ESafeArrayError.CreateHResult(AResult: HRESULT; const AMessage: string);
var
  S: string;
begin
  S := AMessage;
  if S = '' then
    S := Format(SVarArrayWithHResult, [AResult]);
  Create(S);
  FErrorCode := AResult;
end;
{$ENDIF}

{ TIntValueStringList }

{$IFDEF CLR}
function CmpIntValueStrings(Item1, Item2: TObject): Integer;
{$ELSE}
function CmpIntValueStrings(Item1, Item2: IntPtr): Integer;
{$ENDIF}
begin
{$IFDEF CLR}
  Result := System.String.CompareOrdinal(TIntValueItem(Item1).Key, TIntValueItem(Item2).Key);
{$ELSE}
  Result := CompareStr(PIntValueItem(Item1).Key, PIntValueItem(Item2).Key);
{$ENDIF}
end;

constructor TIntValueStringList.Create;
begin
  inherited;
  FCodeStrings := TList.Create;
  FSorted := False;
end;

destructor TIntValueStringList.Destroy;
begin
  Clear;
  FCodeStrings.Free;
  inherited;
end;

function TIntValueStringList.GetKey(Index: integer): string;
begin
{$IFDEF CLR}
  Result := TIntValueItem(FCodeStrings[Index]).Key;
{$ELSE}
  Result := PIntValueItem(FCodeStrings[Index]).Key;
{$ENDIF}
end;

function TIntValueStringList.GetValue(Index: integer): integer;
begin
{$IFDEF CLR}
  Result := TIntValueItem(FCodeStrings[Index]).Value;
{$ELSE}
  Result := PIntValueItem(FCodeStrings[Index]).Value;
{$ENDIF}
end;

function TIntValueStringList.GetCount: Integer;
begin
  Result := FCodeStrings.Count;
end;

procedure TIntValueStringList.Assign(Source: TIntValueStringList);
var
  i: integer;
begin
  Clear;
  for i := 0 to Source.Count - 1 do
    Add(Source.Strings[i], Source.Values[i]);
end;

function TIntValueStringList.Add(S: string; Code: Integer): Integer;
begin
  if not FSorted then
    Result := FCodeStrings.Count
  else
    if Find(S, Result) then
      raise Exception.Create('String "' + S + '" is already in the list.');
  Insert(Result, S, Code);
end;

procedure TIntValueStringList.Insert(Index: Integer; Key: string; Value: Integer);
var
{$IFDEF CLR}
  CodeString: TIntValueItem;
{$ELSE}
  CodeString: PIntValueItem;
{$ENDIF}
begin
{$IFNDEF CLR}
  New(CodeString);
{$ENDIF}
  CodeString.Key := Key;
  CodeString.Value := Value;
  FCodeStrings.Insert(Index, CodeString);
end;

procedure TIntValueStringList.Delete(Index: Integer);
begin
{$IFNDEF CLR}
  Dispose(PIntValueItem(FCodeStrings[Index]));
{$ENDIF}
  FCodeStrings.Delete(Index);
end;

procedure TIntValueStringList.Clear;
{$IFNDEF CLR}
var
  i: integer;
{$ENDIF}
begin
{$IFNDEF CLR}
  for i := 0 to FCodeStrings.Count - 1 do
    Dispose(PIntValueItem(FCodeStrings[i]));
{$ENDIF}
  FCodeStrings.Clear;
end;

procedure TIntValueStringList.Sort;
begin
  FCodeStrings.Sort(CmpIntValueStrings);
  FSorted := True;
end;

function TIntValueStringList.Find(const Key: string; var Index: Integer): Boolean;
var
  L, H, I, C: Integer;
begin
  Result := False;
  L := 0;
  H := FCodeStrings.Count - 1;
  while L <= H do
  begin
    I := (L + H) shr 1;
  {$IFDEF CLR}
    C := System.String.CompareOrdinal(Strings[i], Key);
  {$ELSE}
    C := CompareStr(Strings[i], Key);
  {$ENDIF}
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        L := I;
      end;
    end;
  end;
  Index := L;
end;

function TIntValueStringList.IndexOf(Key: string): integer;
begin
  if not FSorted then begin
    for Result := 0 to Count - 1 do
    {$IFDEF CLR}
      if System.String.CompareOrdinal(Strings[Result], Key) = 0 then
    {$ELSE}
      if CompareStr(Strings[Result], Key) = 0 then
    {$ENDIF}
        exit;
      Result := -1;
  end
  else
    if not Find(Key, Result) then Result := -1;
end;

{ TStrValueStringList }

{$IFDEF CLR}
function CmpStrValueStrings(Item1, Item2: TObject): Integer;
{$ELSE}
function CmpStrValueStrings(Item1, Item2: IntPtr): Integer;
{$ENDIF}
begin
{$IFDEF CLR}
  Result := System.String.CompareOrdinal(TStrValueItem(Item1).Key, TStrValueItem(Item2).Key);
{$ELSE}
  Result := CompareStr(PStrValueItem(Item1).Key, PStrValueItem(Item2).Key);
{$ENDIF}
end;

constructor TStrValueStringList.Create;
begin
  inherited;
  FStrValueStrings := TList.Create;
  FSorted := False;
end;

destructor TStrValueStringList.Destroy;
begin
  Clear;
  FStrValueStrings.Free;
  inherited;
end;

function TStrValueStringList.GetKey(Index: integer): string;
begin
{$IFDEF CLR}
  Result := TStrValueItem(FStrValueStrings[Index]).Key;
{$ELSE}
  Result := PStrValueItem(FStrValueStrings[Index]).Key;
{$ENDIF}
end;

function TStrValueStringList.GetValue(Index: integer): string;
begin
{$IFDEF CLR}
  Result := TStrValueItem(FStrValueStrings[Index]).Value;
{$ELSE}
  Result := PStrValueItem(FStrValueStrings[Index]).Value;
{$ENDIF}
end;

function TStrValueStringList.GetCount: Integer;
begin
  Result := FStrValueStrings.Count;
end;

procedure TStrValueStringList.Assign(Source: TStrValueStringList);
var
  i: integer;
begin
  Clear;
  for i := 0 to Source.Count - 1 do
    Add(Source.Strings[i], Source.Values[i]);
end;

function TStrValueStringList.Add(Key: string; Value: string): Integer;
begin
  if not FSorted then
    Result := FStrValueStrings.Count
  else
    if Find(Key, Result) then
      raise Exception.Create('String "' + Key + '" is already in the list.');
  Insert(Result, Key, Value);
end;

procedure TStrValueStringList.Insert(Index: Integer; Key: string; Value: string);
var
{$IFDEF CLR}
  StrValueItem: TStrValueItem;
{$ELSE}
  StrValueItem: PStrValueItem;
{$ENDIF}
begin
{$IFNDEF CLR}
  New(StrValueItem);
{$ENDIF}
  StrValueItem.Key := Key;
  StrValueItem.Value := Value;
  FStrValueStrings.Insert(Index, StrValueItem);
end;

procedure TStrValueStringList.Delete(Index: Integer);
begin
{$IFNDEF CLR}
  Dispose(PStrValueItem(FStrValueStrings[Index]));
{$ENDIF}
  FStrValueStrings.Delete(Index);
end;

procedure TStrValueStringList.Clear;
{$IFNDEF CLR}
var
  i: integer;
{$ENDIF}
begin
{$IFNDEF CLR}
  for i := 0 to FStrValueStrings.Count - 1 do
    Dispose(PStrValueItem(FStrValueStrings[i]));
{$ENDIF}
  FStrValueStrings.Clear;
end;

procedure TStrValueStringList.Sort;
begin
  FStrValueStrings.Sort(CmpStrValueStrings);
  FSorted := True;
end;

function TStrValueStringList.Find(const Key: string; var Index: Integer): Boolean;
var
  L, H, I, C: Integer;
begin
  Result := False;
  L := 0;
  H := FStrValueStrings.Count - 1;
  while L <= H do
  begin
    I := (L + H) shr 1;
  {$IFDEF CLR}
    C := System.String.CompareOrdinal(Strings[i], Key);
  {$ELSE}
    C := CompareStr(Strings[i], Key);
  {$ENDIF}
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        L := I;
      end;
    end;
  end;
  Index := L;
end;

function TStrValueStringList.IndexOf(Key: string): integer;
begin
  if not FSorted then begin
    for Result := 0 to Count - 1 do
    {$IFDEF CLR}
      if System.String.CompareOrdinal(Strings[Result], Key) = 0 then
    {$ELSE}
      if CompareStr(Strings[Result], Key) = 0 then
    {$ENDIF}
        exit;
      Result := -1;
  end
  else
    if not Find(Key, Result) then Result := -1;
end;

end.
