index 85133c3efbc10d47581f03f132ed6bda137d36b7..cb6d35c0f8c684b58320c369907b9a3141e1d664 100644 (file)
--- a/src/shared/hashtable.pas
+++ b/src/shared/hashtable.pas
-(* Copyright (C) DooM 2D:Forever Developers
+(* Copyright (C) Doom 2D: Forever Developers
*
* 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
*
* 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 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
interface
interface
-
+(*
+ * HashObjT: class that contains class methods:
+ * class function hash (const[ref] k: KeyT): LongWord;
+ * class function equ (const[ref] a, b: KeyT): Boolean;
+ * class procedure freekey (var k: KeyT); // this may free key
+ *)
type
// WARNING! don't put structures into hash, use ponters or ids!
type
// WARNING! don't put structures into hash, use ponters or ids!
- generic THashBase<KeyT, ValueT> = class(TObject)
+ generic THashBase<KeyT, ValueT, HashObjT> = class(TObject)
private
const InitSize = {$IF DEFINED(RBHASH_SANITY_CHECKS)}16{$ELSE}256{$ENDIF}; // *MUST* be power of two
const LoadFactorPrc = 90; // it is ok for robin hood hashes
private
const InitSize = {$IF DEFINED(RBHASH_SANITY_CHECKS)}16{$ELSE}256{$ENDIF}; // *MUST* be power of two
const LoadFactorPrc = 90; // it is ok for robin hood hashes
property keyhash: LongWord read hash; // cannot be 0
end;
property keyhash: LongWord read hash; // cannot be 0
end;
- type THashFn = function (constref o: KeyT): LongWord;
- type TEquFn = function (constref a, b: KeyT): Boolean;
- type TFreeKeyFn = procedure (var k: KeyT); // this may free key
type TFreeValueFn = procedure (var v: ValueT); // this may free value
type TIteratorFn = function (constref k: KeyT; constref v: ValueT): Boolean is nested; // return `true` to stop
type TIteratorExFn = function (constref k: KeyT; constref v: ValueT; keyhash: LongWord): Boolean is nested; // return `true` to stop
type TFreeValueFn = procedure (var v: ValueT); // this may free value
type TIteratorFn = function (constref k: KeyT; constref v: ValueT): Boolean is nested; // return `true` to stop
type TIteratorExFn = function (constref k: KeyT; constref v: ValueT; keyhash: LongWord): Boolean is nested; // return `true` to stop
end;
private
end;
private
- hashfn: THashFn;
- equfn: TEquFn;
- freekeyfn: TFreeKeyFn;
freevalfn: TFreeValueFn;
mBuckets: array of PEntry; // entries, points to mEntries elements
mBucketsUsed: Integer;
freevalfn: TFreeValueFn;
mBuckets: array of PEntry; // entries, points to mEntries elements
mBucketsUsed: Integer;
procedure freeEntries ();
public
procedure freeEntries ();
public
- constructor Create (ahashfn: THashFn; aequfn: TEquFn; afreekeyfn: TFreeKeyFn=nil; afreevalfn: TFreeValueFn=nil);
+ constructor Create (afreevalfn: TFreeValueFn=nil);
destructor Destroy (); override;
procedure clear ();
destructor Destroy (); override;
procedure clear ();
type
type
- THashIntInt = specialize THashBase<Integer, Integer>;
- THashStrInt = specialize THashBase<AnsiString, Integer>;
- THashIntStr = specialize THashBase<Integer, AnsiString>;
- THashStrStr = specialize THashBase<AnsiString, AnsiString>;
+ THashKeyInt = class
+ public
+ class function hash (const k: Integer): LongWord; inline;
+ class function equ (const a, b: Integer): Boolean; inline;
+ class procedure freekey (k: Integer); inline;
+ end;
+
+ THashKeyStr = class
+ public
+ class function hash (const k: AnsiString): LongWord; inline;
+ class function equ (const a, b: AnsiString): Boolean; inline;
+ class procedure freekey (var k: AnsiString); inline;
+ end;
+ // case-insensitive (ansi)
+ THashKeyStrAnsiCI = class
+ public
+ class function hash (const k: AnsiString): LongWord; inline;
+ class function equ (const a, b: AnsiString): Boolean; inline;
+ class procedure freekey (var k: AnsiString); inline;
+ end;
-function hashNewIntInt (): THashIntInt;
-function hashNewStrInt (): THashStrInt;
-function hashNewIntStr (): THashIntStr;
-function hashNewStrStr (): THashStrStr;
+type
+ THashIntInt = specialize THashBase<Integer, Integer, THashKeyInt>;
+ THashStrInt = specialize THashBase<AnsiString, Integer, THashKeyStr>;
+ THashStrCIInt = specialize THashBase<AnsiString, Integer, THashKeyStrAnsiCI>;
+ THashIntStr = specialize THashBase<Integer, AnsiString, THashKeyInt>;
+ THashStrStr = specialize THashBase<AnsiString, AnsiString, THashKeyStr>;
+ THashStrCIStr = specialize THashBase<AnsiString, AnsiString, THashKeyStrAnsiCI>;
+ THashStrVariant = specialize THashBase<AnsiString, Variant, THashKeyStr>;
+ THashStrCIVariant = specialize THashBase<AnsiString, Variant, THashKeyStrAnsiCI>;
function u32Hash (a: LongWord): LongWord; inline;
function fnvHash (constref buf; len: LongWord): LongWord;
function u32Hash (a: LongWord): LongWord; inline;
function fnvHash (constref buf; len: LongWord): LongWord;
-function joaatHash (constref buf; len: LongWord): LongWord;
+function joaatHash (constref buf; len: LongWord; seed: LongWord=0): LongWord;
+function joaatHashPtr (buf: Pointer; len: LongWord; seed: LongWord=0): LongWord;
-function nextPOT (x: LongWord): LongWord; inline;
-
-
-// for integer keys
-function hashIntEqu (constref a, b: Integer): Boolean;
-function hashIntHash (constref k: Integer): LongWord;
-function hashStrEqu (constref a, b: AnsiString): Boolean;
-function hashStrHash (constref k: AnsiString): LongWord;
-procedure hashStrFree (var s: AnsiString);
+// has to be public due to FPC generics limitation
+function nextPOTU32 (x: LongWord): LongWord; inline;
implementation
uses
implementation
uses
- SysUtils;
+ SysUtils, Variants;
// ////////////////////////////////////////////////////////////////////////// //
{$PUSH}
{$RANGECHECKS OFF}
// ////////////////////////////////////////////////////////////////////////// //
{$PUSH}
{$RANGECHECKS OFF}
-function nextPOT (x: LongWord): LongWord; inline;
+function nextPOTU32 (x: LongWord): LongWord; inline;
begin
result := x;
result := result or (result shr 1);
begin
result := x;
result := result or (result shr 1);
{$POP}
{$POP}
-// ////////////////////////////////////////////////////////////////////////// //
-function hashIntEqu (constref a, b: Integer): Boolean; begin result := (a = b); end;
-function hashStrEqu (constref a, b: AnsiString): Boolean; begin result := (a = b); end;
-procedure hashStrFree (var s: AnsiString); begin s := ''; end;
-
-{$PUSH}
-{$RANGECHECKS OFF}
-function hashIntHash (constref k: Integer): LongWord;
-begin
- result := LongWord(k);
- result -= (result shl 6);
- result := result xor (result shr 17);
- result -= (result shl 9);
- result := result xor (result shl 4);
- result -= (result shl 3);
- result := result xor (result shl 10);
- result := result xor (result shr 15);
-end;
-
-function hashStrHash (constref k: AnsiString): LongWord;
-begin
- if (Length(k) > 0) then result := fnvHash(PAnsiChar(k)^, Length(k)) else result := 0;
-end;
-{$POP}
-
-
-function hashNewIntInt (): THashIntInt;
-begin
- result := THashIntInt.Create(hashIntHash, hashIntEqu);
-end;
-
-
-function hashNewStrInt (): THashStrInt;
-begin
- result := THashStrInt.Create(hashStrHash, hashStrEqu, hashStrFree);
-end;
-
-
-function hashNewIntStr (): THashIntStr;
-begin
- result := THashIntStr.Create(hashIntHash, hashIntEqu, nil, hashStrFree);
-end;
-
-
-function hashNewStrStr (): THashStrStr;
-begin
- result := THashStrStr.Create(hashStrHash, hashStrEqu, hashStrFree, hashStrFree);
-end;
-
-
// ////////////////////////////////////////////////////////////////////////// //
{$PUSH}
{$RANGECHECKS OFF}
// ////////////////////////////////////////////////////////////////////////// //
{$PUSH}
{$RANGECHECKS OFF}
reset(aseed);
end;
reset(aseed);
end;
-
procedure TJoaatHasher.reset (); inline; overload;
begin
hash := seed;
end;
procedure TJoaatHasher.reset (); inline; overload;
begin
hash := seed;
end;
-
procedure TJoaatHasher.reset (aseed: LongWord); inline; overload;
begin
seed := aseed;
hash := aseed;
end;
procedure TJoaatHasher.reset (aseed: LongWord); inline; overload;
begin
seed := aseed;
hash := aseed;
end;
-
procedure TJoaatHasher.put (constref buf; len: LongWord);
var
bytes: PByte;
procedure TJoaatHasher.put (constref buf; len: LongWord);
var
bytes: PByte;
hash := h;
end;
hash := h;
end;
-
function TJoaatHasher.value: LongWord; inline;
begin
result := hash;
function TJoaatHasher.value: LongWord; inline;
begin
result := hash;
{$POP}
{$POP}
-function joaatHash (constref buf; len: LongWord): LongWord;
+// ////////////////////////////////////////////////////////////////////////// //
+{$PUSH}
+{$RANGECHECKS OFF}
+function joaatHash (constref buf; len: LongWord; seed: LongWord=0): LongWord;
var
var
- h: TJoaatHasher;
+ b: PByte;
+ f: LongWord;
begin
begin
- h := TJoaatHasher.Create(0);
- h.put(PByte(@buf)^, len);
- result := h.value;
+ result := seed;
+ b := PByte(@buf);
+ for f := 1 to len do
+ begin
+ result += b^;
+ result += (result shl 10);
+ result := result xor (result shr 6);
+ Inc(b);
+ end;
+ // finalize
+ result += (result shl 3);
+ result := result xor (result shr 11);
+ result += (result shl 15);
end;
end;
+function joaatHashPtr (buf: Pointer; len: LongWord; seed: LongWord=0): LongWord;
+var
+ b: PByte;
+ f: LongWord;
+begin
+ result := seed;
+ b := PByte(buf);
+ for f := 1 to len do
+ begin
+ result += b^;
+ result += (result shl 10);
+ result := result xor (result shr 6);
+ Inc(b);
+ end;
+ // finalize
+ result += (result shl 3);
+ result := result xor (result shr 11);
+ result += (result shl 15);
+end;
+{$POP}
-// ////////////////////////////////////////////////////////////////////////// //
{$PUSH}
{$RANGECHECKS OFF}
// fnv-1a: http://www.isthe.com/chongo/tech/comp/fnv/
{$PUSH}
{$RANGECHECKS OFF}
// fnv-1a: http://www.isthe.com/chongo/tech/comp/fnv/
end;
{$POP}
end;
{$POP}
-
{$PUSH}
{$RANGECHECKS OFF}
function u32Hash (a: LongWord): LongWord; inline;
{$PUSH}
{$RANGECHECKS OFF}
function u32Hash (a: LongWord): LongWord; inline;
end;
{$POP}
end;
{$POP}
+function locase1251 (ch: AnsiChar): AnsiChar; inline;
+begin
+ if ch < #128 then
+ begin
+ if (ch >= 'A') and (ch <= 'Z') then Inc(ch, 32);
+ end
+ else
+ begin
+ if (ch >= #192) and (ch <= #223) then
+ begin
+ Inc(ch, 32);
+ end
+ else
+ begin
+ case ch of
+ #168, #170, #175: Inc(ch, 16);
+ #161, #178: Inc(ch);
+ end;
+ end;
+ end;
+ result := ch;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+// THashKeyInt
+class function THashKeyInt.hash (const k: Integer): LongWord; inline;
+begin
+ result := LongWord(k);
+ result -= (result shl 6);
+ result := result xor (result shr 17);
+ result -= (result shl 9);
+ result := result xor (result shl 4);
+ result -= (result shl 3);
+ result := result xor (result shl 10);
+ result := result xor (result shr 15);
+end;
+
+class function THashKeyInt.equ (const a, b: Integer): Boolean; inline; begin result := (a = b); end;
+class procedure THashKeyInt.freekey (k: Integer); inline; begin end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+// THashKeyStr
+class function THashKeyStr.hash (const k: AnsiString): LongWord; inline; begin if (Length(k) > 0) then result := fnvHash((@k[1])^, Length(k)) else result := 0; end;
+class function THashKeyStr.equ (const a, b: AnsiString): Boolean; inline; begin result := (a = b); end;
+class procedure THashKeyStr.freekey (var k: AnsiString); inline; begin k := ''; end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+// case-insensitive (ansi)
+{$PUSH}
+{$RANGECHECKS OFF}
+// fnv-1a: http://www.isthe.com/chongo/tech/comp/fnv/
+function fnvHashLo (constref buf; len: LongWord): LongWord;
+var
+ b: PAnsiChar;
+begin
+ b := @buf;
+ result := 2166136261; // fnv offset basis
+ while (len > 0) do
+ begin
+ result := result xor Byte(locase1251(b^));
+ result := result*16777619; // 32-bit fnv prime
+ Inc(b);
+ Dec(len);
+ end;
+end;
+{$POP}
+
+class function THashKeyStrAnsiCI.hash (const k: AnsiString): LongWord; inline; begin if (Length(k) > 0) then result := fnvHashLo((@k[1])^, Length(k)) else result := 0; end;
+class function THashKeyStrAnsiCI.equ (const a, b: AnsiString): Boolean; inline;
+var
+ f: Integer;
+begin
+ result := false;
+ if (Length(a) = Length(b)) then
+ begin
+ for f := 1 to Length(a) do if (locase1251(a[f]) <> locase1251(b[f])) then exit;
+ end;
+ result := true;
+end;
+class procedure THashKeyStrAnsiCI.freekey (var k: AnsiString); inline; begin k := ''; end;
+
// ////////////////////////////////////////////////////////////////////////// //
function THashBase.TEntry.getEmpty (): Boolean; inline; begin result := (hash = 0); end;
// ////////////////////////////////////////////////////////////////////////// //
function THashBase.TEntry.getEmpty (): Boolean; inline; begin result := (hash = 0); end;
@@ -388,17 +463,12 @@ function THashBase.TEntry.getEmpty (): Boolean; inline; begin result := (hash =
function THashBase.getCapacity (): Integer; inline; begin result := Length(mBuckets); end;
function THashBase.getCapacity (): Integer; inline; begin result := Length(mBuckets); end;
-constructor THashBase.Create (ahashfn: THashFn; aequfn: TEquFn; afreekeyfn: TFreeKeyFn=nil; afreevalfn: TFreeValueFn=nil);
+constructor THashBase.Create (afreevalfn: TFreeValueFn=nil);
begin
begin
- if not assigned(ahashfn) then raise Exception.Create('cannot create hash without hash function');
- if not assigned(aequfn) then raise Exception.Create('cannot create hash without equality function');
-
- hashfn := ahashfn;
- equfn := aequfn;
- freekeyfn := afreekeyfn;
freevalfn := afreevalfn;
mSeed := u32Hash($29a);
freevalfn := afreevalfn;
mSeed := u32Hash($29a);
+ mFreeEntryHead := nil;
mFirstEntry := -1;
mLastEntry := -1;
clear();
mFirstEntry := -1;
mLastEntry := -1;
clear();
destructor THashBase.Destroy ();
begin
destructor THashBase.Destroy ();
begin
+ freeEntries();
mBuckets := nil;
mEntries := nil;
inherited;
mBuckets := nil;
mEntries := nil;
inherited;
e := @mEntries[f];
if not e.empty then
begin
e := @mEntries[f];
if not e.empty then
begin
- if assigned(freekeyfn) then freekeyfn(e.key);
- if assigned(freevalfn) then freevalfn(e.value);
+ HashObjT.freekey(e.key);
+ if assigned(freevalfn) then freevalfn(e.value) else e.value := Default(ValueT);
e.key := Default(KeyT);
e.value := Default(ValueT);
e.hash := 0;
e.key := Default(KeyT);
e.value := Default(ValueT);
e.hash := 0;
procedure THashBase.clear ();
procedure THashBase.clear ();
-//var idx: Integer;
begin
freeEntries();
begin
freeEntries();
+ {
SetLength(mBuckets, InitSize);
FillChar(mBuckets[0], InitSize*sizeof(mBuckets[0]), 0);
SetLength(mBuckets, InitSize);
FillChar(mBuckets[0], InitSize*sizeof(mBuckets[0]), 0);
- //for idx := 0 to High(mBuckets) do mBuckets[idx] := nil;
SetLength(mEntries, InitSize);
FillChar(mEntries[0], InitSize*sizeof(mEntries[0]), 0);
SetLength(mEntries, InitSize);
FillChar(mEntries[0], InitSize*sizeof(mEntries[0]), 0);
+ }
+ mFreeEntryHead := nil;
+ mBuckets := nil;
+ mEntries := nil;
+ mFirstEntry := -1;
+ mLastEntry := -1;
mBucketsUsed := 0;
end;
mBucketsUsed := 0;
end;
begin
if (mFreeEntryHead = nil) then
begin
begin
if (mFreeEntryHead = nil) then
begin
+ // nothing was allocated, so allocate something now
+ if (Length(mBuckets) = 0) then
+ begin
+ assert(Length(mEntries) = 0);
+ assert(mFirstEntry = -1);
+ assert(mLastEntry = -1);
+ assert(mBucketsUsed = 0);
+ {$IFDEF RBHASH_SANITY_CHECKS}
+ mEntriesUsed := 0;
+ {$ENDIF}
+ SetLength(mBuckets, InitSize);
+ FillChar(mBuckets[0], InitSize*sizeof(mBuckets[0]), 0);
+ SetLength(mEntries, InitSize);
+ FillChar(mEntries[0], InitSize*sizeof(mEntries[0]), 0);
+ end;
if (mLastEntry = High(mEntries)) then raise Exception.Create('internal error in hash entry allocator (0.0)');
Inc(mLastEntry);
if (mFirstEntry = -1) then
if (mLastEntry = High(mEntries)) then raise Exception.Create('internal error in hash entry allocator (0.0)');
Inc(mLastEntry);
if (mFirstEntry = -1) then
if (idx < 0) or (idx > High(mEntries)) then raise Exception.Create('internal error in hash entry allocator (invalid entry address)');
if (e <> @mEntries[idx]) then raise Exception.Create('internal error in hash entry allocator (wtf?!)');
{$ENDIF}
if (idx < 0) or (idx > High(mEntries)) then raise Exception.Create('internal error in hash entry allocator (invalid entry address)');
if (e <> @mEntries[idx]) then raise Exception.Create('internal error in hash entry allocator (wtf?!)');
{$ENDIF}
- if assigned(freekeyfn) then freekeyfn(e.key);
- if assigned(freevalfn) then freevalfn(e.value);
+ HashObjT.freekey(e.key);
+ if assigned(freevalfn) then freevalfn(e.value) else e.value := Default(ValueT);
{$IFDEF RBHASH_SANITY_CHECKS}
Dec(mEntriesUsed);
{$ENDIF}
{$IFDEF RBHASH_SANITY_CHECKS}
Dec(mEntriesUsed);
{$ENDIF}
if (keyhashin <> nil) then
begin
khash := keyhashin^;
if (keyhashin <> nil) then
begin
khash := keyhashin^;
- if (khash = 0) then khash := hashfn(akey);
+ if (khash = 0) then khash := HashObjT.hash(akey);
end
else
begin
end
else
begin
- khash := hashfn(akey);
+ khash := HashObjT.hash(akey);
end;
if (khash = 0) then khash := $29a;
end;
if (khash = 0) then khash := $29a;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
- result := (mBuckets[idx].hash = khash) and equfn(mBuckets[idx].key, akey);
+ result := (mBuckets[idx].hash = khash) and HashObjT.equ(mBuckets[idx].key, akey);
if result then break;
idx := (idx+1) and bhigh;
end;
if result then break;
idx := (idx+1) and bhigh;
end;
if (keyhashin <> nil) then
begin
khash := keyhashin^;
if (keyhashin <> nil) then
begin
khash := keyhashin^;
- if (khash = 0) then khash := hashfn(akey);
+ if (khash = 0) then khash := HashObjT.hash(akey);
end
else
begin
end
else
begin
- khash := hashfn(akey);
+ khash := HashObjT.hash(akey);
end;
if (khash = 0) then khash := $29a;
end;
if (khash = 0) then khash := $29a;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
- result := (mBuckets[idx].hash = khash) and equfn(mBuckets[idx].key, akey);
+ result := (mBuckets[idx].hash = khash) and HashObjT.equ(mBuckets[idx].key, akey);
if result then begin rval := mBuckets[idx].value; break; end;
idx := (idx+1) and bhigh;
end;
if result then begin rval := mBuckets[idx].value; break; end;
idx := (idx+1) and bhigh;
end;
bhigh := High(mBuckets);
xseed := mSeed;
bhigh := High(mBuckets);
xseed := mSeed;
- khash := hashfn(akey);
+ khash := HashObjT.hash(akey);
if (khash = 0) then khash := $29a;
if (keyhashout <> nil) then keyhashout^ := khash;
idx := (khash xor xseed) and bhigh;
if (khash = 0) then khash := $29a;
if (keyhashout <> nil) then keyhashout^ := khash;
idx := (khash xor xseed) and bhigh;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
- result := (mBuckets[idx].hash = khash) and equfn(mBuckets[idx].key, akey);
+ result := (mBuckets[idx].hash = khash) and HashObjT.equ(mBuckets[idx].key, akey);
if result then
begin
// replace element
if result then
begin
// replace element
- if assigned(freekeyfn) then freekeyfn(mBuckets[idx].key);
- if assigned(freevalfn) then freevalfn(mBuckets[idx].value);
+ HashObjT.freekey(mBuckets[idx].key);
+ if assigned(freevalfn) then freevalfn(mBuckets[idx].value) else mBuckets[idx].value := Default(ValueT);
mBuckets[idx].key := akey;
mBuckets[idx].value := aval;
exit;
mBuckets[idx].key := akey;
mBuckets[idx].value := aval;
exit;
if (keyhashin <> nil) then
begin
khash := keyhashin^;
if (keyhashin <> nil) then
begin
khash := keyhashin^;
- if (khash = 0) then khash := hashfn(akey);
+ if (khash = 0) then khash := HashObjT.hash(akey);
end
else
begin
end
else
begin
- khash := hashfn(akey);
+ khash := HashObjT.hash(akey);
end;
if (khash = 0) then khash := $29a;
end;
if (khash = 0) then khash := $29a;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
if (mBuckets[idx] = nil) then break;
pdist := distToStIdx(idx);
if (dist > pdist) then break;
- result := (mBuckets[idx].hash = khash) and equfn(mBuckets[idx].key, akey);
+ result := (mBuckets[idx].hash = khash) and HashObjT.equ(mBuckets[idx].key, akey);
if result then break;
idx := (idx+1) and bhigh;
end;
if result then break;
idx := (idx+1) and bhigh;
end;
if (cnt = mBucketsUsed) and (idx <> mLastEntry) then raise Exception.Create('internal error in rehash: inconsistent (2)');
{$ENDIF}
// no need to recalculate hash
if (cnt = mBucketsUsed) and (idx <> mLastEntry) then raise Exception.Create('internal error in rehash: inconsistent (2)');
{$ENDIF}
// no need to recalculate hash
- //e.hash := hashfn(e.key) xor mSeed; if (e.hash = 0) then e.hash := $29a;
putEntryInternal(e);
end
else
putEntryInternal(e);
end
else
lastfree := e;
end;
end;
lastfree := e;
end;
end;
- if (lastfree <> nil) then e.nextFree := nil;
+ if (lastfree <> nil) then lastfree.nextFree := nil;
{$IFDEF RBHASH_SANITY_CHECKS}
if (cnt <> mBucketsUsed) then raise Exception.Create('internal error in hash table resize (invalid first/last range; 0)');
if (cnt <> mEntriesUsed) then raise Exception.Create('internal error in hash table resize (invalid first/last range; 1)');
{$IFDEF RBHASH_SANITY_CHECKS}
if (cnt <> mBucketsUsed) then raise Exception.Create('internal error in hash table resize (invalid first/last range; 0)');
if (cnt <> mEntriesUsed) then raise Exception.Create('internal error in hash table resize (invalid first/last range; 1)');
cnt: Integer;
{$ENDIF}
begin
cnt: Integer;
{$ENDIF}
begin
- newsz := nextPOT(LongWord(mBucketsUsed));
+ newsz := nextPOTU32(LongWord(mBucketsUsed));
if (newsz >= 1024*1024*1024) then exit;
if (newsz*2 >= Length(mBuckets)) then exit;
if (newsz*2 < 128) then exit;
if (newsz >= 1024*1024*1024) then exit;
if (newsz*2 >= Length(mBuckets)) then exit;
if (newsz*2 < 128) then exit;