简体   繁体   English

FNV的实施

[英]Implementation of FNV

I am trying to implement FNV hash from http://isthe.com/chongo/tech/comp/fnv/ 我正在尝试从http://isthe.com/chongo/tech/comp/fnv/实现FNV哈希

I converted the PowerBasic's inline asm on that page into Delphi. 我在该页面上将PowerBasic的嵌入式asm转换为Delphi。

    function ReadFileToMem(sPath:string):Pointer;
    var
    hFile:    THandle;
    pBuffer:  Pointer;
    dSize:    DWORD;
    dRead:    DWORD;
    begin
    hFile := CreateFile(PChar(sPath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
    if hFile <> 0 then
        dSize := GetFileSize(hFile, nil);
        if dSize <> 0 then
        begin
          SetFilePointer(hFile, 0, nil, FILE_BEGIN);
          GetMem(Result, dSize);
          ReadFile(hFile, Result^, dSize, dRead, nil);
          if dRead = 0 then
            MessageBox(0, PChar('Error reading file.'), PChar('Read Error'), MB_ICONEXCLAMATION)
         end;
        CloseHandle(hFile);
    end;

    function GetPointerSize(lpBuffer: Pointer): Cardinal; // Function by ErazerZ
    begin
      if lpBuffer = nil then
        Result := Cardinal(-1)
      else
        Result := Cardinal(Pointer(Cardinal(lpBuffer) -4)^) and $7FFFFFFC -4;
    end;

     FUNCTION FNV32( dwOffset : Pointer; dwLen : DWORD; offset_basis : DWORD) : DWORD ;
      asm
     mov esi, dwOffset      //;esi = ptr to buffer
     mov ecx, dwLen         //;ecx = length of buffer (counter)
     mov eax, offset_basis  //;set to 2166136261 for FNV-1
     mov edi, 16777619//&h01000193    //;FNV_32_PRIME = 16777619
     xor ebx, ebx           //;ebx = 0
      @nextbyte:
     mul edi                //;eax = eax * FNV_32_PRIME
     mov bl, [esi]          //;bl = byte from esi
     xor eax, ebx           //;al = al xor bl
     inc esi                //;esi = esi + 1 (buffer pos)
     dec ecx                //;ecx = ecx - 1 (counter)
     jnz @nextbyte           //;if ecx is 0, jmp to NextByte
     mov @result, eax      //;else, function = eax

    end;

procedure TForm1.Button1Click(Sender: TObject);
var
pFile : Pointer;
hFile : Cardinal;
begin
//Profiler1['Test'].Start;
pFile := ReadFileToMem(fn);
hFile := FNV32(pFile,GetPointerSize(pFile),2166136261);
//Profiler1['Test'].Stop;
//OutputDebugString(pchar(Profiler1['Test'].AsText[tiAll]));
OutputDebugString(pchar(inttostr(hFile)));
end;

If a size of given file is more that 200KB, the output is random (hash) number. 如果给定文件的大小大于200KB,则输出为随机(哈希)数。 Am I missing something? 我想念什么吗?

Your asm code is somewhat buggy, IMHO. 您的asm代码有些错误,恕我直言。 It will crash your application, as it is written. 编写时,它会使应用程序崩溃。

  • You need to preseve esi/edi/ebx registers 您需要预先设置esi / edi / ebx寄存器
  • parameters are passed in eax,ecx,edx registers 参数在eax,ecx,edx寄存器中传递
  • result is the eax register 结果是eax寄存器

Correct way to do it could be (not tested, just written here there): 正确的方法可以进行测试(未经测试,仅在此处编写):

function fnv32(dwOffset : Pointer; dwLen : DWORD; offset_basis: DWORD) : DWORD ;
asm // eax=dwOffset ecx=dwLen edx=offset_basis -> result in eax
  push esi
  push edi
  mov esi,eax
  mov eax,edx
  or ecx,ecx
  je @z
  mov edi,16777619
  xor edx,edx
@1:
  mul edi
  mov dl,[esi]
  xor eax,edx
  inc esi
  dec ecx
  jnz @1
@z:
  pop edi
  pop esi
end;

So to read and hash any file, in a pure Delphi way (don't use Windows API like you did): 因此,以纯Delphi方式读取和哈希任何文件(不要像您一样使用Windows API):

function fnv32file(const aFileName: TFileName): DWORD;
begin
  with TMemoryStream.Create do
  try
    LoadFromFile(aFileName);
    result := fnv32(Memory,Size,0);
  finally
    Free;
  end;
end;

A pure pascal version won't be much slower IMHO (the bottleneck is definitively reading the data from the hard drive): 纯粹的pascal版本不会太慢,恕我直言(瓶颈肯定是从硬盘读取数据):

function fnv32(dwOffset : PByteArray; dwLen : DWORD; offset_basis: DWORD): DWORD ;
var i: integer;
begin
  result := offset_basis;
  for i := 0 to dwLen-1 do
    result := (result*16777619) xor DWORD(dwOffset^[i]);
end;

Enjoy 请享用

function fnv(dwOffset : Pointer; dwLen : NativeUInt; offset_basis: NativeUInt) : NativeUInt ;
//
// http://find.fnvhash.com/ - FNV Hash Calculator Online
// http://www.isthe.com/chongo/tech/comp/fnv/
//
// The offset_basis for FNV-1 is dependent on n, the size of the hash:
// 32 bit offset_basis = 2166136261
// 64 bit offset_basis = 14695981039346656037
//
{$IF Defined(CPUX86)}
asm
    push ebp
    push edi
    push ebx // statement must preserve the EDI, ESI, ESP, EBP, and EBX registers
    mov ebp, edx
    mov edx, ecx // but can freely modify the EAX, ECX, and EDX registers
    mov ecx, eax
    mov eax, edx
    mov edi, 01000193h
    xor ebx, ebx
  @@nexta:
    mov bl, byte ptr [ecx]
    xor eax, ebx
    mul edi
    inc ecx
    dec ebp
    jnz @@nexta
    pop ebx
    pop edi
    pop ebp
end;
{$ELSEIF Defined(CPUX64)}
asm
    mov rax, R8
    mov r8, rdx
    mov r9, 100000001b3h
    xor r10, r10
  @@nexta:
    mov r10b, byte ptr [rcx]
    xor rax, r10
    mul r9
    inc rcx
    dec r8
    jnz @@nexta
end;
{$IFEND}

Where should I start ... 我应该从哪里开始...

1) CreateFile returns INVALID_HANDLE_VALUE on failure, not 0. 1)如果失败,CreateFile返回INVALID_HANDLE_VALUE,而不是0。

2) SetFilePointer is not necessary. 2)不需要SetFilePointer。

