Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions HashLib.Tests/src/Hash64Tests.pas
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
interface

uses
SysUtils,
{$IFDEF FPC}
fpcunit,
testregistry,
{$ELSE}
TestFramework,
{$ENDIF FPC}
HashLibTestBase,
HlpIHashInfo,
HlpHashFactory;

// Hash64
Expand Down Expand Up @@ -70,6 +72,18 @@ TTestXXHash3 = class(THashWithUInt64AsKeyAlgorithmTestCase)
procedure SetUp; override;
procedure TearDown; override;

published
// Known-answer tests for the long-input paths (values cross-checked
// against the official xxHash implementation): the self-consistency
// chunked tests alone cannot catch a wrong SIMD kernel because both
// sides run the same code.
procedure TestChunkedDataKnownAnswer; // 299 bytes: accumulate512
procedure TestTwoKiBDataKnownAnswer; // 2048 bytes: scrambleAcc
procedure TestChunkedDataWithMaxUInt64AsKeyKnownAnswer; // seeded: initSecret
// An asymmetric key (lo32 <> hi32) - a symmetric one like MaxUInt64
// cannot catch seed dword swaps or wrong hi/lo pickup in the kernels.
procedure TestChunkedDataWithAsymmetricKeyKnownAnswer;

end;

implementation
Expand Down Expand Up @@ -188,6 +202,69 @@ procedure TTestXXHash3.TearDown;
inherited;
end;

procedure TTestXXHash3.TestChunkedDataKnownAnswer;
begin
ExpectedString := '3D3D7245B763ACE8';
ActualString := HashInstance.ComputeString(ChunkedData, TEncoding.UTF8)
.ToString();
CheckEquals(ExpectedString, ActualString, Format('Expected %s but got %s.',
[ExpectedString, ActualString]));
end;

procedure TTestXXHash3.TestTwoKiBDataKnownAnswer;
var
LData: TBytes;
LIdx: Int32;
begin
System.SetLength(LData, 2048);
for LIdx := 0 to 2047 do
begin
LData[LIdx] := Byte(LIdx and $FF);
end;
ExpectedString := 'DD420471FF96BD00';
ActualString := HashInstance.ComputeBytes(LData).ToString();
CheckEquals(ExpectedString, ActualString, Format('Expected %s but got %s.',
[ExpectedString, ActualString]));
end;

procedure TTestXXHash3.TestChunkedDataWithAsymmetricKeyKnownAnswer;
var
LIHashWithKey: IHashWithKey;
LKey: TBytes;
begin
// key = $0123456789ABCDEF (little-endian bytes below)
ExpectedString := 'FBF9CD92890CC82A';
CheckTrue(Supports(HashInstance, IHashWithKey, LIHashWithKey),
'HashInstance must support IHashWithKey');
LKey := TBytes.Create($EF, $CD, $AB, $89, $67, $45, $23, $01);
LIHashWithKey.Key := LKey;
ActualString := LIHashWithKey.ComputeString(ChunkedData, TEncoding.UTF8)
.ToString();
CheckEquals(ExpectedString, ActualString, Format('Expected %s but got %s.',
[ExpectedString, ActualString]));
end;

procedure TTestXXHash3.TestChunkedDataWithMaxUInt64AsKeyKnownAnswer;
var
LIHashWithKey: IHashWithKey;
LKey: TBytes;
LIdx: Int32;
begin
ExpectedString := '7DEFEE70FC854900';
CheckTrue(Supports(HashInstance, IHashWithKey, LIHashWithKey),
'HashInstance must support IHashWithKey');
System.SetLength(LKey, System.SizeOf(UInt64));
for LIdx := 0 to System.High(LKey) do
begin
LKey[LIdx] := $FF;
end;
LIHashWithKey.Key := LKey;
ActualString := LIHashWithKey.ComputeString(ChunkedData, TEncoding.UTF8)
.ToString();
CheckEquals(ExpectedString, ActualString, Format('Expected %s but got %s.',
[ExpectedString, ActualString]));
end;

initialization

// Register any test cases with the test runner
Expand Down
46 changes: 26 additions & 20 deletions HashLib/src/Hash64/HlpXXHash3.pas
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface
ASecret: Pointer; ANbStripes: Int32);
TXXH3ScrambleAccProc = procedure(AAcc: Pointer; ASecret: Pointer);
TXXH3InitSecretProc = procedure(ACustomSecret: Pointer;
ADefaultSecret: Pointer; ASeed: UInt64);
ADefaultSecret: Pointer; ASeedPtr: PUInt64);

