Home | History | Annotate | Download | only in Antlr3.Runtime
      1 unit Antlr.Runtime.Tools;
      2 (*
      3 [The "BSD licence"]
      4 Copyright (c) 2008 Erik van Bilsen
      5 All rights reserved.
      6 
      7 Redistribution and use in source and binary forms, with or without
      8 modification, are permitted provided that the following conditions
      9 are met:
     10 1. Redistributions of source code MUST RETAIN the above copyright
     11    notice, this list of conditions and the following disclaimer.
     12 2. Redistributions in binary form MUST REPRODUCE the above copyright
     13    notice, this list of conditions and the following disclaimer in 
     14    the documentation and/or other materials provided with the 
     15    distribution.
     16 3. The name of the author may not be used to endorse or promote products
     17    derived from this software without specific prior WRITTEN permission.
     18 4. Unless explicitly state otherwise, any contribution intentionally 
     19    submitted for inclusion in this work to the copyright owner or licensor
     20    shall be under the terms and conditions of this license, without any 
     21    additional terms or conditions.
     22 
     23 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33 *)
     34 
     35 interface
     36 
     37 {$IF CompilerVersion < 20}
     38 {$MESSAGE ERROR 'You need Delphi 2009 or higher to use the Antlr runtime'}
     39 {$IFEND}
     40 
     41 uses
     42   Classes,
     43   Generics.Defaults,
     44   Generics.Collections;
     45 
     46 type
     47   TSmallintArray = array of Smallint;
     48   TSmallintMatrix = array of TSmallintArray;
     49   TIntegerArray = array of Integer;
     50   TUInt64Array = array of UInt64;
     51   TStringArray = array of String;
     52 
     53 type
     54   /// <summary>
     55   /// Base interface for ANTLR objects
     56   /// </summary>
     57   IANTLRInterface = interface
     58   ['{FA98F2EE-89D3-42A5-BC9C-1E8A9B278C3B}']
     59     function ToString: String;
     60   end;
     61   TANTLRInterfaceArray = array of IANTLRInterface;
     62 
     63 type
     64   /// <summary>
     65   /// Gives access to implementing object
     66   /// </summary>
     67   IANTLRObject = interface
     68   ['{E56CE28B-8D92-4961-90ED-418A1E8FEDF2}']
     69     { Property accessors }
     70     function GetImplementor: TObject;
     71 
     72     { Properties }
     73     property Implementor: TObject read GetImplementor;
     74   end;
     75 
     76 type
     77   /// <summary>
     78   /// Base for ANTLR objects
     79   /// </summary>
     80   TANTLRObject = class(TInterfacedObject, IANTLRInterface, IANTLRObject)
     81   protected
     82     { IANTLRObject }
     83     function GetImplementor: TObject;
     84   end;
     85 
     86 type
     87   /// <summary>
     88   /// Allows strings to be treated as object interfaces
     89   /// </summary>
     90   IANTLRString = interface(IANTLRInterface)
     91   ['{1C7F2030-446C-4756-81E3-EC37E04E2296}']
     92     { Property accessors }
     93     function GetValue: String;
     94     procedure SetValue(const Value: String);
     95 
     96     { Properties }
     97     property Value: String read GetValue write SetValue;
     98   end;
     99 
    100 type
    101   /// <summary>
    102   /// Allows strings to be treated as object interfaces
    103   /// </summary>
    104   TANTLRString = class(TANTLRObject, IANTLRString)
    105   strict private
    106     FValue: String;
    107   protected
    108     { IANTLRString }
    109     function GetValue: String;
    110     procedure SetValue(const Value: String);
    111   public
    112     constructor Create(const AValue: String);
    113 
    114     function ToString: String; override;
    115   end;
    116 
    117 type
    118   /// <summary>
    119   /// Win32 version of .NET's ICloneable
    120   /// </summary>
    121   ICloneable = interface(IANTLRInterface)
    122   ['{90240BF0-3A09-46B6-BC47-C13064809F97}']
    123     { Methods }
    124     function Clone: IANTLRInterface;
    125   end;
    126 
    127 type
    128   IList<T> = interface(IANTLRInterface)
    129   ['{107DB2FE-A351-4F08-B9AD-E1BA8A4690FF}']
    130     { Property accessors }
    131     function GetCapacity: Integer;
    132     procedure SetCapacity(Value: Integer);
    133     function GetCount: Integer;
    134     procedure SetCount(Value: Integer);
    135     function GetItem(Index: Integer): T;
    136     procedure SetItem(Index: Integer; const Value: T);
    137     function GetOnNotify: TCollectionNotifyEvent<T>;
    138     procedure SetOnNotify(Value: TCollectionNotifyEvent<T>);
    139 
    140     { Methods }
    141     function Add(const Value: T): Integer;
    142 
    143     procedure AddRange(const Values: array of T); overload;
    144     procedure AddRange(const Collection: IEnumerable<T>); overload;
    145     procedure AddRange(Collection: TEnumerable<T>); overload;
    146     procedure AddRange(const List: IList<T>); overload;
    147 
    148     procedure Insert(Index: Integer; const Value: T);
    149 
    150     procedure InsertRange(Index: Integer; const Values: array of T); overload;
    151     procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
    152     procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;
    153     procedure InsertRange(Index: Integer; const List: IList<T>); overload;
    154 
    155     function Remove(const Value: T): Integer;
    156     procedure Delete(Index: Integer);
    157     procedure DeleteRange(AIndex, ACount: Integer);
    158     function Extract(const Value: T): T;
    159 
    160     procedure Clear;
    161 
    162     function Contains(const Value: T): Boolean;
    163     function IndexOf(const Value: T): Integer;
    164     function LastIndexOf(const Value: T): Integer;
    165 
    166     procedure Reverse;
    167 
    168     procedure Sort; overload;
    169     procedure Sort(const AComparer: IComparer<T>); overload;
    170     function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
    171     function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;
    172 
    173     procedure TrimExcess;
    174     function GetEnumerator: TList<T>.TEnumerator;
    175     function GetRange(const Index, Count: Integer): IList<T>;
    176 
    177     { Properties }
    178 
    179     property Capacity: Integer read GetCapacity write SetCapacity;
    180     property Count: Integer read GetCount write SetCount;
    181     property Items[Index: Integer]: T read GetItem write SetItem; default;
    182     property OnNotify: TCollectionNotifyEvent<T> read GetOnNotify write SetOnNotify;
    183   end;
    184 
    185 type
    186   IDictionary<TKey,TValue> = interface(IANTLRInterface)
    187   ['{5937BD21-C2C8-4E30-9787-4AEFDF1072CD}']
    188     { Property accessors }
    189     function GetItem(const Key: TKey): TValue;
    190     procedure SetItem(const Key: TKey; const Value: TValue);
    191     function GetCount: Integer;
    192 
    193     { Methods }
    194     procedure Add(const Key: TKey; const Value: TValue);
    195     procedure Remove(const Key: TKey);
    196     procedure Clear;
    197     procedure TrimExcess;
    198     function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
    199     procedure AddOrSetValue(const Key: TKey; const Value: TValue);
    200     function ContainsKey(const Key: TKey): Boolean;
    201     function ContainsValue(const Value: TValue): Boolean;
    202     function GetEnumerator: TEnumerator<TPair<TKey, TValue>>;
    203 
    204     { Properties }
    205     property Items[const Key: TKey]: TValue read GetItem write SetItem; default;
    206     property Count: Integer read GetCount;
    207   end;
    208 
    209 type
    210   TList<T> = class(Generics.Collections.TList<T>, IList<T>)
    211   strict private
    212     FRefCount: Integer;
    213   protected
    214     { IInterface }
    215     function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    216     function _AddRef: Integer; stdcall;
    217     function _Release: Integer; stdcall;
    218 
    219     { IList<T> }
    220     function GetCapacity: Integer;
    221     procedure SetCapacity(Value: Integer);
    222     function GetCount: Integer;
    223     procedure SetCount(Value: Integer);
    224     function GetItem(Index: Integer): T;
    225     procedure SetItem(Index: Integer; const Value: T);
    226     function GetOnNotify: TCollectionNotifyEvent<T>;
    227     procedure SetOnNotify(Value: TCollectionNotifyEvent<T>);
    228     function GetRange(const Index, Count: Integer): IList<T>;
    229     procedure AddRange(const List: IList<T>); overload;
    230     procedure InsertRange(Index: Integer; const List: IList<T>); overload;
    231   end;
    232 
    233 type
    234   TDictionaryArray<TKey,TValue> = array of IDictionary<TKey,TValue>;
    235 
    236   { The TDictionary class in the first release of Delphi 2009 is very buggy.
    237     This is a partial copy of that class with bug fixes. }
    238   TDictionary<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>, IDictionary<TKey, TValue>)
    239   private
    240     type
    241       TItem = record
    242         HashCode: Integer;
    243         Key: TKey;
    244         Value: TValue;
    245       end;
    246       TItemArray = array of TItem;
    247   private
    248     FItems: TItemArray;
    249     FCount: Integer;
    250     FComparer: IEqualityComparer<TKey>;
    251     FGrowThreshold: Integer;
    252 
    253     procedure SetCapacity(ACapacity: Integer);
    254     procedure Rehash(NewCapPow2: Integer);
    255     procedure Grow;
    256     function GetBucketIndex(const Key: TKey; HashCode: Integer): Integer;
    257     function Hash(const Key: TKey): Integer;
    258     procedure RehashAdd(HashCode: Integer; const Key: TKey; const Value: TValue);
    259     procedure DoAdd(HashCode, Index: Integer; const Key: TKey; const Value: TValue);
    260   protected
    261     function DoGetEnumerator: TEnumerator<TPair<TKey,TValue>>; override;
    262   public
    263     constructor Create(ACapacity: Integer = 0); overload;
    264     constructor Create(const AComparer: IEqualityComparer<TKey>); overload;
    265     constructor Create(ACapacity: Integer; const AComparer: IEqualityComparer<TKey>); overload;
    266     constructor Create(Collection: TEnumerable<TPair<TKey,TValue>>); overload;
    267     constructor Create(Collection: TEnumerable<TPair<TKey,TValue>>; const AComparer: IEqualityComparer<TKey>); overload;
    268     destructor Destroy; override;
    269 
    270     type
    271       TPairEnumerator = class(TEnumerator<TPair<TKey,TValue>>)
    272       private
    273         FDictionary: TDictionary<TKey,TValue>;
    274         FIndex: Integer;
    275         function GetCurrent: TPair<TKey,TValue>;
    276       protected
    277         function DoGetCurrent: TPair<TKey,TValue>; override;
    278         function DoMoveNext: Boolean; override;
    279       public
    280         constructor Create(ADictionary: TDictionary<TKey,TValue>);
    281         property Current: TPair<TKey,TValue> read GetCurrent;
    282         function MoveNext: Boolean;
    283       end;
    284   protected
    285     { IInterface }
    286     FRefCount: Integer;
    287     function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    288     function _AddRef: Integer; stdcall;
    289     function _Release: Integer; stdcall;
    290   protected
    291     { IDictionary<TKey, TValue> }
    292     function GetItem(const Key: TKey): TValue;
    293     procedure SetItem(const Key: TKey; const Value: TValue);
    294     function GetCount: Integer;
    295 
    296     procedure Add(const Key: TKey; const Value: TValue);
    297     procedure Remove(const Key: TKey);
    298     procedure Clear;
    299     procedure TrimExcess;
    300     function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
    301     procedure AddOrSetValue(const Key: TKey; const Value: TValue);
    302     function ContainsKey(const Key: TKey): Boolean;
    303     function ContainsValue(const Value: TValue): Boolean;
    304   public
    305     function GetEnumerator: TEnumerator<TPair<TKey, TValue>>;
    306   end;
    307 
    308 type
    309   /// <summary>
    310   /// Helper for storing local variables inside a routine. The code that ANTLR
    311   /// generates contains a lot of block-level variable declarations, which
    312   /// the Delphi language does not support. When generating Delphi source code,
    313   /// I try to detect those declarations and move them to the routine header
    314   /// as much as possible. But sometimes, this is impossible.
    315   /// This is a bit of an ugly (and slow) solution, but it works. Declare an
    316   /// variable of the TLocalStorage type inside a routine, and you can use it
    317   /// to access variables by name. For example, see the following C code:
    318   ///  {
    319   ///    int x = 3;
    320   ///    {
    321   ///      int y = x * 2;
    322   ///    }
    323   ///  }
    324   /// If the Delphi code generator cannot detect the inner "y" variable, then
    325   /// it uses the local storage as follows:
    326   ///  var
    327   ///    x: Integer;
    328   ///    Locals: TLocalStorage;
    329   ///  begin
    330   ///    Locals.Initialize;
    331   ///    try
    332   ///      x := 3;
    333   ///      Locals['y'] := x * 2;
    334   ///    finally
    335   ///      Locals.Finalize;
    336   ///    end;
    337   ///  end;
    338   /// </summary>
    339   /// <remarks>
    340   /// This is a slow solution because it involves looking up variable names.
    341   /// This could be done using hashing or binary search, but this is inefficient
    342   /// with small collections. Since small collections are more typical in these
    343   /// scenarios, we use simple linear search here.
    344   /// </remarks>
    345   /// <remarks>
    346   /// The TLocalStorage record has space for 256 variables. For performance
    347   /// reasons, this space is preallocated on the stack and does not grow if
    348   /// needed. Also, no range checking is done. But 256 local variables should
    349   /// be enough for all generated code.
    350   /// </remarks>
    351   /// <remarks>
    352   /// Also note that the variable names are case sensitive, so 'x' is a
    353   /// different variable than 'X'.
    354   /// </remarks>
    355   /// <remarks>
    356   /// TLocalStorage can only store variables that are 32 bits in size, and
    357   /// supports the following data typesL
    358   ///  -Integer
    359   ///  -IInterface descendants (default property)
    360   /// </remarks>
    361   /// <remarks>
    362   /// You MUST call the Finalize method at the end of the routine to make
    363   /// sure that any stored variables of type IInterface are released.
    364   /// </remarks>
    365   TLocalStorage = record
    366   private
    367     type
    368       TLocalStorageEntry = record
    369         FName: String;
    370         FValue: Pointer;
    371         FDataType: (dtInteger, dtInterface);
    372       end;
    373   private
    374     FEntries: array [0..255] of TLocalStorageEntry;
    375     FCount: Integer;
    376     function GetAsInteger(const Name: String): Integer;
    377     procedure SetAsInteger(const Name: String; const Value: Integer);
    378     function GetAsInterface(const Name: String): IInterface;
    379     procedure SetAsInterface(const Name: String; const Value: IInterface);
    380   public
    381     procedure Initialize;
    382     procedure Finalize;
    383 
    384     property Count: Integer read FCount;
    385     property AsInteger[const Name: String]: Integer read GetAsInteger write SetAsInteger;
    386     property AsInterface[const Name: String]: IInterface read GetAsInterface write SetAsInterface; default;
    387   end;
    388 
    389 function InCircularRange(Bottom, Item, TopInc: Integer): Boolean;
    390 
    391 { Checks if A and B are implemented by the same object }
    392 function SameObj(const A, B: IInterface): Boolean;
    393 
    394 function IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil): IANTLRInterface; overload;
    395 
    396 function IsUpper(const C: Char): Boolean;
    397 
    398 implementation
    399 
    400 uses
    401   Windows,
    402   SysUtils;
    403 
    404 function SameObj(const A, B: IInterface): Boolean;
    405 var
    406   X, Y: IInterface;
    407 begin
    408   if (A = nil) or (B = nil) then
    409     Result := (A = B)
    410   else if (A.QueryInterface(IInterface, X) = S_OK)
    411     and (B.QueryInterface(IInterface, Y) = S_OK)
    412   then
    413     Result := (X = Y)
    414   else
    415     Result := (A = B);
    416 end;
    417 
    418 function IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil): IANTLRInterface; overload;
    419 begin
    420   if AValue then
    421     Result := ATrue
    422   else
    423     Result := AFalse;
    424 end;
    425 
    426 function IsUpper(const C: Char): Boolean;
    427 begin
    428   Result := (C >= 'A') and (C <= 'Z');
    429 
    430 end;
    431 { TANTLRObject }
    432 
    433 function TANTLRObject.GetImplementor: TObject;
    434 begin
    435   Result := Self;
    436 end;
    437 
    438 { TANTLRString }
    439 
    440 constructor TANTLRString.Create(const AValue: String);
    441 begin
    442   inherited Create;
    443   FValue := AValue;
    444 end;
    445 
    446 function TANTLRString.GetValue: String;
    447 begin
    448   Result := FValue;
    449 end;
    450 
    451 procedure TANTLRString.SetValue(const Value: String);
    452 begin
    453   FValue := Value;
    454 end;
    455 
    456 function TANTLRString.ToString: String;
    457 begin
    458   Result := FValue;
    459 end;
    460 
    461 { TList<T> }
    462 
    463 procedure TList<T>.AddRange(const List: IList<T>);
    464 begin
    465   InsertRange(GetCount, List);
    466 end;
    467 
    468 function TList<T>.GetCapacity: Integer;
    469 begin
    470   Result := inherited Capacity;
    471 end;
    472 
    473 function TList<T>.GetCount: Integer;
    474 begin
    475   Result := inherited Count;
    476 end;
    477 
    478 function TList<T>.GetItem(Index: Integer): T;
    479 begin
    480   Result := inherited Items[Index];
    481 end;
    482 
    483 function TList<T>.GetOnNotify: TCollectionNotifyEvent<T>;
    484 begin
    485   Result := inherited OnNotify;
    486 end;
    487 
    488 function TList<T>.GetRange(const Index, Count: Integer): IList<T>;
    489 var
    490   I: Integer;
    491 begin
    492   Result := TList<T>.Create;
    493   Result.Capacity := Count;
    494   for I := Index to Index + Count - 1 do
    495     Result.Add(GetItem(I));
    496 end;
    497 
    498 procedure TList<T>.InsertRange(Index: Integer; const List: IList<T>);
    499 var
    500   Item: T;
    501 begin
    502   for Item in List do
    503   begin
    504     Insert(Index, Item);
    505     Inc(Index);
    506   end;
    507 end;
    508 
    509 function TList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
    510 begin
    511   if GetInterface(IID, Obj) then
    512     Result := 0
    513   else
    514     Result := E_NOINTERFACE;
    515 end;
    516 
    517 procedure TList<T>.SetCapacity(Value: Integer);
    518 begin
    519   inherited Capacity := Value;
    520 end;
    521 
    522 procedure TList<T>.SetCount(Value: Integer);
    523 begin
    524   inherited Count := Value;
    525 end;
    526 
    527 procedure TList<T>.SetItem(Index: Integer; const Value: T);
    528 begin
    529   inherited Items[Index] := Value;
    530 end;
    531 
    532 procedure TList<T>.SetOnNotify(Value: TCollectionNotifyEvent<T>);
    533 begin
    534   inherited OnNotify := Value;
    535 end;
    536 
    537 function TList<T>._AddRef: Integer;
    538 begin
    539   Result := InterlockedIncrement(FRefCount);
    540 end;
    541 
    542 function TList<T>._Release: Integer;
    543 begin
    544   Result := InterlockedDecrement(FRefCount);
    545   if (Result = 0) then
    546     Destroy;
    547 end;
    548 
    549 { TDictionary<TKey, TValue> }
    550 
    551 procedure TDictionary<TKey,TValue>.Rehash(NewCapPow2: Integer);
    552 var
    553   oldItems, newItems: TItemArray;
    554   i: Integer;
    555 begin
    556   if NewCapPow2 = Length(FItems) then
    557     Exit
    558   else if NewCapPow2 < 0 then
    559     OutOfMemoryError;
    560 
    561   oldItems := FItems;
    562   SetLength(newItems, NewCapPow2);
    563   FItems := newItems;
    564   FGrowThreshold := NewCapPow2 shr 1 + NewCapPow2 shr 2;
    565 
    566   for i := 0 to Length(oldItems) - 1 do
    567     if oldItems[i].HashCode <> 0 then
    568       RehashAdd(oldItems[i].HashCode, oldItems[i].Key, oldItems[i].Value);
    569 end;
    570 
    571 procedure TDictionary<TKey,TValue>.SetCapacity(ACapacity: Integer);
    572 var
    573   newCap: Integer;
    574 begin
    575   if ACapacity < FCount then
    576     raise EArgumentOutOfRangeException.CreateRes(@sArgumentOutOfRange);
    577 
    578   if ACapacity = 0 then
    579     Rehash(0)
    580   else
    581   begin
    582     newCap := 4;
    583     while newCap < ACapacity do
    584       newCap := newCap shl 1;
    585     Rehash(newCap);
    586   end
    587 end;
    588 
    589 procedure TDictionary<TKey,TValue>.Grow;
    590 var
    591   newCap: Integer;
    592 begin
    593   newCap := Length(FItems) * 2;
    594   if newCap = 0 then
    595     newCap := 4;
    596   Rehash(newCap);
    597 end;
    598 
    599 function TDictionary<TKey,TValue>.GetBucketIndex(const Key: TKey; HashCode: Integer): Integer;
    600 var
    601   start, hc: Integer;
    602 begin
    603   if Length(FItems) = 0 then
    604     Exit(not High(Integer));
    605 
    606   start := HashCode and (Length(FItems) - 1);
    607   Result := start;
    608   while True do
    609   begin
    610     hc := FItems[Result].HashCode;
    611 
    612     // Not found: return complement of insertion point.
    613     if hc = 0 then
    614       Exit(not Result);
    615 
    616     // Found: return location.
    617     if (hc = HashCode) and FComparer.Equals(FItems[Result].Key, Key) then
    618       Exit(Result);
    619 
    620     Inc(Result);
    621     if Result >= Length(FItems) then
    622       Result := 0;
    623   end;
    624 end;
    625 
    626 function TDictionary<TKey, TValue>.GetCount: Integer;
    627 begin
    628   Result := FCount;
    629 end;
    630 
    631 function TDictionary<TKey,TValue>.Hash(const Key: TKey): Integer;
    632 const
    633   PositiveMask = not Integer($80000000);
    634 begin
    635   // Double-Abs to avoid -MaxInt and MinInt problems.
    636   // Not using compiler-Abs because we *must* get a positive integer;
    637   // for compiler, Abs(Low(Integer)) is a null op.
    638   Result := PositiveMask and ((PositiveMask and FComparer.GetHashCode(Key)) + 1);
    639 end;
    640 
    641 function TDictionary<TKey,TValue>.GetItem(const Key: TKey): TValue;
    642 var
    643   index: Integer;
    644 begin
    645   index := GetBucketIndex(Key, Hash(Key));
    646   if index < 0 then
    647     raise EListError.CreateRes(@sGenericItemNotFound);
    648   Result := FItems[index].Value;
    649 end;
    650 
    651 procedure TDictionary<TKey,TValue>.SetItem(const Key: TKey; const Value: TValue);
    652 var
    653   index: Integer;
    654   oldValue: TValue;
    655 begin
    656   index := GetBucketIndex(Key, Hash(Key));
    657   if index < 0 then
    658     raise EListError.CreateRes(@sGenericItemNotFound);
    659 
    660   oldValue := FItems[index].Value;
    661   FItems[index].Value := Value;
    662 end;
    663 
    664 procedure TDictionary<TKey,TValue>.RehashAdd(HashCode: Integer; const Key: TKey; const Value: TValue);
    665 var
    666   index: Integer;
    667 begin
    668   index := not GetBucketIndex(Key, HashCode);
    669   FItems[index].HashCode := HashCode;
    670   FItems[index].Key := Key;
    671   FItems[index].Value := Value;
    672 end;
    673 
    674 function TDictionary<TKey, TValue>.QueryInterface(const IID: TGUID;
    675   out Obj): HResult;
    676 begin
    677   if GetInterface(IID, Obj) then
    678     Result := 0
    679   else
    680     Result := E_NOINTERFACE;
    681 end;
    682 
    683 function TDictionary<TKey, TValue>._AddRef: Integer;
    684 begin
    685   Result := InterlockedIncrement(FRefCount);
    686 end;
    687 
    688 function TDictionary<TKey, TValue>._Release: Integer;
    689 begin
    690   Result := InterlockedDecrement(FRefCount);
    691   if (Result = 0) then
    692     Destroy;
    693 end;
    694 
    695 constructor TDictionary<TKey,TValue>.Create(ACapacity: Integer = 0);
    696 begin
    697   Create(ACapacity, nil);
    698 end;
    699 
    700 constructor TDictionary<TKey,TValue>.Create(const AComparer: IEqualityComparer<TKey>);
    701 begin
    702   Create(0, AComparer);
    703 end;
    704 
    705 constructor TDictionary<TKey,TValue>.Create(ACapacity: Integer; const AComparer: IEqualityComparer<TKey>);
    706 var
    707   cap: Integer;
    708 begin
    709   inherited Create;
    710   if ACapacity < 0 then
    711     raise EArgumentOutOfRangeException.CreateRes(@sArgumentOutOfRange);
    712   FComparer := AComparer;
    713   if FComparer = nil then
    714     FComparer := TEqualityComparer<TKey>.Default;
    715   SetCapacity(ACapacity);
    716 end;
    717 
    718 constructor TDictionary<TKey, TValue>.Create(
    719   Collection: TEnumerable<TPair<TKey, TValue>>);
    720 var
    721   item: TPair<TKey,TValue>;
    722 begin
    723   Create(0, nil);
    724   for item in Collection do
    725     AddOrSetValue(item.Key, item.Value);
    726 end;
    727 
    728 constructor TDictionary<TKey, TValue>.Create(
    729   Collection: TEnumerable<TPair<TKey, TValue>>;
    730   const AComparer: IEqualityComparer<TKey>);
    731 var
    732   item: TPair<TKey,TValue>;
    733 begin
    734   Create(0, AComparer);
    735   for item in Collection do
    736     AddOrSetValue(item.Key, item.Value);
    737 end;
    738 
    739 destructor TDictionary<TKey,TValue>.Destroy;
    740 begin
    741   Clear;
    742   inherited;
    743 end;
    744 
    745 procedure TDictionary<TKey,TValue>.Add(const Key: TKey; const Value: TValue);
    746 var
    747   index, hc: Integer;
    748 begin
    749   if FCount >= FGrowThreshold then
    750     Grow;
    751 
    752   hc := Hash(Key);
    753   index := GetBucketIndex(Key, hc);
    754   if index >= 0 then
    755     raise EListError.CreateRes(@sGenericDuplicateItem);
    756 
    757   DoAdd(hc, not index, Key, Value);
    758 end;
    759 
    760 function InCircularRange(Bottom, Item, TopInc: Integer): Boolean;
    761 begin
    762   Result := (Bottom < Item) and (Item <= TopInc) // normal
    763     or (TopInc < Bottom) and (Item > Bottom) // top wrapped
    764     or (TopInc < Bottom) and (Item <= TopInc) // top and item wrapped
    765 end;
    766 
    767 procedure TDictionary<TKey,TValue>.Remove(const Key: TKey);
    768 var
    769   gap, index, hc, bucket: Integer;
    770   oldValue: TValue;
    771 begin
    772   hc := Hash(Key);
    773   index := GetBucketIndex(Key, hc);
    774   if index < 0 then
    775     Exit;
    776 
    777   // Removing item from linear probe hash table is moderately
    778   // tricky. We need to fill in gaps, which will involve moving items
    779   // which may not even hash to the same location.
    780   // Knuth covers it well enough in Vol III. 6.4.; but beware, Algorithm R
    781   // (2nd ed) has a bug: step R4 should go to step R1, not R2 (already errata'd).
    782   // My version does linear probing forward, not backward, however.
    783 
    784   // gap refers to the hole that needs filling-in by shifting items down.
    785   // index searches for items that have been probed out of their slot,
    786   // but being careful not to move items if their bucket is between
    787   // our gap and our index (so that they'd be moved before their bucket).
    788   // We move the item at index into the gap, whereupon the new gap is
    789   // at the index. If the index hits a hole, then we're done.
    790 
    791   // If our load factor was exactly 1, we'll need to hit this hole
    792   // in order to terminate. Shouldn't normally be necessary, though.
    793   FItems[index].HashCode := 0;
    794 
    795   gap := index;
    796   while True do
    797   begin
    798     Inc(index);
    799     if index = Length(FItems) then
    800       index := 0;
    801 
    802     hc := FItems[index].HashCode;
    803     if hc = 0 then
    804       Break;
    805 
    806     bucket := hc and (Length(FItems) - 1);
    807     if not InCircularRange(gap, bucket, index) then
    808     begin
    809       FItems[gap] := FItems[index];
    810       gap := index;
    811       // The gap moved, but we still need to find it to terminate.
    812       FItems[gap].HashCode := 0;
    813     end;
    814   end;
    815 
    816   FItems[gap].HashCode := 0;
    817   FItems[gap].Key := Default(TKey);
    818   oldValue := FItems[gap].Value;
    819   FItems[gap].Value := Default(TValue);
    820   Dec(FCount);
    821 end;
    822 
    823 procedure TDictionary<TKey,TValue>.Clear;
    824 begin
    825   FCount := 0;
    826   FGrowThreshold := 0;
    827   SetLength(FItems, 0);
    828   SetCapacity(0);
    829 end;
    830 
    831 procedure TDictionary<TKey,TValue>.TrimExcess;
    832 begin
    833   SetCapacity(FCount);
    834 end;
    835 
    836 function TDictionary<TKey,TValue>.TryGetValue(const Key: TKey; out Value: TValue): Boolean;
    837 var
    838   index: Integer;
    839 begin
    840   index := GetBucketIndex(Key, Hash(Key));
    841   Result := index >= 0;
    842   if Result then
    843     Value := FItems[index].Value
    844   else
    845     Value := Default(TValue);
    846 end;
    847 
    848 procedure TDictionary<TKey,TValue>.DoAdd(HashCode, Index: Integer; const Key: TKey; const Value: TValue);
    849 begin
    850   FItems[Index].HashCode := HashCode;
    851   FItems[Index].Key := Key;
    852   FItems[Index].Value := Value;
    853   Inc(FCount);
    854 end;
    855 
    856 function TDictionary<TKey, TValue>.DoGetEnumerator: TEnumerator<TPair<TKey, TValue>>;
    857 begin
    858   Result := GetEnumerator;
    859 end;
    860 
    861 procedure TDictionary<TKey,TValue>.AddOrSetValue(const Key: TKey; const Value: TValue);
    862 begin
    863   if ContainsKey(Key) then
    864     SetItem(Key,Value)
    865   else
    866     Add(Key,Value);
    867 end;
    868 
    869 function TDictionary<TKey,TValue>.ContainsKey(const Key: TKey): Boolean;
    870 begin
    871   Result := GetBucketIndex(Key, Hash(Key)) >= 0;
    872 end;
    873 
    874 function TDictionary<TKey,TValue>.ContainsValue(const Value: TValue): Boolean;
    875 var
    876   i: Integer;
    877   c: IEqualityComparer<TValue>;
    878 begin
    879   c := TEqualityComparer<TValue>.Default;
    880 
    881   for i := 0 to Length(FItems) - 1 do
    882     if (FItems[i].HashCode <> 0) and c.Equals(FItems[i].Value, Value) then
    883       Exit(True);
    884   Result := False;
    885 end;
    886 
    887 function TDictionary<TKey,TValue>.GetEnumerator: TPairEnumerator;
    888 begin
    889   Result := TPairEnumerator.Create(Self);
    890 end;
    891 
    892 // Pairs
    893 
    894 constructor TDictionary<TKey,TValue>.TPairEnumerator.Create(ADictionary: TDictionary<TKey,TValue>);
    895 begin
    896   inherited Create;
    897   FIndex := -1;
    898   FDictionary := ADictionary;
    899 end;
    900 
    901 function TDictionary<TKey, TValue>.TPairEnumerator.DoGetCurrent: TPair<TKey, TValue>;
    902 begin
    903   Result := GetCurrent;
    904 end;
    905 
    906 function TDictionary<TKey, TValue>.TPairEnumerator.DoMoveNext: Boolean;
    907 begin
    908   Result := MoveNext;
    909 end;
    910 
    911 function TDictionary<TKey,TValue>.TPairEnumerator.GetCurrent: TPair<TKey,TValue>;
    912 begin
    913   Result.Key := FDictionary.FItems[FIndex].Key;
    914   Result.Value := FDictionary.FItems[FIndex].Value;
    915 end;
    916 
    917 function TDictionary<TKey,TValue>.TPairEnumerator.MoveNext: Boolean;
    918 begin
    919   while FIndex < Length(FDictionary.FItems) - 1 do
    920   begin
    921     Inc(FIndex);
    922     if FDictionary.FItems[FIndex].HashCode <> 0 then
    923       Exit(True);
    924   end;
    925   Result := False;
    926 end;
    927 
    928 { TLocalStorage }
    929 
    930 procedure TLocalStorage.Finalize;
    931 var
    932   I: Integer;
    933 begin
    934   for I := 0 to FCount - 1 do
    935     if (FEntries[I].FDataType = dtInterface) then
    936       IInterface(FEntries[I].FValue) := nil;
    937 end;
    938 
    939 function TLocalStorage.GetAsInteger(const Name: String): Integer;
    940 var
    941   I: Integer;
    942 begin
    943   for I := 0 to FCount - 1 do
    944     if (FEntries[I].FName = Name) then
    945       Exit(Integer(FEntries[I].FValue));
    946   Result := 0;
    947 end;
    948 
    949 function TLocalStorage.GetAsInterface(const Name: String): IInterface;
    950 var
    951   I: Integer;
    952 begin
    953   for I := 0 to FCount - 1 do
    954     if (FEntries[I].FName = Name) then
    955       Exit(IInterface(FEntries[I].FValue));
    956   Result := nil;
    957 end;
    958 
    959 procedure TLocalStorage.Initialize;
    960 begin
    961   FCount := 0;
    962 end;
    963 
    964 procedure TLocalStorage.SetAsInteger(const Name: String; const Value: Integer);
    965 var
    966   I: Integer;
    967 begin
    968   for I := 0 to FCount - 1 do
    969     if (FEntries[I].FName = Name) then
    970     begin
    971       FEntries[I].FValue := Pointer(Value);
    972       Exit;
    973     end;
    974   FEntries[FCount].FName := Name;
    975   FEntries[FCount].FValue := Pointer(Value);
    976   FEntries[FCount].FDataType := dtInteger;
    977   Inc(FCount);
    978 end;
    979 
    980 procedure TLocalStorage.SetAsInterface(const Name: String;
    981   const Value: IInterface);
    982 var
    983   I: Integer;
    984 begin
    985   for I := 0 to FCount - 1 do
    986     if (FEntries[I].FName = Name) then
    987     begin
    988       IInterface(FEntries[I].FValue) := Value;
    989       Exit;
    990     end;
    991   FEntries[FCount].FName := Name;
    992   FEntries[FCount].FValue := nil;
    993   IInterface(FEntries[FCount].FValue) := Value;
    994   FEntries[FCount].FDataType := dtInterface;
    995   Inc(FCount);
    996 end;
    997 
    998 end.
    999