3) What if you have to hash 16 GB file? 3)如果必须哈希16 GB文件怎么办?

4) You are not releasing allocated memory - FreeMem(pFile). 4)您不释放分配的内存-FreeMem(pFile)。

5) GetPointerSize is a total hack. 5)GetPointerSize完全是黑客。 You could just return file size from ReadFileToMem. 您可以从ReadFileToMem返回文件大小。

The following code is a rewrite of your approach. 以下代码是您的方法的重写。 It still loads complete file into the memory but is implemented "the Delphi way". 它仍然将完整的文件加载到内存中,但是以“ Delphi方式”实现。

function ReadFileToMem(const sPath: string; var buffer: TMemoryStream): boolean;
var
  fileStr: TFileStream;
begin
  Result := false;
  try
    fileStr := TFileStream.Create(sPath, fmOpenRead);
    try
      buffer.Size := 0;
      buffer.CopyFrom(fileStr, 0);
    finally FreeAndNil(fileStr); end;
    Result := true;
  except
    on E: EFOpenError do
      ShowMessage('Error reading file. ' + E.Message);
  end;
end;

function FNV32(dwOffset: pointer; dwLen: cardinal; offset_basis: cardinal): cardinal;
asm
  mov esi, dwOffset      //;esi = ptr to buffer
  mov ecx, dwLen         //;ecx = length of buffer (counter)
  mov eax, offset_basis  //;set to 2166136261 for FNV-1
  mov edi, 16777619//&h01000193    //;FNV_32_PRIME = 16777619
  xor ebx, ebx           //;ebx = 0
@nextbyte:
  mul edi                //;eax = eax * FNV_32_PRIME
  mov bl, [esi]          //;bl = byte from esi
  xor eax, ebx           //;al = al xor bl
  inc esi                //;esi = esi + 1 (buffer pos)
  dec ecx                //;ecx = ecx - 1 (counter)
  jnz @nextbyte           //;if ecx is 0, jmp to NextByte
  mov @result, eax      //;else, function = eax
end;

procedure TForm16.Button1Click(Sender: TObject);
var
  hFile : cardinal;
  memBuf: TMemoryStream;
begin
  memBuf := TMemoryStream.Create;
  try
    if ReadFileToMem('SomeFile', memBuf) then begin
      hFile := FNV32(memBuf.Memory, memBuf.Size, 2166136261);
      ShowMessageFmt('Hash = %d', [hFile]);
    end;
  finally FreeAndNil(memBuf); end;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM