Browse Source

RandomHash: performance updates and bug fixes

Herman Schoenfeld 7 years ago
parent
commit
316b495c30
2 changed files with 168 additions and 18 deletions
  1. 14 11
      src/core/URandomHash.pas
  2. 154 7
      src/tests/URandomHashTests.pas

+ 14 - 11
src/core/URandomHash.pas

@@ -216,7 +216,7 @@ resourcestring
 
 implementation
 
-uses UMemory;
+uses UCommon, UMemory;
 
 { Global Functions }
 
@@ -228,7 +228,6 @@ begin
   Result := AState;
 end;
 
-
 { TRandomHash }
 
 constructor TRandomHash.Create;
@@ -265,10 +264,10 @@ end;
 
 class function TRandomHash.Compute(const ABlockHeader: TBytes): TBytes;
 var
-  LHasher : TRandomHashFast;
+  LHasher : TRandomHash;
   LDisposables : TDisposables;
 begin
- LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+ LHasher := LDisposables.AddObject( TRandomHash.Create ) as TRandomHash;
  Result := LHasher.Hash(ABlockHeader);
 end;
 
@@ -297,7 +296,7 @@ begin
 
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
-  if ARound = 1 then  begin
+  if ARound = 1 then begin
     LSeed := Checksum(ABlockHeader);
     LGen.Initialize(LSeed);
     LRoundInput := ABlockHeader;
@@ -571,10 +570,10 @@ end;
 
 class function TRandomHashFast.Compute(const ABlockHeader: TBytes): TBytes;
 var
-  LHasher : TRandomHash;
+  LHasher : TRandomHashFast;
   LDisposables : TDisposables;
 begin
- LHasher := LDisposables.AddObject( TRandomHash.Create ) as TRandomHash;
+ LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
  Result := LHasher.Hash(ABlockHeader);
 end;
 
@@ -654,7 +653,11 @@ end;
 
 function TRandomHashFast.Checksum(const AInput: TBytes; AOffset, ALength: Integer): UInt32;
 begin
-   FMurmurHash3_x86_32.TransformBytes(AInput, AOffset, ALength);
+   if AOffset = 0 then
+     FMurmurHash3_x86_32.TransformBytes(AInput, AOffset, ALength)
+   else
+     //TODO: Fix MurMur3 implementation in HashLib4Pascal to support non-zero offset
+     FMurmurHash3_x86_32.TransformBytes(TArrayTool<Byte>.Copy(AInput, AOffset, ALength), 0, ALength);
    Result := FMurmurHash3_x86_32.TransformFinal.GetUInt32();
 end;
 
@@ -712,7 +715,7 @@ begin
 
   // Select random bytes from input using XorShift32 RNG
   for i := AWriteStart to LWriteEnd do
-    ABuffer[i] := ABuffer[TXorShift32.Next(LState) MOD ALength];
+    ABuffer[i] := ABuffer[AReadStart + TXorShift32.Next(LState) MOD ALength];
 end;
 
 procedure TRandomHashFast.MemTransform2(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
@@ -811,7 +814,7 @@ begin
   LOdd := ALength MOD 2;
   for i := 0 to Pred(LPivot) do
   begin
-    ABuffer[AWriteStart + i] := ABuffer[AReadStart + (i * 2)] xor ABuffer[(i * 2) + 1];
+    ABuffer[AWriteStart + i] := ABuffer[AReadStart + (i * 2)] xor ABuffer[AReadStart + (i * 2) + 1];
     ABuffer[AWriteStart + i + LPivot + LOdd] := ABuffer[AReadStart + i] xor ABuffer[AReadStart + ALength - i - 1];
   end;
   // Set middle-byte for odd-lengths
@@ -863,7 +866,7 @@ begin
   // Copy the genesis blob
   Move(AInput[0], LOutput[0], LInputSize);
   LReadEnd := LInputSize - 1;
-  LCopyLen := LReadEnd+1;
+  LCopyLen := LInputSize;
 
   while LReadEnd < Pred(Length(LOutput)) do
   begin

+ 154 - 7
src/tests/URandomHashTests.pas

@@ -68,6 +68,7 @@ type
     TFastTransformProc = procedure(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer) of object;
   private
     procedure TestMemTransform(ATransform : TFastTransformProc; const ATestData : array of TTestItem<Integer, String>);
+    procedure TestMemTransform_Padding(ATransform : TFastTransformProc);
   protected
     procedure SetUp; override;
     procedure TearDown; override;
@@ -78,6 +79,15 @@ type
     procedure TestCompress;
     procedure TestChecksum_1;
     procedure TestChecksum_2;
+
+    procedure TestMemTransform1_Padding;
+    procedure TestMemTransform2_Padding;
+    procedure TestMemTransform3_Padding;
+    procedure TestMemTransform4_Padding;
+    procedure TestMemTransform5_Padding;
+    procedure TestMemTransform6_Padding;
+    procedure TestMemTransform7_Padding;
+    procedure TestMemTransform8_Padding;
     procedure TestMemTransform1;
     procedure TestMemTransform2;
     procedure TestMemTransform3;
@@ -1107,12 +1117,16 @@ var
   LHasher : TRandomHashFast;
   LDisposables : TDisposables;
   LMurMur3 : IHash;
+  i : Integer;
 begin
   LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
   LMurMur3 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  i := 0;
   for LCase in DATA_EXPAND do begin
     LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input1);
