Big update for Generics.Collections to latest version (compilable with trunk and 3.0.4)

git-svn-id: trunk@57795 -
This commit is contained in:
hnb 2018-05-06 17:10:46 +00:00
parent dd7422fede
commit 9b1a4ea081
9 changed files with 4201 additions and 166 deletions

View File

@ -8,7 +8,6 @@ program TStackProject;
uses
SysUtils,
Windows,
Generics.Collections;
type

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,14 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgment
Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
many new types and major refactoring of entire library
Thanks to mORMot (http://synopse.info) project for the best implementations
of hashing functions like crc32c and xxHash32 :)
**********************************************************************}
unit Generics.Defaults;
@ -554,7 +562,7 @@ type
EqualityComparer_Method_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method );
EqualityComparer_Variant_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant);
EqualityComparer_Pointer_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer);
{$WARNINGS ON}
{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
private class var
// IEqualityComparer VMT
FEqualityComparer_Int8_VMT : THashFactory.TEqualityComparerVMT;
@ -673,7 +681,7 @@ type
ExtendedEqualityComparer_Method_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method ; GetHashList: @TExtendedHashFactory.Method );
ExtendedEqualityComparer_Variant_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant; GetHashList: @TExtendedHashFactory.Variant);
ExtendedEqualityComparer_Pointer_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer; GetHashList: @TExtendedHashFactory.Pointer);
{$WARNINGS ON}
{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
private class var
// IExtendedEqualityComparer VMT
FExtendedEqualityComparer_Int8_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
@ -857,6 +865,12 @@ type
class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
end;
TmORMotHashFactory = class(THashFactory)
public
class function GetHashService: THashServiceClass; override;
class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
end;
{ TAdler32HashFactory }
TAdler32HashFactory = class(THashFactory)
@ -922,7 +936,7 @@ type
class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override;
end;
TDefaultHashFactory = TDelphiQuadrupleHashFactory;
TDefaultHashFactory = TmORMotHashFactory;
TDefaultGenericInterface = (giComparer, giEqualityComparer, giExtendedEqualityComparer);
@ -2782,6 +2796,18 @@ begin
Result := DelphiHashLittle(AKey, ASize, AInitVal);
end;
{ TmORMotHashFactory }
class function TmORMotHashFactory.GetHashService: THashServiceClass;
begin
Result := THashService<TmORMotHashFactory>;
end;
class function TmORMotHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
begin
Result := mORMotHasher(AInitVal, AKey, ASize);
end;
{ TAdler32HashFactory }
class function TAdler32HashFactory.GetHashService: THashServiceClass;
@ -2879,7 +2905,7 @@ begin
else
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
end;
{$WARNINGS ON}
{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
end;
{ TDelphiQuadrupleHashFactory }
@ -3255,7 +3281,7 @@ begin
giEqualityComparer:
begin
if AFactory = nil then
AFactory := TDelphiHashFactory;
AFactory := TDefaultHashFactory;
Exit(
AFactory.GetHashService.LookupEqualityComparer(ATypeInfo, ASize));

View File

@ -14,6 +14,14 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgment
Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
many new types and major refactoring of entire library.
Thanks to mORMot (http://synopse.info) project for the best implementations
of hashing functions like crc32c and xxHash32 :)
**********************************************************************}
unit Generics.Hashes;
@ -64,6 +72,14 @@ function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32;
// https://code.google.com/p/hedgewars/source/browse/hedgewars/adler32.pas
function Adler32(AKey: Pointer; ALength: SizeInt): UInt32;
function sdbm(AKey: Pointer; ALength: SizeInt): UInt32;
function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
type
THasher = function(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
var
crc32c: THasher;
mORMotHasher: THasher;
implementation
@ -911,5 +927,663 @@ begin
Result := Int32(c);
end;
{$ifdef CPU64}
{$define PUREPASCAL}
{$ifdef CPUX64}
{$define CPUINTEL}
{$ASMMODE INTEL}
{$endif CPUX64}
{$else}
{$ifdef CPUX86}
{$ifndef FPC_PIC}
{$define CPUINTEL}
{$ASMMODE INTEL}
{$else}
{ Assembler code uses references to static
variables with are not PIC ready }
{$define PUREPASCAL}
{$endif}
{$else CPUX86}
{$define PUREPASCAL}
{$endif}
{$endif CPU64}
{$ifdef CPUARM} // circumvent FPC issue on ARM
function ToByte(value: cardinal): cardinal; inline;
begin
result := value and $ff;
end;
{$else}
type ToByte = byte;
{$endif}
{$ifdef CPUINTEL} // use optimized x86/x64 asm versions for xxHash32
{$ifdef CPUX86}
function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
asm
xchg edx, ecx
push ebp
push edi
lea ebp, [ecx+edx]
push esi
push ebx
sub esp, 8
cmp edx, 15
mov ebx, eax
mov dword ptr [esp], edx
lea eax, [ebx+165667B1H]
jbe @2
lea eax, [ebp-10H]
lea edi, [ebx+24234428H]
lea esi, [ebx-7A143589H]
mov dword ptr [esp+4H], ebp
mov edx, eax
lea eax, [ebx+61C8864FH]
mov ebp, edx
@1: mov edx, dword ptr [ecx]
imul edx, edx, -2048144777
add edi, edx
rol edi, 13
imul edi, edi, -1640531535
mov edx, dword ptr [ecx+4]
imul edx, edx, -2048144777
add esi, edx
rol esi, 13
imul esi, esi, -1640531535
mov edx, dword ptr [ecx+8]
imul edx, edx, -2048144777
add ebx, edx
rol ebx, 13
imul ebx, ebx, -1640531535
mov edx, dword ptr [ecx+12]
lea ecx, [ecx+16]
imul edx, edx, -2048144777
add eax, edx
rol eax, 13
imul eax, eax, -1640531535
cmp ebp, ecx
jnc @1
rol edi, 1
rol esi, 7
rol ebx, 12
add esi, edi
mov ebp, dword ptr [esp+4H]
ror eax, 14
add ebx, esi
add eax, ebx
@2: lea esi, [ecx+4H]
add eax, dword ptr [esp]
cmp ebp, esi
jc @4
mov ebx, esi
nop
@3: imul edx, dword ptr [ebx-4H], -1028477379
add ebx, 4
add eax, edx
ror eax, 15
imul eax, eax, 668265263
cmp ebp, ebx
jnc @3
lea edx, [ebp-4H]
sub edx, ecx
mov ecx, edx
and ecx, 0FFFFFFFCH
add ecx, esi
@4: cmp ebp, ecx
jbe @6
@5: movzx edx, byte ptr [ecx]
add ecx, 1
imul edx, edx, 374761393
add eax, edx
rol eax, 11
imul eax, eax, -1640531535
cmp ebp, ecx
jnz @5
nop
@6: mov edx, eax
add esp, 8
shr edx, 15
xor eax, edx
imul eax, eax, -2048144777
pop ebx
pop esi
mov edx, eax
shr edx, 13
xor eax, edx
imul eax, eax, -1028477379
pop edi
pop ebp
mov edx, eax
shr edx, 16
xor eax, edx
end;
{$endif CPUX86}
{$ifdef CPUX64}
function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
asm
{$ifndef WIN64} // crc=rdi P=rsi len=rdx
mov r8, rdi
mov rcx, rsi
{$else} // crc=r8 P=rcx len=rdx
mov r10, r8
mov r8, rcx
mov rcx, rdx
mov rdx, r10
push rsi // Win64 expects those registers to be preserved
push rdi
{$endif}
// P=r8 len=rcx crc=rdx
push rbx
lea r10, [rcx+rdx]
cmp rdx, 15
lea eax, [r8+165667B1H]
jbe @2
lea rsi, [r10-10H]
lea ebx, [r8+24234428H]
lea edi, [r8-7A143589H]
lea eax, [r8+61C8864FH]
@1: imul r9d, dword ptr [rcx], -2048144777
add rcx, 16
imul r11d, dword ptr [rcx-0CH], -2048144777
add ebx, r9d
lea r9d, [r11+rdi]
rol ebx, 13
rol r9d, 13
imul ebx, ebx, -1640531535
imul edi, r9d, -1640531535
imul r9d, dword ptr [rcx-8H], -2048144777
add r8d, r9d
imul r9d, dword ptr [rcx-4H], -2048144777
rol r8d, 13
imul r8d, r8d, -1640531535
add eax, r9d
rol eax, 13
imul eax, eax, -1640531535
cmp rsi, rcx
jnc @1
rol edi, 7
rol ebx, 1
rol r8d, 12
mov r9d, edi
ror eax, 14
add r9d, ebx
add r8d, r9d
add eax, r8d
@2: lea r9, [rcx+4H]
add eax, edx
cmp r10, r9
jc @4
mov r8, r9
@3: imul edx, dword ptr [r8-4H], -1028477379
add r8, 4
add eax, edx
ror eax, 15
imul eax, eax, 668265263
cmp r10, r8
jnc @3
lea rdx, [r10-4H]
sub rdx, rcx
mov rcx, rdx
and rcx, 0FFFFFFFFFFFFFFFCH
add rcx, r9
@4: cmp r10, rcx
jbe @6
@5: movzx edx, byte ptr [rcx]
add rcx, 1
imul edx, edx, 374761393
add eax, edx
rol eax, 11
imul eax, eax, -1640531535
cmp r10, rcx
jnz @5
@6: mov edx, eax
shr edx, 15
xor eax, edx
imul eax, eax, -2048144777
mov edx, eax
shr edx, 13
xor eax, edx
imul eax, eax, -1028477379
mov edx, eax
shr edx, 16
xor eax, edx
pop rbx
{$ifdef WIN64}
pop rdi
pop rsi
{$endif}
end;
{$endif CPUX64}
{$else not CPUINTEL}
const
PRIME32_1 = 2654435761;
PRIME32_2 = 2246822519;
PRIME32_3 = 3266489917;
PRIME32_4 = 668265263;
PRIME32_5 = 374761393;
// RolDWord is an intrinsic function under FPC :)
function Rol13(value: cardinal): cardinal; inline;
begin
result := RolDWord(value, 13);
end;
function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
var c1, c2, c3, c4: cardinal;
PLimit, PEnd: PAnsiChar;
begin
PEnd := P + len;
if len >= 16 then begin
PLimit := PEnd - 16;
c3 := crc;
c2 := c3 + PRIME32_2;
c1 := c2 + PRIME32_1;
c4 := c3 - PRIME32_1;
repeat
c1 := PRIME32_1 * Rol13(c1 + PRIME32_2 * PCardinal(P)^);
c2 := PRIME32_1 * Rol13(c2 + PRIME32_2 * PCardinal(P+4)^);
c3 := PRIME32_1 * Rol13(c3 + PRIME32_2 * PCardinal(P+8)^);
c4 := PRIME32_1 * Rol13(c4 + PRIME32_2 * PCardinal(P+12)^);
inc(P, 16);
until not (P <= PLimit);
result := RolDWord(c1, 1) + RolDWord(c2, 7) + RolDWord(c3, 12) + RolDWord(c4, 18);
end else
result := crc + PRIME32_5;
inc(result, len);
while P <= PEnd - 4 do begin
inc(result, PCardinal(P)^ * PRIME32_3);
result := RolDWord(result, 17) * PRIME32_4;
inc(P, 4);
end;
while P < PEnd do begin
inc(result, PByte(P)^ * PRIME32_5);
result := RolDWord(result, 11) * PRIME32_1;
inc(P);
end;
result := result xor (result shr 15);
result := result * PRIME32_2;
result := result xor (result shr 13);
result := result * PRIME32_3;
result := result xor (result shr 16);
end;
{$endif CPUINTEL}
{$ifdef CPUINTEL}
type
TRegisters = record
eax,ebx,ecx,edx: cardinal;
end;
{$ifdef CPU64}
procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); nostackframe; assembler;
asm
{$ifdef win64}
mov eax, ecx
mov r9, rdx
{$else}
mov eax, edi
mov r9, rsi
{$endif win64}
mov r10, rbx // preserve rbx
xor ebx, ebx
xor ecx, ecx
xor edx, edx
cpuid
mov TRegisters(r9).&eax, eax
mov TRegisters(r9).&ebx, ebx
mov TRegisters(r9).&ecx, ecx
mov TRegisters(r9).&edx, edx
mov rbx, r10
end;
function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal; nostackframe; assembler;
asm // ecx=crc, rdx=buf, r8=len (Linux: edi,rsi,rdx)
{$ifdef win64}
mov eax, ecx
{$else}
mov eax, edi
mov r8, rdx
mov rdx, rsi
{$endif win64}
not eax
test rdx, rdx
jz @0
test r8, r8
jz @0
@7: test dl, 7
jz @8 // align to 8 bytes boundary
crc32 eax, byte ptr[rdx]
inc rdx
dec r8
jz @0
test dl, 7
jnz @7
@8: mov rcx, r8
shr r8, 3
jz @2
@1:
crc32 rax, qword [rdx] // hash 8 bytes per loop
dec r8
lea rdx, [rdx + 8]
jnz @1
@2: and ecx, 7
jz @0
cmp ecx, 4
jb @4
crc32 eax, dword ptr[rdx]
sub ecx, 4
lea rdx, [rdx + 4]
jz @0
@4: crc32 eax, byte ptr[rdx]
dec ecx
jz @0
crc32 eax, byte ptr[rdx + 1]
dec ecx
jz @0
crc32 eax, byte ptr[rdx + 2]
@0: not eax
end;
{$endif CPU64}
{$ifdef CPUX86}
procedure GetCPUID(Param: Cardinal; var Registers: TRegisters);
asm
push esi
push edi
mov esi, edx
mov edi, eax
pushfd
pop eax
mov edx, eax
xor eax, $200000
push eax
popfd
pushfd
pop eax
xor eax, edx
jz @nocpuid
push ebx
mov eax, edi
xor ecx, ecx
cpuid
mov TRegisters(esi).&eax, eax
mov TRegisters(esi).&ebx, ebx
mov TRegisters(esi).&ecx, ecx
mov TRegisters(esi).&edx, edx
pop ebx
@nocpuid:
pop edi
pop esi
end;
function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
asm // eax=crc, edx=buf, ecx=len
not eax
test ecx, ecx
jz @0
test edx, edx
jz @0
@3: test edx, 3
jz @8 // align to 4 bytes boundary
crc32 eax, byte ptr[edx]
inc edx
dec ecx
jz @0
test edx, 3
jnz @3
@8: push ecx
shr ecx, 3
jz @2
@1:
crc32 eax, dword ptr[edx]
crc32 eax, dword ptr[edx + 4]
dec ecx
lea edx, [edx + 8]
jnz @1
@2: pop ecx
and ecx, 7
jz @0
cmp ecx, 4
jb @4
crc32 eax, dword ptr[edx]
sub ecx, 4
lea edx, [edx + 4]
jz @0
@4:
crc32 eax, byte ptr[edx]
dec ecx
jz @0
crc32 eax, byte ptr[edx + 1]
dec ecx
jz @0
crc32 eax, byte ptr[edx + 2]
@0: not eax
end;
{$endif CPUX86}
type
/// the potential features, retrieved from an Intel CPU
// - see https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
TIntelCpuFeature =
( { in EDX }
cfFPU, cfVME, cfDE, cfPSE, cfTSC, cfMSR, cfPAE, cfMCE,
cfCX8, cfAPIC, cf_d10, cfSEP, cfMTRR, cfPGE, cfMCA, cfCMOV,
cfPAT, cfPSE36, cfPSN, cfCLFSH, cf_d20, cfDS, cfACPI, cfMMX,
cfFXSR, cfSSE, cfSSE2, cfSS, cfHTT, cfTM, cfIA64, cfPBE,
{ in ECX }
cfSSE3, cfCLMUL, cfDS64, cfMON, cfDSCPL, cfVMX, cfSMX, cfEST,
cfTM2, cfSSSE3, cfCID, cfSDBG, cfFMA, cfCX16, cfXTPR, cfPDCM,
cf_c16, cfPCID, cfDCA, cfSSE41, cfSSE42, cfX2A, cfMOVBE, cfPOPCNT,
cfTSC2, cfAESNI, cfXS, cfOSXS, cfAVX, cfF16C, cfRAND, cfHYP,
{ extended features in EBX, ECX }
cfFSGS, cf_b01, cfSGX, cfBMI1, cfHLE, cfAVX2, cf_b06, cfSMEP,
cfBMI2, cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE,
cfAVX512F, cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT, cfCLFLUSH,
cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD, cfSHA, cfAVX512BW, cfAVX512VL,
cfPREFW1, cfAVX512VBMI, cfUMIP, cfPKU, cfOSPKE, cf_c05, cf_c06, cf_c07,
cf_c08, cf_c09, cf_c10, cf_c11, cf_c12, cf_c13, cfAVX512VPC, cf_c15,
cf_cc16, cf_c17, cf_c18, cf_c19, cf_c20, cf_c21, cfRDPID, cf_c23,
cf_c24, cf_c25, cf_c26, cf_c27, cf_c28, cf_c29, cfSGXLC, cf_c31,
cf_d0, cf_d1, cfAVX512NNI, cfAVX512MAS, cf_d4, cf_d5, cf_d6, cf_d7);
/// all features, as retrieved from an Intel CPU
TIntelCpuFeatures = set of TIntelCpuFeature;
var
/// the available CPU features, as recognized at program startup
CpuFeatures: TIntelCpuFeatures;
procedure TestIntelCpuFeatures;
var regs: TRegisters;
begin
regs.edx := 0;
regs.ecx := 0;
GetCPUID(1,regs);
PIntegerArray(@CpuFeatures)^[0] := regs.edx;
PIntegerArray(@CpuFeatures)^[1] := regs.ecx;
GetCPUID(7,regs);
PIntegerArray(@CpuFeatures)^[2] := regs.ebx;
PIntegerArray(@CpuFeatures)^[3] := regs.ecx;
PByte(@PIntegerArray(@CpuFeatures)^[4])^ := regs.edx;
// assert(sizeof(CpuFeatures)=4*4+1);
{$ifdef Darwin}
{$ifdef CPU64}
// SSE42 asm does not (yet) work on Darwin x64 ...
Exclude(CpuFeatures, cfSSE42);
{$endif}
{$endif}
end;
{$endif CPUINTEL}
var
crc32ctab: array[0..{$ifdef PUREPASCAL}3{$else}7{$endif},byte] of cardinal;
function crc32cfast(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
{$ifdef PUREPASCAL}
begin
result := not crc;
if (buf<>nil) and (len>0) then begin
repeat
if PtrUInt(buf) and 3=0 then // align to 4 bytes boundary
break;
result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8);
dec(len);
inc(buf);
until len=0;
while len>=4 do begin
result := result xor PCardinal(buf)^;
inc(buf,4);
result := crc32ctab[3,ToByte(result)] xor
crc32ctab[2,ToByte(result shr 8)] xor
crc32ctab[1,ToByte(result shr 16)] xor
crc32ctab[0,result shr 24];
dec(len,4);
end;
while len>0 do begin
result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8);
dec(len);
inc(buf);
end;
end;
result := not result;
end;
{$else}
// adapted from fast Aleksandr Sharahov version
asm
test edx, edx
jz @ret
neg ecx
jz @ret
not eax
push ebx
@head: test dl, 3
jz @aligned
movzx ebx, byte[edx]
inc edx
xor bl, al
shr eax, 8
xor eax, dword ptr[ebx * 4 + crc32ctab]
inc ecx
jnz @head
pop ebx
not eax
ret
@ret: rep ret
@aligned:
sub edx, ecx
add ecx, 8
jg @bodydone
push esi
push edi
mov edi, edx
mov edx, eax
@bodyloop:
mov ebx, [edi + ecx - 4]
xor edx, [edi + ecx - 8]
movzx esi, bl
mov eax, dword ptr[esi * 4 + crc32ctab + 1024 * 3]
movzx esi, bh
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 2]
shr ebx, 16
movzx esi, bl
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 1]
movzx esi, bh
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 0]
movzx esi, dl
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 7]
movzx esi, dh
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 6]
shr edx, 16
movzx esi, dl
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 5]
movzx esi, dh
xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 4]
add ecx, 8
jg @done
mov ebx, [edi + ecx - 4]
xor eax, [edi + ecx - 8]
movzx esi, bl
mov edx, dword ptr[esi * 4 + crc32ctab + 1024 * 3]
movzx esi, bh
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 2]
shr ebx, 16
movzx esi, bl
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 1]
movzx esi, bh
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 0]
movzx esi, al
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 7]
movzx esi, ah
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 6]
shr eax, 16
movzx esi, al
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 5]
movzx esi, ah
xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 4]
add ecx, 8
jle @bodyloop
mov eax, edx
@done: mov edx, edi
pop edi
pop esi
@bodydone:
sub ecx, 8
jl @tail
pop ebx
not eax
ret
@tail: movzx ebx, byte[edx + ecx]
xor bl, al
shr eax, 8
xor eax, dword ptr[ebx * 4 + crc32ctab]
inc ecx
jnz @tail
pop ebx
not eax
end;
{$endif PUREPASCAL}
procedure InitializeCrc32ctab;
var
i, n: integer;
crc: cardinal;
begin
// initialize tables for crc32cfast() and SymmetricEncrypt/FillRandom
for i := 0 to 255 do begin
crc := i;
for n := 1 to 8 do
if (crc and 1)<>0 then // polynom is not the same as with zlib's crc32()
crc := (crc shr 1) xor $82f63b78 else
crc := crc shr 1;
crc32ctab[0,i] := crc;
end;
for i := 0 to 255 do begin
crc := crc32ctab[0,i];
for n := 1 to high(crc32ctab) do begin
crc := (crc shr 8) xor crc32ctab[0,ToByte(crc)];
crc32ctab[n,i] := crc;
end;
end;
end;
begin
{$ifdef CPUINTEL}
TestIntelCpuFeatures;
if cfSSE42 in CpuFeatures then
begin
crc32c := @crc32csse42;
mORMotHasher := @crc32csse42;
end
else
{$endif CPUINTEL}
begin
InitializeCrc32ctab;
crc32c := @crc32cfast;
mORMotHasher := @xxHash32;
end;
end.

