You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2694 lines
108 KiB

  1. unit uutlGenerics;
  2. {$mode objfpc}{$H+}
  3. {$modeswitch advancedrecords}
  4. interface
  5. uses
  6. Classes, SysUtils, typinfo,
  7. uutlCommon, uutlArrayContainer, uutlListBase, uutlComparer, uutlAlgorithm, uutlInterfaces,
  8. uutlEnumerator;
  9. type
  10. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. generic TutlQueue<T> = class(
  12. specialize TutlArrayContainer<T>
  13. , specialize IEnumerable<T>
  14. , specialize IutlEnumerable<T>
  15. , specialize IutlReadOnlyArray<T>)
  16. private type
  17. TEnumerator = class(
  18. specialize TutlEnumerator<T>
  19. , specialize IEnumerator<T>
  20. , specialize IutlEnumerator<T>)
  21. strict private
  22. fOwner: TutlQueue;
  23. fReversed: Boolean;
  24. fCurrent: Integer;
  25. protected { TutlEnumerator }
  26. function InternalMoveNext: Boolean; override;
  27. procedure InternalReset; override;
  28. {$IFDEF UTL_ENUMERATORS}
  29. public { IutlEnumerator }
  30. function Reverse: IutlEnumerator; override;
  31. {$ENDIF}
  32. public { IEnumerator }
  33. function GetCurrent: T; override;
  34. public
  35. constructor Create(const aOwner: TutlQueue; const aReversed: Boolean);
  36. end;
  37. public type
  38. IEnumerator = specialize IEnumerator<T>;
  39. IutlEnumerator = specialize IutlEnumerator<T>;
  40. strict private
  41. fCount: Integer;
  42. fReadPos: Integer;
  43. fWritePos: Integer;
  44. function GetItem(const aIndex: Integer): T;
  45. procedure SetItem(const aIndex: Integer; aItem: T);
  46. protected
  47. function GetCount: Integer; override;
  48. procedure SetCount(const aValue: Integer); override;
  49. procedure SetCapacity(const aValue: integer); override;
  50. public { IEnumerable }
  51. function GetEnumerator: IEnumerator;
  52. public { IutlEnumerable }
  53. function GetUtlEnumerator: IutlEnumerator;
  54. public
  55. property Count: Integer read GetCount;
  56. property IsEmpty;
  57. property Capacity;
  58. property CanExpand;
  59. property CanShrink;
  60. property OwnsItems;
  61. property Items[const aIndex: Integer]: T read GetItem write SetItem; default;
  62. procedure Enqueue(constref aItem: T);
  63. function Dequeue: T;
  64. function Dequeue(const aFreeItem: Boolean): T;
  65. function Peek: T;
  66. procedure ShrinkToFit;
  67. procedure Clear;
  68. constructor Create(const aOwnsItems: Boolean);
  69. destructor Destroy; override;
  70. end;
  71. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  72. generic TutlStack<T> = class(
  73. specialize TutlArrayContainer<T>
  74. , specialize IEnumerable<T>
  75. , specialize IutlEnumerable<T>
  76. , specialize IutlReadOnlyArray<T>)
  77. private type
  78. TEnumerator = class(
  79. specialize TutlMemoryEnumerator<T>
  80. , specialize IEnumerator<T>
  81. , specialize IutlEnumerator<T>)
  82. private
  83. fOwner: TutlStack;
  84. protected { IEnumerator }
  85. procedure InternalReset; override;
  86. {$IFDEF UTL_ENUMERATORS}
  87. public { IutlEnumerator }
  88. function Reverse: IutlEnumerator; override;
  89. {$ENDIF}
  90. public
  91. constructor Create(const aOwner: TutlStack; const aReversed: Boolean); reintroduce;
  92. end;
  93. public type
  94. IEnumerator = specialize IEnumerator<T>;
  95. IutlEnumerator = specialize IutlEnumerator<T>;
  96. strict private
  97. fCount: Integer;
  98. function GetItem(const aIndex: Integer): T;
  99. procedure SetItem(const aIndex: Integer; aValue: T);
  100. protected
  101. function GetCount: Integer; override;
  102. procedure SetCount(const aValue: Integer); override;
  103. public { IEnumerable }
  104. function GetEnumerator: IEnumerator;
  105. public { IutlEnumerable }
  106. function GetUtlEnumerator: IutlEnumerator;
  107. public
  108. property Count: Integer read GetCount;
  109. property IsEmpty;
  110. property Capacity;
  111. property CanExpand;
  112. property CanShrink;
  113. property OwnsItems;
  114. property Items[const aIndex: Integer]: T read GetItem write SetItem; default;
  115. procedure Push(constref aItem: T);
  116. function Pop: T;
  117. function Pop(const aFreeItem: Boolean): T;
  118. function Peek: T;
  119. procedure ShrinkToFit;
  120. procedure Clear;
  121. constructor Create(const aOwnsItems: Boolean);
  122. destructor Destroy; override;
  123. end;
  124. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  125. generic TutlSimpleList<T> = class(
  126. specialize TutlListBase<T>
  127. , specialize IutlReadOnlyArray<T>
  128. , specialize IutlArray<T>)
  129. strict private
  130. function GetFirst: T;
  131. function GetLast: T;
  132. public
  133. property First: T read GetFirst;
  134. property Last: T read GetLast;
  135. property Items[const aIndex: Integer]: T read GetItem write SetItem; default;
  136. function Add (constref aItem: T): Integer;
  137. procedure Insert (const aIndex: Integer; constref aItem: T);
  138. procedure Exchange (const aIndex1, aIndex2: Integer);
  139. procedure Move (const aCurrentIndex, aNewIndex: Integer);
  140. procedure Delete (const aIndex: Integer);
  141. function Extract (const aIndex: Integer): T;
  142. procedure PushFirst (constref aItem: T);
  143. function PopFirst (const aFreeItem: Boolean): T;
  144. procedure PushLast (constref aItem: T);
  145. function PopLast (const aFreeItem: Boolean): T;
  146. end;
  147. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  148. generic TutlCustomList<T> = class(
  149. specialize TutlSimpleList<T>)
  150. public type
  151. IEqualityComparer = specialize IutlEqualityComparer<T>;
  152. strict private
  153. fEqualityComparer: IEqualityComparer;
  154. public
  155. function IndexOf (const aItem: T): Integer;
  156. function Extract (const aItem: T; const aDefault: T): T; overload;
  157. function Remove (const aItem: T): Integer;
  158. constructor Create (aEqualityComparer: IEqualityComparer; const aOwnsItems: Boolean);
  159. destructor Destroy; override;
  160. end;
  161. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  162. generic TutlList<T> = class(
  163. specialize TutlCustomList<T>)
  164. public type
  165. TEqualityComparer = specialize TutlEqualityComparer<T>;
  166. public
  167. constructor Create(const aOwnsItems: Boolean);
  168. end;
  169. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  170. generic TutlCustomHashSet<T> = class(
  171. specialize TutlListBase<T>
  172. , specialize IutlReadOnlyArray<T>)
  173. private type
  174. TBinarySearch = specialize TutlBinarySearch<T>;
  175. public type
  176. IComparer = specialize IutlComparer<T>;
  177. strict private
  178. fComparer: IComparer;
  179. protected
  180. procedure SetCount (const aValue: Integer); override;
  181. procedure SetItem (const aIndex: Integer; aValue: T); override;
  182. public
  183. property Count: Integer read GetCount;
  184. property Items[const aIndex: Integer]: T read GetItem write SetItem; default;
  185. function Add (constref aItem: T): Boolean;
  186. function Contains (constref aItem: T): Boolean;
  187. function IndexOf (constref aItem: T): Integer;
  188. function Remove (constref aItem: T; const aFreeItem: Boolean = true): Boolean;
  189. procedure Delete (const aIndex: Integer);
  190. constructor Create (aComparer: IComparer; const aOwnsItems: Boolean);
  191. destructor Destroy; override;
  192. end;
  193. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  194. generic TutlHashSet<T> = class(
  195. specialize TutlCustomHashSet<T>)
  196. public type
  197. TComparer = specialize TutlComparer<T>;
  198. public
  199. constructor Create(const aOwnsItems: Boolean);
  200. end;
  201. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202. generic TutlCustomMap<TKey, TValue> = class(
  203. TutlInterfaceNoRefCount
  204. , specialize IutlEnumerable<TValue>)
  205. public type
  206. ////////////////////////////////////////////////////////////////////////////////////////////////
  207. TKeyValuePair = packed record
  208. Key: TKey;
  209. Value: TValue;
  210. end;
  211. ////////////////////////////////////////////////////////////////////////////////////////////////
  212. IValueEnumerator = specialize IEnumerator<TValue>;
  213. IutlValueEnumerator = specialize IutlEnumerator<TValue>;
  214. IKeyEnumerator = specialize IEnumerator<TKey>;
  215. IutlKeyEnumerator = specialize IutlEnumerator<TKey>;
  216. IKeyValuePairEnumerator = specialize IEnumerator<TKeyValuePair>;
  217. IutlKeyValuePairEnumerator = specialize IutlEnumerator<TKeyValuePair>;
  218. ////////////////////////////////////////////////////////////////////////////////////////////////
  219. THashSet = class(
  220. specialize TutlCustomHashSet<TKeyValuePair>)
  221. strict private
  222. fOwner: TutlCustomMap;
  223. protected
  224. procedure Release(var aItem: TKeyValuePair; const aFreeItem: Boolean); override;
  225. public
  226. constructor Create(const aOwner: TutlCustomMap; aComparer: IComparer);
  227. end;
  228. ////////////////////////////////////////////////////////////////////////////////////////////////
  229. IComparer = specialize IutlComparer<TKey>;
  230. TKeyValuePairComparer = class(
  231. TInterfacedObject
  232. , THashSet.IComparer)
  233. strict private
  234. fComparer: IComparer;
  235. public { IutlEqualityComparer }
  236. function EqualityCompare(constref i1, i2: TKeyValuePair): Boolean;
  237. public { IutlComparer }
  238. function Compare(constref i1, i2: TKeyValuePair): Integer;
  239. public
  240. constructor Create(aComparer: IComparer);
  241. destructor Destroy; override;
  242. end;
  243. ////////////////////////////////////////////////////////////////////////////////////////////////
  244. TKeyEnumerator = class(
  245. specialize TutlEnumerator<TKey>
  246. , IKeyEnumerator
  247. , IutlKeyEnumerator)
  248. strict private
  249. fEnumerator: IutlKeyValuePairEnumerator;
  250. protected { TutlEnumerator }
  251. function InternalMoveNext: Boolean; override;
  252. procedure InternalReset; override;
  253. public { IEnumerator }
  254. function GetCurrent: TKey; override;
  255. public
  256. constructor Create(aEnumerator: IutlKeyValuePairEnumerator);
  257. end;
  258. ////////////////////////////////////////////////////////////////////////////////////////////////
  259. TValueEnumerator = class(
  260. specialize TutlEnumerator<TValue>
  261. , IValueEnumerator
  262. , IutlValueEnumerator)
  263. strict private
  264. fEnumerator: IutlKeyValuePairEnumerator;
  265. protected { TutlEnumerator }
  266. function InternalMoveNext: Boolean; override;
  267. procedure InternalReset; override;
  268. public { IEnumerator }
  269. function GetCurrent: TValue; override;
  270. public
  271. constructor Create(aEnumerator: IutlKeyValuePairEnumerator);
  272. end;
  273. ////////////////////////////////////////////////////////////////////////////////////////////////
  274. TKeyCollection = class(
  275. TutlInterfaceNoRefCount
  276. , specialize IutlReadOnlyArray<TKey>
  277. , specialize IutlEnumerable<TKey>)
  278. strict private
  279. fHashSet: THashSet;
  280. public { IEnumerable }
  281. function GetEnumerator: IKeyEnumerator;
  282. public { IutlEnumerable }
  283. function GetUtlEnumerator: IutlKeyEnumerator;
  284. public { IutlReadOnlyArray }
  285. function GetCount: Integer;
  286. function GetItem(const aIndex: Integer): TKey;
  287. property Count: Integer read GetCount;
  288. property Items[const aIndex: Integer]: TKey read GetItem; default;
  289. public
  290. constructor Create(const aHashSet: THashSet);
  291. end;
  292. ////////////////////////////////////////////////////////////////////////////////////////////////
  293. TKeyValuePairCollection = class(
  294. TutlInterfaceNoRefCount
  295. , specialize IutlReadOnlyArray<TKeyValuePair>
  296. , specialize IutlEnumerable<TKeyValuePair>)
  297. strict private
  298. fHashSet: THashSet;
  299. public { IEnumerable }
  300. function GetEnumerator: IKeyValuePairEnumerator;
  301. public { IutlEnumerable }
  302. function GetUtlEnumerator: IutlKeyValuePairEnumerator;
  303. public { IutlReadOnlyArray }
  304. function GetCount: Integer;
  305. function GetItem(const aIndex: Integer): TKeyValuePair;
  306. property Count: Integer read GetCount;
  307. property Items[const aIndex: Integer]: TKeyValuePair read GetItem; default;
  308. public
  309. constructor Create(const aHashSet: THashSet);
  310. end;
  311. strict private
  312. fAutoCreate: Boolean;
  313. fOwnsKeys: Boolean;
  314. fOwnsValues: Boolean;
  315. fHashSetRef: THashSet;
  316. fKeyCollection: TKeyCollection;
  317. fKeyValuePairCollection: TKeyValuePairCollection;
  318. function GetValue (aKey: TKey): TValue; inline;
  319. function GetValueAt (const aIndex: Integer): TValue; inline;
  320. function GetCount: Integer; inline;
  321. function GetIsEmpty: Boolean; inline;
  322. function GetCapacity: Integer; inline;
  323. function GetCanShrink: Boolean; inline;
  324. function GetCanExpand: Boolean; inline;
  325. procedure SetCapacity (const aValue: Integer); inline;
  326. procedure SetCanShrink (const aValue: Boolean); inline;
  327. procedure SetCanExpand (const aValue: Boolean); inline;
  328. protected
  329. procedure SetValue (aKey: TKey; const aValue: TValue); virtual;
  330. procedure SetValueAt (const aIndex: Integer; const aValue: TValue); virtual;
  331. public { IEnumerable }
  332. function GetEnumerator: IValueEnumerator;
  333. public { IutlEnumerable }
  334. function GetUtlEnumerator: IutlValueEnumerator;
  335. public
  336. property Values [aKey: TKey]: TValue read GetValue write SetValue; default;
  337. property ValueAt[const aIndex: Integer]: TValue read GetValueAt write SetValueAt;
  338. property Keys: TKeyCollection read fKeyCollection;
  339. property KeyValuePairs: TKeyValuePairCollection read fKeyValuePairCollection;
  340. property Count: Integer read GetCount;
  341. property IsEmpty: Boolean read GetIsEmpty;
  342. property Capacity: Integer read GetCapacity write SetCapacity;
  343. property CanShrink: Boolean read GetCanShrink write SetCanShrink;
  344. property CanExpand: Boolean read GetCanExpand write SetCanExpand;
  345. property OwnsKeys: Boolean read fOwnsKeys write fOwnsKeys;
  346. property OwnsValues: Boolean read fOwnsValues write fOwnsValues;
  347. property AutoCreate: Boolean read fAutoCreate write fAutoCreate;
  348. procedure Add (constref aKey: TKey; constref aValue: TValue);
  349. function TryAdd (constref aKey: TKey; constref aValue: TValue): Boolean;
  350. function TryGetValue (constref aKey: TKey; out aValue: TValue): Boolean;
  351. function IndexOf (constref aKey: TKey): Integer;
  352. function Contains (constref aKey: TKey): Boolean;
  353. function Remove (constref aKey: TKey; const aFreeItem: Boolean = true): Boolean;
  354. procedure Delete (constref aKey: TKey);
  355. procedure DeleteAt (const aIndex: Integer);
  356. procedure Clear;
  357. constructor Create(const aHashSet: THashSet; const aOwnsKeys: Boolean; const aOwnsValues: Boolean);
  358. destructor Destroy; override;
  359. end;
  360. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  361. generic TutlMap<TKey, TValue> = class(
  362. specialize TutlCustomMap<TKey, TValue>)
  363. public type
  364. TComparer = specialize TutlComparer<TKey>;
  365. strict private
  366. fHashSetImpl: THashSet;
  367. public
  368. constructor Create(const aOwnsKeys: Boolean; const aOwnsValues: Boolean);
  369. destructor Destroy; override;
  370. end;
  371. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  372. TutlHandle = QWord;
  373. generic TutlHandleManager<T> = class(
  374. TutlInterfacedObject
  375. , specialize IutlEnumerable<TutlHandle>)
  376. private type
  377. THandleData = packed record
  378. case Integer of
  379. 0: (
  380. Handle: TutlHandle // 0..63
  381. );
  382. 1: (
  383. Index: Cardinal; // 0..31 index in data array (unique for each priority)
  384. Counter: Word; // 32..47 reusage counter
  385. TypeID: Byte; // 48..55 Stored Data Type
  386. Priority: Byte; // 56..63 Priority to share handles between multiple systems
  387. );
  388. end;
  389. TIndex = Cardinal;
  390. TEntryStatus = byte;
  391. PHandleEntry = ^THandleEntry;
  392. THandleEntry = packed record
  393. Next: TIndex; // 0..31 next used/free item
  394. Prev: TIndex; // 32..63 prev used/free item
  395. Counter: Word; // 64..79 current counter value
  396. Status: TEntryStatus; // 80..87 item status
  397. TypeID: Byte; // 88..95 type id
  398. Data: T; // actual data
  399. end;
  400. THandleEntries = array of THandleEntry;
  401. PPriorityItem = ^TPriorityItem;
  402. TPriorityItem = packed record
  403. FirstFree: TIndex;
  404. LastFree: TIndex;
  405. FirstUsed: TIndex;
  406. LastUsed: TIndex;
  407. Handles: THandleEntries;
  408. function GetHandleEntry(const aIndex: TIndex): PHandleEntry;
  409. procedure Grow(const aSize: Integer = 0);
  410. procedure PushFront(const aIndex: TIndex; var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus);
  411. procedure PushBack (const aIndex: TIndex; var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus);
  412. procedure Remove (const aIndex: TIndex; var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus);
  413. function PopFront(var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus; const aCanGrow: Boolean): TIndex;
  414. function PopBack (var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus; const aCanGrow: Boolean): TIndex;
  415. procedure PushFrontFreeIndex(const aIndex: TIndex); inline;
  416. procedure PushBackFreeIndex (const aIndex: TIndex); inline;
  417. procedure PushFrontUsedIndex(const aIndex: TIndex); inline;
  418. procedure PushBackUsedIndex (const aIndex: TIndex); inline;
  419. procedure RemoveFreeIndex (const aIndex: TIndex); inline;
  420. procedure RemoveUsedIndex (const aIndex: TIndex); inline;
  421. function PopFrontFreeIndex: TIndex;
  422. function PopBackFreeIndex: TIndex;
  423. function PopFrontUsedIndex: TIndex;
  424. function PopBackUsedIndex: TIndex;
  425. end;
  426. TPriorityItems = array of TPriorityItem;
  427. TEnumerator = class(specialize TutlEnumerator<TutlHandle>)
  428. private
  429. fOwner: TutlHandleManager;
  430. fPriority: Integer;
  431. fIndex: TIndex;
  432. fHandle: THandleEntry;
  433. protected
  434. function InternalMoveNext: Boolean; override;
  435. procedure InternalReset; override;
  436. public
  437. function GetCurrent: TutlHandle; override;
  438. constructor Create(const aOwner: TutlHandleManager);
  439. end;
  440. private const
  441. UNKNOWN_INDEX: TIndex = high(TIndex);
  442. GROW_SIZE = 100;
  443. ENTRY_STATUS_UNKNOWN: byte = 0;
  444. ENTRY_STATUS_FREE: byte = 1;
  445. ENTRY_STATUS_USED: byte = 2;
  446. private
  447. class function HighIndex(constref aEntries: THandleEntries): TIndex; inline;
  448. public type
  449. IEnumerator = specialize IEnumerator<TutlHandle>;
  450. IutlEnumerator = specialize IutlEnumerator<TutlHandle>;
  451. private
  452. fCount: Integer;
  453. fItems: TPriorityItems;
  454. fOwnsValues: Boolean;
  455. function GetPriorityItem(const aPriority: Byte): PPriorityItem;
  456. public
  457. function GetValue (const aHandle: TutlHandle): T;
  458. function TryGetValue (const aHandle: TutlHandle; out aData: T): Boolean;
  459. procedure SetValue (const aHandle: TutlHandle; aData: T);
  460. function TrySetValue (const aHandle: TutlHandle; aData: T): Boolean;
  461. function Add (const aTypeID: Byte; const aPriority: Byte; constref aData: T): TutlHandle;
  462. function IsValid (const aHandle: TutlHandle): Boolean; inline;
  463. procedure Update (const aHandle: TutlHandle; aData: T);
  464. function Remove (const aHandle: TutlHandle): Boolean;
  465. procedure Delete (const aHandle: TutlHandle);
  466. procedure Clear;
  467. public { IutlEnumerable }
  468. function GetEnumerator: IEnumerator;
  469. function GetUtlEnumerator: IutlEnumerator;
  470. public
  471. property Items[const aHandle: TutlHandle]: T read GetValue write SetValue; default;
  472. property Count: Integer read fCount;
  473. constructor Create(const aOwnsValues: Boolean);
  474. destructor Destroy; override;
  475. public
  476. class function GetTypeID (const aHandle: TutlHandle): Byte; inline;
  477. class function GetPriority(const aHandle: TutlHandle): Byte; inline;
  478. end;
  479. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  480. EEnumConvertException = class(EConvertError)
  481. public
  482. constructor Create(const aValue, aExpectedType: String);
  483. end;
  484. TutlEnumHelperBaseClass = class of TutlEnumHelperBase;
  485. TutlEnumHelperBase = class
  486. public type
  487. TIntArray = array of Integer;
  488. TStringArray = array of String;
  489. private type
  490. TValuesMap = specialize TutlMap<string, TIntArray>;
  491. TNamesMap = specialize TutlMap<string, TStringArray>;
  492. private class var
  493. fValuesMap: TValuesMap;
  494. fNamesMap: TNamesMap;
  495. protected
  496. class procedure RegisterType (const aValues: TIntArray; const aNames: TStringArray);
  497. class procedure UnregisterType();
  498. public
  499. class function ToString (const aValue: Integer; const aAllowOrd: Boolean = false): String; reintroduce;
  500. class function TryToEnum (const aStr: String; out aValue: Integer; const aAllowOrd: Boolean = false): Boolean;
  501. class function ToEnum (const aStr: String; const aAllowOrd: Boolean = false): Integer; overload;
  502. class function ToEnum (const aStr: String; const aDefault: Integer; const aAllowOrd: Boolean = false): Integer; overload;
  503. class function IntValues: TIntArray;
  504. class function Names: TStringArray;
  505. public
  506. class constructor Initialize;
  507. class destructor Finalize;
  508. end;
  509. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  510. generic TutlEnumHelper<T> = class(TutlEnumHelperBase)
  511. public type
  512. TEnumType = T;
  513. TValueArray = array of T;
  514. private class var
  515. fValues: TValueArray;
  516. fNames: TStringArray;
  517. fIntValues: TIntArray;
  518. fTypeInfo: PTypeInfo;
  519. public
  520. class function ToString (const aValue: T; const aAllowOrd: Boolean = false): String; reintroduce;
  521. class function TryToEnum (const aStr: String; out aValue: T; const aAllowOrd: Boolean = false): Boolean;
  522. class function ToEnum (const aStr: String; const aAllowOrd: Boolean = false): T; overload;
  523. class function ToEnum (const aStr: String; const aDefault: T; const aAllowOrd: Boolean = false): T; overload;
  524. class function Values: TValueArray; inline;
  525. class function IntValues: TIntArray; inline;
  526. class function Names: TStringArray; inline;
  527. class function TypeInfo: PTypeInfo; inline;
  528. class constructor Initialize;
  529. class destructor Finalize;
  530. end;
  531. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  532. TutlSetHelperBase = class
  533. private type
  534. TEnumHelperMap = specialize TutlMap<string, TutlEnumHelperBaseClass>;
  535. private class var
  536. fEnumHelpers: TEnumHelperMap;
  537. private
  538. class function IsSet (const aSet; const aSize: Integer; const aValue: Integer): Boolean;
  539. class procedure SetValue (var aSet; const aSize: Integer; const aValue: Integer);
  540. class procedure ClearValue(var aSet; const aSize: Integer; const aValue: Integer);
  541. protected
  542. class procedure RegisterEnumHelper(const aHelper: TutlEnumHelperBaseClass);
  543. class procedure UnregisterEnumHelper;
  544. public
  545. class function ToString(
  546. const aSet;
  547. const aSize: Integer;
  548. const aSeparator: String = ', ';
  549. const aAllowOrd: Boolean = false): String; reintroduce;
  550. class function TryToSet(
  551. const aStr: String;
  552. out aSet;
  553. const aSize: Integer;
  554. const aAllowOrd: Boolean = false): Boolean;
  555. class function TryToSet(
  556. const aStr: String;
  557. const aSeparator: String;
  558. out aSet;
  559. const aSize: Integer;
  560. const aAllowOrd: Boolean = false): Boolean;
  561. class function Compare(
  562. const aSet1;
  563. const aSet2;
  564. const aSize: Integer): Integer;
  565. class function EnumHelper: TutlEnumHelperBaseClass;
  566. class constructor Initialize;
  567. class destructor Finalize;
  568. end;
  569. TutlSetHelperBaseClass = class of TutlSetHelperBase;
  570. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  571. generic TutlSetHelper<TEnum, TSet> = class(TutlSetHelperBase)
  572. public type
  573. TEnumHelper = specialize TutlEnumHelper<TEnum>;
  574. TEnumType = TEnum;
  575. TSetType = TSet;
  576. public
  577. class function ToString(
  578. const aValue: TSet;
  579. const aSeparator: String = ', ';
  580. const aAllowOrd: Boolean = false): String; overload;
  581. class function TryToSet(
  582. const aStr: String;
  583. out aValue: TSet;
  584. const aAllowOrd: Boolean = false): Boolean; overload;
  585. class function TryToSet(
  586. const aStr: String;
  587. const aSeparator: String;
  588. out aValue: TSet;
  589. const aAllowOrd: Boolean = false): Boolean; overload;
  590. class function ToSet(
  591. const aStr: String;
  592. const aDefault: TSet;
  593. const aAllowOrd: Boolean = false): TSet; overload;
  594. class function ToSet(
  595. const aStr: String;
  596. const aAllowOrd: Boolean = false): TSet; overload;
  597. class function Compare(
  598. const aSet1, aSet2: TSet): Integer; overload;
  599. class constructor Initialize;
  600. class destructor Finalize;
  601. end;
  602. implementation
  603. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  604. //TutlQueue.TEnumerator/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  605. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  606. function TutlQueue.TEnumerator.InternalMoveNext: Boolean;
  607. begin
  608. if fReversed
  609. then dec(fCurrent)
  610. else inc(fCurrent);
  611. result := (0 <= fCurrent) and (fCurrent < fOwner.Count);
  612. end;
  613. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  614. procedure TutlQueue.TEnumerator.InternalReset;
  615. begin
  616. if fReversed
  617. then fCurrent := fOwner.Count
  618. else fCurrent := -1;
  619. end;
  620. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  621. {$IFDEF UTL_ENUMERATORS}
  622. function TutlQueue.TEnumerator.Reverse: IutlEnumerator;
  623. begin
  624. result := TEnumerator.Create(fOwner, not fReversed);
  625. end;
  626. {$ENDIF}
  627. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  628. function TutlQueue.TEnumerator.GetCurrent: T;
  629. begin
  630. result := fOwner[fCurrent];
  631. end;
  632. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  633. constructor TutlQueue.TEnumerator.Create(const aOwner: TutlQueue; const aReversed: Boolean);
  634. begin
  635. if not Assigned(aOwner) then
  636. raise EArgumentNilException.Create('aOwner');
  637. fOwner := aOwner;
  638. fReversed := aReversed;
  639. inherited Create;
  640. end;
  641. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  642. //TutlQueue/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  643. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  644. function TutlQueue.GetItem(const aIndex: Integer): T;
  645. var
  646. i: Integer;
  647. begin
  648. if (aIndex < 0) or (aIndex >= fCount) then
  649. raise EOutOfRangeException.Create(aIndex, 0, fCount-1);
  650. i := fReadPos + aIndex;
  651. if (i >= Capacity) then
  652. i := i - Capacity;
  653. result := GetInternalItem(i)^;
  654. end;
  655. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  656. procedure TutlQueue.SetItem(const aIndex: Integer; aItem: T);
  657. var
  658. i: Integer;
  659. begin
  660. if (aIndex < 0) or (aIndex >= fCount) then
  661. raise EOutOfRangeException.Create(aIndex, 0, fCount-1);
  662. i := fReadPos + aIndex;
  663. if (i >= Capacity) then
  664. i := i - Capacity;
  665. GetInternalItem(i)^ := aItem;
  666. end;
  667. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  668. function TutlQueue.GetCount: Integer;
  669. begin
  670. result := fCount;
  671. end;
  672. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  673. procedure TutlQueue.SetCount(const aValue: Integer);
  674. begin
  675. raise ENotSupportedException.Create('SetCount not supported');
  676. end;
  677. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  678. procedure TutlQueue.SetCapacity(const aValue: integer);
  679. var
  680. cnt: Integer;
  681. begin
  682. if (aValue < Count) then
  683. raise EArgumentException.Create('can not reduce capacity below count');
  684. if (aValue < Capacity) then begin // is shrinking
  685. if (fReadPos <= fWritePos) then begin // ReadPos Before WritePos -> Move To Begin
  686. System.Move(GetInternalItem(fReadPos)^, GetInternalItem(0)^, SizeOf(T) * Count);
  687. fReadPos := 0;
  688. fWritePos := Count;
  689. end else if (fReadPos > fWritePos) then begin // ReadPos Behind WritePos
  690. cnt := Capacity - aValue;
  691. System.Move(GetInternalItem(fReadPos)^, GetInternalItem(fReadPos - cnt)^, SizeOf(T) * cnt);
  692. dec(fReadPos, cnt);
  693. end;
  694. end;
  695. inherited SetCapacity(aValue);
  696. // ReadPos After WritePos and Expanding
  697. if (fReadPos > fWritePos) and (aValue > Capacity) then begin
  698. cnt := aValue - Capacity;
  699. System.Move(GetInternalItem(fReadPos)^, GetInternalItem(fReadPos - cnt)^, SizeOf(T) * cnt);
  700. inc(fReadPos, cnt);
  701. end;
  702. end;
  703. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  704. function TutlQueue.GetEnumerator: IEnumerator;
  705. begin
  706. result := TEnumerator.Create(self, false);
  707. result.Reset;
  708. end;
  709. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  710. function TutlQueue.GetUtlEnumerator: IutlEnumerator;
  711. begin
  712. result := TEnumerator.Create(self, false);
  713. result.Reset;
  714. end;
  715. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  716. procedure TutlQueue.Enqueue(constref aItem: T);
  717. begin
  718. if (Count = Capacity) then
  719. Expand;
  720. fWritePos := fWritePos mod Capacity;
  721. GetInternalItem(fWritePos)^ := aItem;
  722. inc(fCount);
  723. inc(fWritePos);
  724. end;
  725. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  726. function TutlQueue.Dequeue: T;
  727. begin
  728. result := Dequeue(false);
  729. end;
  730. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  731. function TutlQueue.Dequeue(const aFreeItem: Boolean): T;
  732. var
  733. p: PT;
  734. begin
  735. if IsEmpty then
  736. raise EInvalidOperation.Create('queue is empty');
  737. p := GetInternalItem(fReadPos);
  738. if aFreeItem
  739. then FillByte(result{%H-}, SizeOf(result), 0)
  740. else result := p^;
  741. Release(p^, aFreeItem);
  742. dec(fCount);
  743. fReadPos := (fReadPos + 1) mod Capacity;
  744. end;
  745. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  746. function TutlQueue.Peek: T;
  747. begin
  748. if IsEmpty then
  749. raise EInvalidOperation.Create('queue is empty');
  750. result := GetInternalItem(fReadPos)^;
  751. end;
  752. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  753. procedure TutlQueue.ShrinkToFit;
  754. begin
  755. Shrink(true);
  756. end;
  757. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  758. procedure TutlQueue.Clear;
  759. begin
  760. while (fReadPos <> fWritePos) do begin
  761. Release(GetInternalItem(fReadPos)^, true);
  762. fReadPos := (fReadPos + 1) mod Capacity;
  763. end;
  764. fCount := 0;
  765. if CanShrink then
  766. ShrinkToFit;
  767. end;
  768. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  769. constructor TutlQueue.Create(const aOwnsItems: Boolean);
  770. begin
  771. inherited Create(aOwnsItems);
  772. fCount := 0;
  773. fReadPos := 0;
  774. fWritePos := 0;
  775. end;
  776. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  777. destructor TutlQueue.Destroy;
  778. begin
  779. Clear;
  780. inherited Destroy;
  781. end;
  782. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  783. //TutlStack.TEnumerator/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  784. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  785. procedure TutlStack.TEnumerator.InternalReset;
  786. begin
  787. First := 0;
  788. Last := fOwner.Count-1;
  789. if (Last >= First)
  790. then Memory := fOwner.GetInternalItem(0)
  791. else Memory := nil;
  792. inherited InternalReset;
  793. end;
  794. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  795. {$IFDEF UTL_ENUMERATORS}
  796. function TutlStack.TEnumerator.Reverse: IutlEnumerator;
  797. begin
  798. result := TEnumerator.Create(fOwner, not Reversed);
  799. end;
  800. {$ENDIF}
  801. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  802. constructor TutlStack.TEnumerator.Create(const aOwner: TutlStack; const aReversed: Boolean);
  803. begin
  804. if not Assigned(aOwner) then
  805. raise EArgumentNilException.Create('aOwner');
  806. fOwner := aOwner;
  807. inherited Create(nil, aReversed, 0, -1);
  808. end;
  809. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  810. //TutlStack/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  811. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  812. function TutlStack.GetItem(const aIndex: Integer): T;
  813. begin
  814. if (aIndex < 0) or (aIndex >= fCount) then
  815. raise EOutOfRangeException.Create(aIndex, 0, fCount-1);
  816. result := GetInternalItem(aIndex)^;
  817. end;
  818. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  819. procedure TutlStack.SetItem(const aIndex: Integer; aValue: T);
  820. begin
  821. if (aIndex < 0) or (aIndex >= fCount) then
  822. raise EOutOfRangeException.Create(aIndex, 0, fCount-1);
  823. GetInternalItem(aIndex)^ := aValue;
  824. end;
  825. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  826. function TutlStack.GetCount: Integer;
  827. begin
  828. result := fCount;
  829. end;
  830. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  831. procedure TutlStack.SetCount(const aValue: Integer);
  832. begin
  833. raise ENotSupportedException.Create('SetCount not supported');
  834. end;
  835. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  836. function TutlStack.GetEnumerator: IEnumerator;
  837. begin
  838. result := TEnumerator.Create(self, false);
  839. end;
  840. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  841. function TutlStack.GetUtlEnumerator: IutlEnumerator;
  842. begin
  843. result := TEnumerator.Create(self, false);
  844. end;
  845. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  846. procedure TutlStack.Push(constref aItem: T);
  847. begin
  848. if (Count = Capacity) then
  849. Expand;
  850. GetInternalItem(fCount)^ := aItem;
  851. inc(fCount);
  852. end;
  853. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  854. function TutlStack.Pop: T;
  855. begin
  856. Pop(false);
  857. end;
  858. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  859. function TutlStack.Pop(const aFreeItem: Boolean): T;
  860. var
  861. p: PT;
  862. begin
  863. if IsEmpty then
  864. raise EInvalidOperation.Create('stack is empty');
  865. p := GetInternalItem(fCount-1);
  866. if aFreeItem
  867. then FillByte(result{%H-}, SizeOf(result), 0)
  868. else result := p^;
  869. Release(p^, aFreeItem);
  870. dec(fCount);
  871. end;
  872. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  873. function TutlStack.Peek: T;
  874. begin
  875. if IsEmpty then
  876. raise EInvalidOperation.Create('stack is empty');
  877. result := GetInternalItem(fCount-1)^;
  878. end;
  879. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  880. procedure TutlStack.ShrinkToFit;
  881. begin
  882. Shrink(true);
  883. end;
  884. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  885. procedure TutlStack.Clear;
  886. begin
  887. while (fCount > 0) do begin
  888. dec(fCount);
  889. Release(GetInternalItem(fCount)^, true);
  890. end;
  891. if CanShrink then
  892. ShrinkToFit;
  893. end;
  894. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  895. constructor TutlStack.Create(const aOwnsItems: Boolean);
  896. begin
  897. inherited Create(aOwnsItems);
  898. fCount := 0
  899. end;
  900. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  901. destructor TutlStack.Destroy;
  902. begin
  903. Clear;
  904. inherited Destroy;
  905. end;
  906. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  907. //TutlSimpleList////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  908. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  909. function TutlSimpleList.GetFirst: T;
  910. begin
  911. if IsEmpty then
  912. raise EInvalidOperation.Create('list is empty');
  913. result := GetInternalItem(0)^;
  914. end;
  915. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  916. function TutlSimpleList.GetLast: T;
  917. begin
  918. if IsEmpty then
  919. raise EInvalidOperation.Create('list is empty');
  920. result := GetInternalItem(Count-1)^;
  921. end;
  922. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  923. function TutlSimpleList.Add(constref aItem: T): Integer;
  924. begin
  925. result := Count;
  926. InsertIntern(result, aItem);
  927. end;
  928. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  929. procedure TutlSimpleList.Insert(const aIndex: Integer; constref aItem: T);
  930. begin
  931. InsertIntern(aIndex, aItem);
  932. end;
  933. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  934. procedure TutlSimpleList.Exchange(const aIndex1, aIndex2: Integer);
  935. var
  936. tmp: T;
  937. p1, p2: PT;
  938. begin
  939. if (aIndex1 < 0) or (aIndex1 >= Count) then
  940. raise EOutOfRangeException.Create(aIndex1, 0, Count-1);
  941. if (aIndex2 < 0) or (aIndex2 >= Count) then
  942. raise EOutOfRangeException.Create(aIndex2, 0, Count-1);
  943. p1 := GetInternalItem(aIndex1);
  944. p2 := GetInternalItem(aIndex2);
  945. System.Move(p1^, tmp{%H-}, SizeOf(T));
  946. System.Move(p2^, p1^, SizeOf(T));
  947. System.Move(tmp, p2^, SizeOf(T));
  948. FillByte(tmp, SizeOf(tmp), 0)
  949. end;
  950. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  951. procedure TutlSimpleList.Move(const aCurrentIndex, aNewIndex: Integer);
  952. var
  953. tmp: T;
  954. cur, new: PT;
  955. begin
  956. if (aCurrentIndex < 0) or (aCurrentIndex >= Count) then
  957. raise EOutOfRangeException.Create(aCurrentIndex, 0, Count-1);
  958. if (aNewIndex < 0) or (aNewIndex >= Count) then
  959. raise EOutOfRangeException.Create(aNewIndex, 0, Count-1);
  960. if (aCurrentIndex = aNewIndex) then
  961. exit;
  962. cur := GetInternalItem(aCurrentIndex);
  963. new := GetInternalItem(aNewIndex);
  964. System.Move(cur^, tmp{%H-}, SizeOf(T));
  965. if (aNewIndex > aCurrentIndex) then begin
  966. System.Move((cur+1)^, cur^, SizeOf(T) * (aNewIndex - aCurrentIndex));
  967. end else begin
  968. System.Move(new^, (new+1)^, SizeOf(T) * (aCurrentIndex - aNewIndex));
  969. end;
  970. System.Move(tmp, new^, SizeOf(T));
  971. FillByte(tmp, SizeOf(tmp), 0);
  972. end;
  973. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  974. procedure TutlSimpleList.Delete(const aIndex: Integer);
  975. begin
  976. DeleteIntern(aIndex, true);
  977. end;
  978. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  979. function TutlSimpleList.Extract(const aIndex: Integer): T;
  980. begin
  981. result := GetItem(aIndex);
  982. DeleteIntern(aIndex, false);
  983. end;
  984. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  985. procedure TutlSimpleList.PushFirst(constref aItem: T);
  986. begin
  987. InsertIntern(0, aItem);
  988. end;
  989. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  990. function TutlSimpleList.PopFirst(const aFreeItem: Boolean): T;
  991. begin
  992. if aFreeItem
  993. then FillByte(result{%H-}, SizeOf(result), 0)
  994. else result := GetItem(0);
  995. DeleteIntern(0, aFreeItem);
  996. end;
  997. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  998. procedure TutlSimpleList.PushLast(constref aItem: T);
  999. begin
  1000. InsertIntern(Count, aItem);
  1001. end;
  1002. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1003. function TutlSimpleList.PopLast(const aFreeItem: Boolean): T;
  1004. begin
  1005. if aFreeItem
  1006. then FillByte(result{%H-}, SizeOf(result), 0)
  1007. else result := GetItem(Count-1);
  1008. DeleteIntern(Count-1, aFreeItem);
  1009. end;
  1010. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1011. //TutlCustomList////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1012. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1013. function TutlCustomList.IndexOf(const aItem: T): Integer;
  1014. begin
  1015. result := Count-1;
  1016. while (result >= 0)
  1017. and not fEqualityComparer.EqualityCompare(Items[result], aItem)
  1018. do
  1019. dec(result);
  1020. end;
  1021. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1022. function TutlCustomList.Extract(const aItem: T; const aDefault: T): T;
  1023. var
  1024. i: Integer;
  1025. begin
  1026. i := IndexOf(aItem);
  1027. if (i >= 0) then begin
  1028. result := Items[i];
  1029. DeleteIntern(i, false);
  1030. end else
  1031. result := aDefault;
  1032. end;
  1033. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1034. function TutlCustomList.Remove(const aItem: T): Integer;
  1035. begin
  1036. result := IndexOf(aItem);
  1037. if (result >= 0) then
  1038. DeleteIntern(result, true);
  1039. end;
  1040. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1041. constructor TutlCustomList.Create(aEqualityComparer: IEqualityComparer; const aOwnsItems: Boolean);
  1042. begin
  1043. if not Assigned(aEqualityComparer) then
  1044. raise EArgumentNilException.Create('aEqualityComparer');
  1045. inherited Create(aOwnsItems);
  1046. fEqualityComparer := aEqualityComparer;
  1047. end;
  1048. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1049. destructor TutlCustomList.Destroy;
  1050. begin
  1051. fEqualityComparer := nil;
  1052. inherited Destroy;
  1053. end;
  1054. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1055. //TutlList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1056. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1057. constructor TutlList.Create(const aOwnsItems: Boolean);
  1058. begin
  1059. inherited Create(TEqualityComparer.Create, aOwnsItems);
  1060. end;
  1061. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1062. //TutlCustomHashSet/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1063. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1064. procedure TutlCustomHashSet.SetCount(const aValue: Integer);
  1065. begin
  1066. raise ENotSupportedException.Create('SetCount not supported');
  1067. end;
  1068. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1069. procedure TutlCustomHashSet.SetItem(const aIndex: Integer; aValue: T);
  1070. begin
  1071. if not fComparer.EqualityCompare(GetItem(aIndex), aValue) then
  1072. EInvalidOperation.Create('values are not equal');
  1073. inherited SetItem(aIndex, aValue);
  1074. end;
  1075. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1076. function TutlCustomHashSet.Add(constref aItem: T): Boolean;
  1077. var
  1078. i: Integer;
  1079. begin
  1080. result := not TBinarySearch.Search(self, fComparer, aItem, i);
  1081. if result then
  1082. InsertIntern(i, aItem);
  1083. end;
  1084. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1085. function TutlCustomHashSet.Contains(constref aItem: T): Boolean;
  1086. var
  1087. i: Integer;
  1088. begin
  1089. result := TBinarySearch.Search(self, fComparer, aItem, i);
  1090. end;
  1091. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1092. function TutlCustomHashSet.IndexOf(constref aItem: T): Integer;
  1093. begin
  1094. if not TBinarySearch.Search(self, fComparer, aItem, result) then
  1095. result := -1;
  1096. end;
  1097. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1098. function TutlCustomHashSet.Remove(constref aItem: T; const aFreeItem: Boolean): Boolean;
  1099. var
  1100. i: Integer;
  1101. begin
  1102. result := TBinarySearch.Search(self, fComparer, aItem, i);
  1103. if result then
  1104. DeleteIntern(i, aFreeItem);
  1105. end;
  1106. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1107. procedure TutlCustomHashSet.Delete(const aIndex: Integer);
  1108. begin
  1109. DeleteIntern(aIndex, true);
  1110. end;
  1111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1112. constructor TutlCustomHashSet.Create(aComparer: IComparer; const aOwnsItems: Boolean);
  1113. begin
  1114. inherited Create(aOwnsItems);
  1115. fComparer := aComparer;
  1116. end;
  1117. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1118. destructor TutlCustomHashSet.Destroy;
  1119. begin
  1120. fComparer := nil;
  1121. inherited Destroy;
  1122. end;
  1123. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1124. //TutlHastSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1125. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1126. constructor TutlHashSet.Create(const aOwnsItems: Boolean);
  1127. begin
  1128. inherited Create(TComparer.Create, aOwnsItems);
  1129. end;
  1130. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1131. //TutlCustomMap.THashSet////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1132. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1133. procedure TutlCustomMap.THashSet.Release(var aItem: TKeyValuePair; const aFreeItem: Boolean);
  1134. begin
  1135. if not utlFinalizeObject(aItem.Key, TypeInfo(aItem.Key), fOwner.OwnsKeys and aFreeItem) then
  1136. Finalize(aItem.Key);
  1137. if not utlFinalizeObject(aItem.Value, TypeInfo(aItem.Value), fOwner.OwnsValues and aFreeItem) then
  1138. Finalize(aItem.Key);
  1139. inherited Release(aItem, aFreeItem);
  1140. end;
  1141. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1142. constructor TutlCustomMap.THashSet.Create(const aOwner: TutlCustomMap; aComparer: IComparer);
  1143. begin
  1144. inherited Create(aComparer, true);
  1145. fOwner := aOwner;
  1146. end;
  1147. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1148. //TutlCustomMap.TKeyValuePairComparer///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1149. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1150. function TutlCustomMap.TKeyValuePairComparer.EqualityCompare(constref i1, i2: TKeyValuePair): Boolean;
  1151. begin
  1152. result := fComparer.EqualityCompare(i1.Key, i2.Key);
  1153. end;
  1154. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1155. function TutlCustomMap.TKeyValuePairComparer.Compare(constref i1, i2: TKeyValuePair): Integer;
  1156. begin
  1157. result := fComparer.Compare(i1.Key, i2.Key);
  1158. end;
  1159. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1160. constructor TutlCustomMap.TKeyValuePairComparer.Create(aComparer: IComparer);
  1161. begin
  1162. if not Assigned(aComparer) then
  1163. raise EArgumentNilException.Create('aComparer');
  1164. inherited Create;
  1165. fComparer := aComparer;
  1166. end;
  1167. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1168. destructor TutlCustomMap.TKeyValuePairComparer.Destroy;
  1169. begin
  1170. fComparer := nil;
  1171. inherited Destroy;
  1172. end;
  1173. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1174. //TutlCustomMap.TKeyEnumerator//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1175. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1176. function TutlCustomMap.TKeyEnumerator.InternalMoveNext: Boolean;
  1177. begin
  1178. result := fEnumerator.MoveNext;
  1179. end;
  1180. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1181. procedure TutlCustomMap.TKeyEnumerator.InternalReset;
  1182. begin
  1183. fEnumerator.Reset;
  1184. end;
  1185. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1186. function TutlCustomMap.TKeyEnumerator.GetCurrent: TKey;
  1187. begin
  1188. result := fEnumerator.GetCurrent.Key;
  1189. end;
  1190. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1191. constructor TutlCustomMap.TKeyEnumerator.Create(aEnumerator: IutlKeyValuePairEnumerator);
  1192. begin
  1193. if not Assigned(aEnumerator) then
  1194. raise EArgumentNilException.Create('aEnumerator');
  1195. fEnumerator := aEnumerator;
  1196. inherited Create;
  1197. end;
  1198. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1199. //TutlCustomMap.TValueEnumerator////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1200. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1201. function TutlCustomMap.TValueEnumerator.InternalMoveNext: Boolean;
  1202. begin
  1203. result := fEnumerator.MoveNext;
  1204. end;
  1205. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1206. procedure TutlCustomMap.TValueEnumerator.InternalReset;
  1207. begin
  1208. fEnumerator.Reset;
  1209. end;
  1210. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1211. function TutlCustomMap.TValueEnumerator.GetCurrent: TValue;
  1212. begin
  1213. result := fEnumerator.GetCurrent.Value;
  1214. end;
  1215. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1216. constructor TutlCustomMap.TValueEnumerator.Create(aEnumerator: IutlKeyValuePairEnumerator);
  1217. begin
  1218. if not Assigned(aEnumerator) then
  1219. raise EArgumentNilException.Create('aEnumerator');
  1220. fEnumerator := aEnumerator;
  1221. inherited Create;
  1222. end;
  1223. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1224. //TutlCustomMap.TKeyCollection//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1225. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1226. function TutlCustomMap.TKeyCollection.GetEnumerator: IKeyEnumerator;
  1227. begin
  1228. result := TKeyEnumerator.Create(fHashSet.GetUtlEnumerator);
  1229. end;
  1230. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1231. function TutlCustomMap.TKeyCollection.GetUtlEnumerator: IutlKeyEnumerator;
  1232. begin
  1233. result := TKeyEnumerator.Create(fHashSet.GetUtlEnumerator);
  1234. end;
  1235. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1236. function TutlCustomMap.TKeyCollection.GetCount: Integer;
  1237. begin
  1238. result := fHashSet.Count;
  1239. end;
  1240. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1241. function TutlCustomMap.TKeyCollection.GetItem(const aIndex: Integer): TKey;
  1242. begin
  1243. result := fHashSet[aIndex].Key;
  1244. end;
  1245. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1246. constructor TutlCustomMap.TKeyCollection.Create(const aHashSet: THashSet);
  1247. begin
  1248. inherited Create;
  1249. fHashSet := aHashSet;
  1250. end;
  1251. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1252. //TutlCustomMap.TKeyValuePairCollection/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1253. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1254. function TutlCustomMap.TKeyValuePairCollection.GetEnumerator: IKeyValuePairEnumerator;
  1255. begin
  1256. result := fHashSet.GetEnumerator;
  1257. end;
  1258. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1259. function TutlCustomMap.TKeyValuePairCollection.GetUtlEnumerator: IutlKeyValuePairEnumerator;
  1260. begin
  1261. result := fHashSet.GetUtlEnumerator;
  1262. end;
  1263. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1264. function TutlCustomMap.TKeyValuePairCollection.GetCount: Integer;
  1265. begin
  1266. result := fHashSet.Count;
  1267. end;
  1268. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1269. function TutlCustomMap.TKeyValuePairCollection.GetItem(const aIndex: Integer): TKeyValuePair;
  1270. begin
  1271. result := fHashSet[aIndex];
  1272. end;
  1273. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1274. constructor TutlCustomMap.TKeyValuePairCollection.Create(const aHashSet: THashSet);
  1275. begin
  1276. inherited Create;
  1277. fHashSet := aHashSet;
  1278. end;
  1279. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1280. //TutlCustomMap/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1281. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1282. function TutlCustomMap.GetValue(aKey: TKey): TValue;
  1283. var
  1284. i: Integer;
  1285. kvp: TKeyValuePair;
  1286. begin
  1287. kvp.Key := aKey;
  1288. i := fHashSetRef.IndexOf(kvp);
  1289. if (i < 0)
  1290. then FillByte(result{%H-}, SizeOf(result), 0)
  1291. else result := fHashSetRef[i].Value;
  1292. end;
  1293. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1294. function TutlCustomMap.GetValueAt(const aIndex: Integer): TValue;
  1295. begin
  1296. result := fHashSetRef[aIndex].Value;
  1297. end;
  1298. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1299. function TutlCustomMap.GetCount: Integer;
  1300. begin
  1301. result := fHashSetRef.Count;
  1302. end;
  1303. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1304. function TutlCustomMap.GetIsEmpty: Boolean;
  1305. begin
  1306. result := fHashSetRef.IsEmpty;
  1307. end;
  1308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1309. function TutlCustomMap.GetCapacity: Integer;
  1310. begin
  1311. result := fHashSetRef.Capacity;
  1312. end;
  1313. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1314. function TutlCustomMap.GetCanShrink: Boolean;
  1315. begin
  1316. result := fHashSetRef.CanShrink;
  1317. end;
  1318. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1319. function TutlCustomMap.GetCanExpand: Boolean;
  1320. begin
  1321. result := fHashSetRef.CanExpand;
  1322. end;
  1323. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1324. procedure TutlCustomMap.SetCapacity(const aValue: Integer);
  1325. begin
  1326. fHashSetRef.Capacity := aValue;
  1327. end;
  1328. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1329. procedure TutlCustomMap.SetCanShrink(const aValue: Boolean);
  1330. begin
  1331. fHashSetRef.CanShrink := aValue;
  1332. end;
  1333. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1334. procedure TutlCustomMap.SetCanExpand(const aValue: Boolean);
  1335. begin
  1336. fHashSetRef.CanExpand := aValue;
  1337. end;
  1338. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1339. procedure TutlCustomMap.SetValue(aKey: TKey; const aValue: TValue);
  1340. var
  1341. i: Integer;
  1342. kvp: TKeyValuePair;
  1343. begin
  1344. kvp.Key := aKey;
  1345. kvp.Value := aValue;
  1346. i := fHashSetRef.IndexOf(kvp);
  1347. if (i < 0) then begin
  1348. if not fAutoCreate then
  1349. raise EInvalidOperation.Create('key not found');
  1350. fHashSetRef.Add(kvp);
  1351. end else
  1352. fHashSetRef[i] := kvp;
  1353. end;
  1354. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1355. procedure TutlCustomMap.SetValueAt(const aIndex: Integer; const aValue: TValue);
  1356. var
  1357. kvp: TKeyValuePair;
  1358. begin
  1359. kvp := fHashSetRef[aIndex];
  1360. kvp.Value := aValue;
  1361. fHashSetRef[aIndex] := kvp;
  1362. end;
  1363. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1364. function TutlCustomMap.GetEnumerator: IValueEnumerator;
  1365. begin
  1366. result := TValueEnumerator.Create(fHashSetRef.GetUtlEnumerator);
  1367. end;
  1368. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1369. function TutlCustomMap.GetUtlEnumerator: IutlValueEnumerator;
  1370. begin
  1371. result := TValueEnumerator.Create(fHashSetRef.GetUtlEnumerator);
  1372. end;
  1373. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1374. procedure TutlCustomMap.Add(constref aKey: TKey; constref aValue: TValue);
  1375. begin
  1376. if not TryAdd(aKey, aValue) then
  1377. raise EInvalidOperation.Create('key already exists');
  1378. end;
  1379. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1380. function TutlCustomMap.TryAdd(constref aKey: TKey; constref aValue: TValue): Boolean;
  1381. var
  1382. kvp: TKeyValuePair;
  1383. begin
  1384. kvp.Key := aKey;
  1385. kvp.Value := aValue;
  1386. result := fHashSetRef.Add(kvp);
  1387. end;
  1388. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1389. function TutlCustomMap.TryGetValue(constref aKey: TKey; out aValue: TValue): Boolean;
  1390. var
  1391. i: Integer;
  1392. begin
  1393. i := IndexOf(aKey);
  1394. result := (i >= 0);
  1395. if result
  1396. then aValue := fHashSetRef[i].Value
  1397. else FillByte(result, SizeOf(result), 0);
  1398. end;
  1399. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1400. function TutlCustomMap.IndexOf(constref aKey: TKey): Integer;
  1401. var
  1402. kvp: TKeyValuePair;
  1403. begin
  1404. kvp.Key := aKey;
  1405. result := fHashSetRef.IndexOf(kvp);
  1406. end;
  1407. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1408. function TutlCustomMap.Contains(constref aKey: TKey): Boolean;
  1409. begin
  1410. result := (IndexOf(aKey) >= 0);
  1411. end;
  1412. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1413. function TutlCustomMap.Remove(constref aKey: TKey; const aFreeItem: Boolean): Boolean;
  1414. var
  1415. kvp: TKeyValuePair;
  1416. begin
  1417. kvp.Key := aKey;
  1418. result := fHashSetRef.Remove(kvp, aFreeItem);
  1419. end;
  1420. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1421. procedure TutlCustomMap.Delete(constref aKey: TKey);
  1422. begin
  1423. if not Remove(aKey) then
  1424. raise EInvalidOperation.Create('key not found');
  1425. end;
  1426. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1427. procedure TutlCustomMap.DeleteAt(const aIndex: Integer);
  1428. begin
  1429. fHashSetRef.Delete(aIndex);
  1430. end;
  1431. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1432. procedure TutlCustomMap.Clear;
  1433. begin
  1434. fHashSetRef.Clear;
  1435. end;
  1436. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1437. constructor TutlCustomMap.Create(
  1438. const aHashSet: THashSet;
  1439. const aOwnsKeys: Boolean;
  1440. const aOwnsValues: Boolean);
  1441. begin
  1442. if not Assigned(aHashSet) then
  1443. EArgumentNilException.Create('aHashSet');
  1444. inherited Create;
  1445. fAutoCreate := false;
  1446. fHashSetRef := aHashSet;
  1447. fOwnsKeys := aOwnsKeys;
  1448. fOwnsValues := aOwnsValues;
  1449. fKeyCollection := TKeyCollection.Create(fHashSetRef);
  1450. fKeyValuePairCollection := TKeyValuePairCollection.Create(fHashSetRef);
  1451. end;
  1452. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1453. destructor TutlCustomMap.Destroy;
  1454. begin
  1455. Clear;
  1456. FreeAndNil(fKeyValuePairCollection);
  1457. FreeAndNil(fKeyCollection);
  1458. fHashSetRef := nil;
  1459. inherited Destroy;
  1460. end;
  1461. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1462. //TutlMap///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1463. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1464. constructor TutlMap.Create(const aOwnsKeys: Boolean; const aOwnsValues: Boolean);
  1465. begin
  1466. fHashSetImpl := THashSet.Create(self, TKeyValuePairComparer.Create(TComparer.Create));
  1467. inherited Create(fHashSetImpl, aOwnsKeys, aOwnsValues);
  1468. end;
  1469. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1470. destructor TutlMap.Destroy;
  1471. begin
  1472. inherited Destroy;
  1473. FreeAndNil(fHashSetImpl);
  1474. end;
  1475. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1476. //TutlHandleManager.TPriorityItem////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1477. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1478. function TutlHandleManager.TPriorityItem.GetHandleEntry(const aIndex: TIndex): PHandleEntry;
  1479. begin
  1480. if (aIndex > HighIndex(Handles)) then
  1481. Grow;
  1482. result := @Handles[aIndex];
  1483. end;
  1484. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1485. procedure TutlHandleManager.TPriorityItem.Grow(const aSize: Integer);
  1486. var
  1487. oldIdx, newIdx, i: TIndex;
  1488. begin
  1489. oldIdx := Length(Handles);
  1490. if (aSize = 0) then
  1491. SetLength(Handles, Length(Handles) + GROW_SIZE)
  1492. else if (Length(Handles) >= aSize) then
  1493. exit
  1494. else
  1495. SetLength(Handles, aSize);
  1496. newIdx := High(Handles);
  1497. for i := oldIdx to newIdx do begin
  1498. FillByte(Handles[i].Data, SizeOf(T), 0);
  1499. Handles[i].Counter := 0;
  1500. Handles[i].Status := ENTRY_STATUS_UNKNOWN;
  1501. Handles[i].Next := High(TIndex);
  1502. Handles[i].Prev := High(TIndex);
  1503. PushBackFreeIndex(i);
  1504. end;
  1505. end;
  1506. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1507. procedure TutlHandleManager.TPriorityItem.PushFront(const aIndex: TIndex; var FirstIndex, LastIndex: TIndex;
  1508. const aStatus: TEntryStatus);
  1509. begin
  1510. Assert(aIndex <= HighIndex(Handles));
  1511. Assert(Handles[aIndex].Status = ENTRY_STATUS_UNKNOWN);
  1512. if (FirstIndex <> UNKNOWN_INDEX)
  1513. and (FirstIndex <= HighIndex(Handles))
  1514. and (Handles[FirstIndex].Status = aStatus)
  1515. then
  1516. Handles[FirstIndex].Prev := aIndex;
  1517. with Handles[aIndex] do begin
  1518. Prev := UNKNOWN_INDEX;
  1519. Next := FirstIndex;
  1520. Status := aStatus;
  1521. end;
  1522. FirstIndex := aIndex;
  1523. if (LastIndex = UNKNOWN_INDEX) then
  1524. LastIndex := aIndex;
  1525. end;
  1526. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1527. procedure TutlHandleManager.TPriorityItem.PushBack(const aIndex: TIndex; var FirstIndex, LastIndex: TIndex;
  1528. const aStatus: TEntryStatus);
  1529. begin
  1530. Assert(aIndex <= HighIndex(Handles));
  1531. Assert(Handles[aIndex].Status = ENTRY_STATUS_UNKNOWN);
  1532. if (LastIndex <> UNKNOWN_INDEX)
  1533. and (LastIndex <= HighIndex(Handles))
  1534. and (Handles[LastIndex].Status = aStatus)
  1535. then
  1536. Handles[LastIndex].Next := aIndex;
  1537. with Handles[aIndex] do begin
  1538. Prev := LastIndex;
  1539. Next := UNKNOWN_INDEX;
  1540. Status := aStatus;
  1541. end;
  1542. LastIndex := aIndex;
  1543. if (FirstIndex = UNKNOWN_INDEX) then
  1544. FirstIndex := aIndex;
  1545. end;
  1546. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1547. procedure TutlHandleManager.TPriorityItem.Remove(const aIndex: TIndex; var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus);
  1548. begin
  1549. Assert(aIndex <> UNKNOWN_INDEX);
  1550. Assert(aIndex <= HighIndex(Handles));
  1551. Assert(Handles[aIndex].Status = aStatus);
  1552. with Handles[aIndex] do begin
  1553. if (Prev <> UNKNOWN_INDEX) then begin
  1554. Assert(Prev <= HighIndex(Handles));
  1555. Handles[Prev].Next := Next;
  1556. end;
  1557. if (Next <> UNKNOWN_INDEX) then begin
  1558. Assert(Next <= HighIndex(Handles));
  1559. Handles[Next].Prev := Prev;
  1560. end;
  1561. if (aIndex = FirstIndex) then
  1562. FirstIndex := Next;
  1563. if (aIndex = LastIndex) then
  1564. LastIndex := Prev;
  1565. Prev := UNKNOWN_INDEX;
  1566. Next := UNKNOWN_INDEX;
  1567. Status := ENTRY_STATUS_UNKNOWN;
  1568. end;
  1569. end;
  1570. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1571. function TutlHandleManager.TPriorityItem.PopFront(var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus; const aCanGrow: Boolean): TIndex;
  1572. begin
  1573. if aCanGrow
  1574. and ( (FirstIndex = UNKNOWN_INDEX)
  1575. or (FirstIndex > HighIndex(Handles)))
  1576. then
  1577. Grow;
  1578. Assert(FirstIndex <> UNKNOWN_INDEX);
  1579. Assert(FirstIndex <= HighIndex(Handles));
  1580. Assert(Handles[FirstIndex].Status = aStatus);
  1581. result := FirstIndex;
  1582. with Handles[result] do begin
  1583. if (LastIndex = FirstIndex) then
  1584. LastIndex := Next;
  1585. FirstIndex := Next;
  1586. Prev := UNKNOWN_INDEX;
  1587. Next := UNKNOWN_INDEX;
  1588. Status := ENTRY_STATUS_UNKNOWN;
  1589. end;
  1590. if (FirstIndex <> UNKNOWN_INDEX)
  1591. and (FirstIndex <= HighIndex(Handles))
  1592. and (Handles[FirstIndex].Status = aStatus)
  1593. then
  1594. Handles[FirstIndex].Prev := UNKNOWN_INDEX;
  1595. end;
  1596. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1597. function TutlHandleManager.TPriorityItem.PopBack(var FirstIndex, LastIndex: TIndex; const aStatus: TEntryStatus; const aCanGrow: Boolean): TIndex;
  1598. begin
  1599. if aCanGrow
  1600. and ( (LastIndex <> UNKNOWN_INDEX)
  1601. or (LastIndex <= HighIndex(Handles)))
  1602. then
  1603. Grow;
  1604. Assert(LastIndex <> UNKNOWN_INDEX);
  1605. Assert(LastIndex <= HighIndex(Handles));
  1606. Assert(Handles[LastIndex].Status = aStatus);
  1607. result := LastIndex;
  1608. with Handles[result] do begin
  1609. if (FirstIndex = LastIndex) then
  1610. FirstIndex := Next;
  1611. LastIndex := Prev;
  1612. Prev := UNKNOWN_INDEX;
  1613. Next := UNKNOWN_INDEX;
  1614. Status := ENTRY_STATUS_UNKNOWN;
  1615. end;
  1616. if (LastIndex <> UNKNOWN_INDEX)
  1617. and (LastIndex <= HighIndex(Handles))
  1618. and (Handles[LastIndex].Status = aStatus)
  1619. then
  1620. Handles[LastIndex].Next := UNKNOWN_INDEX;
  1621. end;
  1622. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1623. procedure TutlHandleManager.TPriorityItem.PushFrontFreeIndex(const aIndex: TIndex);
  1624. begin
  1625. PushFront(aIndex, FirstFree, LastFree, ENTRY_STATUS_FREE);
  1626. end;
  1627. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1628. procedure TutlHandleManager.TPriorityItem.PushBackFreeIndex(const aIndex: TIndex);
  1629. begin
  1630. PushBack(aIndex, FirstFree, LastFree, ENTRY_STATUS_FREE);
  1631. end;
  1632. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1633. procedure TutlHandleManager.TPriorityItem.PushFrontUsedIndex(const aIndex: TIndex);
  1634. begin
  1635. PushFront(aIndex, FirstUsed, LastUsed, ENTRY_STATUS_USED);
  1636. end;
  1637. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1638. procedure TutlHandleManager.TPriorityItem.PushBackUsedIndex(const aIndex: TIndex);
  1639. begin
  1640. PushBack(aIndex, FirstUsed, LastUsed, ENTRY_STATUS_USED);
  1641. end;
  1642. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1643. procedure TutlHandleManager.TPriorityItem.RemoveFreeIndex(const aIndex: TIndex);
  1644. begin
  1645. Remove(aIndex, FirstFree, LastFree, ENTRY_STATUS_FREE);
  1646. end;
  1647. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1648. procedure TutlHandleManager.TPriorityItem.RemoveUsedIndex(const aIndex: TIndex);
  1649. begin
  1650. Remove(aIndex, FirstUsed, LastUsed, ENTRY_STATUS_USED);
  1651. end;
  1652. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1653. function TutlHandleManager.TPriorityItem.PopFrontFreeIndex: TIndex;
  1654. begin
  1655. result := PopFront(FirstFree, LastFree, ENTRY_STATUS_FREE, true);
  1656. end;
  1657. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1658. function TutlHandleManager.TPriorityItem.PopBackFreeIndex: TIndex;
  1659. begin
  1660. result := PopBack(FirstFree, LastFree, ENTRY_STATUS_FREE, false);
  1661. end;
  1662. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1663. function TutlHandleManager.TPriorityItem.PopFrontUsedIndex: TIndex;
  1664. begin
  1665. result := PopFront(FirstUsed, LastUsed, ENTRY_STATUS_USED, false);
  1666. end;
  1667. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1668. function TutlHandleManager.TPriorityItem.PopBackUsedIndex: TIndex;
  1669. begin
  1670. result := PopBack(FirstUsed, LastUsed, ENTRY_STATUS_USED, false);
  1671. end;
  1672. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1673. //TutlHandleManager.TEnumerator//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1674. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1675. function TutlHandleManager.TEnumerator.InternalMoveNext: Boolean;
  1676. var
  1677. p: PPriorityItem;
  1678. begin
  1679. repeat
  1680. if (fIndex = UNKNOWN_INDEX) then begin
  1681. inc(fPriority);
  1682. if (fPriority > High(fOwner.fItems)) then
  1683. break;
  1684. p := fOwner.GetPriorityItem(fPriority);
  1685. fIndex := p^.FirstUsed;
  1686. end else begin
  1687. p := fOwner.GetPriorityItem(fPriority);
  1688. fIndex := p^.Handles[fIndex].Next;
  1689. if (fIndex > HighIndex(p^.Handles)) then
  1690. fIndex := UNKNOWN_INDEX;
  1691. end;
  1692. until (fPriority > High(fOwner.fItems)) or (fIndex <> UNKNOWN_INDEX);
  1693. result := (fPriority <= High(fOwner.fItems));
  1694. if result then begin
  1695. p := fOwner.GetPriorityItem(fPriority);
  1696. fHandle := p^.Handles[fIndex];
  1697. end else
  1698. FillByte(fHandle, SizeOf(fHandle), 0);
  1699. end;
  1700. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1701. procedure TutlHandleManager.TEnumerator.InternalReset;
  1702. begin
  1703. fPriority := -1;
  1704. fIndex := UNKNOWN_INDEX;
  1705. end;
  1706. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1707. function TutlHandleManager.TEnumerator.GetCurrent: TutlHandle;
  1708. begin
  1709. if (fHandle.Status <> ENTRY_STATUS_USED) then
  1710. raise EInvalidOperation.Create('enumerator not initialized or collection changed');
  1711. with THandleData(result) do begin
  1712. Index := fIndex;
  1713. Counter := fHandle.Counter;
  1714. TypeID := fHandle.TypeID;
  1715. Priority := fPriority;
  1716. end;
  1717. end;
  1718. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1719. constructor TutlHandleManager.TEnumerator.Create(const aOwner: TutlHandleManager);
  1720. begin
  1721. if not Assigned(aOwner) then
  1722. raise EArgumentNilException.Create('aOwner');
  1723. inherited Create;
  1724. fOwner := aOwner;
  1725. FillByte(fHandle, SizeOf(fHandle), 0);
  1726. end;
  1727. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1728. //TutlHandleManager Class Methods////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1729. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1730. class function TutlHandleManager.HighIndex(constref aEntries: THandleEntries): TIndex;
  1731. begin
  1732. result := TIndex(High(aEntries));
  1733. end;
  1734. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1735. //TutlHandleManager//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1736. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1737. function TutlHandleManager.GetPriorityItem(const aPriority: Byte): PPriorityItem;
  1738. var
  1739. oldIdx, newIdx, i: Integer;
  1740. begin
  1741. if (aPriority > High(fItems)) then begin
  1742. oldIdx := Length(fItems);
  1743. SetLength(fItems, aPriority + 1);
  1744. newIdx := High(fItems);
  1745. for i := oldIdx to newIdx do with fItems[i] do begin
  1746. FirstFree := UNKNOWN_INDEX;
  1747. LastFree := UNKNOWN_INDEX;
  1748. FirstUsed := UNKNOWN_INDEX;
  1749. LastUsed := UNKNOWN_INDEX;
  1750. SetLength(Handles, 0);
  1751. end;
  1752. end;
  1753. result := @fItems[aPriority];
  1754. end;
  1755. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1756. function TutlHandleManager.GetValue(const aHandle: TutlHandle): T;
  1757. begin
  1758. if not TryGetValue(aHandle, result) then
  1759. raise EArgumentException.Create('unknown or invalid handle');
  1760. end;
  1761. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1762. function TutlHandleManager.TryGetValue(const aHandle: TutlHandle; out aData: T): Boolean;
  1763. begin
  1764. result := IsValid(aHandle);
  1765. with THandleData(aHandle) do begin
  1766. if result
  1767. then aData := fItems[Priority].Handles[Index].Data
  1768. else FillByte(aData, SizeOf(T), 0);
  1769. end;
  1770. end;
  1771. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1772. procedure TutlHandleManager.SetValue(const aHandle: TutlHandle; aData: T);
  1773. begin
  1774. if not TrySetValue(aHandle, aData) then
  1775. raise EArgumentException.Create('unknown or invalid handle');
  1776. end;
  1777. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1778. function TutlHandleManager.TrySetValue(const aHandle: TutlHandle; aData: T): Boolean;
  1779. var
  1780. p: PPriorityItem;
  1781. h: PHandleEntry;
  1782. begin
  1783. if not IsValid(aHandle) then with THandleData(aHandle) do begin
  1784. p := GetPriorityItem(Priority);
  1785. p^.Grow(Index + 1);
  1786. h := p^.GetHandleEntry(Index);
  1787. result := (h^.Status = ENTRY_STATUS_FREE);
  1788. if result then begin
  1789. p^.RemoveFreeIndex (Index);
  1790. p^.PushBackUsedIndex(Index);
  1791. h^.Counter := Counter;
  1792. h^.Data := aData;
  1793. h^.TypeID := TypeID;
  1794. end;
  1795. end else
  1796. Update(aHandle, aData);
  1797. end;
  1798. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1799. function TutlHandleManager.Add(const aTypeID: Byte; const aPriority: Byte; constref aData: T): TutlHandle;
  1800. var
  1801. p: PPriorityItem;
  1802. h: PHandleEntry;
  1803. i: Integer;
  1804. begin
  1805. p := GetPriorityItem(aPriority);
  1806. i := p^.PopFrontFreeIndex;
  1807. p^.PushBackUsedIndex(i);
  1808. h := p^.GetHandleEntry(i);
  1809. h^.TypeID := aTypeID;
  1810. h^.Counter := h^.Counter + 1;
  1811. if (h^.Counter = 0) then
  1812. h^.Counter := 1;
  1813. h^.Data := aData;
  1814. with THandleData(result) do begin
  1815. Index := i;
  1816. Counter := h^.Counter;
  1817. TypeID := aTypeID;
  1818. Priority := aPriority;
  1819. end;
  1820. inc(fCount);
  1821. end;
  1822. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1823. function TutlHandleManager.IsValid(const aHandle: TutlHandle): Boolean;
  1824. begin
  1825. with THandleData(aHandle) do begin
  1826. result := (Priority <= High(fItems))
  1827. and (Index <= HighIndex(fItems[Priority].Handles))
  1828. and (fItems[Priority].Handles[Index].Counter = Counter)
  1829. and (fItems[Priority].Handles[Index].Status = ENTRY_STATUS_USED);
  1830. end;
  1831. end;
  1832. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1833. procedure TutlHandleManager.Update(const aHandle: TutlHandle; aData: T);
  1834. begin
  1835. if not IsValid(aHandle) then
  1836. raise EArgumentException.Create('unknown or invalid handle');
  1837. with THandleData(aHandle) do begin
  1838. if not utlFinalizeObject(fItems[Priority].Handles[Index].Data, TypeInfo(T), fOwnsValues) then
  1839. Finalize(fItems[Priority].Handles[Index].Data);
  1840. fItems[Priority].Handles[Index].Data := aData;
  1841. end;
  1842. end;
  1843. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1844. function TutlHandleManager.Remove(const aHandle: TutlHandle): Boolean;
  1845. var
  1846. p: PPriorityItem;
  1847. begin
  1848. result := IsValid(aHandle);
  1849. if not result then
  1850. exit;
  1851. with THandleData(aHandle) do begin
  1852. p := GetPriorityItem(Priority);
  1853. p^.RemoveUsedIndex(Index);
  1854. p^.PushFrontFreeIndex(Index);
  1855. end;
  1856. dec(fCount);
  1857. end;
  1858. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1859. procedure TutlHandleManager.Delete(const aHandle: TutlHandle);
  1860. begin
  1861. if not Remove(aHandle) then
  1862. raise EArgumentException.Create('unknown or invalid handle');
  1863. end;
  1864. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1865. procedure TutlHandleManager.Clear;
  1866. var
  1867. i, j: Integer;
  1868. begin
  1869. for j := low(fItems) to high(fItems) do begin
  1870. for i := low(fItems[j].Handles) to high(fItems[j].Handles) do begin
  1871. if (fItems[j].Handles[i].Status = ENTRY_STATUS_USED) and not utlFinalizeObject(fItems[j].Handles[i].Data, TypeInfo(T), fOwnsValues) then
  1872. Finalize(fItems[j].Handles[i].Data);
  1873. FillByte(fItems[j].Handles[i].Data, SizeOf(T), 0);
  1874. end;
  1875. SetLength(fItems[j].Handles, 0);
  1876. end;
  1877. SetLength(fItems, 0);
  1878. end;
  1879. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1880. function TutlHandleManager.GetEnumerator: IEnumerator;
  1881. begin
  1882. result := TEnumerator.Create(self);
  1883. end;
  1884. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1885. function TutlHandleManager.GetUtlEnumerator: IutlEnumerator;
  1886. begin
  1887. result := TEnumerator.Create(self);
  1888. end;
  1889. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1890. constructor TutlHandleManager.Create(const aOwnsValues: Boolean);
  1891. begin
  1892. inherited Create;
  1893. fCount := 0;
  1894. fOwnsValues := aOwnsValues;
  1895. end;
  1896. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1897. destructor TutlHandleManager.Destroy;
  1898. begin
  1899. // Clear;
  1900. inherited Destroy;
  1901. end;
  1902. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1903. class function TutlHandleManager.GetTypeID(const aHandle: TutlHandle): Byte;
  1904. begin
  1905. result := THandleData(aHandle).TypeID;
  1906. end;
  1907. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1908. class function TutlHandleManager.GetPriority(const aHandle: TutlHandle): Byte;
  1909. begin
  1910. result := THandleData(aHandle).Priority;
  1911. end;
  1912. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1913. //EutlEnumConvert///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1914. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1915. constructor EEnumConvertException.Create(const aValue, aExpectedType: String);
  1916. begin
  1917. inherited Create(Format('%s is not a %s', [aValue, aExpectedType]));
  1918. end;
  1919. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1920. //TutlEnumHelperBase////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1921. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1922. class procedure TutlEnumHelperBase.RegisterType(const aValues: TIntArray; const aNames: TStringArray);
  1923. begin
  1924. fValuesMap.Add(ClassName, aValues);
  1925. fNamesMap.Add (ClassName, aNames);
  1926. end;
  1927. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1928. class procedure TutlEnumHelperBase.UnregisterType;
  1929. begin
  1930. fValuesMap.Remove(ClassName);
  1931. fNamesMap.Remove(ClassName);
  1932. end;
  1933. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1934. class function TutlEnumHelperBase.ToString(const aValue: Integer; const aAllowOrd: Boolean = false): String;
  1935. var
  1936. i: Integer;
  1937. iArr: TIntArray;
  1938. sArr: TStringArray;
  1939. begin
  1940. iArr := fValuesMap[ClassName];
  1941. sArr := fNamesMap[ClassName];
  1942. for i := low(iArr) to high(iArr) do begin
  1943. if (iArr[i] = aValue) then begin
  1944. result := sArr[i];
  1945. exit;
  1946. end;
  1947. end;
  1948. if aAllowOrd
  1949. then result := IntToStr(aValue)
  1950. else result := '';
  1951. end;
  1952. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1953. class function TutlEnumHelperBase.TryToEnum(const aStr: String; out aValue: Integer; const aAllowOrd: Boolean = false): Boolean;
  1954. var
  1955. i: Integer;
  1956. iArr: TIntArray;
  1957. sArr: TStringArray;
  1958. begin
  1959. iArr := fValuesMap[ClassName];
  1960. sArr := fNamesMap[ClassName];
  1961. for i := low(sArr) to high(sArr) do begin
  1962. if (sArr[i] = aStr) then begin
  1963. result := true;
  1964. aValue := iArr[i];
  1965. exit;
  1966. end;
  1967. end;
  1968. if aAllowOrd
  1969. then result := TryStrToInt(aStr, aValue)
  1970. else result := false;
  1971. end;
  1972. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1973. class function TutlEnumHelperBase.ToEnum(const aStr: String; const aAllowOrd: Boolean): Integer;
  1974. begin
  1975. if not TryToEnum(aStr, result, aAllowOrd) then
  1976. raise EConvertError.Create(aStr + ' is an unknown enum value');
  1977. end;
  1978. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1979. class function TutlEnumHelperBase.ToEnum(const aStr: String; const aDefault: Integer; const aAllowOrd: Boolean): Integer;
  1980. begin
  1981. if not TryToEnum(aStr, result, aAllowOrd) then
  1982. result := aDefault;
  1983. end;
  1984. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1985. class function TutlEnumHelperBase.IntValues: TIntArray;
  1986. begin
  1987. result := fValuesMap[ClassName];
  1988. end;
  1989. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1990. class function TutlEnumHelperBase.Names: TStringArray;
  1991. begin
  1992. result := fNamesMap[ClassName];
  1993. end;
  1994. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1995. class constructor TutlEnumHelperBase.Initialize;
  1996. begin
  1997. fNamesMap := TNamesMap.Create(true, true);
  1998. fValuesMap := TValuesMap.Create(true, true);
  1999. end;
  2000. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2001. class destructor TutlEnumHelperBase.Finalize;
  2002. begin
  2003. FreeAndNil(fNamesMap);
  2004. FreeAndNil(fValuesMap);
  2005. end;
  2006. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2007. //TutlEnumHelper////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2008. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2009. class function TutlEnumHelper.ToString(const aValue: T; const aAllowOrd: Boolean): String;
  2010. begin
  2011. {$Push}
  2012. {$IOChecks OFF}
  2013. WriteStr(Result, aValue);
  2014. if IOResult = 107 then
  2015. Result := '';
  2016. {$Pop}
  2017. end;
  2018. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2019. class function TutlEnumHelper.TryToEnum(const aStr: String; out aValue: T; const aAllowOrd: Boolean): Boolean;
  2020. var
  2021. a: T;
  2022. i: Integer;
  2023. begin
  2024. a := T(0);
  2025. Result := false;
  2026. if Length(aStr) = 0 then
  2027. exit;
  2028. {$Push}
  2029. {$IOChecks OFF}
  2030. ReadStr(aStr, a);
  2031. Result := IOResult <> 106;
  2032. {$Pop}
  2033. if Result then
  2034. aValue := a
  2035. else if aAllowOrd then begin
  2036. result := TryStrToInt(aStr, i);
  2037. if result then
  2038. aValue := T(i);
  2039. end;
  2040. end;
  2041. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2042. class function TutlEnumHelper.ToEnum(const aStr: String; const aAllowOrd: Boolean): T;
  2043. begin
  2044. if not TryToEnum(aStr, result, aAllowOrd) then
  2045. raise EEnumConvertException.Create(aStr, TypeInfo^.Name);
  2046. end;
  2047. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2048. class function TutlEnumHelper.ToEnum(const aStr: String; const aDefault: T; const aAllowOrd: Boolean): T;
  2049. begin
  2050. if not TryToEnum(aStr, result, aAllowOrd) then
  2051. result := aDefault;
  2052. end;
  2053. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2054. class function TutlEnumHelper.Values: TValueArray;
  2055. begin
  2056. result := fValues;
  2057. end;
  2058. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2059. class function TutlEnumHelper.IntValues: TIntArray;
  2060. begin
  2061. result := fIntValues;
  2062. end;
  2063. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2064. class function TutlEnumHelper.Names: TStringArray;
  2065. begin
  2066. result := fNames;
  2067. end;
  2068. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2069. class function TutlEnumHelper.TypeInfo: PTypeInfo;
  2070. begin
  2071. result := fTypeInfo;
  2072. end;
  2073. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2074. class constructor TutlEnumHelper.Initialize;
  2075. var
  2076. tiArray: PTypeInfo;
  2077. tdArray, tdEnum: PTypeData;
  2078. PName: PShortString;
  2079. i: integer;
  2080. en: T;
  2081. sl: TStringList;
  2082. begin
  2083. {
  2084. See FPC Bug http://bugs.freepascal.org/view.php?id=27622
  2085. For Sparse Enums, the compiler won't give us TypeInfo, because it contains some wrong data. This is
  2086. safe, but sadly we don't even get the *correct* fields (TypeName, NameList), even though they are
  2087. generated in any case.
  2088. Fortunately, arrays do know this type info segment as their Element Type (and we declared one anyway).
  2089. }
  2090. tiArray := System.TypeInfo(TValueArray);
  2091. tdArray := GetTypeData(tiArray);
  2092. fTypeInfo := tdArray^.elType2;
  2093. {
  2094. Now that we have the TypeInfo, fill our values from it. This is safe because while the *values* in
  2095. TypeData are wrong for Sparse Enums, the *PName* are always correct.
  2096. }
  2097. tdEnum := GetTypeData(FTypeInfo);
  2098. PName := @tdEnum^.NameList;
  2099. sl := TStringList.Create;
  2100. try
  2101. while Length(PName^) > 0 do begin
  2102. {
  2103. Memory layout for TTypeData has the declaring EnumUnitName after the last NameList entry.
  2104. This can normally not be the same as a valid enum value, because it is in the same identifier
  2105. namespace. However, with scoped enums we might have the same name for module and element, because
  2106. the full identifier for the element would be TypeName.ElementName.
  2107. In either case, the next PShortString will point to a zero-length string, and the loop is left
  2108. with the last element being invalid (either empty or whatever value the unit-named element has).
  2109. }
  2110. sl.Add(PName^);
  2111. if TryToEnum(PName^, en) then
  2112. sl.Objects[sl.Count-1] := TObject({%H-}Pointer(PtrUInt(en)));
  2113. inc(PByte(PName), Length(PName^) + 1);
  2114. end;
  2115. sl.Delete(sl.Count-1); // remove the EnumUnitName item
  2116. SetLength(fValues, sl.Count);
  2117. SetLength(fIntValues, sl.Count);
  2118. SetLength(fNames, sl.Count);
  2119. for i := 0 to sl.Count-1 do begin
  2120. fNames[i] := sl[i];
  2121. fValues[i] := T(PtrUInt(sl.Objects[i]));
  2122. fIntValues[i] := Integer(fValues[i]);
  2123. end;
  2124. finally
  2125. FreeAndNil(sl);
  2126. end;
  2127. RegisterType(fIntValues, fNames);
  2128. end;
  2129. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2130. class destructor TutlEnumHelper.Finalize;
  2131. begin
  2132. Finalize(fNames);
  2133. Finalize(fValues);
  2134. Finalize(fIntValues);
  2135. UnregisterType;
  2136. end;
  2137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2138. //TutlSetHelperBase/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2139. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2140. class function TutlSetHelperBase.IsSet(const aSet; const aSize: Integer; const aValue: Integer): Boolean;
  2141. begin
  2142. if (aValue >= 8*aSize) then
  2143. raise EOutOfRangeException.Create(aValue, 0, 8*aSize-1);
  2144. result := ((PByte(@aSet)[aValue shr 3] and (1 shl (aValue and 7))) <> 0);
  2145. end;
  2146. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2147. class procedure TutlSetHelperBase.SetValue(var aSet; const aSize: Integer; const aValue: Integer);
  2148. begin
  2149. if (aValue >= 8*aSize) then
  2150. raise EOutOfRangeException.Create(aValue, 0, 8*aSize-1);
  2151. PByte(@aSet)[aValue shr 3] := PByte(@aSet)[aValue shr 3] or (1 shl (aValue and 7));
  2152. end;
  2153. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2154. class procedure TutlSetHelperBase.ClearValue(var aSet; const aSize: Integer; const aValue: Integer);
  2155. begin
  2156. if (aValue >= 8*aSize) then
  2157. raise EOutOfRangeException.Create(aValue, 0, 8*aSize-1);
  2158. PByte(@aSet)[aValue shr 3] := PByte(@aSet)[aValue shr 3] and not (1 shl (aValue and 7));
  2159. end;
  2160. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2161. class procedure TutlSetHelperBase.RegisterEnumHelper(const aHelper: TutlEnumHelperBaseClass);
  2162. begin
  2163. fEnumHelpers.Add(ClassName, aHelper);
  2164. end;
  2165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2166. class procedure TutlSetHelperBase.UnregisterEnumHelper;
  2167. begin
  2168. fEnumHelpers.Remove(ClassName);
  2169. end;
  2170. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2171. class function TutlSetHelperBase.ToString(const aSet; const aSize: Integer; const aSeparator: String;
  2172. const aAllowOrd: Boolean): String;
  2173. var
  2174. i: Integer;
  2175. h: TutlEnumHelperBaseClass;
  2176. arr: TutlEnumHelperBase.TIntArray;
  2177. begin
  2178. h := EnumHelper;
  2179. if not Assigned(h) then
  2180. raise EInvalidOperation.Create('enum helper class is not set');
  2181. result := '';
  2182. arr := h.IntValues;
  2183. for i in arr do begin
  2184. if IsSet(aSet, aSize, i) then begin
  2185. if result > '' then
  2186. result := result + aSeparator;
  2187. result := result + h.ToString(i, aAllowOrd);
  2188. end;
  2189. end;
  2190. end;
  2191. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2192. class function TutlSetHelperBase.TryToSet(const aStr: String; out aSet; const aSize: Integer; const aAllowOrd: Boolean): Boolean;
  2193. begin
  2194. result := TryToSet(aStr, ',', aSet, aSize, aAllowOrd);
  2195. end;
  2196. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2197. class function TutlSetHelperBase.TryToSet(const aStr: String; const aSeparator: String; out aSet; const aSize: Integer; const aAllowOrd: Boolean): Boolean;
  2198. var
  2199. i, j, e: Integer;
  2200. s: String;
  2201. h: TutlEnumHelperBaseClass;
  2202. begin
  2203. if (aSeparator = '') then
  2204. raise EArgumentException.Create('''aSeparator'' can not be empty');
  2205. h := EnumHelper;
  2206. if not Assigned(h) then
  2207. raise EInvalidOperation.Create('enum helper class is not set');
  2208. result := true;
  2209. i := 1;
  2210. j := 1;
  2211. FillByte(aSet{%H-}, aSize, 0);
  2212. while (i <= Length(aStr)) do begin
  2213. if (Copy(aStr, i, Length(aSeparator)) = aSeparator) then begin
  2214. s := Trim(copy(aStr, j, i - j));
  2215. if (s <> '') then begin
  2216. result := result and h.TryToEnum(s, e);
  2217. if not result then
  2218. exit;
  2219. SetValue(aSet, aSize, e);
  2220. j := i + Length(aSeparator);
  2221. end;
  2222. end;
  2223. inc(i);
  2224. end;
  2225. s := Trim(copy(aStr, j, i - j));
  2226. if (s <> '') then begin
  2227. result := result and h.TryToEnum(s, e);
  2228. if not result then
  2229. exit;
  2230. SetValue(aSet, aSize, e);
  2231. end;
  2232. end;
  2233. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2234. class function TutlSetHelperBase.Compare(const aSet1; const aSet2; const aSize: Integer): Integer;
  2235. var
  2236. e: Integer;
  2237. h: TutlEnumHelperBaseClass;
  2238. begin
  2239. h := EnumHelper;
  2240. if not Assigned(h) then
  2241. raise EInvalidOperation.Create('enum helper class is not set');
  2242. result := 0;
  2243. for e in h.IntValues do begin
  2244. if IsSet(aSet1, aSize, e) and not IsSet(aSet2, aSize, e) then begin
  2245. result := 1;
  2246. break;
  2247. end else
  2248. if not IsSet(aSet1, aSize, e) and IsSet(aSet2, aSize, e) then begin
  2249. result := -1;
  2250. break;
  2251. end;
  2252. end;
  2253. end;
  2254. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2255. class function TutlSetHelperBase.EnumHelper: TutlEnumHelperBaseClass;
  2256. begin
  2257. result := fEnumHelpers[ClassName];
  2258. end;
  2259. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2260. class constructor TutlSetHelperBase.Initialize;
  2261. begin
  2262. fEnumHelpers := TEnumHelperMap.Create(true, true);
  2263. end;
  2264. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2265. class destructor TutlSetHelperBase.Finalize;
  2266. begin
  2267. FreeAndNil(fEnumHelpers);
  2268. end;
  2269. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2270. //TutlSetHelper/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2271. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2272. class function TutlSetHelper.ToString(const aValue: TSet; const aSeparator: String; const aAllowOrd: Boolean): String;
  2273. begin
  2274. result := ToString(aValue, SizeOf(aValue), aSeparator, aAllowOrd);
  2275. end;
  2276. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2277. class function TutlSetHelper.TryToSet(const aStr: String; out aValue: TSet; const aAllowOrd: Boolean): Boolean;
  2278. begin
  2279. result := TryToSet(aStr, ',', aValue, SizeOf(aValue), aAllowOrd);
  2280. end;
  2281. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2282. class function TutlSetHelper.TryToSet(const aStr: String; const aSeparator: String; out aValue: TSet; const aAllowOrd: Boolean): Boolean;
  2283. begin
  2284. result := TryToSet(aStr, aSeparator, aValue, SizeOf(aValue), aAllowOrd);
  2285. end;
  2286. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2287. class function TutlSetHelper.ToSet(const aStr: String; const aDefault: TSet; const aAllowOrd: Boolean): TSet;
  2288. begin
  2289. if not TryToSet(aStr, ',', result, SizeOf(result), aAllowOrd) then
  2290. result := aDefault;
  2291. end;
  2292. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2293. class function TutlSetHelper.ToSet(const aStr: String; const aAllowOrd: Boolean): TSet;
  2294. begin
  2295. if not TryToSet(aStr, ',', result, SizeOf(result), aAllowOrd) then
  2296. raise EEnumConvertException.CreateFmt('"%s" is an invalid value', [aStr]);
  2297. end;
  2298. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2299. class function TutlSetHelper.Compare(const aSet1, aSet2: TSet): Integer;
  2300. begin
  2301. result := Compare(aSet1, aSet2, SizeOf(aSet1));
  2302. end;
  2303. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2304. class constructor TutlSetHelper.Initialize;
  2305. begin
  2306. RegisterEnumHelper(TEnumHelper);
  2307. end;
  2308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2309. class destructor TutlSetHelper.Finalize;
  2310. begin
  2311. UnregisterEnumHelper();
  2312. end;
  2313. end.