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