View File

@ -20,6 +20,8 @@ unit Generics.Helpers;
{$MODE DELPHI}{$H+}
{$MODESWITCH TYPEHELPERS}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
interface

View File

@ -21,6 +21,8 @@ unit Generics.MemoryExpanders;
{$mode delphi}
{$MACRO ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{.$WARN 5024 OFF}
{.$WARN 4079 OFF}
@ -38,7 +40,7 @@ type
TLinearProbing = class(TProbeSequence)
public
class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline;
class function Probe(I, Hash: UInt32): UInt32; static; inline;
const MAX_LOAD_FACTOR = 1;
const DEFAULT_LOAD_FACTOR = 0.75;
@ -47,13 +49,8 @@ type
{ TQuadraticProbing }
TQuadraticProbing = class(TProbeSequence)
private
class constructor Create;
public
class var C1: UInt32;
class var C2: UInt32;
class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline;
class function Probe(I, Hash: UInt32): UInt32; static; inline;
const MAX_LOAD_FACTOR = 0.5;
const DEFAULT_LOAD_FACTOR = 0.5;
@ -63,7 +60,7 @@ type
TDoubleHashing = class(TProbeSequence)
public
class function Probe(I, {%H-}M, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline;
class function Probe(I, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline;
const MAX_LOAD_FACTOR = 1;
const DEFAULT_LOAD_FACTOR = 0.85;
@ -207,27 +204,21 @@ end;
{ TLinearProbing }
class function TLinearProbing.Probe(I, M, Hash: UInt32): UInt32;
class function TLinearProbing.Probe(I, Hash: UInt32): UInt32;
begin
Result := (Hash + I)
end;
{ TQuadraticProbing }
class constructor TQuadraticProbing.Create;
class function TQuadraticProbing.Probe(I, Hash: UInt32): UInt32;
begin
C1 := 1;
C2 := 1;
end;
class function TQuadraticProbing.Probe(I, M, Hash: UInt32): UInt32;
begin
Result := (Hash + C1 * I {%H-}+ C2 * Sqr(I));
Result := (Hash + Sqr(I));
end;
{ TDoubleHashingNoMod }
class function TDoubleHashing.Probe(I, M, Hash1: UInt32; Hash2: UInt32): UInt32;
class function TDoubleHashing.Probe(I, Hash1: UInt32; Hash2: UInt32): UInt32;
begin
Result := Hash1 + I * Hash2;
end;

View File

@ -24,7 +24,10 @@ interface
resourcestring
SArgumentOutOfRange = 'Argument out of range';
SArgumentNilNode = 'Node is nil';
SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary';
SCollectionInconsistency = 'Collection inconsistency';
SCollectionDuplicate = 'Collection does not allow duplicates';
SDictionaryKeyDoesNotExist = 'Dictionary key does not exist';
SItemNotFound = 'Item not found';

View File

@ -16,6 +16,14 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgment
Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
many new types and major refactoring of entire library
Thanks to mORMot (http://synopse.info) project for the best implementations
of hashing functions like crc32c and xxHash32 :)
**********************************************************************}
{ TPair<TKey,TValue> }
@ -29,7 +37,7 @@ end;
{ TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> }
procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.PairNotify(constref APair: TPair<TKey, TValue>;
procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.PairNotify(constref APair: TDictionaryPair;
ACollectionNotification: TCollectionNotification);
begin
KeyNotify(APair.Key, ACollectionNotification);
@ -88,16 +96,35 @@ begin
Create(ACollection, TEqualityComparer<TKey>.Default(THashFactory));
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
begin
Create(ACollection, TEqualityComparer<TKey>.Default(THashFactory));
end;
{$ENDIF}
constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
const AComparer: IEqualityComparer<TKey>); overload;
var
LItem: TPair<TKey, TValue>;
LItem: TDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection do
Add(LItem);
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
const AComparer: IEqualityComparer<TKey>); overload;
var
LItem: PDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection.Ptr^ do
Add(LItem^);
end;
{$ENDIF}
destructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Destroy;
begin
Clear;
@ -143,27 +170,32 @@ begin
Result := GetCurrent;
end;
{ TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS> }
{ TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS> }
constructor TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create(
function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetPtrEnumerator: TEnumerator<PT>;
begin
Result := TDictionaryPointersEnumerator.Create(FDictionary);
end;
constructor TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create(
ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
begin
FDictionary := ADictionary;
end;
function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.
function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.
DoGetEnumerator: TDictionaryEnumerator;
begin
Result := TDictionaryEnumerator(TDictionaryEnumerator.NewInstance);
TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>(Result).Create(FDictionary);
end;
function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetCount: SizeInt;
function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetCount: SizeInt;
begin
Result := TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>(FDictionary).Count;
end;
function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray<T>;
function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray<T>;
begin
Result := ToArrayImpl(FDictionary.Count);
end;
@ -193,6 +225,89 @@ begin
Result := True;
end;
{ TOpenAddressingPointersEnumerator<TItem, PDictionaryPair> }
function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.DoMoveNext: boolean;
var
LLength: SizeInt;
begin
Inc(FIndex);
LLength := Length(FItems^);
if FIndex >= LLength then
Exit(False);
// maybe related to bug #24098
// compiler error for (TDictionary<DICTIONARY_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash and UInt32.GetSignMask) = 0
while (FItems^[FIndex].Hash and UInt32.GetSignMask) = 0 do
begin
Inc(FIndex);
if FIndex = LLength then
Exit(False);
end;
Result := True;
end;
function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.DoGetCurrent: PDictionaryPair;
begin
Result := GetCurrent;
end;
function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.GetCurrent: PDictionaryPair;
begin
Result := @FItems^[FIndex].Pair;
end;
constructor TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.Create(var AItems);
begin
FIndex := -1;
FItems := @AItems;
end;
{ TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair> }
function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.Items: PArray;
begin
Result := PArray(@((@Self)^));
end;
function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.GetCount: SizeInt;
begin
Result := PSizeInt(PByte(@((@Self)^))-SizeOf(SizeInt))^;
end;
function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.GetEnumerator: TPointersEnumerator;
begin
Result := TPointersEnumerator(TPointersEnumerator.NewInstance);
TPointersEnumerator(Result).Create(Items^);
end;
function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.ToArray: TArray<PDictionaryPair>;
{begin
Result := ToArrayImpl(FList.Count);
end;}
var
i: SizeInt;
LEnumerator: TPointersEnumerator;
begin
SetLength(Result, GetCount);
try
LEnumerator := GetEnumerator;
i := 0;
while LEnumerator.MoveNext do
begin
Result[i] := LEnumerator.Current;
Inc(i);
end;
finally
LEnumerator.Free;
end;
end;
{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> }
constructor TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
@ -224,7 +339,7 @@ begin
Result := FindBucketIndex(FItems, AKey, LHash);
end;
function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.PrepareAddingItem: SizeInt;
procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.PrepareAddingItem;
begin
if RealItemsLength > FItemsThreshold then
Rehash(Length(FItems) shl 1)
@ -235,9 +350,6 @@ begin
end
else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch
OutOfMemoryError;
Result := FItemsLength;
Inc(FItemsLength);
end;
procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
@ -255,9 +367,17 @@ begin
AItem.Pair.Key := AKey;
AItem.Pair.Value := AValue;
// ! very important. FItemsLength must be increased after above code (because constref has meaning)
Inc(FItemsLength);
PairNotify(AItem.Pair, cnAdded);
end;
function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetPointers: PPointersCollection;
begin
Result := PPointersCollection(@FItems);
end;
procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue);
begin
DoAdd(AKey, AValue);
@ -302,7 +422,7 @@ var
LIndex: SizeInt;
begin
LIndex := FindBucketIndex(AKey);
if LIndex < 0 then
if LIndex < 0 then
Exit;
DoRemove(LIndex, cnRemoved);
@ -313,7 +433,7 @@ var
LIndex: SizeInt;
begin
LIndex := FindBucketIndex(AKey);
if LIndex < 0 then
if LIndex < 0 then
Exit(Default(TPair<TKey, TValue>));
Result.Key := AKey;
@ -555,6 +675,13 @@ begin
Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Value;
end;
{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPValueEnumerator }
function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPValueEnumerator.GetCurrent: PValue;
begin
Result := @(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Value);
end;
{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator }
function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey;
@ -562,6 +689,13 @@ begin
Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Key;
end;
{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPKeyEnumerator }
function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPKeyEnumerator.GetCurrent: PKey;
begin
Result := @(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Key);
end;
{ TOpenAddressingLP<DICTIONARY_CONSTRAINTS> }
procedure TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.NotifyIndexChange(AFrom, ATo: SizeInt);
@ -574,7 +708,7 @@ var
LItem: PItem;
LPair: TPair<TKey, TValue>;
LLengthMask: SizeInt;
i, m, LIndex, LGapIndex: SizeInt;
i, LIndex, LGapIndex: SizeInt;
LHash, LBucket: UInt32;
begin
LItem := @FItems[AIndex];
@ -583,12 +717,11 @@ begin
// try fill gap
LHash := LItem.Hash;
LItem.Hash := 0; // prevents an infinite searching loop
m := Length(FItems);
LLengthMask := m - 1;
LLengthMask := Length(FItems) - 1;
i := Succ(AIndex - (LHash and LLengthMask));
LGapIndex := AIndex;
repeat
LIndex := TProbeSequence.Probe(i, m, LHash) and LLengthMask;
LIndex := TProbeSequence.Probe(i, LHash) and LLengthMask;
LItem := @FItems[LIndex];
// Empty position
@ -650,7 +783,7 @@ begin
Inc(i);
Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask;
Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
until false;
end;
@ -743,7 +876,7 @@ begin
Inc(i);
Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask;
Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
until false;
end;
@ -783,11 +916,93 @@ begin
Inc(i);
Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask;
Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
until false;
end;
{ TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS> }
procedure TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
begin
if ASize = $40000000 then
FItemsThreshold := $40000001
else
begin
FPrimaryNumberAsSizeApproximation := PrimaryNumbersJustLessThanPowerOfTwo[
MultiplyDeBruijnBitPosition[UInt32(((ASize and -ASize) * $077CB531)) shr 27]];
FItemsThreshold := Pred(Round(FPrimaryNumberAsSizeApproximation * FMaxLoadFactor));
end;
end;
function TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>;
constref AKey: TKey; out AHash: UInt32): SizeInt;
var
LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
i: SizeInt;
LHash: UInt32;
begin
LHash := FEqualityComparer.GetHashCode(AKey);
i := 0;
AHash := LHash or UInt32.GetSignMask;
if Length(AItems) = 0 then
Exit(-1);
for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do
begin
Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation;
LItem := _TItem(AItems[Result]);
// Empty position
if LItem.Hash = 0 then
Exit(not Result); // insert!
// Same position?
if LItem.Hash = AHash then
if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
Exit;
end;
Result := -1;
end;
function TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
constref AKey: TKey; out AHash: UInt32): SizeInt;
var
LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
i: SizeInt;
LHash: UInt32;
begin
LHash := FEqualityComparer.GetHashCode(AKey);
i := 0;
AHash := LHash or UInt32.GetSignMask;
if Length(AItems) = 0 then
Exit(-1);
for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do
begin
Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation;
LItem := _TItem(AItems[Result]);
// Empty position or tombstone
if LItem.Hash and UInt32.GetSignMask = 0 then
Exit(not Result); // insert!
// Same position?
if LItem.Hash = AHash then
if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
Exit;
end;
Result := -1;
end;
{ TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> }
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
@ -804,6 +1019,13 @@ constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: T
begin
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
const AComparer: IEqualityComparer<TKey>);
begin
end;
{$ENDIF}
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt);
begin
Create(ACapacity, TExtendedEqualityComparer<TKey>.Default(THashFactory));
@ -814,6 +1036,13 @@ begin
Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
begin
Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
end;
{$ENDIF}
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
const AComparer: IExtendedEqualityComparer<TKey>);
begin
@ -830,13 +1059,25 @@ end;
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
const AComparer: IExtendedEqualityComparer<TKey>);
var
LItem: TPair<TKey, TValue>;
LItem: TDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection do
Add(LItem);
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
const AComparer: IExtendedEqualityComparer<TKey>);
var
LItem: PDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection.Ptr^ do
Add(LItem^);
end;
{$ENDIF}
procedure TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
begin
inherited;
@ -885,7 +1126,7 @@ begin
Inc(i);
Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask;
Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask;
until false;
end;
@ -929,7 +1170,7 @@ begin
Inc(i);
Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask;
Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask;
until false;
end;
@ -992,6 +1233,126 @@ begin
Result := True;
end;
{ TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair> }
function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.DoMoveNext: boolean;
var
LLength: SizeInt;
LArray: TItemsArray;
begin
Inc(FIndex);
if (FMainIndex = TCuckooCfg.D) then // queue
begin
LLength := Length(FQueue.FItems);
if FIndex >= LLength then
Exit(False);
while ((FQueue.FItems[FIndex].Hash)
and UInt32.GetSignMask) = 0 do
begin
Inc(FIndex);
if FIndex = LLength then
Exit(False);
end;
end
else // d-array
begin
LArray := FItems^[FMainIndex];
LLength := Length(LArray);
if FIndex >= LLength then
begin
Inc(FMainIndex);
FIndex := -1;
Exit(DoMoveNext);
end;
while (((LArray[FIndex]).Hash) and UInt32.GetSignMask) = 0 do
begin
Inc(FIndex);
if FIndex = LLength then
begin
Inc(FMainIndex);
FIndex := -1;
Exit(DoMoveNext);
end;
end;
end;
Result := True;
end;
function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.DoGetCurrent: PDictionaryPair;
begin
Result := GetCurrent;
end;
function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetCurrent: PDictionaryPair;
begin
if FMainIndex = TCuckooCfg.D then
Result := @(FQueue.FItems[FIndex].Pair.Value.Pair)
else
Result := @((FItems^[FMainIndex])[FIndex].Pair);
end;
constructor TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt);
begin
FIndex := -1;
if ACount = 0 then
FMainIndex := TCuckooCfg.D
else
FMainIndex := 0;
FQueue := AQueue;
FItems := @AItems;
end;
{ TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItem, TQueueDictionary, PDictionaryPair> }
function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.Items: PArray;
begin
Result := PArray(@((@Self)^));
end;
function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetCount: SizeInt;
begin
Result := SizeInt((@PByte(@((@Self)^))[-SizeOf(SizeInt)])^);
end;
function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetQueue: TQueueDictionary;
begin
Result := TQueueDictionary((@PByte(@((@Self)^))[SizeOf(TItemsDArray)])^);
end;
function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetEnumerator: TPointersEnumerator;
begin
Result := TPointersEnumerator(TPointersEnumerator.NewInstance);
TPointersEnumerator(Result).Create(Items^, GetQueue, GetCount);
end;
function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.ToArray: TArray<PDictionaryPair>;
{begin
Result := ToArrayImpl(FList.Count);
end;}
var
i: SizeInt;
LEnumerator: TPointersEnumerator;
begin
SetLength(Result, GetCount);
try
LEnumerator := GetEnumerator;
i := 0;
while LEnumerator.MoveNext do
begin
Result[i] := LEnumerator.Current;
Inc(i);
end;
finally
LEnumerator.Free;
end;
end;
{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> }
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Rehash(ASizePow2: SizeInt;
@ -1052,7 +1413,7 @@ end;
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Pop: Pointer;
var
AIndex, LGap: SizeInt;
AIndex: SizeInt;
//LResult: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TItem; !!!bug #25917
begin
AIndex := FIdx.DoRemove(FIdx.Count - 1, cnExtracted);
@ -1092,6 +1453,13 @@ constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection:
begin
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
const AComparer: IEqualityComparer<TKey>);
begin
end;
{$ENDIF}
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create;
begin
Create(0);
@ -1107,6 +1475,13 @@ begin
Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
begin
Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
end;
{$ENDIF}
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt;
const AComparer: IExtendedEqualityComparer<TKey>);
begin
@ -1136,13 +1511,25 @@ end;
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
const AComparer: IExtendedEqualityComparer<TKey>);
var
LItem: TPair<TKey, TValue>;
LItem: TDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection do
Add(LItem);
end;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
const AComparer: IExtendedEqualityComparer<TKey>);
var
LItem: PDictionaryPair;
begin
Create(AComparer);
for LItem in ACollection.Ptr^ do
Add(LItem^);
end;
{$ENDIF}
destructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Destroy;
begin
inherited;
@ -1164,6 +1551,11 @@ begin
Result := TValueCollection(FValues);
end;
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetPointers: PPointersCollection;
begin
Result := PPointersCollection(@FItems);
end;
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Lookup(constref AKey: TKey;
var AHashListOrIndex: PUInt32): SizeInt;
begin
@ -1229,7 +1621,7 @@ begin
Result := LR_NIL;
end;
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PrepareAddingItem: SizeInt;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PrepareAddingItem;
var
i: SizeInt;
begin
@ -1243,9 +1635,6 @@ begin
end
else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch
OutOfMemoryError;
Result := FItemsLength;
Inc(FItemsLength);
end;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
@ -1267,7 +1656,7 @@ var
y: boolean = false;
b: UInt32;
LIndex: UInt32;
i, j, LLengthMask: SizeInt;
i, LLengthMask: SizeInt;
LTempItem: TItem;
LHashList: array[0..1] of UInt32;
LHashListParams: array[0..3] of UInt16 absolute LHashList;
@ -1330,10 +1719,11 @@ begin
FQueue.InsertIntoHead(@LNewItem);
end;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoAdd(constref AKey: TKey; constref AValue: TValue;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoAdd(const AKey: TKey; const AValue: TValue;
const AHashList: PUInt32);
begin
AddItem(FItems, AKey, AValue, AHashList);
Inc(FItemsLength);
KeyNotify(AKey, cnAdded);
ValueNotify(AValue, cnAdded);
end;
@ -1464,10 +1854,8 @@ end;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Rehash(ASizePow2: SizeInt);
var
LNewItems: TItemsDArray;
LHash: UInt32;
LIndex: SizeInt;
i, j: SizeInt;
LItem, LNewItem: PItem;
LItem: PItem;
LOldQueue: TQueueDictionary;
var
LHashList: array[0..1] of UInt32;
@ -1607,6 +1995,8 @@ end;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TrimExcess;
begin
SetCapacity(Succ(FItemsLength));
FQueue.TrimExcess;
FQueue.FIdx.TrimExcess;
end;
procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetItem(constref AValue: TValue;
@ -1630,7 +2020,6 @@ var
LHashList: array[0..TCuckooCfg.D] of UInt32;
LHashListOrIndex: PUint32;
LLookupResult: SizeInt;
LIndex: UInt32;
begin
LHashListOrIndex := @LHashList[0];
LLookupResult := Lookup(AKey, LHashListOrIndex);
@ -1666,16 +2055,17 @@ var
LHashList: array[0..TCuckooCfg.D] of UInt32;
LHashListOrIndex: PUint32;
LLookupResult: SizeInt;
LIndex: UInt32;
begin
LHashListOrIndex := @LHashList[0];
LLookupResult := Lookup(AKey, LHashListOrIndex);
if LLookupResult = LR_NIL then
begin
PrepareAddingItem;
DoAdd(AKey, AValue, LHashListOrIndex);
end
Add(AKey, AValue)
// more optimal version for AddOrSetValue has some bug : see Test_CuckooD2_Notification
//begin
// PrepareAddingItem;
// DoAdd(AKey, AValue, LHashListOrIndex);
//end
else
SetItem(AValue, LHashListOrIndex, LLookupResult);
end;
@ -1751,6 +2141,16 @@ begin
Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Value;
end;
{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPValueEnumerator }
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPValueEnumerator.GetCurrent: PValue;
begin
if FMainIndex = TCuckooCfg.D then
Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Value)
else
Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Value);
end;
{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator }
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey;
@ -1761,6 +2161,16 @@ begin
Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Key;
end;
{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPKeyEnumerator }
function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPKeyEnumerator.GetCurrent: TKey;
begin
if FMainIndex = TCuckooCfg.D then
Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Key)
else
Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Key);
end;
{ TObjectDictionary<DICTIONARY_CONSTRAINTS> }
procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.KeyNotify(
@ -1769,7 +2179,7 @@ begin
inherited;
if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then
TObject(AKey).Free;
TObject((@AKey)^).Free;
end;
procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ValueNotify(constref AValue: TValue;
@ -1778,7 +2188,7 @@ begin
inherited;
if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then
TObject(AValue).Free;
TObject((@AValue)^).Free;
end;
constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(
@ -1817,7 +2227,7 @@ begin
inherited;
if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then
TObject(AKey).Free;
TObject((@AKey)^).Free;
end;
procedure TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.ValueNotify(
@ -1826,7 +2236,7 @@ begin
inherited;
if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then
TObject(AValue).Free;
TObject((@AValue)^).Free;
end;
constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships);

View File

@ -16,6 +16,14 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgment
Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
many new types and major refactoring of entire library
Thanks to mORMot (http://synopse.info) project for the best implementations
of hashing functions like crc32c and xxHash32 :)
**********************************************************************}
{$WARNINGS OFF}
@ -44,8 +52,7 @@ type
PKey = ^TKey;
PValue = ^TValue;
THashFactoryClass = THashFactory;
public
FItemsLength: SizeInt;
protected
FEqualityComparer: IEqualityComparer<TKey>;
FKeys: TEnumerable<TKey>;
FValues: TEnumerable<TValue>;
@ -63,8 +70,6 @@ type
property LoadFactor: single read GetLoadFactor;
property Capacity: SizeInt read GetCapacity write SetCapacity;
property Count: SizeInt read FItemsLength;
procedure Clear; virtual; abstract;
procedure Add(constref APair: TPair<TKey, TValue>); virtual; abstract;
strict private // bug #24283. workaround for this class because can't inherit from TEnumerable
@ -78,6 +83,10 @@ type
constructor Create(const AComparer: IEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); virtual; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); virtual; overload;
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload;
{$ENDIF}
destructor Destroy; override;
private
@ -88,11 +97,15 @@ type
procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); virtual;
procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); virtual;
procedure PairNotify(constref APair: TPair<TKey, TValue>; ACollectionNotification: TCollectionNotification); inline;
procedure PairNotify(constref APair: TDictionaryPair; ACollectionNotification: TCollectionNotification); inline;
procedure SetValue(var AValue: TValue; constref ANewValue: TValue);
public
property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify;
property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify;
protected // FItemsLength must be declared at the end of TCustomDictionary
FItemsLength: SizeInt;
public
property Count: SizeInt read FItemsLength;
end;
{ TCustomDictionaryEnumerator }
@ -110,27 +123,52 @@ type
{ TDictionaryEnumerable }
TDictionaryEnumerable<TDictionaryEnumerator, // ... inherits from TCustomDictionaryEnumerator. workaround...
T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerable<T>)
TDictionaryEnumerable<TDictionaryEnumerator: TObject; TDictionaryPointersEnumerator, // ... inherits from TCustomDictionaryEnumerator. workaround...
T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerableWithPointers<T>)
private
FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>;
function GetCount: SizeInt;
protected
function GetPtrEnumerator: TEnumerator<PT>; override;
function DoGetEnumerator: TDictionaryEnumerator; override;
public
constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
function DoGetEnumerator: TDictionaryEnumerator; override;
function ToArray: TArray<T>; override; final;
property Count: SizeInt read GetCount;
end;
// more info : http://en.wikipedia.org/wiki/Open_addressing
{ TDictionaryEnumerable }
{ TOpenAddressingEnumerator }
TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
protected
function DoMoveNext: Boolean; override;
end;
TOpenAddressingPointersEnumerator<TItem, PDictionaryPair> = class abstract(TEnumerator<PDictionaryPair>)
private var
FItems: ^TArray<TItem>;
FIndex: SizeInt;
protected
function DoMoveNext: boolean; override;
function DoGetCurrent: PDictionaryPair; override;
function GetCurrent: PDictionaryPair; virtual;
public
constructor Create(var AItems);
end;
TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair> = record
private type
PArray = ^TArray<TItem>;
function Items: PArray; inline;
function GetCount: SizeInt; inline;
public
function GetEnumerator: TPointersEnumerator;
function ToArray: TArray<PDictionaryPair>;
property Count: SizeInt read GetCount;
end;
TOnGetMemoryLayoutKeyPosition = procedure(Sender: TObject; AKeyPos: UInt32) of object;
TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>)
@ -142,12 +180,16 @@ type
end;
TItemsArray = array of TItem;
private var
FItemsThreshold: SizeInt;
TPointersEnumerator = class(TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>);
TPointersCollection = TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>;
public type
PPointersCollection = ^TPointersCollection;
private var // FItems must be declared as first field
FItems: TItemsArray;
FItemsThreshold: SizeInt;
procedure Resize(ANewSize: SizeInt);
function PrepareAddingItem: SizeInt;
procedure PrepareAddingItem;
protected
function RealItemsLength: SizeInt; virtual;
function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; virtual;
@ -166,21 +208,32 @@ type
function GetCurrent: TValue; override;
end;
TPValueEnumerator = class(TOpenAddressingEnumerator<PValue, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: PValue; override;
end;
TKeyEnumerator = class(TOpenAddressingEnumerator<TKey, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: TKey; override;
end;
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TPKeyEnumerator = class(TOpenAddressingEnumerator<PKey, OPEN_ADDRESSING_CONSTRAINTS>)
protected
function GetCurrent: PKey; override;
end;
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TPValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TPKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// bug #24283 - workaround related to lack of DoGetEnumerator
function GetEnumerator: TPairEnumerator; reintroduce;
private
function GetKeys: TKeyCollection;
function GetValues: TValueCollection;
function GetPointers: PPointersCollection; inline;
private
function GetItem(const AKey: TKey): TValue; inline;
procedure SetItem(const AKey: TKey; const AValue: TValue); inline;
@ -217,6 +270,7 @@ type
property Items[Index: TKey]: TValue read GetItem write SetItem; default;
property Keys: TKeyCollection read GetKeys;
property Values: TValueCollection read GetValues;
property Ptr: PPointersCollection read GetPointers;
procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
end;
@ -267,6 +321,17 @@ type
out AHash: UInt32): SizeInt; override;
end;
TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>)
private
FPrimaryNumberAsSizeApproximation: SizeInt;
protected
procedure UpdateItemsThreshold(ASize: SizeInt); override;
function FindBucketIndex(constref AItems: TArray<TItem>;
constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload;
function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
constref AKey: TKey; out AHash: UInt32): SizeInt; override;
end;
TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>)
private type // for workaround Lazarus bug #25613
_TItem = record
@ -285,12 +350,21 @@ type
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
{$ENDIF}
public // bug #26181 (redundancy of constructors)
constructor Create(ACapacity: SizeInt); override; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); override; overload;
{$ENDIF}
constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
{$ENDIF}
end;
TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
@ -308,6 +382,32 @@ type
constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
end;
TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair> = class abstract(TEnumerator<PDictionaryPair>)
private var // FItems must be declared as first field and FQueue as second
FItems: ^TItemsDArray;
FQueue: TQueueDictionary;
FIndex: SizeInt;
FMainIndex: SizeInt;
protected
function DoMoveNext: boolean; override;
function DoGetCurrent: PDictionaryPair; override;
function GetCurrent: PDictionaryPair; virtual;
public
constructor Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt);
end;
TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair> = record
private type
PArray = ^TItemsDArray;
function Items: PArray; inline;
function GetCount: SizeInt; inline;
function GetQueue: TQueueDictionary; inline;
public
function GetEnumerator: TPointersEnumerator;
function ToArray: TArray<PDictionaryPair>;
property Count: SizeInt read GetCount;
end;
// more info :
// http://arxiv.org/abs/0903.0391
@ -323,7 +423,7 @@ type
end;
TValueForQueue = TItem;
TQueueDictionary = class(TOpenAddressingLP<TKey, TValueForQueue, TDelphiHashFactory, TLinearProbing>)
TQueueDictionary = class(TOpenAddressingLP<TKey, TValueForQueue, TDefaultHashFactory, TLinearProbing>)
private type // for workaround Lazarus bug #25613
_TItem = record
Hash: UInt32;
@ -344,27 +444,31 @@ type
end;
// cycle-detection mechanism class
TCDM = class(TOpenAddressingSH<TKey, TEmptyRecord, TDelphiHashFactory, TLinearProbing>);
TCDM = class(TOpenAddressingSH<TKey, TEmptyRecord, TDefaultHashFactory, TLinearProbing>);
TItemsArray = array of TItem;
TItemsDArray = array[0..Pred(TCuckooCfg.D)] of TItemsArray;
TPointersEnumerator = class(TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>);
TPointersCollection = TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>;
public type
PPointersCollection = ^TPointersCollection;
private var
FItems: TItemsDArray;
FQueue: TQueueDictionary; // probably can be optimized - hash TItem give information from TItem.Hash for cuckoo ...
// currently is kept in "TQueueDictionary = class(TOpenAddressingSH<TKey, TItem, ...>"
FCDM: TCDM; // cycle-detection mechanism
FItemsThreshold: SizeInt;
FItems: TItemsDArray;
// sadly there is bug #24848 for class var ...
{class} var
CUCKOO_SIGN, CUCKOO_INDEX_SIZE, CUCKOO_HASH_SIGN: UInt32;
// CUCKOO_MAX_ITEMS_LENGTH: <- to do : calc max length for items based on CUCKOO sign
// maybe some CDM bloom filter?
procedure UpdateItemsThreshold(ASize: SizeInt); override;
procedure Resize(ANewSize: SizeInt);
procedure Rehash(ASizePow2: SizeInt);
function PrepareAddingItem: SizeInt;
procedure PrepareAddingItem;
protected
procedure UpdateItemsThreshold(ASize: SizeInt); override;
function Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; inline; overload;
function Lookup(constref AItems: TItemsDArray; constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; virtual; overload;
public
@ -380,28 +484,39 @@ type
function GetCurrent: TValue; override;
end;
TPValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<PValue, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: PValue; override;
end;
TKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TKey, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: TKey; override;
end;
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TPKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<PKey, CUCKOO_CONSTRAINTS>)
protected
function GetCurrent: PKey; override;
end;
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// Collections
TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TPValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TPKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
// bug #24283 - workaround related to lack of DoGetEnumerator
function GetEnumerator: TPairEnumerator; reintroduce;
private
function GetKeys: TKeyCollection;
function GetValues: TValueCollection;
function GetPointers: PPointersCollection; inline;
private
function GetItem(const AKey: TKey): TValue; inline;
procedure SetItem(const AKey: TKey; const AValue: TValue); overload; inline;
procedure SetItem(constref AValue: TValue; const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); overload;
procedure AddItem(constref AItems: TItemsDArray; constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload;
procedure DoAdd(constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; inline;
procedure DoAdd(const AKey: TKey; const AValue: TValue; const AHashList: PUInt32); overload; inline;
function DoRemove(const AHashListOrIndex: PUInt32; ALookupResult: SizeInt;
ACollectionNotification: TCollectionNotification): TValue;
@ -417,15 +532,24 @@ type
constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
{$ENDIF}
public
// TODO: function TryFlushQueue(ACount: SizeInt): SizeInt;
constructor Create; override; overload;
constructor Create(ACapacity: SizeInt); override; overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); override; overload;
{$ENDIF}
constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
{$ENDIF}
destructor Destroy; override;
procedure Add(constref APair: TPair<TKey, TValue>); override; overload;
@ -443,6 +567,7 @@ type
property Items[Index: TKey]: TValue read GetItem write SetItem; default;
property Keys: TKeyCollection read GetKeys;
property Values: TValueCollection read GetValues;
property Ptr: PPointersCollection read GetPointers;
property QueueCount: SizeInt read GetQueueCount;
procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
@ -486,17 +611,17 @@ type
// useful generics overloads
TOpenAddressingLP<TKey, TValue, THashFactory> = class(TOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
TOpenAddressingLP<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
TOpenAddressingLP<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
TObjectOpenAddressingLP<TKey, TValue, THashFactory> = class(TObjectOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
TObjectOpenAddressingLP<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
TObjectOpenAddressingLP<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
// Linear Probing with Tombstones (LPT)
TOpenAddressingLPT<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TLinearProbing>);
TOpenAddressingLPT<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TLinearProbing>);
TOpenAddressingLPT<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
TOpenAddressingQP<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TQuadraticProbing>);
TOpenAddressingQP<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TQuadraticProbing>);
TOpenAddressingQP<TKey, TValue, THashFactory> = class(TOpenAddressingQP<TKey, TValue, THashFactory, TQuadraticProbing>);
TOpenAddressingQP<TKey, TValue> = class(TOpenAddressingQP<TKey, TValue, TDefaultHashFactory, TQuadraticProbing>);
TOpenAddressingDH<TKey, TValue, THashFactory> = class(TOpenAddressingDH<TKey, TValue, THashFactory, TDoubleHashing>);
TOpenAddressingDH<TKey, TValue> = class(TOpenAddressingDH<TKey, TValue, TDelphiDoubleHashFactory, TDoubleHashing>);
@ -528,6 +653,3 @@ type
THashMap<TKey, TValue> = class(TCuckooD4<TKey, TValue>);
TObjectHashMap<TKey, TValue> = class(TObjectCuckooD4<TKey, TValue>);
var
EmptyRecord: TEmptyRecord;