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