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}']
ToString()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 }
GetImplementor()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 }
GetImplementor()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 }
GetValue()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 }
GetValue()109     function GetValue: String;
110     procedure SetValue(const Value: String);
111   public
112     constructor Create(const AValue: String);
113 
ToString()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 }
Clone()124     function Clone: IANTLRInterface;
125   end;
126 
127 type
128   IList<T> = interface(IANTLRInterface)
129   ['{107DB2FE-A351-4F08-B9AD-E1BA8A4690FF}']
130     { Property accessors }
GetCapacity()131     function GetCapacity: Integer;
132     procedure SetCapacity(Value: Integer);
GetCount()133     function GetCount: Integer;
134     procedure SetCount(Value: Integer);
GetItem(Index: Integer)135     function GetItem(Index: Integer): T;
136     procedure SetItem(Index: Integer; const Value: T);
GetOnNotify()137     function GetOnNotify: TCollectionNotifyEvent<T>;
138     procedure SetOnNotify(Value: TCollectionNotifyEvent<T>);
139 
140     { Methods }
Add(const Value: T)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 
Remove(const Value: T)155     function Remove(const Value: T): Integer;
156     procedure Delete(Index: Integer);
157     procedure DeleteRange(AIndex, ACount: Integer);
Extract(const Value: T)158     function Extract(const Value: T): T;
159 
160     procedure Clear;
161 
Contains(const Value: T)162     function Contains(const Value: T): Boolean;
IndexOf(const Value: T)163     function IndexOf(const Value: T): Integer;
LastIndexOf(const Value: T)164     function LastIndexOf(const Value: T): Integer;
165 
166     procedure Reverse;
167 
168     procedure Sort; overload;
169     procedure Sort(const AComparer: IComparer<T>); overload;
BinarySearch(const Item: T; out Index: Integer)170     function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>)171     function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;
172 
173     procedure TrimExcess;
GetEnumerator()174     function GetEnumerator: TList<T>.TEnumerator;
GetRange(const Index, Count: Integer)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 }
GetItem(const Key: TKey)189     function GetItem(const Key: TKey): TValue;
190     procedure SetItem(const Key: TKey; const Value: TValue);
GetCount()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;
TryGetValue(const Key: TKey; out Value: TValue)198     function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
199     procedure AddOrSetValue(const Key: TKey; const Value: TValue);
ContainsKey(const Key: TKey)200     function ContainsKey(const Key: TKey): Boolean;
ContainsValue(const Value: TValue)201     function ContainsValue(const Value: TValue): Boolean;
GetEnumerator()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 }
QueryInterface(const IID: TGUID; out Obj)215     function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
_AddRef()216     function _AddRef: Integer; stdcall;
_Release()217     function _Release: Integer; stdcall;
218 
219     { IList<T> }
GetCapacity()220     function GetCapacity: Integer;
221     procedure SetCapacity(Value: Integer);
GetCount()222     function GetCount: Integer;
223     procedure SetCount(Value: Integer);
GetItem(Index: Integer)224     function GetItem(Index: Integer): T;
225     procedure SetItem(Index: Integer; const Value: T);
GetOnNotify()226     function GetOnNotify: TCollectionNotifyEvent<T>;
227     procedure SetOnNotify(Value: TCollectionNotifyEvent<T>);
GetRange(const Index, Count: Integer)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;
GetBucketIndex(const Key: TKey; HashCode: Integer)256     function GetBucketIndex(const Key: TKey; HashCode: Integer): Integer;
Hash(const Key: TKey)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
DoGetEnumerator()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;
GetCurrent()275         function GetCurrent: TPair<TKey,TValue>;
276       protected
DoGetCurrent()277         function DoGetCurrent: TPair<TKey,TValue>; override;
DoMoveNext()278         function DoMoveNext: Boolean; override;
279       public
280         constructor Create(ADictionary: TDictionary<TKey,TValue>);
281         property Current: TPair<TKey,TValue> read GetCurrent;
MoveNext()282         function MoveNext: Boolean;
283       end;
284   protected
285     { IInterface }
286     FRefCount: Integer;
QueryInterface(const IID: TGUID; out Obj)287     function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
_AddRef()288     function _AddRef: Integer; stdcall;
_Release()289     function _Release: Integer; stdcall;
290   protected
291     { IDictionary<TKey, TValue> }
GetItem(const Key: TKey)292     function GetItem(const Key: TKey): TValue;
293     procedure SetItem(const Key: TKey; const Value: TValue);
GetCount()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;
TryGetValue(const Key: TKey; out Value: TValue)300     function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
301     procedure AddOrSetValue(const Key: TKey; const Value: TValue);
ContainsKey(const Key: TKey)302     function ContainsKey(const Key: TKey): Boolean;
ContainsValue(const Value: TValue)303     function ContainsValue(const Value: TValue): Boolean;
304   public
GetEnumerator()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;
GetAsInteger(const Name: String)376     function GetAsInteger(const Name: String): Integer;
377     procedure SetAsInteger(const Name: String; const Value: Integer);
GetAsInterface(const Name: String)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 
InCircularRange(Bottom, Item, TopInc: Integer)389 function InCircularRange(Bottom, Item, TopInc: Integer): Boolean;
390 
391 { Checks if A and B are implemented by the same object }
SameObj(const A, B: IInterface)392 function SameObj(const A, B: IInterface): Boolean;
393 
IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil)394 function IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil): IANTLRInterface; overload;
395 
IsUpper(const C: Char)396 function IsUpper(const C: Char): Boolean;
397 
398 implementation
399 
400 uses
401   Windows,
402   SysUtils;
403 
SameObj(const A, B: IInterface)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 
IfThen(const AValue: Boolean; const ATrue: IANTLRInterface; const AFalse: IANTLRInterface = nil)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 
IsUpper(const C: Char)426 function IsUpper(const C: Char): Boolean;
427 begin
428   Result := (C >= 'A') and (C <= 'Z');
429 
430 end;
431 { TANTLRObject }
432 
GetImplementornull433 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 
TANTLRString.GetValue()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 
ToStringnull456 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 
TList()468 function TList<T>.GetCapacity: Integer;
469 begin
470   Result := inherited Capacity;
471 end;
472 
TList()473 function TList<T>.GetCount: Integer;
474 begin
475   Result := inherited Count;
476 end;
477 
GetItemnull478 function TList<T>.GetItem(Index: Integer): T;
479 begin
480   Result := inherited Items[Index];
481 end;
482 
TList()483 function TList<T>.GetOnNotify: TCollectionNotifyEvent<T>;
484 begin
485   Result := inherited OnNotify;
486 end;
487 
TList(const Index, Count: Integer)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 
TList(const IID: TGUID; out Obj)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 
TList()537 function TList<T>._AddRef: Integer;
538 begin
539   Result := InterlockedIncrement(FRefCount);
540 end;
541 
_Releasenull542 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 
TDictionary(const Key: TKey; HashCode: Integer)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 
GetCountnull626 function TDictionary<TKey, TValue>.GetCount: Integer;
627 begin
628   Result := FCount;
629 end;
630 
TDictionary(const Key: TKey)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 
GetItemnull641 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 
QueryInterfacenull674 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 
_AddRefnull683 function TDictionary<TKey, TValue>._AddRef: Integer;
684 begin
685   Result := InterlockedIncrement(FRefCount);
686 end;
687 
_Releasenull688 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 
InCircularRange(Bottom, Item, TopInc: Integer)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 
TryGetValuenull836 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 
TDictionary()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 
TDictionary(const Key: TKey)869 function TDictionary<TKey,TValue>.ContainsKey(const Key: TKey): Boolean;
870 begin
871   Result := GetBucketIndex(Key, Hash(Key)) >= 0;
872 end;
873 
ContainsValuenull874 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 
GetEnumeratornull887 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 
TPairEnumeratornull901 function TDictionary<TKey, TValue>.TPairEnumerator.DoGetCurrent: TPair<TKey, TValue>;
902 begin
903   Result := GetCurrent;
904 end;
905 
TPairEnumeratornull906 function TDictionary<TKey, TValue>.TPairEnumerator.DoMoveNext: Boolean;
907 begin
908   Result := MoveNext;
909 end;
910 
TPairEnumeratornull911 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 
TPairEnumeratornull917 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 
GetAsIntegernull939 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 
TLocalStorage.GetAsInterface(const Name: String)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