diff --git a/uutlGenerics.pas b/uutlGenerics.pas index 84fdba3..7193162 100644 --- a/uutlGenerics.pas +++ b/uutlGenerics.pas @@ -477,6 +477,60 @@ type destructor Destroy; override; end; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + TutlHandle = QWord; + generic TutlHandleManager = class(TObject) + private type + THandleData = packed record + case Integer of + 0: ( + Handle: TutlHandle // 0..63 + ); + + 1: ( + Index: Cardinal; // 0..31 + Counter: Word; // 32..47 + Identifier: Word // 48..63 + ); + end; + + THandleEntry = bitpacked record + Data: T; + NextFreeIndex: Cardinal; // 0..31 + Counter: Word; // 32..47 + IsActive: Boolean; // 48 + __reserved: 0..((1 shl 15)-1); // 49..63 + end; + + private + fCount: Integer; + fGrowSize: Integer; + fEntries: array of THandleEntry; + fFirstFreeIndex: Cardinal; + fOwnsValues: Boolean; + + procedure Grow; + + public + function Get (const aHandle: TutlHandle): T; + function TryGet (const aHandle: TutlHandle; out aData: T): Boolean; + function Add (constref aData: T; const aIdentifier: Word): TutlHandle; + function IsValid(const aHandle: TutlHandle): Boolean; inline; + procedure Update (const aHandle: TutlHandle; aData: T); + procedure Remove (const aHandle: TutlHandle); + procedure Clear; + + public + property Items[const aHandle: TutlHandle]: T read Get write Update; + property Count: Integer read fCount; + + constructor Create(const aOwnsValues: Boolean); + destructor Destroy; override; + + public + class function GetIdentifier(const aHandle: TutlHandle): Word; inline; + end; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EEnumConvertException = class(EConvertError) public @@ -515,7 +569,6 @@ type class destructor Finalize; end; - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// generic TutlEnumHelper = class(TutlEnumHelperBase) public type @@ -1628,6 +1681,134 @@ begin FreeAndNil(fHashSetImpl); end; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//TutlHandleManager////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TutlHandleManager.Grow; +var + oldIdx, newIdx, i: Integer; +begin + oldIdx := High(fEntries); + SetLength(fEntries, Length(fEntries) + fGrowSize); + newIdx := High(fEntries); + for i := oldIdx to newIdx do + fEntries[i].NextFreeIndex := i + 1; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +function TutlHandleManager.Get(const aHandle: TutlHandle): T; +begin + if not TryGet(aHandle, result) then + raise EArgumentException.Create('unknown or invalid handle'); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +function TutlHandleManager.TryGet(const aHandle: TutlHandle; out aData: T): Boolean; +begin + with THandleData(aHandle) do begin + result := IsValid(aHandle); + if result + then aData := fEntries[Index].Data + else FillByte(aData, SizeOf(T), 0); + end; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +function TutlHandleManager.Add(constref aData: T; const aIdentifier: Word): TutlHandle; +var + i: Integer; +begin + i := fFirstFreeIndex; + if (High(fEntries) < i) then + Grow; + Assert(not fEntries[i].IsActive); + fFirstFreeIndex := fEntries[i].NextFreeIndex; + fEntries[i].NextFreeIndex := 0; + fEntries[i].Counter := fEntries[i].Counter + 1; + if (fEntries[i].Counter = 0) then + fEntries[i].Counter := 1; + fEntries[i].IsActive := true; + fEntries[i].Data := aData; + inc(fCount); + with THandleData(result) do begin + Index := i; + Counter := fEntries[i].Counter; + Identifier := aIdentifier; + end; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +function TutlHandleManager.IsValid(const aHandle: TutlHandle): Boolean; +begin + with THandleData(aHandle) do begin + result := (fEntries[Index].Counter = Counter) + and (fEntries[Index].IsActive) + and (h.Index <= High(fEntries)); + end; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TutlHandleManager.Update(const aHandle: TutlHandle; aData: T); +begin + if not IsValid(aHandle) then + raise EArgumentException.Create('unknown or invalid handle'); + with THandleData(aHandle) do begin + if not utlFinalizeObject(fEntries[Index].Data, TypeInfo(T), fOwnsValues) then + Finalize(fEntries[Index].Data); + fEntries[Index].Data := aData; + end; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TutlHandleManager.Remove(const aHandle: TutlHandle); +begin + if not IsValid(aHandle) then + raise EArgumentException.Create('unknown or invalid handle'); + with THandleData(aHandle) do begin + fEntries[Index].NextFreeIndex := fFirstFreeIndex; + fEntries[Index].IsActive := false; + fFirstFreeIndex := Index; + if not utlFinalizeObject(fEntries[Index].Data, TypeInfo(T), fOwnsValues) then + Finalize(fEntries[Index].Data); + end; + dec(fCount); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +procedure TutlHandleManager.Clear; +var + i: Integer; +begin + for i := low(fEntries) to high(fEntries) do begin + if fEntries[i].IsActive and not utlFinalizeObject(fEntries[i].Data, TypeInfo(T), fOwnsValues) then + Finalize(fEntries[i].Data); + end; + SetLength(fEntries, 0); +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +constructor TutlHandleManager.Create(const aOwnsValues: Boolean); +begin + inherited Create; + fGrowSize := 50; + fCount := 0; + fFirstFreeIndex := 0; + fOwnsValues := aOwnsValues; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +destructor TutlHandleManager.Destroy; +begin + Clear; + inherited Destroy; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class function TutlHandleManager.GetIdentifier(const aHandle: TutlHandle): Word; +begin + result := THandleData(aHandle).Identifier; +end; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //EutlEnumConvert/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////