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