var
XXH3_Accumulate512: TXXH3Accumulate512Proc;
Expand Down Expand Up @@ -208,60 +208,66 @@ implementation
procedure XXH3_Accumulate512_Scalar(AAcc: Pointer; AInput: Pointer;
ASecret: Pointer);
var
LPAcc: PUInt64;
LPInput, LPSecret: PByte;
LPAcc, LPInput, LPSecret: PByte;
LPLane: PUInt64;
I: Int32;
LDataVal, LDataKey: UInt64;
begin
LPAcc := PUInt64(AAcc);
LPAcc := PByte(AAcc);
LPInput := PByte(AInput);
LPSecret := PByte(ASecret);
for I := 0 to XXH_ACC_NB - 1 do
begin
LDataVal := TBinaryPrimitives.ReadUInt64LittleEndian(LPInput, I * 8);
LDataKey := LDataVal xor TBinaryPrimitives.ReadUInt64LittleEndian(LPSecret, I * 8);
PUInt64(PByte(LPAcc) + (I xor 1) * 8)^ :=
PUInt64(PByte(LPAcc) + (I xor 1) * 8)^ + LDataVal;
PUInt64(PByte(LPAcc) + I * 8)^ :=
PUInt64(PByte(LPAcc) + I * 8)^ +
UInt64(UInt32(LDataKey)) * UInt64(UInt32(LDataKey shr 32));
LPLane := PUInt64(LPAcc + (I xor 1) * 8);
TBinaryPrimitives.StoreUInt64(LPLane,
TBinaryPrimitives.LoadUInt64(LPLane) + LDataVal);
LPLane := PUInt64(LPAcc + I * 8);
TBinaryPrimitives.StoreUInt64(LPLane,
TBinaryPrimitives.LoadUInt64(LPLane) +
UInt64(UInt32(LDataKey)) * UInt64(UInt32(LDataKey shr 32)));
end;
end;

procedure XXH3_ScrambleAcc_Scalar(AAcc: Pointer; ASecret: Pointer);
var
LPAcc: PUInt64;
LPSecret: PByte;
LPAcc, LPSecret: PByte;
LPLane: PUInt64;
I: Int32;
LKey64, LAcc64: UInt64;
begin
LPAcc := PUInt64(AAcc);
LPAcc := PByte(AAcc);
LPSecret := PByte(ASecret);
for I := 0 to XXH_ACC_NB - 1 do
begin
LKey64 := TBinaryPrimitives.ReadUInt64LittleEndian(LPSecret, I * 8);
LAcc64 := PUInt64(PByte(LPAcc) + I * 8)^;
LPLane := PUInt64(LPAcc + I * 8);
LAcc64 := TBinaryPrimitives.LoadUInt64(LPLane);
LAcc64 := LAcc64 xor (LAcc64 shr 47);
LAcc64 := LAcc64 xor LKey64;
LAcc64 := LAcc64 * XXH_PRIME32_1;
PUInt64(PByte(LPAcc) + I * 8)^ := LAcc64;
TBinaryPrimitives.StoreUInt64(LPLane, LAcc64);
end;
end;

procedure XXH3_InitSecret_Scalar(ACustomSecret: Pointer;
ADefaultSecret: Pointer; ASeed: UInt64);
ADefaultSecret: Pointer; ASeedPtr: PUInt64);
var
I: Int32;
LPSrc, LPDst: PByte;
ASeed: UInt64;
begin
ASeed := ASeedPtr^;
LPSrc := PByte(ADefaultSecret);
LPDst := PByte(ACustomSecret);

for I := 0 to (192 div 16) - 1 do
begin
PUInt64(LPDst + 16 * I)^ :=
TBinaryPrimitives.ReadUInt64LittleEndian(LPSrc, 16 * I) + ASeed;
PUInt64(LPDst + 16 * I + 8)^ :=
TBinaryPrimitives.ReadUInt64LittleEndian(LPSrc, 16 * I + 8) - ASeed;
TBinaryPrimitives.WriteUInt64LittleEndian(LPDst, 16 * I,
TBinaryPrimitives.ReadUInt64LittleEndian(LPSrc, 16 * I) + ASeed);
TBinaryPrimitives.WriteUInt64LittleEndian(LPDst, 16 * I + 8,
TBinaryPrimitives.ReadUInt64LittleEndian(LPSrc, 16 * I + 8) - ASeed);
end;
end;

Expand Down Expand Up @@ -448,7 +454,7 @@ class procedure TXXH3Core.XXH3_hashLong_internal_loop(
class procedure TXXH3Core.XXH3_initCustomSecret(ACustomSecret: PByte;
ASeed: UInt64);
begin
HlpXXHash3.XXH3_InitSecret(ACustomSecret, @XXH3_SECRET[0], ASeed);
HlpXXHash3.XXH3_InitSecret(ACustomSecret, @XXH3_SECRET[0], @ASeed);
end;

class procedure TXXH3Core.XXH3_consumeStripes(var AAcc: TXXH3AccArray;
Expand Down
2 changes: 1 addition & 1 deletion HashLib/src/Include/HashLib.inc
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@

{$IFDEF HASHLIB_ARM_SIMD}
// Uncomment at most ONE to force a specific Arm SIMD dispatch level:
// {$DEFINE HASHLIB_FORCE_NEON}
{$DEFINE HASHLIB_FORCE_NEON}
// {$DEFINE HASHLIB_FORCE_SVE}

{$IF (DEFINED(HASHLIB_FORCE_SCALAR) AND DEFINED(HASHLIB_FORCE_NEON))
Expand Down
Loading
Loading