1unit Antlr.Runtime.Tools;
2(*
3[The "BSD licence"]
4Copyright (c) 2008 Erik van Bilsen
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions
9are met:
101. Redistributions of source code MUST RETAIN the above copyright
11   notice, this list of conditions and the following disclaimer.
122. 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.
163. The name of the author may not be used to endorse or promote products
17   derived from this software without specific prior WRITTEN permission.
184. 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
23THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33*)
34
35interface
36
37{$IF CompilerVersion < 20}
38{$MESSAGE ERROR 'You need Delphi 2009 or higher to use the Antlr runtime'}
39{$IFEND}
40
41uses
42  Classes,
43  Generics.Defaults,
44  Generics.Collections;
45
46type
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
53type
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
63type
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
76type
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
86type
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
100type
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
117type
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
127type
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
185type
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
209type
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
233type
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
308type
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
389function InCircularRange(Bottom, Item, TopInc: Integer): Boolean;
390
391{ Checks if A and B are implemented by the same object }
392function SameObj(const A, B: IInterface): Boolean;
393
394function IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil): IANTLRInterface; overload;
395
396function IsUpper(const C: Char): Boolean;
397
398implementation
399
400uses
401  Windows,
402  SysUtils;
403
404function SameObj(const A, B: IInterface): Boolean;
405var
406  X, Y: IInterface;
407begin
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);
416end;
417
418function IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil): IANTLRInterface; overload;
419begin
420  if AValue then
421    Result := ATrue
422  else
423    Result := AFalse;
424end;
425
426function IsUpper(const C: Char): Boolean;
427begin
428  Result := (C >= 'A') and (C <= 'Z');
429
430end;
431{ TANTLRObject }
432
433function TANTLRObject.GetImplementor: TObject;
434begin
435  Result := Self;
436end;
437
438{ TANTLRString }
439
440constructor TANTLRString.Create(const AValue: String);
441begin
442  inherited Create;
443  FValue := AValue;
444end;
445
446function TANTLRString.GetValue: String;
447begin
448  Result := FValue;
449end;
450
451procedure TANTLRString.SetValue(const Value: String);
452begin
453  FValue := Value;
454end;
455
456function TANTLRString.ToString: String;
457begin
458  Result := FValue;
459end;
460
461{ TList<T> }
462
463procedure TList<T>.AddRange(const List: IList<T>);
464begin
465  InsertRange(GetCount, List);
466end;
467
468function TList<T>.GetCapacity: Integer;
469begin
470  Result := inherited Capacity;
471end;
472
473function TList<T>.GetCount: Integer;
474begin
475  Result := inherited Count;
476end;
477
478function TList<T>.GetItem(Index: Integer): T;
479begin
480  Result := inherited Items[Index];
481end;
482
483function TList<T>.GetOnNotify: TCollectionNotifyEvent<T>;
484begin
485  Result := inherited OnNotify;
486end;
487
488function TList<T>.GetRange(const Index, Count: Integer): IList<T>;
489var
490  I: Integer;
491begin
492  Result := TList<T>.Create;
493  Result.Capacity := Count;
494  for I := Index to Index + Count - 1 do
495    Result.Add(GetItem(I));
496end;
497
498procedure TList<T>.InsertRange(Index: Integer; const List: IList<T>);
499var
500  Item: T;
501begin
502  for Item in List do
503  begin
504    Insert(Index, Item);
505    Inc(Index);
506  end;
507end;
508
509function TList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
510begin
511  if GetInterface(IID, Obj) then
512    Result := 0
513  else
514    Result := E_NOINTERFACE;
515end;
516
517procedure TList<T>.SetCapacity(Value: Integer);
518begin
519  inherited Capacity := Value;
520end;
521
522procedure TList<T>.SetCount(Value: Integer);
523begin
524  inherited Count := Value;
525end;
526
527procedure TList<T>.SetItem(Index: Integer; const Value: T);
528begin
529  inherited Items[Index] := Value;
530end;
531
532procedure TList<T>.SetOnNotify(Value: TCollectionNotifyEvent<T>);
533begin
534  inherited OnNotify := Value;
535end;
536
537function TList<T>._AddRef: Integer;
538begin
539  Result := InterlockedIncrement(FRefCount);
540end;
541
542function TList<T>._Release: Integer;
543begin
544  Result := InterlockedDecrement(FRefCount);
545  if (Result = 0) then
546    Destroy;
547end;
548
549{ TDictionary<TKey, TValue> }
550
551procedure TDictionary<TKey,TValue>.Rehash(NewCapPow2: Integer);
552var
553  oldItems, newItems: TItemArray;
554  i: Integer;
555begin
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);
569end;
570
571procedure TDictionary<TKey,TValue>.SetCapacity(ACapacity: Integer);
572var
573  newCap: Integer;
574begin
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
587end;
588
589procedure TDictionary<TKey,TValue>.Grow;
590var
591  newCap: Integer;
592begin
593  newCap := Length(FItems) * 2;
594  if newCap = 0 then
595    newCap := 4;
596  Rehash(newCap);
597end;
598
599function TDictionary<TKey,TValue>.GetBucketIndex(const Key: TKey; HashCode: Integer): Integer;
600var
601  start, hc: Integer;
602begin
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;
624end;
625
626function TDictionary<TKey, TValue>.GetCount: Integer;
627begin
628  Result := FCount;
629end;
630
631function TDictionary<TKey,TValue>.Hash(const Key: TKey): Integer;
632const
633  PositiveMask = not Integer($80000000);
634begin
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);
639end;
640
641function TDictionary<TKey,TValue>.GetItem(const Key: TKey): TValue;
642var
643  index: Integer;
644begin
645  index := GetBucketIndex(Key, Hash(Key));
646  if index < 0 then
647    raise EListError.CreateRes(@sGenericItemNotFound);
648  Result := FItems[index].Value;
649end;
650
651procedure TDictionary<TKey,TValue>.SetItem(const Key: TKey; const Value: TValue);
652var
653  index: Integer;
654  oldValue: TValue;
655begin
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;
662end;
663
664procedure TDictionary<TKey,TValue>.RehashAdd(HashCode: Integer; const Key: TKey; const Value: TValue);
665var
666  index: Integer;
667begin
668  index := not GetBucketIndex(Key, HashCode);
669  FItems[index].HashCode := HashCode;
670  FItems[index].Key := Key;
671  FItems[index].Value := Value;
672end;
673
674function TDictionary<TKey, TValue>.QueryInterface(const IID: TGUID;
675  out Obj): HResult;
676begin
677  if GetInterface(IID, Obj) then
678    Result := 0
679  else
680    Result := E_NOINTERFACE;
681end;
682
683function TDictionary<TKey, TValue>._AddRef: Integer;
684begin
685  Result := InterlockedIncrement(FRefCount);
686end;
687
688function TDictionary<TKey, TValue>._Release: Integer;
689begin
690  Result := InterlockedDecrement(FRefCount);
691  if (Result = 0) then
692    Destroy;
693end;
694
695constructor TDictionary<TKey,TValue>.Create(ACapacity: Integer = 0);
696begin
697  Create(ACapacity, nil);
698end;
699
700constructor TDictionary<TKey,TValue>.Create(const AComparer: IEqualityComparer<TKey>);
701begin
702  Create(0, AComparer);
703end;
704
705constructor TDictionary<TKey,TValue>.Create(ACapacity: Integer; const AComparer: IEqualityComparer<TKey>);
706var
707  cap: Integer;
708begin
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);
716end;
717
718constructor TDictionary<TKey, TValue>.Create(
719  Collection: TEnumerable<TPair<TKey, TValue>>);
720var
721  item: TPair<TKey,TValue>;
722begin
723  Create(0, nil);
724  for item in Collection do
725    AddOrSetValue(item.Key, item.Value);
726end;
727
728constructor TDictionary<TKey, TValue>.Create(
729  Collection: TEnumerable<TPair<TKey, TValue>>;
730  const AComparer: IEqualityComparer<TKey>);
731var
732  item: TPair<TKey,TValue>;
733begin
734  Create(0, AComparer);
735  for item in Collection do
736    AddOrSetValue(item.Key, item.Value);
737end;
738
739destructor TDictionary<TKey,TValue>.Destroy;
740begin
741  Clear;
742  inherited;
743end;
744
745procedure TDictionary<TKey,TValue>.Add(const Key: TKey; const Value: TValue);
746var
747  index, hc: Integer;
748begin
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);
758end;
759
760function InCircularRange(Bottom, Item, TopInc: Integer): Boolean;
761begin
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
765end;
766
767procedure TDictionary<TKey,TValue>.Remove(const Key: TKey);
768var
769  gap, index, hc, bucket: Integer;
770  oldValue: TValue;
771begin
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);
821end;
822
823procedure TDictionary<TKey,TValue>.Clear;
824begin
825  FCount := 0;
826  FGrowThreshold := 0;
827  SetLength(FItems, 0);
828  SetCapacity(0);
829end;
830
831procedure TDictionary<TKey,TValue>.TrimExcess;
832begin
833  SetCapacity(FCount);
834end;
835
836function TDictionary<TKey,TValue>.TryGetValue(const Key: TKey; out Value: TValue): Boolean;
837var
838  index: Integer;
839begin
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);
846end;
847
848procedure TDictionary<TKey,TValue>.DoAdd(HashCode, Index: Integer; const Key: TKey; const Value: TValue);
849begin
850  FItems[Index].HashCode := HashCode;
851  FItems[Index].Key := Key;
852  FItems[Index].Value := Value;
853  Inc(FCount);
854end;
855
856function TDictionary<TKey, TValue>.DoGetEnumerator: TEnumerator<TPair<TKey, TValue>>;
857begin
858  Result := GetEnumerator;
859end;
860
861procedure TDictionary<TKey,TValue>.AddOrSetValue(const Key: TKey; const Value: TValue);
862begin
863  if ContainsKey(Key) then
864    SetItem(Key,Value)
865  else
866    Add(Key,Value);
867end;
868
869function TDictionary<TKey,TValue>.ContainsKey(const Key: TKey): Boolean;
870begin
871  Result := GetBucketIndex(Key, Hash(Key)) >= 0;
872end;
873
874function TDictionary<TKey,TValue>.ContainsValue(const Value: TValue): Boolean;
875var
876  i: Integer;
877  c: IEqualityComparer<TValue>;
878begin
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;
885end;
886
887function TDictionary<TKey,TValue>.GetEnumerator: TPairEnumerator;
888begin
889  Result := TPairEnumerator.Create(Self);
890end;
891
892// Pairs
893
894constructor TDictionary<TKey,TValue>.TPairEnumerator.Create(ADictionary: TDictionary<TKey,TValue>);
895begin
896  inherited Create;
897  FIndex := -1;
898  FDictionary := ADictionary;
899end;
900
901function TDictionary<TKey, TValue>.TPairEnumerator.DoGetCurrent: TPair<TKey, TValue>;
902begin
903  Result := GetCurrent;
904end;
905
906function TDictionary<TKey, TValue>.TPairEnumerator.DoMoveNext: Boolean;
907begin
908  Result := MoveNext;
909end;
910
911function TDictionary<TKey,TValue>.TPairEnumerator.GetCurrent: TPair<TKey,TValue>;
912begin
913  Result.Key := FDictionary.FItems[FIndex].Key;
914  Result.Value := FDictionary.FItems[FIndex].Value;
915end;
916
917function TDictionary<TKey,TValue>.TPairEnumerator.MoveNext: Boolean;
918begin
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;
926end;
927
928{ TLocalStorage }
929
930procedure TLocalStorage.Finalize;
931var
932  I: Integer;
933begin
934  for I := 0 to FCount - 1 do
935    if (FEntries[I].FDataType = dtInterface) then
936      IInterface(FEntries[I].FValue) := nil;
937end;
938
939function TLocalStorage.GetAsInteger(const Name: String): Integer;
940var
941  I: Integer;
942begin
943  for I := 0 to FCount - 1 do
944    if (FEntries[I].FName = Name) then
945      Exit(Integer(FEntries[I].FValue));
946  Result := 0;
947end;
948
949function TLocalStorage.GetAsInterface(const Name: String): IInterface;
950var
951  I: Integer;
952begin
953  for I := 0 to FCount - 1 do
954    if (FEntries[I].FName = Name) then
955      Exit(IInterface(FEntries[I].FValue));
956  Result := nil;
957end;
958
959procedure TLocalStorage.Initialize;
960begin
961  FCount := 0;
962end;
963
964procedure TLocalStorage.SetAsInteger(const Name: String; const Value: Integer);
965var
966  I: Integer;
967begin
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);
978end;
979
980procedure TLocalStorage.SetAsInterface(const Name: String;
981  const Value: IInterface);
982var
983  I: Integer;
984begin
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);
996end;
997
998end.
999