+    //WriteLn(Format('Case %d', [i]));
     AssertEquals(LCase.Expected, LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
+    Inc(i);
     //WriteLn(LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
   end;
 end;
@@ -1174,6 +1188,79 @@ begin
   end;
 end;
 
+procedure TRandomHashFastTest.TestMemTransform1_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform1);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform2_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform2);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform3_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform3);
+end;
+
+
+procedure TRandomHashFastTest.TestMemTransform4_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform4);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform5_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform5);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform6_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform6);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform7_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform7);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform8_Padding;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform_Padding(LHasher.MemTransform8);
+end;
+
 procedure TRandomHashFastTest.TestMemTransform1;
 var
   LHasher : TRandomHashFast;
@@ -1246,19 +1333,79 @@ begin
   TestMemTransform(LHasher.MemTransform8, DATA_MEMTRANSFORM8);
 end;
 
+procedure TRandomHashFastTest.TestMemTransform_Padding(ATransform : TFastTransformProc);
+var
+  LCase : TTestItem<Integer, String>;
+  LInput, LLeftPad, LRightPad, LExpected, LOutput, LBuff : TBytes;
+  i, LInputLen : Integer;
+begin
+  // Setup input
+  LInput := TBytes.Create(01, 02, 03, 04, 05);// ParseBytes(DATA_BYTES);
+  LInputLen := Length(LInput);
+
+  // Determine the expected value (transform with no padding)
+  SetLength(LBuff, LInputLen*2);
+  Move(LInput[0], LBuff[0], LInputLen);
+  ATransform(LBuff, 0, LInputLen, LInputLen);
+  LExpected := TArrayTool<byte>.Copy(LBuff, LInputLen, LInputLen);
+
+  for i := 0 to 999 do begin
+    // prepare padding
+    SetLength(LLeftPad, i);
+    SetLength(LRightPad, i);
+    FillByte(LLeftPad[0], i, 0);
+    FillByte(LRightPad[0], i, 0);
+
+    // prepare output
+    SetLength(LOutput, Length(LInput));
+    FillByte(LOutput[0], Length(LOutput), 0);
+
+    // prepare input buffer
+    LBuff := TArrayTool<byte>.Concat([LLeftPad, LInput, LOutput, LRightPad]);
+
+    // transform the input in-place
+    ATransform(LBuff, Length(LLeftPad), Length(LLeftPad) + Length(LInput), Length(LInput));
+
+    // extract the output
+    LOutput := TArrayTool<byte>.Copy(LBuff, Length(LLeftPad) + Length(LInput), Length(LInput));
+
+    // perform check
+    AssertEquals(LExpected, LOutput);
+  end;
+end;
+
 procedure TRandomHashFastTest.TestMemTransform(ATransform : TFastTransformProc; const ATestData : array of TTestItem<Integer, String>);
 var
   LCase : TTestItem<Integer, String>;
-  LInput, LBuff : TBytes;
+  LInput, LLeftPad, LRightPad, LOutput, LBuff : TBytes;
+  i : Integer;
 begin
+  i := 0;
+  SetLength(LLeftPad, i);
+  SetLength(LRightPad, i);
   for LCase in ATestData do begin
+    { Buffer set as LLLINPUT00000RRR where L= Left padding, INPUT = data item to transform, 0 = where transform will be written, R = right padding }
     LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
-    SetLength(LBuff, Length(LInput)*2);
-    System.Move(LInput[0], LBuff[0], Length(LInput));
-    ATransform(LBuff, 0, Length(LInput), Length(LInput));
-    LBuff := TArrayTool<byte>.Copy(LBuff, Length(LInput), Length(LInput));
-    AssertEquals(Hex2Bytes(LCase.Expected), LBuff);
-    //WriteLn((Bytes2Hex(ATransform(LInput), True)));
+    SetLength(LOutput, Length(LInput));
+
+    // prepare input buffer
+    LBuff := TArrayTool<byte>.Concat([LLeftPad, LInput, LOutput, LRightPad]);
+
+    // transform the input in-place
+    ATransform(LBuff, Length(LLeftPad), Length(LLeftPad) + Length(LInput), Length(LInput));
+
+    // extract the output
+    LOutput := TArrayTool<byte>.Copy(LBuff, Length(LLeftPad) + Length(LInput), Length(LInput));
+
+    // perform check
+    AssertEquals(Hex2Bytes(LCase.Expected), LOutput);
+
+    // alter padding for next round
+    Inc(i);
+    SetLength(LLeftPad, i);
+    SetLength(LRightPad, i);
+    LLeftPad[i - 1] := i mod 256;
+    LRightPad[i - 1] := i mod 256;
   end;
 end;