Browse Source

RandomHash: added TRandomHashFast (WIP)

Herman Schoenfeld 7 years ago
parent
commit
7a005f42b6

+ 406 - 13
src/core/URandomHash.pas

@@ -112,7 +112,7 @@ type
       FMurmurHash3_x86_32 : IHash;
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
-      function MemTransform1(const AChunk: TBytes): TBytes; {$IFDEF FPC}inline;{$ENDIF}
+      function MemTransform1(const AChunk: TBytes): TBytes; inline;
       function MemTransform2(const AChunk: TBytes): TBytes; inline;
       function MemTransform3(const AChunk: TBytes): TBytes; inline;
       function MemTransform4(const AChunk: TBytes): TBytes; inline;
@@ -133,6 +133,39 @@ type
       class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
   end;
 
+ { TRandomHashFast }
+
+  TRandomHashFast = class sealed(TObject)
+    const
+      N = 5;               // Number of hashing rounds required to compute a nonce (total rounds = 2^N - 1)
+      M = (10 * 1024) * 5; // 10KB The memory expansion unit (in bytes)
+
+    {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
+      FMurmurHash3_x86_32 : IHash;
+      FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
+      function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
+      procedure MemTransform1(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform2(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform3(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform4(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform5(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform6(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform7(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      procedure MemTransform8(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
+      function Expand(const AInput: TBytes; AExpansionFactor: Int32) : TBytes;
+      function Compress(const AInputs: TArray<TBytes>): TBytes; inline;
+      function ChangeNonce(const ABlockHeader: TBytes; ANonce: Int32): TBytes; inline;
+      function Checksum(const AInput: TBytes; AOffset, ALength: Integer): UInt32; overload; inline;
+      function Checksum(const AInput: TBytes): UInt32; overload; inline;
+      function Checksum(const AInput: TArray<TBytes>): UInt32; overload; inline;
+      function Hash(const ABlockHeader: TBytes; ARound: Int32) : TArray<TBytes>; overload;
+    public
+      constructor Create;
+      destructor Destroy; override;
+      function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
+      class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
+  end;
+
   { ERandomHash }
 
   ERandomHash = class(Exception);
@@ -168,14 +201,34 @@ type
       function NextUSingle: Single; inline;
   end;
 
+  { TXorShift32 }
+
+  TXorShift32 = class
+  public
+    class function Next(var AState : UInt32) : UInt32; inline; static;
+  end;
+
 resourcestring
   SUnSupportedHash = 'Unsupported Hash Selected';
   SInvalidRound = 'Round must be between 0 and N inclusive';
+  SOverlappingArgs = 'Overlapping read/write regions';
+  SBufferTooSmall = 'Buffer too small to apply memory transform';
 
 implementation
 
 uses UMemory;
 
+{ Global Functions }
+
+class function TXorShift32.Next(var AState : UInt32) : UInt32;
+begin
+  AState := AState XOR (AState SHL 13);
+  AState := AState XOR (AState SHR 17);
+  AState := AState XOR (AState SHL 5);
+  Result := AState;
+end;
+
+
 { TRandomHash }
 
 constructor TRandomHash.Create;
@@ -212,10 +265,10 @@ end;
 
 class function TRandomHash.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;
 
@@ -334,15 +387,6 @@ function TRandomHash.MemTransform1(const AChunk: TBytes): TBytes;
 var
   i, LChunkLength : UInt32;
   LState : UInt32;
-
-  function XorShift32 : UInt32; {$IFDEF FPC}inline;{$ENDIF}
-  begin
-    LState := LState XOR (LState SHL 13);
-    LState := LState XOR (LState SHR 17);
-    LState := LState XOR (LState SHL 5);
-    Result := LState;
-  end;
-
 begin
   // Seed XorShift32 with non-zero seed (checksum of input or 1)
   LState := Checksum(AChunk);
@@ -353,7 +397,7 @@ begin
   LChunkLength := Length(AChunk);
   SetLength(Result, LChunkLength);
   for i := 0 to High(AChunk) do
-    Result[i] := AChunk[XorShift32 MOD LChunkLength];
+    Result[i] := AChunk[TXorShift32.Next(LState) MOD LChunkLength];
 end;
 
 function TRandomHash.MemTransform2(const AChunk: TBytes): TBytes;
@@ -491,6 +535,355 @@ begin
   Result := LOutput;
 end;
 
+{ TRandomHashFast }
+
+constructor TRandomHashFast.Create;
+begin
+  FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[4] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[5] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[6] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[7] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[9] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[10] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[11] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[12] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[13] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[15] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[16] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[17] := THashFactory.TCrypto.CreateWhirlPool();
+end;
+
+destructor TRandomHashFast.Destroy;
+var i : integer;
+begin
+ FMurmurHash3_x86_32 := nil;
+ for i := Low(FHashAlg) to High(FHashAlg) do
+   FHashAlg[i] := nil;
+ inherited Destroy;
+end;
+
+class function TRandomHashFast.Compute(const ABlockHeader: TBytes): TBytes;
+var
+  LHasher : TRandomHash;
+  LDisposables : TDisposables;
+begin
+ LHasher := LDisposables.AddObject( TRandomHash.Create ) as TRandomHash;
+ Result := LHasher.Hash(ABlockHeader);
+end;
+
+function TRandomHashFast.Hash(const ABlockHeader: TBytes): TBytes;
+var
+  LAllOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+begin
+  LAllOutputs := Hash(ABlockHeader, N);
+  Result := FHashAlg[0].ComputeBytes(Compress(LAllOutputs)).GetBytes;
+end;
+
+function TRandomHashFast.Hash(const ABlockHeader: TBytes; ARound: Int32) : TArray<TBytes>;
+var
+  LRoundOutputs: TList<TBytes>;
+  LSeed: UInt32;
+  LGen: TMersenne32;
+  LRoundInput, LOtherNonceHeader, LOutput, LBytes: TBytes;
+  LParentOutputs, LNeighborOutputs, LToArray: TArray<TBytes>;
+  LHashFunc: IHash;
+  i: Int32;
+  LDisposables : TDisposables;
+begin
+  if (ARound < 1) or (ARound > N) then
+    raise EArgumentOutOfRangeException.CreateRes(@SInvalidRound);
+
+  LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
+  LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
+  if ARound = 1 then  begin
+    LSeed := Checksum(ABlockHeader);
+    LGen.Initialize(LSeed);
+    LRoundInput := ABlockHeader;
+  end else begin
+    LParentOutputs := Hash(ABlockHeader, ARound - 1);
+    LSeed := Checksum(LParentOutputs);
+    LGen.Initialize(LSeed);
+    LRoundOutputs.AddRange( LParentOutputs );
+    LOtherNonceHeader := ChangeNonce(ABlockHeader, LGen.NextUInt32);
+    LNeighborOutputs := Hash(LOtherNonceHeader, ARound - 1);
+    LRoundOutputs.AddRange(LNeighborOutputs);
+    LRoundInput := Compress( LRoundOutputs.ToArray );
+  end;
+
+  LHashFunc := FHashAlg[LGen.NextUInt32 mod 18];
+  LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
+  LOutput := Expand(LOutput, N - ARound);
+  LRoundOutputs.Add(LOutput);
+
+  Result := LRoundOutputs.ToArray;
+end;
+
+function TRandomHashFast.ChangeNonce(const ABlockHeader: TBytes;  ANonce: Int32): TBytes;
+var
+  LHeaderLength : Integer;
+begin
+  // NOTE: NONCE is last 4 bytes of header!
+
+  // Clone the original header
+  Result := Copy(ABlockHeader);
+
+  // If digest not big enough to contain a nonce, just return the clone
+  LHeaderLength := Length(ABlockHeader);
+  if LHeaderLength < 4 then
+    exit;
+
+  // Overwrite the nonce in little-endian
+  Result[LHeaderLength - 4] := Byte(ANonce);
+  Result[LHeaderLength - 3] := (ANonce SHR 8) AND 255;
+  Result[LHeaderLength - 2] := (ANonce SHR 16) AND 255;
+  Result[LHeaderLength - 1] := (ANonce SHR 24) AND 255;
+end;
+
+function TRandomHashFast.Checksum(const AInput: TBytes): UInt32;
+begin
+  Result := Checksum(AInput, 0, Length(AInput));
+end;
+
+function TRandomHashFast.Checksum(const AInput: TBytes; AOffset, ALength: Integer): UInt32;
+begin
+   FMurmurHash3_x86_32.TransformBytes(AInput, AOffset, ALength);
+   Result := FMurmurHash3_x86_32.TransformFinal.GetUInt32();
+end;
+
+function TRandomHashFast.Checksum(const AInput : TArray<TBytes>): UInt32;
+var
+  i: Int32;
+begin
+  FMurmurHash3_x86_32.Initialize;
+  for i := Low(AInput) to High(AInput) do
+  begin
+    FMurmurHash3_x86_32.TransformBytes(AInput[i]);
+  end;
+  Result := FMurmurHash3_x86_32.TransformFinal.GetUInt32;
+end;
+
+function TRandomHashFast.Compress(const AInputs : TArray<TBytes>): TBytes;
+var
+  i: Int32;
+  LSeed: UInt32;
+  LSource: TBytes;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  SetLength(Result, 100);
+  LSeed := Checksum(AInputs);
+  LGen := LDisposables.AddObject( TMersenne32.Create( LSeed ) ) as TMersenne32;
+  for i := 0 to 99 do
+  begin
+    LSource := AInputs[LGen.NextUInt32 mod Length(AInputs)];
+    Result[i] := LSource[LGen.NextUInt32 mod Length(LSource)];
+  end;
+end;
+
+function TRandomHashFast.ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes;
+begin
+  SetLength(Result, Length(AChunk1) + Length(AChunk2));
+  Move(AChunk1[0], Result[0], Length(AChunk1));
+  Move(AChunk2[0], Result[Length(AChunk1)], Length(AChunk2));
+end;
+
+procedure TRandomHashFast.MemTransform1(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd : UInt32;
+  LState : UInt32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+
+  // Seed XorShift32 with non-zero seed (checksum of input or 1)
+  LState := Checksum(ABuffer, AReadStart, ALength);
+  if LState = 0 then
+    LState := 1;
+
+  // Select random bytes from input using XorShift32 RNG
+  for i := AWriteStart to LWriteEnd do
+    ABuffer[i] := ABuffer[TXorShift32.Next(LState) MOD ALength];
+end;
+
+procedure TRandomHashFast.MemTransform2(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  Move(ABuffer[AReadStart + LPivot + LOdd], ABuffer[AWriteStart], LPivot);
+  Move(ABuffer[AReadStart], ABuffer[AWriteStart + LPivot + LOdd], LPivot);
+  // Set middle-byte for odd-length arrays
+  if LOdd = 1 then
+    ABuffer[AWriteStart + LPivot] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHashFast.MemTransform3(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  for i := 0 to ALength do
+    ABuffer[AWriteStart + i] := ABuffer[AReadStart + ALength - i - 1];
+end;
+
+procedure TRandomHashFast.MemTransform4(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  for i := 0 to Pred(LPivot) do
+  begin
+    ABuffer[AWriteStart + (i * 2)] := ABuffer[AReadStart + i];
+    ABuffer[AWriteStart + (i * 2) + 1] := ABuffer[AReadStart + i + LPivot + LOdd];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[LWriteEnd] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHashFast.MemTransform5(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  LOdd := ALength MOD 2;
+  for i := 0 to Pred(LPivot) do
+  begin
+    ABuffer[AWriteStart + (i * 2)] := ABuffer[AReadStart + i + LPivot + LOdd];
+    ABuffer[AWriteStart + (i * 2) + 1] := ABuffer[AReadStart + i];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[LWriteEnd] := ABuffer[AReadStart + LPivot];
+end;
+
+procedure TRandomHashFast.MemTransform6(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd, LPivot, LOdd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  LPivot := ALength SHR 1;
+  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 + LPivot + LOdd] := ABuffer[AReadStart + i] xor ABuffer[AReadStart + ALength - i - 1];
+  end;
+  // Set middle-byte for odd-lengths
+  if LOdd = 1 THEN
+    ABuffer[AWriteStart + LPivot] := ABuffer[AReadStart + ALength - 1];
+end;
+
+procedure TRandomHashFast.MemTransform7(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LReadEnd, LWriteEnd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  for i := 0 to Pred(ALength) do
+    ABuffer[AWriteStart + i] := TBits.RotateLeft8(ABuffer[AReadStart + i], ALength - i);
+end;
+
+procedure TRandomHashFast.MemTransform8(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer);
+var
+  i, LChunkLength, LReadEnd, LWriteEnd: Int32;
+begin
+  LReadEnd := AReadStart + ALength - 1;
+  LWriteEnd := AWriteStart + ALength - 1;
+  if LReadEnd >= AWriteStart then
+    raise EArgumentOutOfRangeException.Create(SOverlappingArgs);
+  if LWriteEnd >= Length(ABuffer) then
+    raise EArgumentOutOfRangeException.Create(SBufferTooSmall);
+
+  for i := 0 to Pred(ALength) do
+    ABuffer[AWriteStart + i] := TBits.RotateRight8(ABuffer[AReadStart + i], ALength - i);
+end;
+
+function TRandomHashFast.Expand(const AInput: TBytes; AExpansionFactor: Int32): TBytes;
+var
+  LOutput: TBytes;
+  LReadEnd, LCopyLen, LInputSize : UInt32;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  LInputSize := Length (AInput);
+  LGen := LDisposables.AddObject( TMersenne32.Create ( Checksum(AInput) ) ) as TMersenne32;
+  SetLength(LOutput, LInputSize + (AExpansionFactor * M));
+
+  // Copy the genesis blob
+  Move(AInput[0], LOutput[0], LInputSize);
+  LReadEnd := LInputSize - 1;
+  LCopyLen := LReadEnd+1;
+
+  while LReadEnd < Pred(Length(LOutput)) do
+  begin
+    if (LReadEnd + 1 + LCopyLen) > Length(LOutput) then
+      LCopyLen := Length(LOutput) - (LReadEnd + 1);
+    case LGen.NextUInt32 mod 8 of
+      0: MemTransform1(LOutput, 0, LReadEnd+1, LCopyLen);
+      1: MemTransform2(LOutput, 0, LReadEnd+1, LCopyLen);
+      2: MemTransform3(LOutput, 0, LReadEnd+1, LCopyLen);
+      3: MemTransform4(LOutput, 0, LReadEnd+1, LCopyLen);
+      4: MemTransform5(LOutput, 0, LReadEnd+1, LCopyLen);
+      5: MemTransform6(LOutput, 0, LReadEnd+1, LCopyLen);
+      6: MemTransform7(LOutput, 0, LReadEnd+1, LCopyLen);
+      7: MemTransform8(LOutput, 0, LReadEnd+1, LCopyLen);
+    end;
+    Inc(LReadEnd, LCopyLen);
+  end;
+  Result := LOutput;
+end;
+
 { TMersenne32 }
 
 constructor TMersenne32.Create(ASeed: UInt32);

+ 0 - 120
src/tests/UPascalCoinUnitTests.lpi

@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<CONFIG>
-  <ProjectOptions>
-    <Version Value="10"/>
-    <PathDelim Value="\"/>
-    <General>
-      <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>
-      <Title Value="PascalCoinUnitTests"/>
-      <ResourceType Value="res"/>
-      <UseXPManifest Value="True"/>
-    </General>
-    <BuildModes Count="1">
-      <Item1 Name="Default" Default="True"/>
-    </BuildModes>
-    <PublishOptions>
-      <Version Value="2"/>
-    </PublishOptions>
-    <RunParams>
-      <local>
-        <FormatVersion Value="1"/>
-      </local>
-    </RunParams>
-    <RequiredPackages Count="3">
-      <Item1>
-        <PackageName Value="fpcunittestrunner"/>
-      </Item1>
-      <Item2>
-        <PackageName Value="LCL"/>
-      </Item2>
-      <Item3>
-        <PackageName Value="FCL"/>
-      </Item3>
-    </RequiredPackages>
-    <Units Count="11">
-      <Unit0>
-        <Filename Value="UPascalCoinUnitTests.lpr"/>
-        <IsPartOfProject Value="True"/>
-      </Unit0>
-      <Unit1>
-        <Filename Value="..\core\URandomHash.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit1>
-      <Unit2>
-        <Filename Value="RandomHashTests.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit2>
-      <Unit3>
-        <Filename Value="..\libraries\generics.collections\generics.collections.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit3>
-      <Unit4>
-        <Filename Value="..\libraries\generics.collections\generics.defaults.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit4>
-      <Unit5>
-        <Filename Value="..\libraries\generics.collections\generics.hashes.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit5>
-      <Unit6>
-        <Filename Value="..\libraries\generics.collections\generics.helpers.pas"/>
-        <IsPartOfProject Value="True"/>
-        <UnitName Value="Generics.Helpers"/>
-      </Unit6>
-      <Unit7>
-        <Filename Value="..\libraries\generics.collections\generics.memoryexpanders.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit7>
-      <Unit8>
-        <Filename Value="..\libraries\generics.collections\generics.strings.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit8>
-      <Unit9>
-        <Filename Value="..\libraries\sphere10\UMemory.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit9>
-      <Unit10>
-        <Filename Value="..\libraries\sphere10\UCommon.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit10>
-    </Units>
-  </ProjectOptions>
-  <CompilerOptions>
-    <Version Value="11"/>
-    <PathDelim Value="\"/>
-    <Target>
-      <Filename Value=".\bin\PascalCoinUnitTests"/>
-    </Target>
-    <SearchPaths>
-      <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="..\core;..\libraries\generics.collections;..\libraries\hashlib4pascal;..\libraries\sphere10"/>
-      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-    </SearchPaths>
-    <CodeGeneration>
-      <Optimizations>
-        <OptimizationLevel Value="4"/>
-      </Optimizations>
-    </CodeGeneration>
-    <Linking>
-      <Options>
-        <Win32>
-          <GraphicApplication Value="True"/>
-        </Win32>
-      </Options>
-    </Linking>
-  </CompilerOptions>
-  <Debugging>
-    <Exceptions Count="3">
-      <Item1>
-        <Name Value="EAbort"/>
-      </Item1>
-      <Item2>
-        <Name Value="ECodetoolError"/>
-      </Item2>
-      <Item3>
-        <Name Value="EFOpenError"/>
-      </Item3>
-    </Exceptions>
-  </Debugging>
-</CONFIG>

+ 0 - 18
src/tests/UPascalCoinUnitTests.lpr

@@ -1,18 +0,0 @@
-program UPascalCoinUnitTests;
-
-{$mode objfpc}{$H+}
-
-uses
-  Interfaces,
-  Forms,
-  GuiTestRunner,
-  RandomHashTests;
-
-{$R *.res}
-
-begin
-  Application.Initialize;
-  Application.CreateForm(TGuiTestRunner, TestRunner);
-  Application.Run;
-end.
-

BIN
src/tests/UPascalCoinUnitTests.res


+ 228 - 6
src/tests/URandomHashTests.pas

@@ -61,6 +61,33 @@ type
     procedure TestMURMUR3_32;
   end;
 
+  { TRandomHashFastTest }
+
+  TRandomHashFastTest = class(TPascalCoinUnitTest)
+  public type
+    TFastTransformProc = procedure(const ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer) of object;
+  private
+    procedure TestMemTransform(ATransform : TFastTransformProc; const ATestData : array of TTestItem<Integer, String>);
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+  published
+    procedure TestRandomHash_Standard;
+    procedure TestRandomHash;
+    procedure TestExpand;
+    procedure TestCompress;
+    procedure TestChecksum_1;
+    procedure TestChecksum_2;
+    procedure TestMemTransform1;
+    procedure TestMemTransform2;
+    procedure TestMemTransform3;
+    procedure TestMemTransform4;
+    procedure TestMemTransform5;
+    procedure TestMemTransform6;
+    procedure TestMemTransform7;
+    procedure TestMemTransform8;
+  end;
+
 implementation
 
 uses variants, UCommon, UCommon.Collections, UMemory, URandomHash, HlpHashFactory, HlpBitConverter, strutils;
@@ -749,8 +776,8 @@ var
   LCase : TTestItem<String, String>;
 begin
   for LCase in DATA_RANDOMHASH_STANDARD do
-    //AssertEquals(ParseBytes(LCase.Expected), TRandomHash.Compute(ParseBytes(LCase.Input)));
-    WriteLn(Format('%s', [Bytes2Hex(TRandomHash.Compute(ParseBytes(LCase.Input)), True)]));
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHash.Compute(ParseBytes(LCase.Input)));
+    //WriteLn(Format('%s', [Bytes2Hex(TRandomHash.Compute(ParseBytes(LCase.Input)), True)]));
 end;
 
 procedure TRandomHashTest.TestRandomHash;
@@ -760,8 +787,8 @@ var
 begin
   for LCase in DATA_RANDOMHASH do begin
     LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
-    //AssertEquals(ParseBytes(LCase.Expected), TRandomHash.Compute(LInput));
-    WriteLn(Format('%s', [Bytes2Hex(TRandomHash.Compute(LInput), True)]));
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHash.Compute(LInput));
+    //WriteLn(Format('%s', [Bytes2Hex(TRandomHash.Compute(LInput), True)]));
   end;
 end;
 
@@ -777,8 +804,8 @@ begin
   LMurMur3 := THashFactory.THash32.CreateMurmurHash3_x86_32();
   for LCase in DATA_EXPAND do begin
     LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input1);
-    //AssertEquals(LCase.Expected, LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
-    WriteLn(LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
+    AssertEquals(LCase.Expected, LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
+    //WriteLn(LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
   end;
 
 end;
@@ -1042,12 +1069,207 @@ begin
   end;
 end;
 
+{ TRandomHashFastTest }
+
+procedure TRandomHashFastTest.SetUp;
+begin
+  inherited;
+end;
+
+procedure TRandomHashFastTest.TearDown;
+begin
+  inherited;
+end;
+
+procedure TRandomHashFastTest.TestRandomHash_Standard;
+var
+  LCase : TTestItem<String, String>;
+begin
+  for LCase in DATA_RANDOMHASH_STANDARD do
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHashFast.Compute(ParseBytes(LCase.Input)));
+end;
+
+procedure TRandomHashFastTest.TestRandomHash;
+var
+  LInput : TBytes;
+  LCase : TTestItem<Integer, String>;
+begin
+  for LCase in DATA_RANDOMHASH do begin
+    LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHashFast.Compute(LInput));
+  end;
+end;
+
+procedure TRandomHashFastTest.TestExpand;
+var
+  LCase : TTestItem<UInt32, UInt32, UInt32>;
+  LInput : TBytes;
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+  LMurMur3 : IHash;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  LMurMur3 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  for LCase in DATA_EXPAND do begin
+    LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input1);
+    AssertEquals(LCase.Expected, LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
+    //WriteLn(LMurMur3.ComputeBytes(LHasher.Expand(LInput, LCase.Input2)).GetUInt32);
+  end;
+end;
+
+procedure TRandomHashFastTest.TestCompress;
+var
+  LCase : TTestItem<String, String>;
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+  LInputs : TArray<TBytes>;
+
+  function ParseHex(constref AHex : String) : TBytes;
+  begin
+    Result := Hex2Bytes(AHex);
+  end;
+
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  for LCase in DATA_COMPRESS do begin
+    LInputs := TListTool<String, TBytes>.Transform(LCase.Input.Split([';']), ParseHex);
+    AssertEquals(Hex2Bytes(LCase.Expected), LHasher.Compress(LInputs));
+   //WriteLn(Bytes2Hex(LHasher.Compress(LInputs)));
+  end;
+end;
+
+procedure TRandomHashFastTest.TestChecksum_1;
+var
+  LInput : TBytes;
+  LCase : TTestItem<Integer, UInt32>;
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  for LCase in DATA_CHECKSUM do begin
+    LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
+    AssertEquals(LCase.Expected, LHasher.CheckSum(LInput));
+  end;
+end;
+
+procedure TRandomHashFastTest.TestChecksum_2;
+var
+  LInput : TBytes;
+  LInputs : TArray<TBytes>;
+  LCase : TTestItem<Integer, UInt32>;
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+  i : UInt32;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  for LCase in DATA_CHECKSUM do begin
+    LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
+    // Split into arrays of 1 byte
+    SetLength(LInputs, Length(LInput));
+    for i := 0 to Pred(Length(LInput)) do begin
+      SetLength(LInputs[i], 1);
+      LInputs[i][0] := LInput[i];
+    end;
+    AssertEquals(LCase.Expected, LHasher.CheckSum(LInputs));
+  end;
+end;
+
+procedure TRandomHashFastTest.TestMemTransform1;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform1, DATA_MEMTRANSFORM1);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform2;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform2, DATA_MEMTRANSFORM2);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform3;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform3, DATA_MEMTRANSFORM3);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform4;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform4, DATA_MEMTRANSFORM4);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform5;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform5, DATA_MEMTRANSFORM5);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform6;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform6, DATA_MEMTRANSFORM6);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform7;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform7, DATA_MEMTRANSFORM7);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform8;
+var
+  LHasher : TRandomHashFast;
+  LDisposables : TDisposables;
+begin
+  LHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  TestMemTransform(LHasher.MemTransform8, DATA_MEMTRANSFORM8);
+end;
+
+procedure TRandomHashFastTest.TestMemTransform(ATransform : TFastTransformProc; const ATestData : array of TTestItem<Integer, String>);
+var
+  LCase : TTestItem<Integer, String>;
+  LInput, LBuff : TBytes;
+begin
+  for LCase in ATestData do begin
+    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)));
+  end;
+end;
+
 initialization
 
 {$IFDEF FPC}
   RegisterTest(TRandomHashTest);
+  RegisterTest(TRandomHashFastTest);
 {$ELSE}
   TDUnitX.RegisterTextFixture(TRandomHashTest);
+  TDUnitX.RegisterTextFixture(TRandomHashFastTest);
 {$ENDIF FPC}
 
 end.