Browse Source

rtl: started generic TList

mattias 6 years ago
parent
commit
ddc8bafa81

+ 859 - 0
packages/rtl/generics.collections.pas

@@ -0,0 +1,859 @@
+unit Generics.Collections;
+
+{$Mode Delphi}
+{$COperators On}
+
+interface
+
+uses
+  Classes, SysUtils,
+  {$IFDEF Pas2js}JS,{$ENDIF}
+  Generics.Strings, Generics.Defaults;
+
+type
+  TCollectionNotification = (cnAdded, cnRemoved, cnExtracted);
+  TCollectionNotifyEvent<T> = procedure(ASender: TObject; const AItem: T;
+    AAction: TCollectionNotification) of object;
+
+  { TBinarySearchResult }
+
+  TBinarySearchResult = record
+    FoundIndex, CandidateIndex: SizeInt;
+    CompareResult: SizeInt;
+  end;
+
+  { TCustomArrayHelper }
+
+  TCustomArrayHelper<T> = class abstract
+  protected
+    class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt;
+      const AComparer: IComparer<T>); virtual; abstract;
+  public
+    //class procedure Sort(var AValues: array of T); overload;
+    class procedure Sort(var AValues: array of T;
+      const AComparer: IComparer<T>); overload;
+    class procedure Sort(var AValues: array of T;
+      const AComparer: IComparer<T>; AIndex, ACount: SizeInt); overload;
+
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>;
+      AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload;
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
+      AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload;
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out AFoundIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload;
+    //class function BinarySearch(const AValues: array of T; const AItem: T;
+    //  out AFoundIndex: SizeInt): Boolean; overload;
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>): Boolean; overload;
+    //class function BinarySearch(const AValues: array of T; const AItem: T;
+    //  out ASearchResult: TBinarySearchResult): Boolean; overload;
+  end;
+
+  { TArrayHelper }
+
+  TArrayHelper<T> = class(TCustomArrayHelper<T>)
+  protected
+    class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt;
+      const AComparer: IComparer<T>); override;
+  public
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>;
+      AIndex, ACount: SizeInt): Boolean; override; overload;
+    class function BinarySearch(const AValues: array of T; const AItem: T;
+      out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
+      AIndex, ACount: SizeInt): Boolean; override; overload;
+  end;
+
+  { TEnumerator }
+
+  TEnumerator<T> = class abstract
+  protected
+    function DoGetCurrent: T; virtual; abstract;
+    function DoMoveNext: boolean; virtual; abstract;
+  public
+    property Current: T read DoGetCurrent;
+    function MoveNext: boolean;
+  end;
+
+  { TEnumerable }
+
+  TEnumerable<T> = class abstract
+  protected
+    type
+      TMyEnumerator = TEnumerator<T>;
+      TMyArray = TArray<T>;
+    function DoGetEnumerator: TMyEnumerator; virtual; abstract;
+  public
+    function GetEnumerator: TMyEnumerator; inline;
+    function ToArray: TMyArray; virtual; overload;
+  end;
+
+  { TCustomList }
+
+  TCustomList<T> = class abstract(TEnumerable<T>)
+  private
+    FOnNotify: TCollectionNotifyEvent<T>;
+    function GetCapacity: SizeInt; inline;
+  protected
+    type TMyArrayHelper = TArrayHelper<T>;
+  protected
+    FLength: SizeInt;
+    FItems: array of T;
+    function PrepareAddingItem: SizeInt; virtual;
+    function PrepareAddingRange(ACount: SizeInt): SizeInt; virtual;
+    procedure Notify(const AValue: T; ACollectionNotification: TCollectionNotification); virtual;
+    function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; virtual;
+    procedure SetCapacity(AValue: SizeInt); virtual; abstract;
+    function GetCount: SizeInt; virtual;
+  public
+    function ToArray: TArray<T>; override;
+
+    property Count: SizeInt read GetCount;
+    property Capacity: SizeInt read GetCapacity write SetCapacity;
+    property OnNotify: TCollectionNotifyEvent<T> read FOnNotify write FOnNotify;
+
+    procedure TrimExcess; virtual; abstract;
+  end;
+
+  { TCustomListEnumerator }
+
+  TCustomListEnumerator<T> = class abstract(TEnumerator<T>)
+  private
+    FList: TCustomList<T>;
+    FIndex: SizeInt;
+  protected
+    function DoMoveNext: boolean; override;
+    function DoGetCurrent: T; override;
+    function GetCurrent: T; virtual;
+  public
+    constructor Create(AList: TCustomList<T>);
+  end;
+
+  { TList }
+
+  TList<T> = class(TCustomList<T>)
+  private
+    FComparer: IComparer<T>;
+  protected
+    procedure SetCapacity(AValue: SizeInt); override;
+    procedure SetCount(AValue: SizeInt);
+    procedure InitializeList; virtual;
+    procedure InternalInsert(AIndex: SizeInt; const AValue: T);
+    function DoGetEnumerator: TEnumerator<T>; override;
+  private
+    function GetItem(AIndex: SizeInt): T;
+    procedure SetItem(AIndex: SizeInt; const AValue: T);
+  public
+    type
+      TEnumerator = class(TCustomListEnumerator<T>);
+    function GetEnumerator: TEnumerator; reintroduce;
+  public
+    constructor Create; overload;
+    constructor Create(const AComparer: IComparer<T>); overload;
+    constructor Create(ACollection: TEnumerable<T>); overload;
+
+    destructor Destroy; override;
+
+    function Add(const AValue: T): SizeInt; virtual;
+    procedure AddRange(const AValues: array of T); virtual; overload;
+    procedure AddRange(const AEnumerable: IEnumerable<T>); overload;
+    procedure AddRange(AEnumerable: TEnumerable<T>); overload;
+
+    procedure Insert(AIndex: SizeInt; const AValue: T); virtual;
+    procedure InsertRange(AIndex: SizeInt; const AValues: array of T); virtual; overload;
+    procedure InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>); overload;
+    procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>); overload;
+
+    function Remove(const AValue: T): SizeInt;
+    procedure Delete(AIndex: SizeInt); inline;
+    procedure DeleteRange(AIndex, ACount: SizeInt);
+    function ExtractIndex(const AIndex: SizeInt): T; overload;
+    function Extract(const AValue: T): T; overload;
+
+    procedure Exchange(AIndex1, AIndex2: SizeInt); virtual;
+    procedure Move(AIndex, ANewIndex: SizeInt); virtual;
+
+    function First: T; inline;
+    function Last: T; inline;
+
+    procedure Clear;
+
+    function Contains(const AValue: T): Boolean; inline;
+    function IndexOf(const AValue: T): SizeInt; virtual;
+    function LastIndexOf(const AValue: T): SizeInt; virtual;
+
+    procedure Reverse;
+
+    procedure TrimExcess; override;
+
+    procedure Sort; overload;
+    procedure Sort(const AComparer: IComparer<T>); overload;
+    function BinarySearch(const AItem: T; out AIndex: SizeInt): Boolean; overload;
+    function BinarySearch(const AItem: T; out AIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload;
+
+    property Count: SizeInt read FLength write SetCount;
+    property Items[Index: SizeInt]: T read GetItem write SetItem; default;
+  end;
+
+implementation
+
+{ TCustomArrayHelper }
+
+class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T;
+  const AComparer: IComparer<T>);
+begin
+  QuickSort(AValues, Low(AValues), High(AValues), AComparer);
+end;
+
+class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T;
+  const AComparer: IComparer<T>; AIndex, ACount: SizeInt);
+begin
+  if ACount <= 1 then
+    Exit;
+  QuickSort(AValues, AIndex, Pred(AIndex + ACount), AComparer);
+end;
+
+class function TCustomArrayHelper<T>.BinarySearch(const AValues: array of T;
+  const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer<T>
+  ): Boolean;
+begin
+  Result := BinarySearch(AValues, AItem, AFoundIndex, AComparer,
+                         Low(AValues), High(AValues));
+end;
+
+class function TCustomArrayHelper<T>.BinarySearch(const AValues: array of T;
+  const AItem: T; out ASearchResult: TBinarySearchResult;
+  const AComparer: IComparer<T>): Boolean;
+begin
+  Result := BinarySearch(AValues, AItem, ASearchResult, AComparer,
+                         Low(AValues), High(AValues));
+end;
+
+{ TArrayHelper }
+
+class procedure TArrayHelper<T>.QuickSort(var AValues: array of T; ALeft,
+  ARight: SizeInt; const AComparer: IComparer<T>);
+var
+  I, J: SizeInt;
+  P, Q: T;
+begin
+  if ((ARight - ALeft) <= 0) or (Length(AValues) = 0) then
+    Exit;
+  repeat
+    I := ALeft;
+    J := ARight;
+    P := AValues[ALeft + (ARight - ALeft) shr 1];
+    repeat
+      while AComparer.Compare(AValues[I], P) < 0 do
+        Inc(I);
+      while AComparer.Compare(AValues[J], P) > 0 do
+        Dec(J);
+      if I <= J then
+      begin
+        if I <> J then
+        begin
+          Q := AValues[I];
+          AValues[I] := AValues[J];
+          AValues[J] := Q;
+        end;
+        Inc(I);
+        Dec(J);
+      end;
+    until I > J;
+    // sort the smaller range recursively
+    // sort the bigger range via the loop
+    // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
+    if J - ALeft < ARight - I then
+    begin
+      if ALeft < J then
+        QuickSort(AValues, ALeft, J, AComparer);
+      ALeft := I;
+    end
+    else
+    begin
+      if I < ARight then
+        QuickSort(AValues, I, ARight, AComparer);
+      ARight := J;
+    end;
+  until ALeft >= ARight;
+end;
+
+class function TArrayHelper<T>.BinarySearch(const AValues: array of T;
+  const AItem: T; out ASearchResult: TBinarySearchResult;
+  const AComparer: IComparer<T>; AIndex, ACount: SizeInt): Boolean;
+var
+  imin, imax, imid: Int32;
+begin
+  // continually narrow search until just one element remains
+  imin := AIndex;
+  imax := Pred(AIndex + ACount);
+
+  while (imin < imax) do
+  begin
+    imid := (imax+imin) div 2;
+
+    ASearchResult.CompareResult := AComparer.Compare(AValues[imid], AItem);
+    // reduce the search
+    if (ASearchResult.CompareResult < 0) then
+      imin := imid + 1
+    else
+    begin
+      if ASearchResult.CompareResult = 0 then
+      begin
+        ASearchResult.FoundIndex := imid;
+        ASearchResult.CandidateIndex := imid;
+        Exit(True);
+      end;
+      imax := imid;
+    end;
+  end;
+  // At exit of while:
+  //   if A[] is empty, then imax < imin
+  //   otherwise imax == imin
+
+  // deferred test for equality
+
+  if (imax = imin) then
+  begin
+    ASearchResult.CompareResult := AComparer.Compare(AValues[imin], AItem);
+    ASearchResult.CandidateIndex := imin;
+    if (ASearchResult.CompareResult = 0) then
+    begin
+      ASearchResult.FoundIndex := imin;
+      Exit(True);
+    end else
+    begin
+      ASearchResult.FoundIndex := -1;
+      Exit(False);
+    end;
+  end
+  else
+  begin
+    ASearchResult.CompareResult := 0;
+    ASearchResult.FoundIndex := -1;
+    ASearchResult.CandidateIndex := -1;
+    Exit(False);
+  end;
+end;
+
+class function TArrayHelper<T>.BinarySearch(const AValues: array of T;
+  const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
+  AIndex, ACount: SizeInt): Boolean;
+var
+  imin, imax, imid: Int32;
+  LCompare: SizeInt;
+begin
+  // continually narrow search until just one element remains
+  imin := AIndex;
+  imax := Pred(AIndex + ACount);
+
+  // http://en.wikipedia.org/wiki/Binary_search_algorithm
+  while (imin < imax) do
+  begin
+    imid := (imin + imax) div 2;
+
+    // code must guarantee the interval is reduced at each iteration
+    // assert(imid < imax);
+    // note: 0 <= imin < imax implies imid will always be less than imax
+
+    LCompare := AComparer.Compare(AValues[imid], AItem);
+    // reduce the search
+    if (LCompare < 0) then
+      imin := imid + 1
+    else
+    begin
+      if LCompare = 0 then
+      begin
+        AFoundIndex := imid;
+        Exit(True);
+      end;
+      imax := imid;
+    end;
+  end;
+  // At exit of while:
+  //   if A[] is empty, then imax < imin
+  //   otherwise imax == imin
+
+  // deferred test for equality
+  LCompare := AComparer.Compare(AValues[imin], AItem);
+  if (imax = imin) and (LCompare = 0) then
+  begin
+    AFoundIndex := imin;
+    Exit(True);
+  end
+  else
+  begin
+    AFoundIndex := -1;
+    Exit(False);
+  end;
+end;
+
+{ TEnumerator }
+
+function TEnumerator<T>.MoveNext: boolean;
+begin
+  Exit(DoMoveNext);
+end;
+
+{ TEnumerable }
+
+function TEnumerable<T>.GetEnumerator: TMyEnumerator;
+begin
+  Exit(DoGetEnumerator);
+end;
+
+function TEnumerable<T>.ToArray: TMyArray;
+var
+  LEnumerator: TMyEnumerator;
+begin
+  Result:=[];
+  LEnumerator := GetEnumerator;
+  try
+    while LEnumerator.MoveNext do
+      TJSArray(Result).push(LEnumerator.Current);
+  finally
+    LEnumerator.Free;
+  end;
+end;
+
+{ TCustomList }
+
+function TCustomList<T>.GetCapacity: SizeInt;
+begin
+  Result:=length(FItems);
+end;
+
+function TCustomList<T>.PrepareAddingItem: SizeInt;
+begin
+  if FLength=length(FItems) then
+    TJSArray(FItems).push(Default(T));
+
+  Result := FLength;
+  Inc(FLength);
+end;
+
+function TCustomList<T>.PrepareAddingRange(ACount: SizeInt): SizeInt;
+var
+  l: SizeInt;
+begin
+  if ACount < 0 then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+  if ACount = 0 then
+    Exit(FLength - 1);
+
+  for l:=length(FItems)+1 to FLength+ACount do
+    TJSArray(FItems).push(Default(T));
+
+  Result := FLength;
+  Inc(FLength, ACount);
+end;
+
+procedure TCustomList<T>.Notify(const AValue: T;
+  ACollectionNotification: TCollectionNotification);
+begin
+  if Assigned(FOnNotify) then
+    FOnNotify(Self, AValue, ACollectionNotification);
+end;
+
+function TCustomList<T>.DoRemove(AIndex: SizeInt;
+  ACollectionNotification: TCollectionNotification): T;
+begin
+  if (AIndex < 0) or (AIndex >= FLength) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  Result := FItems[AIndex];
+  Dec(FLength);
+
+  FItems[AIndex] := Default(T); // needed for refcounted types
+  TJSArray(FItems).splice(AIndex,1);
+
+  Notify(Result, ACollectionNotification);
+end;
+
+function TCustomList<T>.GetCount: SizeInt;
+begin
+  Result := FLength;
+end;
+
+function TCustomList<T>.ToArray: TArray<T>;
+begin
+  Result := ToArray;
+end;
+
+{ TCustomListEnumerator }
+
+function TCustomListEnumerator<T>.DoMoveNext: boolean;
+begin
+  Inc(FIndex);
+  Result := (FList.FLength > 0) and (FIndex < FList.FLength)
+end;
+
+function TCustomListEnumerator<T>.DoGetCurrent: T;
+begin
+  Result := GetCurrent;
+end;
+
+function TCustomListEnumerator<T>.GetCurrent: T;
+begin
+  Result := FList.FItems[FIndex];
+end;
+
+constructor TCustomListEnumerator<T>.Create(AList: TCustomList<T>);
+begin
+  inherited Create;
+  FIndex := -1;
+  FList := AList;
+end;
+
+{ TList }
+
+procedure TList<T>.SetCapacity(AValue: SizeInt);
+begin
+  if AValue < Count then
+    Count := AValue;
+
+  SetLength(FItems, AValue);
+end;
+
+procedure TList<T>.SetCount(AValue: SizeInt);
+begin
+  if AValue < 0 then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  if AValue > Capacity then
+    Capacity := AValue
+  else if AValue < Count then
+    DeleteRange(AValue, Count - AValue);
+
+  FLength := AValue;
+end;
+
+procedure TList<T>.InitializeList;
+begin
+end;
+
+procedure TList<T>.InternalInsert(AIndex: SizeInt; const AValue: T);
+begin
+  if (AIndex < 0) or (AIndex > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+  TJSArray(FItems).splice(AIndex,0,AValue);
+  inc(FLength);
+
+  FItems[AIndex] := AValue;
+  Notify(AValue, cnAdded);
+end;
+
+function TList<T>.DoGetEnumerator: TEnumerator<T>;
+begin
+  Result := GetEnumerator;
+end;
+
+function TList<T>.GetItem(AIndex: SizeInt): T;
+begin
+  if (AIndex < 0) or (AIndex >= Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  Result := FItems[AIndex];
+end;
+
+procedure TList<T>.SetItem(AIndex: SizeInt; const AValue: T);
+begin
+  if (AIndex < 0) or (AIndex >= Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+  Notify(FItems[AIndex], cnRemoved);
+  FItems[AIndex] := AValue;
+  Notify(AValue, cnAdded);
+end;
+
+function TList<T>.GetEnumerator: TEnumerator;
+begin
+  Result := TEnumerator.Create(Self);
+end;
+
+constructor TList<T>.Create;
+begin
+  InitializeList;
+  FComparer := TComparer<T>.Default;
+end;
+
+constructor TList<T>.Create(const AComparer: IComparer<T>);
+begin
+  InitializeList;
+  FComparer := AComparer;
+end;
+
+constructor TList<T>.Create(ACollection: TEnumerable<T>);
+var
+  LItem: T;
+begin
+  Create;
+  for LItem in ACollection do
+    Add(LItem);
+end;
+
+destructor TList<T>.Destroy;
+begin
+  SetCapacity(0);
+end;
+
+function TList<T>.Add(const AValue: T): SizeInt;
+begin
+  Result := PrepareAddingItem;
+  FItems[Result] := AValue;
+  Notify(AValue, cnAdded);
+end;
+
+procedure TList<T>.AddRange(const AValues: array of T);
+begin
+  InsertRange(Count, AValues);
+end;
+
+procedure TList<T>.AddRange(const AEnumerable: IEnumerable<T>);
+var
+  LValue: T;
+begin
+  for LValue in AEnumerable do
+    Add(LValue);
+end;
+
+procedure TList<T>.AddRange(AEnumerable: TEnumerable<T>);
+var
+  LValue: T;
+begin
+  for LValue in AEnumerable do
+    Add(LValue);
+end;
+
+procedure TList<T>.Insert(AIndex: SizeInt; const AValue: T);
+begin
+  if (AIndex < 0) or (AIndex > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  InternalInsert(AIndex, AValue);
+end;
+
+procedure TList<T>.InsertRange(AIndex: SizeInt; const AValues: array of T);
+var
+  LLength, i: sizeint;
+  LValue: T;
+begin
+  if (AIndex < 0) or (AIndex > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  LLength := Length(AValues);
+  if LLength = 0 then
+    Exit;
+
+  if AIndex <> PrepareAddingRange(LLength) then
+  begin
+    for i := AIndex to Count - LLength -1 do
+      FItems[i+LLength] := FItems[i];
+    for i := 0 to LLength -1 do
+      FItems[AIndex+i] := Default(T);
+  end;
+
+  for i := 0 to Pred(LLength) do
+  begin
+    LValue:=AValues[i];
+    FItems[i+AIndex] := LValue;
+    Notify(LValue, cnAdded);
+  end;
+end;
+
+procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>);
+var
+  LValue: T;
+  i: SizeInt;
+begin
+  if (AIndex < 0) or (AIndex > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  i := 0;
+  for LValue in AEnumerable do
+  begin
+    InternalInsert(AIndex + i, LValue);
+    Inc(i);
+  end;
+end;
+
+procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>);
+var
+  LValue: T;
+  i:  SizeInt;
+begin
+  if (AIndex < 0) or (AIndex > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  i := 0;
+  for LValue in AEnumerable do
+  begin
+    InternalInsert(Aindex + i, LValue);
+    Inc(i);
+  end;
+end;
+
+function TList<T>.Remove(const AValue: T): SizeInt;
+begin
+  Result := IndexOf(AValue);
+  if Result >= 0 then
+    DoRemove(Result, cnRemoved);
+end;
+
+procedure TList<T>.Delete(AIndex: SizeInt);
+begin
+  DoRemove(AIndex, cnRemoved);
+end;
+
+procedure TList<T>.DeleteRange(AIndex, ACount: SizeInt);
+var
+  LDeleted: TMyArray;
+  i: SizeInt;
+begin
+  if ACount = 0 then
+    Exit;
+
+  if (ACount < 0) or (AIndex < 0) or (AIndex + ACount > Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  LDeleted:=TMyArray(TJSArray(FItems).splice(AIndex,Count));
+  Dec(FLength, ACount);
+
+  for i := 0 to High(LDeleted) do
+    Notify(LDeleted[i], cnRemoved);
+end;
+
+function TList<T>.ExtractIndex(const AIndex: SizeInt): T;
+begin
+  Result := DoRemove(AIndex, cnExtracted);
+end;
+
+function TList<T>.Extract(const AValue: T): T;
+var
+  LIndex: SizeInt;
+begin
+  LIndex := IndexOf(AValue);
+  if LIndex < 0 then
+    Exit(Default(T));
+
+  Result := DoRemove(LIndex, cnExtracted);
+end;
+
+procedure TList<T>.Exchange(AIndex1, AIndex2: SizeInt);
+var
+  LTemp: T;
+begin
+  LTemp := FItems[AIndex1];
+  FItems[AIndex1] := FItems[AIndex2];
+  FItems[AIndex2] := LTemp;
+end;
+
+procedure TList<T>.Move(AIndex, ANewIndex: SizeInt);
+var
+  Arr: TJSArray;
+  LTemp: JSValue;
+  i: SizeInt;
+begin
+  if ANewIndex = AIndex then
+    Exit;
+
+  if (ANewIndex < 0) or (ANewIndex >= Count) then
+    raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange);
+
+  Arr := TJSArray(FItems);
+  LTemp := Arr[AIndex];
+  if AIndex < ANewIndex then
+    for i := AIndex to ANewIndex-1 do
+      Arr[i] := Arr[i+1]
+  else
+    for i := ANewIndex downto AIndex+1 do
+      Arr[i] := Arr[i-1];
+  Arr[ANewIndex] := LTemp;
+end;
+
+function TList<T>.First: T;
+begin
+  Result := Items[0];
+end;
+
+function TList<T>.Last: T;
+begin
+  Result := Items[Pred(Count)];
+end;
+
+procedure TList<T>.Clear;
+begin
+  SetCount(0);
+  SetCapacity(0);
+end;
+
+function TList<T>.Contains(const AValue: T): Boolean;
+begin
+  Result := IndexOf(AValue) >= 0;
+end;
+
+function TList<T>.IndexOf(const AValue: T): SizeInt;
+var
+  i: SizeInt;
+begin
+  for i := 0 to Count - 1 do
+    if FComparer.Compare(AValue, FItems[i]) = 0 then
+      Exit(i);
+  Result := -1;
+end;
+
+function TList<T>.LastIndexOf(const AValue: T): SizeInt;
+var
+  i: SizeInt;
+begin
+  for i := Count - 1 downto 0 do
+    if FComparer.Compare(AValue, FItems[i]) = 0 then
+      Exit(i);
+  Result := -1;
+end;
+
+procedure TList<T>.Reverse;
+var
+  a, b: SizeInt;
+  LTemp: T;
+begin
+  a := 0;
+  b := Count - 1;
+  while a < b do
+  begin
+    LTemp := FItems[a];
+    FItems[a] := FItems[b];
+    FItems[b] := LTemp;
+    Inc(a);
+    Dec(b);
+  end;
+end;
+
+procedure TList<T>.TrimExcess;
+begin
+  SetCapacity(Count);
+end;
+
+procedure TList<T>.Sort;
+begin
+  TMyArrayHelper.Sort(FItems, FComparer, 0, Count);
+end;
+
+procedure TList<T>.Sort(const AComparer: IComparer<T>);
+begin
+  TMyArrayHelper.Sort(FItems, AComparer, 0, Count);
+end;
+
+function TList<T>.BinarySearch(const AItem: T; out AIndex: SizeInt): Boolean;
+begin
+  Result := TMyArrayHelper.BinarySearch(FItems, AItem, AIndex, FComparer, 0, Count);
+end;
+
+function TList<T>.BinarySearch(const AItem: T; out AIndex: SizeInt;
+  const AComparer: IComparer<T>): Boolean;
+begin
+  Result := TMyArrayHelper.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count);
+end;
+
+end.

+ 134 - 0
packages/rtl/generics.defaults.pas

@@ -0,0 +1,134 @@
+unit Generics.Defaults;
+
+{$MODE DELPHI}
+
+interface
+
+//uses Classes;
+
+type
+  TArray<T> = array of T;
+
+  { IComparer }
+
+  IComparer<T> = interface
+    function Compare(const Left, Right: T): Integer; overload;
+  end;
+
+  TOnComparison<T> = function(const Left, Right: T): Integer of object;
+  TComparisonFunc<T> = function(const Left, Right: T): Integer;
+
+  { TComparer }
+
+  TComparer<T> = class(TInterfacedObject, IComparer<T>)
+  private
+    type TMyComparer = TComparer<T>;
+    class var DefaultComparer: TMyComparer;
+  public
+    class function Default: IComparer<T>; static;
+    function Compare(const ALeft, ARight: T): Integer; virtual; abstract; overload;
+
+    class function Construct(const AComparison: TOnComparison<T>): IComparer<T>; overload;
+    class function Construct(const AComparison: TComparisonFunc<T>): IComparer<T>; overload;
+  end;
+
+  { TDefaultComparer }
+
+  TDefaultComparer<T> = class(TComparer<T>)
+  public
+    function Compare(const ALeft, ARight: T): Integer; override; overload;
+  end;
+
+  { TDelegatedComparerEvents }
+
+  TDelegatedComparerEvents<T> = class(TComparer<T>)
+  private
+    FComparison: TOnComparison<T>;
+  public
+    function Compare(const ALeft, ARight: T): Integer; override;
+    constructor Create(AComparison: TOnComparison<T>);
+  end;
+
+  { TDelegatedComparerFunc }
+
+  TDelegatedComparerFunc<T> = class(TComparer<T>)
+  private
+    FComparison: TComparisonFunc<T>;
+  public
+    function Compare(const ALeft, ARight: T): Integer; override;
+    constructor Create(AComparison: TComparisonFunc<T>);
+  end;
+
+  IEnumerator<T> = interface
+    function GetCurrent: T;
+    function MoveNext: Boolean;
+    procedure Reset;
+    property Current: T read GetCurrent;
+  end;
+
+  IEnumerable<T> = interface
+    function GetEnumerator: IEnumerator<T>;
+  end;
+
+implementation
+
+{ TComparer }
+
+class function TComparer<T>.Default: IComparer<T>;
+begin
+  if DefaultComparer=nil then
+    DefaultComparer:=TDefaultComparer<T>.Create;
+  Result:=DefaultComparer;
+end;
+
+class function TComparer<T>.Construct(const AComparison: TOnComparison<T>
+  ): IComparer<T>;
+begin
+  Result := TDelegatedComparerEvents<T>.Create(AComparison);
+end;
+
+class function TComparer<T>.Construct(const AComparison: TComparisonFunc<T>
+  ): IComparer<T>;
+begin
+  Result := TDelegatedComparerFunc<T>.Create(AComparison);
+end;
+
+{ TDefaultComparer }
+
+function TDefaultComparer<T>.Compare(const ALeft, ARight: T): Integer;
+begin
+  asm
+    if (ALeft < ARight) return -1;
+    if (ALeft > ARight) return 1;
+  end;
+  Result:=0;
+  if ALeft = ARight then exit;
+end;
+
+{ TDelegatedComparerEvents }
+
+function TDelegatedComparerEvents<T>.Compare(const ALeft, ARight: T
+  ): Integer;
+begin
+  Result := FComparison(ALeft, ARight);
+end;
+
+constructor TDelegatedComparerEvents<T>.Create(AComparison: TOnComparison<T>);
+begin
+  FComparison := AComparison;
+end;
+
+{ TDelegatedComparerFunc }
+
+function TDelegatedComparerFunc<T>.Compare(const ALeft, ARight: T): Integer;
+begin
+  Result := FComparison(ALeft, ARight);
+end;
+
+constructor TDelegatedComparerFunc<T>.Create(AComparison: TComparisonFunc<T>);
+begin
+  FComparison := AComparison;
+end;
+
+end.
+

+ 19 - 0
packages/rtl/generics.strings.pas

@@ -0,0 +1,19 @@
+unit Generics.Strings;
+
+{$Mode Delphi}
+
+interface
+
+resourcestring
+  SArgumentOutOfRange = 'Argument out of range';
+  //SArgumentNilNode = 'Node is nil';
+  //SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary';
+  //SCollectionInconsistency = 'Collection inconsistency';
+  //SCollectionDuplicate = 'Collection does not allow duplicates';
+  //SDictionaryKeyDoesNotExist = 'Dictionary key does not exist';
+  //SItemNotFound = 'Item not found';
+
+implementation
+
+end.
+