{ Copyright (c) 1998-2002 by Florian Klaempfl and Peter Vreman This module provides some basic classes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit cclasses; {$i fpcdefs.inc} {$define CCLASSESINLINE} interface uses {$IFNDEF USE_FAKE_SYSUTILS} SysUtils, {$ELSE} fksysutl, {$ENDIF} globtype, CUtils,CStreams; {******************************************** TMemDebug ********************************************} type tmemdebug = class private totalmem, startmem : int64; infostr : string[40]; public constructor Create(const s:string); destructor Destroy;override; procedure show; procedure start; procedure stop; end; {******************************************************* TFPList (From rtl/objpas/classes/classesh.inc) ********************************************************} const SListIndexError = 'List index exceeds bounds (%d)'; SListCapacityError = 'The maximum list capacity is reached (%d)'; SListCapacityPower2Error = 'The capacity has to be a power of 2, but is set to %d'; SListCountError = 'List count too large (%d)'; type EListError = class(Exception); const MaxListSize = Maxint div 16; type TListSortCompare = function (Item1, Item2: Pointer): Integer; TListCallback = procedure(data,arg:pointer) of object; TListStaticCallback = procedure(data,arg:pointer); TDynStringArray = Array Of String; TDirection = (FromBeginning,FromEnd); TFPList = class(TObject) private FList: PPointer; FCount: Integer; FCapacity: Integer; protected function Get(Index: Integer): Pointer; procedure Put(Index: Integer; Item: Pointer); procedure SetCapacity(NewCapacity: Integer); procedure SetCount(NewCount: Integer); Procedure RaiseIndexError(Index : Integer); property List: PPointer read FList; public destructor Destroy; override; function Add(Item: Pointer): Integer; procedure Clear; procedure Delete(Index: Integer); class procedure Error(const Msg: string; Data: PtrInt); procedure Exchange(Index1, Index2: Integer); function Expand: TFPList; function Extract(item: Pointer): Pointer; function First: Pointer; function IndexOf(Item: Pointer): Integer; function IndexOfItem(Item: Pointer; Direction: TDirection): Integer; procedure Insert(Index: Integer; Item: Pointer); function Last: Pointer; procedure Move(CurIndex, NewIndex: Integer); procedure Assign(Obj:TFPList); function Remove(Item: Pointer): Integer; procedure Pack; procedure Sort(Compare: TListSortCompare); procedure ForEachCall(proc2call:TListCallback;arg:pointer); procedure ForEachCall(proc2call:TListStaticCallback;arg:pointer); property Capacity: Integer read FCapacity write SetCapacity; property Count: Integer read FCount write SetCount; property Items[Index: Integer]: Pointer read Get write Put; default; { Add to list, creating it if required. } class procedure AddOnDemand(var Lst: TFPList; Item: Pointer); static; { FreeAndNil the list, and its items as TObjects. } class procedure FreeAndNilObjects(var Lst: TFPList); static; { FreeAndNil the list, and dispose() its items. 'ItemType' is TypeInfo() of items. } class procedure FreeAndNilDisposing(var Lst: TFPList; ItemType: Pointer); static; end; {******************************************************* TFPObjectList (From fcl/inc/contnrs.pp) ********************************************************} TObjectListCallback = procedure(data:TObject;arg:pointer) of object; TObjectListStaticCallback = procedure(data:TObject;arg:pointer); TFPObjectList = class(TObject) private FFreeObjects : Boolean; FList: TFPList; function GetCount: integer; {$ifdef CCLASSESINLINE}inline;{$endif} procedure SetCount(const AValue: integer); protected function GetItem(Index: Integer): TObject; {$ifdef CCLASSESINLINE}inline;{$endif} procedure SetItem(Index: Integer; AObject: TObject); procedure SetCapacity(NewCapacity: Integer); {$ifdef CCLASSESINLINE}inline;{$endif} function GetCapacity: integer; {$ifdef CCLASSESINLINE}inline;{$endif} public constructor Create; constructor Create(FreeObjects : Boolean); destructor Destroy; override; procedure Clear; function Add(AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} procedure Delete(Index: Integer); procedure Exchange(Index1, Index2: Integer); {$ifdef CCLASSESINLINE}inline;{$endif} function Expand: TFPObjectList;{$ifdef CCLASSESINLINE}inline;{$endif} function Extract(Item: TObject): TObject; {$ifdef CCLASSESINLINE}inline;{$endif} function Remove(AObject: TObject): Integer; function IndexOf(AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function IndexOfItem(AObject: TObject; Direction: TDirection): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt: Integer): Integer; procedure Insert(Index: Integer; AObject: TObject); {$ifdef CCLASSESINLINE}inline;{$endif} function First: TObject; {$ifdef CCLASSESINLINE}inline;{$endif} function Last: TObject; {$ifdef CCLASSESINLINE}inline;{$endif} procedure Move(CurIndex, NewIndex: Integer); {$ifdef CCLASSESINLINE}inline;{$endif} procedure Assign(Obj:TFPObjectList); procedure ConcatListCopy(Obj:TFPObjectList); procedure Pack; {$ifdef CCLASSESINLINE}inline;{$endif} procedure Sort(Compare: TListSortCompare); {$ifdef CCLASSESINLINE}inline;{$endif} procedure ForEachCall(proc2call:TObjectListCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif} procedure ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif} property Capacity: Integer read GetCapacity write SetCapacity; property Count: Integer read GetCount write SetCount; property OwnsObjects: Boolean read FFreeObjects write FFreeObjects; property Items[Index: Integer]: TObject read GetItem write SetItem; default; property List: TFPList read FList; end; { Memory region that allocates chunks with .Push and frees them all at once with .Done, useful for storing shortstrings. Alignment of the sizes is the user's responsibility, but shortstrings are composed of bytes and unaffected, and, in general, objects of the same nature will have same alignment and be sized as its multiple, not to mention using such a region exclusively for arrays of the same type, for example. } PMemoryRegionNode = ^TMemoryRegionNode; TMemoryRegionNode = record n, alloc: uint32; next: PMemoryRegionNode; data: array[0 .. 0] of byte; { variable-sized; and aligned to pointer. } end; const MinMemoryRegionNodeSize=64; type TMemoryRegion = object procedure Init(preallocate: SizeUint=0); procedure Done; {$ifdef CCLASSESINLINE}inline;{$endif} function Push(n: SizeUint): pointer; procedure Clear; function CalcSumSize: SizeUint; { don't want to store it as its retrieval is logarithmic. } private FTop: PMemoryRegionNode; class function AllocateNode(n, alloc: SizeUint): PMemoryRegionNode; static; function PushNewNode(n: SizeUint): pointer; end; { "Vi" stands for variable-sized indices. Variable-sized indices use less space and reduce the size of a region with potentially chaotic accesses (FHash). Indices are bitpacked. For speed and simplicity, bitfield base type is the same as index type (SizeUint), and maximum bit size is bitsizeof(SizeUint) - 1, to allow unconditional masking with "1 shl bitsPerIndex - 1", etc. } function ViGet(data: PSizeUint; index, bitsPerIndex: SizeUint): SizeUint; procedure ViSet(data: PSizeUint; index, bitsPerIndex, value: SizeUint); function ViDataSize(n, bitsPerIndex: SizeUint): SizeUint; const ViEmpty = 0; ViRealIndexOffset = 1; type PViHashListItem = ^TViHashListItem; TViHashListItem = record HashValue: uint32; Next: int32; Str: {$ifdef symansistr} TSymStr {$else} PSymStr {$endif}; Data: Pointer; end; TViRehashMode = (vi_Auto, vi_Tight, vi_Pack); TViHashList = class(TObject) private { When not special "empty list", that is, when Assigned(FItems), FHash is a memory region containing FHash + FItems. } FHash: PSizeUint; { Bitpacked hash table. ViEmpty means empty cell, ViRealIndexOffset+i references FItems[i]. } FItems: PViHashListItem; FBitsPerIndex: uint8; { Size of indices in FHash. } FHashMask: uint32; { Count of indices in FHash is always "FHashMask + 1" and is always a power of two. } FCount: int32; FCapacity: uint32; { Allocation size of FItems. Generally speaking, can be arbitrary, without any relation to "FHashMask + 1". } {$ifndef symansistr} FShortstringRegion: TMemoryRegion; {$endif} function Get(Index: SizeInt): Pointer; procedure Put(Index: SizeInt; Item: Pointer); class procedure RaiseIndexError(Index: SizeInt); static; procedure SetupEmptyTable; procedure Rehash(ForItems: SizeUint; mode: TViRehashMode=vi_Auto); {$ifndef symansistr} function AddStrToRegion(const s: TSymStr): PSymStr; {$endif} procedure Shrink; procedure AddToHashTable(Item: PViHashListItem; Index: SizeUint); function InternalFind(AHash:LongWord;const AName:TSymStr;out PrevIndex:SizeInt):SizeInt; procedure RemoveFromHashTable(AHash:LongWord;Index, PrevIndex: SizeInt); procedure SetCapacity(NewCapacity: uint32); public constructor Create; destructor Destroy; override; function Add(const AName:TSymStr;Item: Pointer): SizeInt; procedure Clear; function NameOfIndex(Index: SizeInt): TSymStr; function HashOfIndex(Index: SizeInt): LongWord; function GetNextCollision(Index: SizeInt): SizeInt; {$ifdef CCLASSESINLINE}inline;{$endif} procedure Delete(Index: SizeInt); function Extract(item: Pointer): Pointer; function IndexOf(Item: Pointer): SizeInt; function Find(const AName:TSymStr): Pointer; {$ifdef CCLASSESINLINE}inline;{$endif} function FindIndexOf(const AName:TSymStr): SizeInt; {$ifdef CCLASSESINLINE}inline;{$endif} function FindWithHash(const AName:TSymStr;AHash:LongWord): Pointer; function Rename(const AOldName,ANewName:TSymStr): SizeInt; function Remove(Item: Pointer): SizeInt; procedure Pack; procedure ShowStatistics; procedure ForEachCall(proc2call:TListCallback;arg:pointer); procedure ForEachCall(proc2call:TListStaticCallback;arg:pointer); property Count: int32 read FCount; property Capacity: uint32 read FCapacity write SetCapacity; property Items[Index: SizeInt]: Pointer read Get write Put; default; property List: PViHashListItem read FItems; end; TFPHashList=TViHashList; const MaxHashListSize = Maxint div 16; {******************************************************* TFPHashObjectList (From fcl/inc/contnrs.pp) ********************************************************} type TFPHashObjectList = class; { TFPHashObject } TFPHashObject = class private FOwner : TFPHashObjectList; FStr : {$ifdef symansistr} TSymStr {$else} PSymStr {$endif}; FHash : LongWord; procedure InternalChangeOwner(HashObjectList:TFPHashObjectList;const s:TSymStr); protected function GetName:TSymStr;virtual; function GetHash:Longword;virtual; public constructor CreateNotOwned; constructor Create(HashObjectList:TFPHashObjectList;const s:TSymStr); procedure ChangeOwner(HashObjectList:TFPHashObjectList); procedure ChangeOwnerAndName(HashObjectList:TFPHashObjectList;const s:TSymStr); {$ifdef CCLASSESINLINE}inline;{$endif} procedure Rename(const ANewName:TSymStr); property Name:TSymStr read GetName; property Hash:Longword read GetHash; property OwnerList: TFPHashObjectList read FOwner; end; TFPHashObjectList = class(TObject) private FFreeObjects : Boolean; FHashList: TFPHashList; function GetCount: integer; {$ifdef CCLASSESINLINE}inline;{$endif} protected function GetItem(Index: Integer): TObject; {$ifdef CCLASSESINLINE}inline;{$endif} procedure SetItem(Index: Integer; AObject: TObject); procedure SetCapacity(NewCapacity: Integer); {$ifdef CCLASSESINLINE}inline;{$endif} function GetCapacity: integer; {$ifdef CCLASSESINLINE}inline;{$endif} public constructor Create(FreeObjects : boolean = True); destructor Destroy; override; procedure Clear; function Add(const AName:TSymStr;AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function NameOfIndex(Index: Integer): TSymStr; {$ifdef CCLASSESINLINE}inline;{$endif} function HashOfIndex(Index: Integer): LongWord; {$ifdef CCLASSESINLINE}inline;{$endif} function GetNextCollision(Index: Integer): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} procedure Delete(Index: Integer); function Extract(Item: TObject): TObject; {$ifdef CCLASSESINLINE}inline;{$endif} function Remove(AObject: TObject): Integer; function IndexOf(AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function Find(const s:TSymStr): TObject; {$ifdef CCLASSESINLINE}inline;{$endif} function FindIndexOf(const s:TSymStr): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function FindWithHash(const AName:TSymStr;AHash:LongWord): Pointer; {$ifdef CCLASSESINLINE}inline;{$endif} function Rename(const AOldName,ANewName:TSymStr): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} function FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt: Integer): Integer; procedure Pack; {$ifdef CCLASSESINLINE}inline;{$endif} procedure ShowStatistics; {$ifdef CCLASSESINLINE}inline;{$endif} procedure ForEachCall(proc2call:TObjectListCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif} procedure ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif} property Capacity: Integer read GetCapacity write SetCapacity; property Count: Integer read GetCount; property OwnsObjects: Boolean read FFreeObjects write FFreeObjects; property Items[Index: Integer]: TObject read GetItem write SetItem; default; property List: TFPHashList read FHashList; end; {******************************************** TLinkedList ********************************************} type TLinkedListItem = class public Previous, Next : TLinkedListItem; Constructor Create; Destructor Destroy;override; Function GetCopy:TLinkedListItem;virtual; end; TLinkedListItemClass = class of TLinkedListItem; TLinkedList = class private FCount : integer; FFirst, FLast : TLinkedListItem; FNoClear : boolean; public constructor Create; destructor Destroy;override; { true when the List is empty } function Empty:boolean; {$ifdef CCLASSESINLINE}inline;{$endif} { deletes all Items } procedure Clear; { inserts an Item } procedure Insert(Item:TLinkedListItem); { inserts an Item before Loc } procedure InsertBefore(Item,Loc : TLinkedListItem); { inserts an Item after Loc } procedure InsertAfter(Item,Loc : TLinkedListItem);virtual; { concatenate an Item } procedure Concat(Item:TLinkedListItem); { deletes an Item } procedure Remove(Item:TLinkedListItem); { Gets First Item } function GetFirst:TLinkedListItem; { Gets last Item } function GetLast:TLinkedListItem; { inserts another List at the begin and make this List empty } procedure insertList(p : TLinkedList); virtual; { inserts another List before the provided item and make this List empty } procedure insertListBefore(Item:TLinkedListItem;p : TLinkedList); virtual; { inserts another List after the provided item and make this List empty } procedure insertListAfter(Item:TLinkedListItem;p : TLinkedList); virtual; { concatenate another List at the end and make this List empty } procedure concatList(p : TLinkedList); virtual; { concatenate another List at the start and makes a copy the list is ordered in reverse. } procedure insertListcopy(p : TLinkedList); virtual; { concatenate another List at the end and makes a copy } procedure concatListcopy(p : TLinkedList); virtual; { removes all items from the list, the items are not freed } procedure RemoveAll; virtual; property First:TLinkedListItem read FFirst; property Last:TLinkedListItem read FLast; property Count:Integer read FCount; property NoClear:boolean write FNoClear; end; {******************************************** TCmdStrList ********************************************} { string containerItem } TCmdStrListItem = class(TLinkedListItem) FPStr : TCmdStr; public constructor Create(const s:TCmdStr); destructor Destroy;override; function GetCopy:TLinkedListItem;override; property Str: TCmdStr read FPStr; end; { string container } TCmdStrList = class(TLinkedList) private FDoubles : boolean; { if this is set to true, doubles (case insensitive!) are allowed } public constructor Create; constructor Create_No_Double; { inserts an Item } procedure Insert(const s:TCmdStr); { concatenate an Item } procedure Concat(const s:TCmdStr); { deletes an Item } procedure Remove(const s:TCmdStr); { Gets First Item } function GetFirst:TCmdStr; { Gets last Item } function GetLast:TCmdStr; { true if string is in the container, compare case sensitive } function FindCase(const s:TCmdStr):TCmdStrListItem; { true if string is in the container } function Find(const s:TCmdStr):TCmdStrListItem; { inserts an item } procedure InsertItem(item:TCmdStrListItem); { concatenate an item } procedure ConcatItem(item:TCmdStrListItem); property Doubles:boolean read FDoubles write FDoubles; end; {******************************************** DynamicArray ********************************************} type { can't use sizeof(integer) because it crashes gdb } tdynamicblockdata=array[0..1024*1024-1] of byte; pdynamicblock = ^tdynamicblock; tdynamicblock = record pos, size, used : longword; Next : pdynamicblock; data : tdynamicblockdata; end; tdynamicblockarray = array of tdynamicblock; const dynamicblockbasesize = sizeof(tdynamicblock)-sizeof(tdynamicblockdata); mindynamicblocksize = 8*sizeof(pointer); type tdynamicarray = class private FPosn : longword; FPosnblock : pdynamicblock; FCurrBlocksize, FMaxBlocksize : longword; FFirstblock, FLastblock : pdynamicblock; procedure grow; public constructor Create(Ablocksize:longword); destructor Destroy;override; procedure reset; function size:longword; {$ifdef CCLASSESINLINE}inline;{$endif} procedure align(i:longword); procedure seek(i:longword); function read(var d;len:longword):longword; procedure write(const d;len:longword); procedure writestr(const s:string); {$ifdef CCLASSESINLINE}inline;{$endif} procedure readstream(f:TCStream;maxlen:longword); procedure writestream(f:TCStream); function equal(other:tdynamicarray):boolean; property CurrBlockSize : longword read FCurrBlocksize; property FirstBlock : PDynamicBlock read FFirstBlock; property Pos : longword read FPosn; end; {****************************************************************** THashSet (keys not limited to ShortString, no indexed access) *******************************************************************} PPHashSetItem = ^PHashSetItem; PHashSetItem = ^THashSetItem; THashSetItem = record Next: PHashSetItem; Key: Pointer; { With FOwnKeys, item and its key are allocated at once, and Key points inside. } KeyLength: Integer; HashValue: LongWord; Data: TObject; end; THashSet = class(TObject) private FCount: LongWord; FOwnsObjects: Boolean; FOwnsKeys: Boolean; function Lookup(Key: Pointer; KeyLen: Integer; var Found: Boolean; CanCreate: Boolean): PHashSetItem; procedure Resize(NewCapacity: LongWord); protected FBucket: PPHashSetItem; FBucketCount: LongWord; class procedure FreeItem(item:PHashSetItem); virtual; class function SizeOfItem: Integer; virtual; function CreateItem(Key: Pointer; KeyLen: Integer; HashValue: LongWord): PHashSetItem; public constructor Create(InitSize: Integer; OwnKeys, OwnObjects: Boolean); destructor Destroy; override; procedure Clear; { finds an entry by key } function Find(Key: Pointer; KeyLen: Integer): PHashSetItem;virtual; { finds an entry, creates one if not exists } function FindOrAdd(Key: Pointer; KeyLen: Integer; var Found: Boolean): PHashSetItem;virtual; { finds an entry, creates one if not exists } function FindOrAdd(Key: Pointer; KeyLen: Integer): PHashSetItem;virtual; { returns Data by given Key } function Get(Key: Pointer; KeyLen: Integer): TObject;virtual; { removes an entry, returns False if entry wasn't there } function Remove(Entry: PHashSetItem): Boolean; property Count: LongWord read FCount; end; {****************************************************************** TTagHasSet *******************************************************************} PPTagHashSetItem = ^PTagHashSetItem; PTagHashSetItem = ^TTagHashSetItem; TTagHashSetItem = record Item: THashSetItem; Tag: LongWord; end; TTagHashSet = class(THashSet) private function Lookup(Key: Pointer; KeyLen: Integer; Tag: LongWord; var Found: Boolean; CanCreate: Boolean): PTagHashSetItem; protected class function SizeOfItem: Integer; override; public { finds an entry by key } function Find(Key: Pointer; KeyLen: Integer; Tag: LongWord): PTagHashSetItem; reintroduce; { finds an entry, creates one if not exists } function FindOrAdd(Key: Pointer; KeyLen: Integer; Tag: LongWord; var Found: Boolean): PTagHashSetItem; reintroduce; { finds an entry, creates one if not exists } function FindOrAdd(Key: Pointer; KeyLen: Integer; Tag: LongWord): PTagHashSetItem; reintroduce; { returns Data by given Key } function Get(Key: Pointer; KeyLen: Integer; Tag: LongWord): TObject; reintroduce; end; {****************************************************************** tbitset *******************************************************************} { tbitset } tbitset = class private fdata: TByteDynArray; function getdatasize: longint; public constructor create(initsize: longint); constructor create_bytesize(bytesize: longint); destructor destroy; override; procedure clear; {$ifdef CCLASSESINLINE}inline;{$endif} procedure grow(nsize: longint); { sets a bit } procedure include(index: longint); { clears a bit } procedure exclude(index: longint); { finds an entry, creates one if not exists } function isset(index: longint): boolean; procedure addset(aset: tbitset); procedure subset(aset: tbitset); property data: TByteDynArray read fdata; property datasize: longint read getdatasize; end; function FPHash(P: PChar; Len: Integer; Tag: LongWord): LongWord; function FPHash(P: PChar; Len: Integer): LongWord; inline; function FPHash(const s:shortstring):LongWord; inline; function FPHash(const a:ansistring):LongWord; inline; function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; var Strings: TDynStringArray; AddEmptyStrings : Boolean = False): Integer; implementation {***************************************************************************** Memory debug *****************************************************************************} function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; var Strings: TDynStringArray; AddEmptyStrings : Boolean = False): Integer; var b, c : pchar; procedure SkipWhitespace; begin while (c^ in Whitespace) do inc (c); end; procedure AddString; var l : integer; s : string; begin l := c-b; s:=''; if (l > 0) or AddEmptyStrings then begin setlength(s, l); if l>0 then move (b^, s[1],l*SizeOf(char)); l:=length(Strings); setlength(Strings,l+1); Strings[l]:=S; inc (result); end; end; var quoted : char; begin result := 0; c := Content; Quoted := #0; Separators := Separators + [#13, #10] - ['''','"']; SkipWhitespace; b := c; while (c^ <> #0) do begin if (c^ = Quoted) then begin if ((c+1)^ = Quoted) then inc (c) else Quoted := #0 end else if (Quoted = #0) and (c^ in ['''','"']) then Quoted := c^; if (Quoted = #0) and (c^ in Separators) then begin AddString; inc (c); SkipWhitespace; b := c; end else inc (c); end; if (c <> b) then AddString; end; constructor tmemdebug.create(const s:string); begin infostr:=s; totalmem:=0; Start; end; procedure tmemdebug.start; var status : TFPCHeapStatus; begin status:=GetFPCHeapStatus; startmem:=status.CurrHeapUsed; end; procedure tmemdebug.stop; var status : TFPCHeapStatus; begin if startmem<>0 then begin status:=GetFPCHeapStatus; inc(TotalMem,startmem-status.CurrHeapUsed); startmem:=0; end; end; destructor tmemdebug.destroy; begin Stop; show; end; procedure tmemdebug.show; begin write('memory [',infostr,'] '); if TotalMem>0 then writeln(DStr(TotalMem shr 10),' Kb released') else writeln(DStr((-TotalMem) shr 10),' Kb allocated'); end; {***************************************************************************** TFPObjectList (Copied from rtl/objpas/classes/lists.inc) *****************************************************************************} procedure TFPList.RaiseIndexError(Index : Integer); begin Error(SListIndexError, Index); end; function TFPList.Get(Index: Integer): Pointer; begin If (Index < 0) or (Index >= FCount) then RaiseIndexError(Index); Result:=FList[Index]; end; procedure TFPList.Put(Index: Integer; Item: Pointer); begin if (Index < 0) or (Index >= FCount) then RaiseIndexError(Index); Flist[Index] := Item; end; function TFPList.Extract(item: Pointer): Pointer; var i : Integer; begin result := nil; i := IndexOf(item); if i >= 0 then begin Result := item; FList[i] := nil; Delete(i); end; end; procedure TFPList.SetCapacity(NewCapacity: Integer); begin If (NewCapacity < FCount) or (NewCapacity > MaxListSize) then Error (SListCapacityError, NewCapacity); if NewCapacity = FCapacity then exit; ReallocMem(FList, SizeOf(Pointer)*NewCapacity); FCapacity := NewCapacity; end; procedure TFPList.SetCount(NewCount: Integer); begin if (NewCount < 0) or (NewCount > MaxListSize)then Error(SListCountError, NewCount); If NewCount > FCount then begin If NewCount > FCapacity then SetCapacity(NewCount); If FCount < NewCount then FillChar(Flist[FCount], (NewCount-FCount) * sizeof(Pointer), 0); end; FCount := Newcount; end; destructor TFPList.Destroy; begin Self.Clear; inherited Destroy; end; function TFPList.Add(Item: Pointer): Integer; begin if FCount = FCapacity then Self.Expand; FList[FCount] := Item; Result := FCount; inc(FCount); end; procedure TFPList.Clear; begin if Assigned(FList) then begin SetCount(0); SetCapacity(0); FList := nil; end; end; procedure TFPList.Delete(Index: Integer); begin If (Index<0) or (Index>=FCount) then Error (SListIndexError, Index); dec(FCount); System.Move (FList[Index+1], FList[Index], (FCount - Index) * SizeOf(Pointer)); { Shrink the list if appropriate } if (FCapacity > 256) and (FCount < FCapacity shr 2) then begin FCapacity := FCapacity shr 1; ReallocMem(FList, SizeOf(Pointer) * FCapacity); end; end; class procedure TFPList.Error(const Msg: string; Data: PtrInt); begin Raise EListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame); end; procedure TFPList.Exchange(Index1, Index2: Integer); var Temp : Pointer; begin If ((Index1 >= FCount) or (Index1 < 0)) then Error(SListIndexError, Index1); If ((Index2 >= FCount) or (Index2 < 0)) then Error(SListIndexError, Index2); Temp := FList[Index1]; FList[Index1] := FList[Index2]; FList[Index2] := Temp; end; function TFPList.Expand: TFPList; var IncSize : Longint; begin Result := Self; if FCount < FCapacity then exit; IncSize := sizeof(ptrint)*2; if FCapacity > 127 then Inc(IncSize, FCapacity shr 2) else if FCapacity > sizeof(ptrint)*4 then Inc(IncSize, FCapacity shr 1) else if FCapacity >= sizeof(ptrint) then inc(IncSize,sizeof(ptrint)); SetCapacity(FCapacity + IncSize); end; function TFPList.First: Pointer; begin If FCount<>0 then Result := Items[0] else Result := Nil; end; function TFPList.IndexOf(Item: Pointer): Integer; begin Result:= {$if sizeof(pointer)=sizeof(dword)} IndexDWord {$elseif sizeof(pointer)=sizeof(qword)} IndexQWord {$else} {$error unknown pointer size} {$endif} (FList^, FCount, PtrUint(Item)); end; function TFPList.IndexOfItem(Item: Pointer; Direction: TDirection): Integer; var psrc : PPointer; Index : Integer; begin if Direction=FromBeginning then Result:=IndexOf(Item) else begin Result:=-1; if FCount>0 then begin psrc:=@FList[FCount-1]; For Index:=FCount-1 downto 0 Do begin if psrc^=Item then begin Result:=Index; exit; end; dec(psrc); end; end; end; end; procedure TFPList.Insert(Index: Integer; Item: Pointer); begin if (Index < 0) or (Index > FCount )then Error(SlistIndexError, Index); iF FCount = FCapacity then Self.Expand; if Index0 then Result := Items[FCount - 1] else Result := nil end; procedure TFPList.Move(CurIndex, NewIndex: Integer); var Temp : Pointer; begin if ((CurIndex < 0) or (CurIndex > Count - 1)) then Error(SListIndexError, CurIndex); if (NewINdex < 0) then Error(SlistIndexError, NewIndex); Temp := FList[CurIndex]; FList[CurIndex] := nil; Self.Delete(CurIndex); Self.Insert(NewIndex, nil); FList[NewIndex] := Temp; end; function TFPList.Remove(Item: Pointer): Integer; begin Result := IndexOf(Item); If Result <> -1 then Self.Delete(Result); end; procedure TFPList.Pack; var NewCount, i : integer; pdest, psrc : PPointer; begin NewCount:=0; psrc:=@FList[0]; pdest:=psrc; For I:=0 To FCount-1 Do begin if assigned(psrc^) then begin pdest^:=psrc^; inc(pdest); inc(NewCount); end; inc(psrc); end; FCount:=NewCount; end; Procedure QuickSort(FList: PPointer; L, R : Longint;Compare: TListSortCompare); var I, J, P: Longint; PItem, Q : Pointer; begin repeat I := L; J := R; P := (L + R) div 2; repeat PItem := FList[P]; while Compare(PItem, FList[i]) > 0 do I := I + 1; while Compare(PItem, FList[J]) < 0 do J := J - 1; If I <= J then begin Q := FList[I]; Flist[I] := FList[J]; FList[J] := Q; if P = I then P := J else if P = J then P := I; I := I + 1; J := J - 1; end; until I > J; if L < J then QuickSort(FList, L, J, Compare); L := I; until I >= R; end; procedure TFPList.Sort(Compare: TListSortCompare); begin if Not Assigned(FList) or (FCount < 2) then exit; QuickSort(Flist, 0, FCount-1, Compare); end; procedure TFPList.Assign(Obj: TFPList); var i: Integer; begin Clear; for I := 0 to Obj.Count - 1 do Add(Obj[i]); end; procedure TFPList.ForEachCall(proc2call:TListCallback;arg:pointer); var i : integer; p : pointer; begin For I:=0 To Count-1 Do begin p:=FList[i]; if assigned(p) then proc2call(p,arg); end; end; procedure TFPList.ForEachCall(proc2call:TListStaticCallback;arg:pointer); var i : integer; p : pointer; begin For I:=0 To Count-1 Do begin p:=FList[i]; if assigned(p) then proc2call(p,arg); end; end; class procedure TFPList.AddOnDemand(var Lst: TFPList; Item: Pointer); begin if not Assigned(Lst) then Lst := TFPList.Create; Lst.Add(Item); end; class procedure TFPList.FreeAndNilObjects(var Lst: TFPList); var Lp: PPointer; I: SizeInt; begin if not Assigned(Lst) then exit; Lp := Lst.FList; for I := 0 to Lst.Count-1 do TObject(Lp[I]).Free; // no nil needed Lst.Free; Lst := nil; end; procedure fpc_finalize(data, typeinfo: pointer); external; class procedure TFPList.FreeAndNilDisposing(var Lst: TFPList; ItemType: Pointer); var Lp: PPointer; I: SizeInt; begin if not Assigned(Lst) then exit; Lp := Lst.FList; for I := 0 to Lst.Count-1 do if Assigned(Lp[I]) then begin fpc_finalize(Lp[I],ItemType); FreeMem(Lp[I]); end; Lst.Free; Lst := nil; end; {***************************************************************************** TFPObjectList (Copied from rtl/objpas/classes/lists.inc) *****************************************************************************} constructor TFPObjectList.Create(FreeObjects : boolean); begin Create; FFreeObjects := Freeobjects; end; destructor TFPObjectList.Destroy; begin if (FList <> nil) then begin Clear; FList.Destroy; FList:=nil; end; inherited Destroy; end; procedure TFPObjectList.Clear; var i: integer; begin if FFreeObjects then for i := 0 to FList.Count - 1 do TObject(FList[i]).Free; // no nil needed FList.Clear; end; constructor TFPObjectList.Create; begin inherited Create; FList := TFPList.Create; FFreeObjects := True; end; function TFPObjectList.IndexOf(AObject: TObject): Integer; begin Result := FList.IndexOf(Pointer(AObject)); end; function TFPObjectList.IndexOfItem(AObject: TObject; Direction: TDirection): Integer; {$ifdef CCLASSESINLINE}inline;{$endif} begin Result := FList.IndexOfItem(Pointer(AObject),Direction); end; function TFPObjectList.GetCount: integer; begin Result := FList.Count; end; procedure TFPObjectList.SetCount(const AValue: integer); begin if FList.Count <> AValue then FList.Count := AValue; end; function TFPObjectList.GetItem(Index: Integer): TObject; begin Result := TObject(FList[Index]); end; procedure TFPObjectList.SetItem(Index: Integer; AObject: TObject); begin if OwnsObjects then TObject(FList[Index]).Free; // no nil needed FList[index] := AObject; end; procedure TFPObjectList.SetCapacity(NewCapacity: Integer); begin FList.Capacity := NewCapacity; end; function TFPObjectList.GetCapacity: integer; begin Result := FList.Capacity; end; function TFPObjectList.Add(AObject: TObject): Integer; begin Result := FList.Add(AObject); end; procedure TFPObjectList.Delete(Index: Integer); begin if OwnsObjects then TObject(FList[Index]).Free; // no nil needed FList.Delete(Index); end; procedure TFPObjectList.Exchange(Index1, Index2: Integer); begin FList.Exchange(Index1, Index2); end; function TFPObjectList.Expand: TFPObjectList; begin FList.Expand; Result := Self; end; function TFPObjectList.Extract(Item: TObject): TObject; begin Result := TObject(FList.Extract(Item)); end; function TFPObjectList.Remove(AObject: TObject): Integer; begin Result := IndexOf(AObject); if (Result <> -1) then begin if OwnsObjects then TObject(FList[Result]).Free; // no nil needed FList.Delete(Result); end; end; function TFPObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt : Integer): Integer; var I : Integer; begin I:=AStartAt; Result:=-1; If AExact then while (I 0 then begin { tail is 1 to 3 bytes } case nTail of 3: tail := unaligned(pUint16(p)^) or uint32(p[2]) shl 16; { unaligned(pUint16(p^)) can be LEtoNed for portability } 2: tail := unaligned(pUint16(p)^); { unaligned(pUint16(p^)) can be LEtoNed for portability } {1:} else tail := uint32(p^); end; h := h xor (RolDWord(tail * C1, 15) * C2); end; h := h xor uint32(len); h := (h xor (h shr 16)) * $85ebca6b; h := (h xor (h shr 13)) * $c2b2ae35; result := h xor (h shr 16); {$pop} end; function FPHash(P: PChar; Len: Integer): LongWord; inline; begin result:=fphash(P,Len, 0); end; function FPHash(const s: shortstring): LongWord; inline; begin result:=fphash(pchar(@s[1]),length(s)); end; function FPHash(const a: ansistring): LongWord; inline; begin result:=fphash(pchar(a),length(a)); end; procedure TMemoryRegion.Init(preallocate: SizeUint=0); begin FTop:=nil; if preallocate>MinMemoryRegionNodeSize then FTop:=AllocateNode(0, preallocate); end; procedure TMemoryRegion.Done; begin Clear; end; function TMemoryRegion.Push(n: SizeUint): pointer; var top: PMemoryRegionNode; start: SizeUint; begin top:=FTop; if Assigned(top) then begin start:=top^.n; if n<=SizeUint(top^.alloc-start) then begin top^.n:=start+n; exit(PByte(top^.data)+start); end; end; result:=PushNewNode(n); end; procedure TMemoryRegion.Clear; var cur, next: PMemoryRegionNode; begin cur:=FTop; FTop:=nil; while Assigned(cur) do begin next:=cur^.next; FreeMem(cur); cur:=next; end; end; function TMemoryRegion.CalcSumSize: SizeUint; var n: PMemoryRegionNode; begin result:=0; n:=FTop; while Assigned(n) do begin result:=result+n^.n; n:=n^.next; end; end; class function TMemoryRegion.AllocateNode(n, alloc: SizeUint): PMemoryRegionNode; begin result:=GetMem(PtrUint(@PMemoryRegionNode(nil)^.data)+alloc); result^.n:=n; result^.alloc:=alloc; result^.next:=nil; end; function TMemoryRegion.PushNewNode(n: SizeUint): pointer; var alloc, sumSize: SizeUint; newNode: PMemoryRegionNode; begin { The absolute minimum to allocate is the required contiguous n. } sumSize:=CalcSumSize; alloc:=MinMemoryRegionNodeSize+n+sumSize div 4; { const+n+25%. } newNode:=AllocateNode(n, alloc); newNode^.next:=FTop; FTop:=newNode; result:=PByte(newNode^.data); end; function ViGet(data: PSizeUint; index, bitsPerIndex: SizeUint): SizeUint; begin index:=index*bitsPerIndex; data:=data+index div bitsizeof(SizeUint); index:=index mod bitsizeof(SizeUint); result:=data^ shr index; index:=bitsizeof(data^)-index; if bitsPerIndex<=index then result:=result and (SizeUint(1) shl bitsPerIndex-1) else result:=result or data[1] shl index and (SizeUint(1) shl bitsPerIndex-1); end; procedure ViSet(data: PSizeUint; index, bitsPerIndex, value: SizeUint); begin index:=index*bitsPerIndex; data:=data+index div bitsizeof(SizeUint); index:=index mod bitsizeof(SizeUint); if index+bitsPerIndex<=bitsizeof(data^) then data^:=data^ and not ((SizeUint(1) shl bitsPerIndex-1) shl index) or value shl index else begin data^:=SizeUint(data^ and (SizeUint(1) shl index - 1) or value shl index); index:=bitsizeof(data^)-index; value:=value shr index; index:=bitsPerIndex-index; data[1]:=data[1] shr index shl index or value; end; end; function ViDataSize(n, bitsPerIndex: SizeUint): SizeUint; begin result:=(n*bitsPerIndex+(bitsizeof(SizeUint)-1)) div bitsizeof(SizeUint)*sizeof(SizeUint); end; function TViHashList.Get(Index: SizeInt): Pointer; begin If SizeUint(Index)>=SizeUint(FCount) then RaiseIndexError(Index); Result:=FItems[Index].Data; end; procedure TViHashList.Put(Index: SizeInt; Item: Pointer); begin If SizeUint(Index)>=SizeUint(FCount) then RaiseIndexError(Index); FItems[Index].Data:=Item; end; class procedure TViHashList.RaiseIndexError(Index: SizeInt); begin TFPList.Error(SListIndexError, Index); end; procedure TViHashList.SetupEmptyTable; const { 1-element FHash array containing one zero, which is ViEmpty. Any searches will answer "not found", and any additions will instantly rehash. } EmptyFHash: SizeUint = 0; begin FHash:=@EmptyFHash; FItems:=nil; FBitsPerIndex:=1; FHashMask:=0; FCapacity:=0; end; procedure TViHashList.Rehash(ForItems: SizeUint; mode: TViRehashMode=vi_Auto); var newCapacity, newHashMask, newBitsPerIndex, itemsOffset, regionSize: SizeUint; i: SizeInt; newHash: PSizeUint; newItems: PViHashListItem; shortcutReAdd: boolean; begin if ForItems=0 then begin Clear; exit; end; if ForItems>MaxHashListSize then TFPList.Error(SListCapacityError, ForItems); { Can be something like "137.5% ForItems", but with bitwise indices, better to just derive the capacity later from chosen index type limit, which will be 200% at most - this way, both capacity and hash mask size become beautiful powers of two, saving on rehashes ("shortcutReAdd" branch, while still required for degenerate scenarios, becomes de facto unreachable), and often even on memory (though the reason for the latter is unclear to me; maybe "137.5%" in conjunction with "UpToPow2" introduces extra breakpoints). } newCapacity:=ForItems; { Max index for "capacity" items is "ViRealIndexOffset + (capacity - 1)", which can be rewritten as "capacity + (ViRealIndexOffset - 1)". } newBitsPerIndex:=1+BsrDWord(newCapacity+(ViRealIndexOffset-1)); if not ((newBitsPerIndex>=1) and (newBitsPerIndex<=bitsizeof(SizeUint)-1)) then InternalErrorProc(2022120701); { In place of explicit over-allocation, increase capacity to index type limit. } if mode<>vi_Tight then newCapacity:=(SizeUint(1) shl newBitsPerIndex-1)-(ViRealIndexOffset-1); { Take item list capacity rounded up to power of two. This can give 50% to 100% load factor. If it gives more than 3/4, double the hash capacity again. After that, possible load factors will range from 37.5% to 75%. Even load factors greater than 100% will work though. Low factors are just slightly faster, at the expense of memory. } newHashMask:=SizeUint(1) shl (1+BsrDWord((newCapacity-1) or 1))-1; { UpToPow2(newCapacity)-1 } if newHashMask div 4*3vi_Pack); if shortcutReAdd then begin { If even index type hasn't changed, just copy FHash. Else convert. } if newBitsPerIndex=FBitsPerIndex then Move(FHash^, newHash^, ViDataSize(newHashMask+1,newBitsPerIndex)) else for i:=0 to newHashMask do ViSet(newHash, i, newBitsPerIndex, ViGet(FHash, i, FBitsPerIndex)); end else { Otherwise set all indices to ViEmpty. } FillChar(newHash^, ViDataSize(newHashMask+1,newBitsPerIndex), 0); { Move items as raw memory, even managed (old area is then deallocated without finalizing). } Move(FItems^, newItems^, FCount*sizeof(TViHashListItem)); { Free the old table. "Assigned(FItems)" means that the table was not the fake table set up by SetupEmptyTable. Items were just moved into a new place so shouldn't be finalized. } if Assigned(FItems) then FreeMem(FHash); FHash:=newHash; FItems:=newItems; FBitsPerIndex:=newBitsPerIndex; FHashMask:=newHashMask; FCapacity:=newCapacity; { Re-add items if re-adding was not shortcutted before. } if not shortcutReAdd then for i:=0 to FCount-1 do AddToHashTable(FItems+i, i); end; {$ifndef symansistr} function TViHashList.AddStrToRegion(const s: TSymStr): PSymStr; var size: SizeUint; begin size:=1+length(s); result:=FShortstringRegion.Push(size); System.Move(s[0],result^,size); end; {$endif} procedure TViHashList.Shrink; begin if (FCapacity >= 64) and (uint32(FCount) < FCapacity div 4) then Rehash(uint32(FCount)+uint32(FCount) div 4); end; procedure TViHashList.AddToHashTable(Item: PViHashListItem; Index: SizeUint); var HashIndex: SizeUint; begin if not Assigned(Item^.Data) then exit; HashIndex:=Item^.HashValue and FHashMask; FItems[Index].Next:=SizeInt(ViGet(FHash, HashIndex, FBitsPerIndex))-ViRealIndexOffset; ViSet(FHash, HashIndex, FBitsPerIndex, ViRealIndexOffset+Index); end; function TViHashList.InternalFind(AHash:LongWord;const AName:TSymStr;out PrevIndex:SizeInt):SizeInt; var it: PViHashListItem; begin Result:=SizeInt(ViGet(FHash, AHash and FHashMask, FBitsPerIndex))-ViRealIndexOffset; PrevIndex:=-1; repeat if Result<0 then exit; it:=FItems+Result; if Assigned(it^.Data) and (AHash=it^.HashValue) and (AName=it^.Str {$ifndef symansistr} ^ {$endif}) then exit; PrevIndex:=Result; Result:=FItems[Result].Next; until false; end; procedure TViHashList.RemoveFromHashTable(AHash:LongWord;Index, PrevIndex: SizeInt); var next: SizeInt; begin next:=SizeInt(FItems[Index].Next); if PrevIndex<0 then ViSet(FHash, AHash and FHashMask, FBitsPerIndex, ViRealIndexOffset+next) else FItems[PrevIndex].Next:=next; end; procedure TViHashList.SetCapacity(NewCapacity: uint32); begin if NewCapacity < uint32(FCount) then internalerrorproc(2021122605); Rehash(NewCapacity, vi_Tight); end; constructor TViHashList.Create; begin inherited Create; {$ifndef symansistr} FShortstringRegion.Init; {$endif} SetupEmptyTable; end; destructor TViHashList.Destroy; begin Clear; {$ifndef symansistr} FShortstringRegion.Done; {$endif} inherited Destroy; end; function TViHashList.Add(const AName:TSymStr;Item: Pointer): SizeInt; var it: PViHashListItem; begin result:=FCount; if uint32(result)=FCapacity then Rehash(result+1); it:=FItems+result; Initialize(it^); it^.HashValue:=FPHash(AName); it^.Data:=Item; {$ifdef symansistr} it^.Str:=AName; {$else} it^.Str:=AddStrToRegion(AName); {$endif} AddToHashTable(it, result); FCount:=result+1; end; procedure TViHashList.Clear; begin if Assigned(FItems) then begin Finalize(FItems^, FCount); FreeMem(FHash); SetupEmptyTable; FCount:=0; {$ifndef symansistr} FShortstringRegion.Clear; {$endif} end; end; function TViHashList.NameOfIndex(Index: SizeInt): TSymStr; begin if SizeUint(Index)>=SizeUint(FCount) then RaiseIndexError(Index); result:=FItems[Index].Str {$ifndef symansistr} ^ {$endif}; end; function TViHashList.HashOfIndex(Index: SizeInt): LongWord; begin if SizeUint(Index)>=SizeUint(FCount) then RaiseIndexError(Index); result:=FItems[Index].HashValue; end; function TViHashList.GetNextCollision(Index: SizeInt): SizeInt; begin Result:=FItems[Index].Next; end; procedure TViHashList.Delete(Index: SizeInt); var it: PViHashListItem; prev, i: SizeInt; begin If SizeUint(Index)>=SizeUint(FCount) then RaiseIndexError(Index); { Remove from array, shifting items above. } Finalize(FItems[Index]); Move(FItems[Index+1], FItems[Index], (FCount-Index-1)*sizeof(TViHashListItem)); dec(FCount); { Rebuild the table. This is much faster than trying to fix up indices. :( } FillChar(FHash^, ViDataSize(FHashMask+1, FBitsPerIndex), 0); for i:=0 to FCount-1 do AddToHashTable(FItems+i, i); Shrink; end; function TViHashList.Extract(item: Pointer): Pointer; var i : SizeInt; begin result:=nil; i:=IndexOf(item); if i>=0 then begin Result:=item; Delete(i); end; end; function TViHashList.IndexOf(Item: Pointer): SizeInt; var itemp, iteme: PViHashListItem; begin Result:=0; itemp:=FItems; iteme:=itemp+FCount; while itemp=0 then Result:=FItems[Index].Data; end; function TViHashList.Rename(const AOldName,ANewName:TSymStr): SizeInt; var PrevIndex : SizeInt; OldHash : LongWord; it: PViHashListItem; begin OldHash:=FPHash(AOldName); result:=InternalFind(OldHash,AOldName,PrevIndex); if result<0 then exit; RemoveFromHashTable(OldHash, result, PrevIndex); it:=FItems+result; it^.HashValue:=FPHash(ANewName); {$ifdef symansistr} it^.Str:=ANewName; {$else} it^.Str:=AddStrToRegion(ANewName); {$endif} AddToHashTable(it, result); end; function TViHashList.Remove(Item: Pointer): SizeInt; begin Result:=IndexOf(Item); if Result>=0 then Delete(Result); end; procedure TViHashList.Pack; var itemp, iteme, target: PViHashListItem; removed: SizeUint; begin itemp:=FItems; iteme:=itemp+FCount; while itempFCapacity then Rehash(FCount, vi_Pack); end; procedure TViHashList.ShowStatistics; var HashMean, HashStdDev : Double; Index, i,j : SizeInt; begin { Calculate Mean and StdDev } HashMean:=0; HashStdDev:=0; for i:=0 to FHashMask do begin j:=0; Index:=SizeInt(ViGet(FHash, i, FBitsPerIndex))-ViRealIndexOffset; while Index>=0 do begin inc(j); Index:=FItems[Index].Next; end; HashMean:=HashMean+j; HashStdDev:=HashStdDev+Sqr(j); end; HashMean:=HashMean/(FHashMask+1); HashStdDev:=(HashStdDev-(FHashMask+1)*Sqr(HashMean)); If FHashMask>0 then HashStdDev:=Sqrt(HashStdDev/FHashMask) else HashStdDev:=0; { Print info to stdout } Writeln('HashSize : ',FHashMask+1); Writeln('HashMean : ',HashMean:1:4); Writeln('HashStdDev : ',HashStdDev:1:4); Writeln('ListSize : ',FCount,'/',FCapacity); {$ifndef symansistr} Writeln('StringSize : ',FShortstringRegion.CalcSumSize); {$endif} end; procedure TViHashList.ForEachCall(proc2call:TListCallback;arg:pointer); var i: SizeInt; p: pointer; begin for i:=0 to FCount-1 do begin p:=FItems[i].Data; if assigned(p) then proc2call(p,arg); end; end; procedure TViHashList.ForEachCall(proc2call:TListStaticCallback;arg:pointer); var i: SizeInt; p: pointer; begin for i:=0 to FCount-1 do begin p:=FItems[i].Data; if assigned(p) then proc2call(p,arg); end; end; {***************************************************************************** TFPHashObjectList (Copied from rtl/objpas/classes/lists.inc) *****************************************************************************} constructor TFPHashObjectList.Create(FreeObjects : boolean = True); begin inherited Create; FHashList := TFPHashList.Create; FFreeObjects := Freeobjects; end; destructor TFPHashObjectList.Destroy; begin if (FHashList <> nil) then begin Clear; FHashList.Destroy; FHashList:=nil; end; inherited Destroy; end; procedure TFPHashObjectList.Clear; var i: integer; begin if FFreeObjects then for i := 0 to FHashList.Count - 1 do TObject(FHashList[i]).Free; // no nil needed FHashList.Clear; end; function TFPHashObjectList.IndexOf(AObject: TObject): Integer; begin Result := FHashList.IndexOf(Pointer(AObject)); end; function TFPHashObjectList.GetCount: integer; begin Result := FHashList.Count; end; function TFPHashObjectList.GetItem(Index: Integer): TObject; begin Result := TObject(FHashList[Index]); end; procedure TFPHashObjectList.SetItem(Index: Integer; AObject: TObject); begin if OwnsObjects then TObject(FHashList[Index]).Free; // no nil needed FHashList[index] := AObject; end; procedure TFPHashObjectList.SetCapacity(NewCapacity: Integer); begin FHashList.Capacity := NewCapacity; end; function TFPHashObjectList.GetCapacity: integer; begin Result := FHashList.Capacity; end; function TFPHashObjectList.Add(const AName:TSymStr;AObject: TObject): Integer; begin Result := FHashList.Add(AName,AObject); end; function TFPHashObjectList.NameOfIndex(Index: Integer): TSymStr; begin Result := FHashList.NameOfIndex(Index); end; function TFPHashObjectList.HashOfIndex(Index: Integer): LongWord; begin Result := FHashList.HashOfIndex(Index); end; function TFPHashObjectList.GetNextCollision(Index: Integer): Integer; begin Result := FHashList.GetNextCollision(Index); end; procedure TFPHashObjectList.Delete(Index: Integer); begin if OwnsObjects then TObject(FHashList[Index]).Free; // no nil needed FHashList.Delete(Index); end; function TFPHashObjectList.Extract(Item: TObject): TObject; begin Result := TObject(FHashList.Extract(Item)); end; function TFPHashObjectList.Remove(AObject: TObject): Integer; begin Result := IndexOf(AObject); if (Result <> -1) then begin if OwnsObjects then TObject(FHashList[Result]).Free; // no nil needed FHashList.Delete(Result); end; end; function TFPHashObjectList.Find(const s:TSymStr): TObject; begin result:=TObject(FHashList.Find(s)); end; function TFPHashObjectList.FindIndexOf(const s:TSymStr): Integer; begin result:=FHashList.FindIndexOf(s); end; function TFPHashObjectList.FindWithHash(const AName:TSymStr;AHash:LongWord): Pointer; begin Result:=TObject(FHashList.FindWithHash(AName,AHash)); end; function TFPHashObjectList.Rename(const AOldName,ANewName:TSymStr): Integer; begin Result:=FHashList.Rename(AOldName,ANewName); end; function TFPHashObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt : Integer): Integer; var I : Integer; begin I:=AStartAt; Result:=-1; If AExact then while (I=0 then begin it:=FOwner.List.List+Index; {$ifdef symansistr} FStr:=ANewName; {$else} FStr:=it^.Str; {$endif} FHash:=it^.HashValue; end; end; function TFPHashObject.GetName:TSymStr; begin Result:=FStr {$ifndef symansistr} ^ {$endif}; end; function TFPHashObject.GetHash:Longword; begin Result:=FHash; end; {**************************************************************************** TLinkedListItem ****************************************************************************} constructor TLinkedListItem.Create; begin Previous:=nil; Next:=nil; end; destructor TLinkedListItem.Destroy; begin end; function TLinkedListItem.GetCopy:TLinkedListItem; var p : TLinkedListItem; l : integer; begin p:=TLinkedListItemClass(ClassType).Create; l:=InstanceSize; Move(pointer(self)^,pointer(p)^,l); Result:=p; end; {**************************************************************************** TLinkedList ****************************************************************************} constructor TLinkedList.Create; begin FFirst:=nil; Flast:=nil; FCount:=0; FNoClear:=False; end; destructor TLinkedList.destroy; begin if not FNoClear then Clear; end; function TLinkedList.empty:boolean; begin Empty:=(FFirst=nil); end; procedure TLinkedList.Insert(Item:TLinkedListItem); begin if FFirst=nil then begin FLast:=Item; Item.Previous:=nil; Item.Next:=nil; end else begin FFirst.Previous:=Item; Item.Previous:=nil; Item.Next:=FFirst; end; FFirst:=Item; inc(FCount); end; procedure TLinkedList.InsertBefore(Item,Loc : TLinkedListItem); begin Item.Previous:=Loc.Previous; Item.Next:=Loc; Loc.Previous:=Item; if assigned(Item.Previous) then Item.Previous.Next:=Item else { if we've no next item, we've to adjust FFist } FFirst:=Item; inc(FCount); end; procedure TLinkedList.InsertAfter(Item,Loc : TLinkedListItem); begin Item.Next:=Loc.Next; Loc.Next:=Item; Item.Previous:=Loc; if assigned(Item.Next) then Item.Next.Previous:=Item else { if we've no next item, we've to adjust FLast } FLast:=Item; inc(FCount); end; procedure TLinkedList.Concat(Item:TLinkedListItem); begin if FFirst=nil then begin FFirst:=Item; Item.Previous:=nil; Item.Next:=nil; end else begin Flast.Next:=Item; Item.Previous:=Flast; Item.Next:=nil; end; Flast:=Item; inc(FCount); end; procedure TLinkedList.remove(Item:TLinkedListItem); begin if Item=nil then exit; if (FFirst=Item) and (Flast=Item) then begin FFirst:=nil; Flast:=nil; end else if FFirst=Item then begin FFirst:=Item.Next; if assigned(FFirst) then FFirst.Previous:=nil; end else if Flast=Item then begin Flast:=Flast.Previous; if assigned(Flast) then Flast.Next:=nil; end else begin Item.Previous.Next:=Item.Next; Item.Next.Previous:=Item.Previous; end; Item.Next:=nil; Item.Previous:=nil; dec(FCount); end; procedure TLinkedList.clear; var NewNode, Next : TLinkedListItem; begin NewNode:=FFirst; while assigned(NewNode) do begin Next:=NewNode.Next; prefetch(pointer(Next)^); NewNode.Free; NewNode:=Next; end; FLast:=nil; FFirst:=nil; FCount:=0; end; function TLinkedList.GetFirst:TLinkedListItem; begin if FFirst=nil then GetFirst:=nil else begin GetFirst:=FFirst; if FFirst=FLast then FLast:=nil; FFirst:=FFirst.Next; dec(FCount); end; end; function TLinkedList.GetLast:TLinkedListItem; begin if FLast=nil then Getlast:=nil else begin Getlast:=FLast; if FLast=FFirst then FFirst:=nil; FLast:=FLast.Previous; dec(FCount); end; end; procedure TLinkedList.insertList(p : TLinkedList); begin { empty List ? } if (p.FFirst=nil) then exit; p.Flast.Next:=FFirst; { we have a double Linked List } if assigned(FFirst) then FFirst.Previous:=p.Flast; FFirst:=p.FFirst; if (FLast=nil) then Flast:=p.Flast; inc(FCount,p.FCount); { p becomes empty } p.FFirst:=nil; p.Flast:=nil; p.FCount:=0; end; procedure TLinkedList.insertListBefore(Item:TLinkedListItem;p : TLinkedList); begin { empty List ? } if (p.FFirst=nil) then exit; if (Item=nil) then begin { Insert at begin } InsertList(p); exit; end else begin p.FLast.Next:=Item; p.FFirst.Previous:=Item.Previous; if assigned(Item.Previous) then Item.Previous.Next:=p.FFirst else FFirst:=p.FFirst; Item.Previous:=p.FLast; inc(FCount,p.FCount); end; { p becomes empty } p.FFirst:=nil; p.Flast:=nil; p.FCount:=0; end; procedure TLinkedList.insertListAfter(Item:TLinkedListItem;p : TLinkedList); begin { empty List ? } if (p.FFirst=nil) then exit; if (Item=nil) then begin { Insert at begin } InsertList(p); exit; end else begin p.FFirst.Previous:=Item; p.FLast.Next:=Item.Next; if assigned(Item.Next) then Item.Next.Previous:=p.FLast else FLast:=p.FLast; Item.Next:=p.FFirst; inc(FCount,p.FCount); end; { p becomes empty } p.FFirst:=nil; p.Flast:=nil; p.FCount:=0; end; procedure TLinkedList.concatList(p : TLinkedList); begin if (p.FFirst=nil) then exit; if FFirst=nil then FFirst:=p.FFirst else begin FLast.Next:=p.FFirst; p.FFirst.Previous:=Flast; end; Flast:=p.Flast; inc(FCount,p.FCount); { make p empty } p.Flast:=nil; p.FFirst:=nil; p.FCount:=0; end; procedure TLinkedList.insertListcopy(p : TLinkedList); var NewNode,NewNode2 : TLinkedListItem; begin NewNode:=p.Last; while assigned(NewNode) do begin NewNode2:=NewNode.Getcopy; if assigned(NewNode2) then Insert(NewNode2); NewNode:=NewNode.Previous; end; end; procedure TLinkedList.concatListcopy(p : TLinkedList); var NewNode,NewNode2 : TLinkedListItem; begin NewNode:=p.First; while assigned(NewNode) do begin NewNode2:=NewNode.Getcopy; if assigned(NewNode2) then Concat(NewNode2); NewNode:=NewNode.Next; end; end; procedure TLinkedList.RemoveAll; begin FFirst:=nil; FLast:=nil; FCount:=0; end; {**************************************************************************** TCmdStrListItem ****************************************************************************} constructor TCmdStrListItem.Create(const s:TCmdStr); begin inherited Create; FPStr:=s; end; destructor TCmdStrListItem.Destroy; begin FPStr:=''; end; function TCmdStrListItem.GetCopy:TLinkedListItem; begin Result:=(inherited GetCopy); { TLinkedListItem.GetCopy performs a "move" to copy all data -> reinit the ansistring, so the refcount is properly increased } Initialize(TCmdStrListItem(Result).FPStr); TCmdStrListItem(Result).FPStr:=FPstr; end; {**************************************************************************** TCmdStrList ****************************************************************************} constructor TCmdStrList.Create; begin inherited Create; FDoubles:=true; end; constructor TCmdStrList.Create_no_double; begin inherited Create; FDoubles:=false; end; procedure TCmdStrList.insert(const s : TCmdStr); begin if (s='') or ((not FDoubles) and (findcase(s)<>nil)) then exit; inherited insert(TCmdStrListItem.create(s)); end; procedure TCmdStrList.concat(const s : TCmdStr); begin if (s='') or ((not FDoubles) and (findcase(s)<>nil)) then exit; inherited concat(TCmdStrListItem.create(s)); end; procedure TCmdStrList.remove(const s : TCmdStr); var p : TCmdStrListItem; begin if s='' then exit; p:=findcase(s); if assigned(p) then begin inherited Remove(p); p.Free; p := nil; end; end; function TCmdStrList.GetFirst : TCmdStr; var p : TCmdStrListItem; begin p:=TCmdStrListItem(inherited GetFirst); if p=nil then GetFirst:='' else begin GetFirst:=p.FPStr; p.free; p := nil; end; end; function TCmdStrList.Getlast : TCmdStr; var p : TCmdStrListItem; begin p:=TCmdStrListItem(inherited Getlast); if p=nil then Getlast:='' else begin Getlast:=p.FPStr; p.free; p := nil; end; end; function TCmdStrList.FindCase(const s:TCmdStr):TCmdStrListItem; var NewNode : TCmdStrListItem; begin result:=nil; if s='' then exit; NewNode:=TCmdStrListItem(FFirst); while assigned(NewNode) do begin if NewNode.FPStr=s then begin result:=NewNode; exit; end; NewNode:=TCmdStrListItem(NewNode.Next); end; end; function TCmdStrList.Find(const s:TCmdStr):TCmdStrListItem; var NewNode : TCmdStrListItem; begin result:=nil; if s='' then exit; NewNode:=TCmdStrListItem(FFirst); while assigned(NewNode) do begin if SysUtils.CompareText(s, NewNode.FPStr)=0 then begin result:=NewNode; exit; end; NewNode:=TCmdStrListItem(NewNode.Next); end; end; procedure TCmdStrList.InsertItem(item:TCmdStrListItem); begin inherited Insert(item); end; procedure TCmdStrList.ConcatItem(item:TCmdStrListItem); begin inherited Concat(item); end; {**************************************************************************** tdynamicarray ****************************************************************************} constructor tdynamicarray.create(Ablocksize:longword); begin FPosn:=0; FPosnblock:=nil; FFirstblock:=nil; FLastblock:=nil; FCurrBlockSize:=0; { Every block needs at least a header and alignment slack, therefore its size cannot be arbitrarily small. However, the blocksize argument is often confused with data size. See e.g. Mantis #20929. } if Ablocksize 255 then Inc(IncSize, FCurrBlockSize shr 2); inc(FCurrBlockSize,IncSize); end; if CurrBlockSize>FMaxBlocksize then FCurrBlockSize:=FMaxBlocksize; { Calculate the most optimal size so there is no alignment overhead lost in the heap manager } OptBlockSize:=cutils.Align(CurrBlockSize+dynamicblockbasesize,16)-dynamicblockbasesize-sizeof(ptrint); Getmem(nblock,OptBlockSize+dynamicblockbasesize); if not assigned(FFirstblock) then begin FFirstblock:=nblock; FPosnblock:=nblock; nblock^.pos:=0; end else begin FLastblock^.Next:=nblock; nblock^.pos:=FLastblock^.pos+FLastblock^.size; end; nblock^.used:=0; nblock^.size:=OptBlockSize; nblock^.Next:=nil; fillchar(nblock^.data,nblock^.size,0); FLastblock:=nblock; end; procedure tdynamicarray.align(i:longword); var j : longword; begin j:=(FPosn mod i); if j<>0 then begin j:=i-j; if FPosnblock^.used+j>FPosnblock^.size then begin dec(j,FPosnblock^.size-FPosnblock^.used); FPosnblock^.used:=FPosnblock^.size; grow; FPosnblock:=FLastblock; end; inc(FPosnblock^.used,j); inc(FPosn,j); end; end; procedure tdynamicarray.seek(i:longword); begin if (i=FPosnblock^.pos+FPosnblock^.size) then begin { set FPosnblock correct if the size is bigger then the current block } if FPosnblock^.pos>i then FPosnblock:=FFirstblock; while assigned(FPosnblock) do begin if FPosnblock^.pos+FPosnblock^.size>i then break; FPosnblock:=FPosnblock^.Next; end; { not found ? then increase blocks } if not assigned(FPosnblock) then begin repeat { the current FLastblock is now also fully used } FLastblock^.used:=FLastblock^.size; grow; FPosnblock:=FLastblock; until FPosnblock^.pos+FPosnblock^.size>=i; end; end; FPosn:=i; if FPosn-FPosnblock^.pos>FPosnblock^.used then FPosnblock^.used:=FPosn-FPosnblock^.pos; end; procedure tdynamicarray.write(const d;len:longword); var p : pchar; i,j : longword; begin p:=pchar(@d); while (len>0) do begin i:=FPosn-FPosnblock^.pos; if i+len>=FPosnblock^.size then begin j:=FPosnblock^.size-i; move(p^,FPosnblock^.data[i],j); inc(p,j); inc(FPosn,j); dec(len,j); FPosnblock^.used:=FPosnblock^.size; if assigned(FPosnblock^.Next) then FPosnblock:=FPosnblock^.Next else begin grow; FPosnblock:=FLastblock; end; end else begin move(p^,FPosnblock^.data[i],len); inc(p,len); inc(FPosn,len); i:=FPosn-FPosnblock^.pos; if i>FPosnblock^.used then FPosnblock^.used:=i; len:=0; end; end; end; procedure tdynamicarray.writestr(const s:string); begin write(s[1],length(s)); end; function tdynamicarray.read(var d;len:longword):longword; var p : pchar; i,j,res : longword; begin res:=0; p:=pchar(@d); while (len>0) do begin i:=FPosn-FPosnblock^.pos; if i+len>=FPosnblock^.used then begin j:=FPosnblock^.used-i; move(FPosnblock^.data[i],p^,j); inc(p,j); inc(FPosn,j); inc(res,j); dec(len,j); if assigned(FPosnblock^.Next) then FPosnblock:=FPosnblock^.Next else break; end else begin move(FPosnblock^.data[i],p^,len); inc(p,len); inc(FPosn,len); inc(res,len); len:=0; end; end; read:=res; end; procedure tdynamicarray.readstream(f:TCStream;maxlen:longword); var i,left : longword; begin repeat left:=FPosnblock^.size-FPosnblock^.used; if left>maxlen then left:=maxlen; i:=f.Read(FPosnblock^.data[FPosnblock^.used],left); dec(maxlen,i); inc(FPosnblock^.used,i); if FPosnblock^.used=FPosnblock^.size then begin if assigned(FPosnblock^.Next) then FPosnblock:=FPosnblock^.Next else begin grow; FPosnblock:=FLastblock; end; end; until (iother.size then exit(false); blockthis:=Firstblock; blockother:=other.FirstBlock; ofsthis:=0; ofsother:=0; while assigned(blockthis) and assigned(blockother) do begin remthis:=blockthis^.used-ofsthis; remother:=blockother^.used-ofsother; len:=min(remthis,remother); if not CompareMem(@blockthis^.data[ofsthis],@blockother^.data[ofsother],len) then exit(false); inc(ofsthis,len); inc(ofsother,len); if ofsthis=blockthis^.used then begin blockthis:=blockthis^.next; ofsthis:=0; end; if ofsother=blockother^.used then begin blockother:=blockother^.next; ofsother:=0; end; end; if assigned(blockthis) and not assigned(blockother) then result:=blockthis^.used=0 else if assigned(blockother) and not assigned(blockthis) then result:=blockother^.used=0 else result:=true; end; {**************************************************************************** thashset ****************************************************************************} constructor THashSet.Create(InitSize: Integer; OwnKeys, OwnObjects: Boolean); var I: Integer; begin inherited Create; FOwnsObjects := OwnObjects; FOwnsKeys := OwnKeys; I := 64; while I < InitSize do I := I shl 1; FBucketCount := I; FBucket := AllocMem(I * sizeof(PHashSetItem)); end; destructor THashSet.Destroy; begin Clear; FreeMem(FBucket); inherited Destroy; end; procedure THashSet.Clear; var I: Integer; item, next: PHashSetItem; begin for I := 0 to FBucketCount-1 do begin item := FBucket[I]; while Assigned(item) do begin next := item^.Next; if FOwnsObjects then FreeAndNil(item^.Data); FreeItem(item); item := next; end; end; FillChar(FBucket^, FBucketCount * sizeof(PHashSetItem), 0); end; function THashSet.Find(Key: Pointer; KeyLen: Integer): PHashSetItem; var Dummy: Boolean; begin Result := Lookup(Key, KeyLen, Dummy, False); end; function THashSet.FindOrAdd(Key: Pointer; KeyLen: Integer; var Found: Boolean): PHashSetItem; begin Result := Lookup(Key, KeyLen, Found, True); end; function THashSet.FindOrAdd(Key: Pointer; KeyLen: Integer): PHashSetItem; var Dummy: Boolean; begin Result := Lookup(Key, KeyLen, Dummy, True); end; function THashSet.Get(Key: Pointer; KeyLen: Integer): TObject; var e: PHashSetItem; Dummy: Boolean; begin e := Lookup(Key, KeyLen, Dummy, False); if Assigned(e) then Result := e^.Data else Result := nil; end; function THashSet.Lookup(Key: Pointer; KeyLen: Integer; var Found: Boolean; CanCreate: Boolean): PHashSetItem; var EntryPtr: PPHashSetItem; Entry: PHashSetItem; h: LongWord; begin h := FPHash(Key, KeyLen); EntryPtr := @FBucket[h and (FBucketCount-1)]; Entry := EntryPtr^; while Assigned(Entry) and not ((Entry^.HashValue = h) and (Entry^.KeyLength = KeyLen) and (CompareByte(Entry^.Key^, Key^, KeyLen) = 0)) do begin EntryPtr := @Entry^.Next; Entry := EntryPtr^; end; Found := Assigned(Entry); if Found or (not CanCreate) then begin Result := Entry; Exit; end; if FCount > FBucketCount then { arbitrary limit, probably too high } begin { rehash and repeat search } Resize(FBucketCount * 2); Result := Lookup(Key, KeyLen, Found, CanCreate); end else begin Result := CreateItem(Key, KeyLen, h); Inc(FCount); EntryPtr^ := Result; end; end; procedure THashSet.Resize(NewCapacity: LongWord); var p, chain: PPHashSetItem; i: Integer; e, n: PHashSetItem; begin p := AllocMem(NewCapacity * SizeOf(PHashSetItem)); for i := 0 to FBucketCount-1 do begin e := FBucket[i]; while Assigned(e) do begin chain := @p[e^.HashValue and (NewCapacity-1)]; n := e^.Next; e^.Next := chain^; chain^ := e; e := n; end; end; FBucketCount := NewCapacity; FreeMem(FBucket); FBucket := p; end; class procedure THashSet.FreeItem(item: PHashSetItem); begin Dispose(item); end; class function THashSet.SizeOfItem: Integer; begin Result := SizeOf(THashSetItem); end; function THashSet.CreateItem(Key: Pointer; KeyLen: Integer; HashValue: LongWord): PHashSetItem; var itemSize, keyOfs: SizeUint; begin itemSize := SizeOfItem; if FOwnsKeys then begin keyOfs := itemSize; Result := GetMem(keyOfs + SizeUint(KeyLen)); Result^.Key := Pointer(Result) + keyOfs; Move(Key^, Result^.Key^, KeyLen); end else begin Result := GetMem(itemSize); Result^.Key := Key; end; Result^.Next := nil; Result^.KeyLength := KeyLen; Result^.HashValue := HashValue; Result^.Data := nil; end; function THashSet.Remove(Entry: PHashSetItem): Boolean; var chain: PPHashSetItem; begin chain := @FBucket[Entry^.HashValue mod FBucketCount]; while Assigned(chain^) do begin if chain^ = Entry then begin chain^ := Entry^.Next; if FOwnsObjects then FreeAndNil(Entry^.Data); FreeItem(Entry); Dec(FCount); Result := True; Exit; end; chain := @chain^^.Next; end; Result := False; end; {**************************************************************************** ttaghashset ****************************************************************************} function TTagHashSet.Lookup(Key: Pointer; KeyLen: Integer; Tag: LongWord; var Found: Boolean; CanCreate: Boolean): PTagHashSetItem; var EntryPtr: PPTagHashSetItem; Entry: PTagHashSetItem; h: LongWord; begin h := FPHash(Key, KeyLen, Tag); EntryPtr := @PPTagHashSetItem(FBucket)[h and (FBucketCount-1)]; Entry := EntryPtr^; while Assigned(Entry) and not ((Entry^.Item.HashValue = h) and (Entry^.Item.KeyLength = KeyLen) and (Entry^.Tag = Tag) and (CompareByte(Entry^.Item.Key^, Key^, KeyLen) = 0)) do begin EntryPtr := @Entry^.Item.Next; Entry := EntryPtr^; end; Found := Assigned(Entry); if Found or (not CanCreate) then begin Result := Entry; Exit; end; if FCount > FBucketCount then { arbitrary limit, probably too high } begin { rehash and repeat search } Resize(FBucketCount * 2); Result := Lookup(Key, KeyLen, Tag, Found, CanCreate); end else begin Result := PTagHashSetItem(CreateItem(Key, KeyLen, h)); Result^.Tag := Tag; Inc(FCount); EntryPtr^ := Result; end; end; class function TTagHashSet.SizeOfItem: Integer; begin Result := SizeOf(TTagHashSetItem); end; function TTagHashSet.Find(Key: Pointer; KeyLen: Integer; Tag: LongWord): PTagHashSetItem; var Dummy: Boolean; begin Result := Lookup(Key, KeyLen, Tag, Dummy, False); end; function TTagHashSet.FindOrAdd(Key: Pointer; KeyLen: Integer; Tag: LongWord; var Found: Boolean): PTagHashSetItem; begin Result := Lookup(Key, KeyLen, Tag, Found, True); end; function TTagHashSet.FindOrAdd(Key: Pointer; KeyLen: Integer; Tag: LongWord): PTagHashSetItem; var Dummy: Boolean; begin Result := Lookup(Key, KeyLen, Tag, Dummy, True); end; function TTagHashSet.Get(Key: Pointer; KeyLen: Integer; Tag: LongWord): TObject; var e: PTagHashSetItem; Dummy: Boolean; begin e := Lookup(Key, KeyLen, Tag, Dummy, False); if Assigned(e) then Result := e^.Item.Data else Result := nil; end; {**************************************************************************** tbitset ****************************************************************************} function tbitset.getdatasize: longint; begin result:=length(fdata); end; constructor tbitset.create(initsize: longint); begin create_bytesize((initsize+7) div 8); end; constructor tbitset.create_bytesize(bytesize: longint); begin setLength(fdata,bytesize); clear; end; destructor tbitset.destroy; begin fdata:=Nil; inherited destroy; end; procedure tbitset.clear; begin if assigned(fdata) then fillchar(fdata[0],length(fdata),0); end; procedure tbitset.grow(nsize: longint); begin setlength(fdata,nsize); end; procedure tbitset.include(index: longint); var dataindex: longint; begin { don't use bitpacked array, not endian-safe } dataindex:=index shr 3; if (dataindex>=datasize) then grow(dataindex+16); fdata[dataindex]:=fdata[dataindex] or (1 shl (index and 7)); end; procedure tbitset.exclude(index: longint); var dataindex: longint; begin dataindex:=index shr 3; if (dataindex>=datasize) then exit; fdata[dataindex]:=fdata[dataindex] and not(1 shl (index and 7)); end; function tbitset.isset(index: longint): boolean; var dataindex: longint; begin dataindex:=index shr 3; result:= (dataindex0); end; procedure tbitset.addset(aset: tbitset); var i: longint; begin if (aset.datasize>datasize) then grow(aset.datasize); for i:=0 to aset.datasize-1 do fdata[i]:=fdata[i] or aset.data[i]; end; procedure tbitset.subset(aset: tbitset); var i: longint; begin for i:=0 to min(datasize,aset.datasize)-1 do fdata[i]:=fdata[i] and not(aset.data[i]); end; end.