Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#76

Re: Eindeutiger Vergleich für große Dateien gesucht

  Alt 4. Aug 2005, 12:01
Jaja, wenn man einfach stupid irgendwas abtippt das ein Fachidiot ohne eigene Tests gepostet hat

Beachtet bitte die beiden Zeilen die mit 1.) und 2.) makiert wurden.

Delphi-Quellcode:
function GetFileSizeInt64(const FileName: String): Int64;
var
  Handle: THandle;
  Data: WIN32_FIND_DATA;
begin
  Result := -1;
  Handle := FindFirstFile(Pointer(FileName), Data);
  if Handle <> INVALID_HANDLE_VALUE then
  try
    Int64Rec(Result).Hi := Data.nFileSizeHigh;
    Int64Rec(Result).Lo := Data.nFileSizeLow;
  finally
    Windows.FindClose(Handle);
  end;
end;

function GetSystemAllocationGranularity: DWord;
var
  Info: TSystemInfo;
begin
  GetSystemInfo(Info);
  Result := Info.dwAllocationGranularity;
end;

function CompareFile(const FileName1, FileName2: String): Boolean;

  procedure DoError;
  begin
    RaiseLastWin32Error; // auskommentieren falls keine Exceptions erwünscht
// Result := False;
  end;

var
  AllocSize: DWord;
  CurSize: DWord;
  CurPos: Int64;
  FileHandle1, FileHandle2: THandle;
  MapHandle1, MapHandle2: THandle;
  FileSize1, FileSize2: Int64;
  View1, View2: Pointer;
begin
  Result := AnsiCompareText(FileName1, FileName2) = 0;
  if Result then Exit;
  FileSize1 := GetFileSizeInt64(FileName1);
  FileSize2 := GetFileSizeInt64(FileName2);
  if FileSize1 or FileSize2 >= 0 then
  begin
    Result := FileSize1 = FileSize2;
    if not Result or (FileSize1 = 0) then Exit;
    AllocSize := GetSystemAllocationGranularity * 8;
    Assert(AllocSize < MaxInt); // CompareMem() Size Param is Integer
    FileHandle1 := CreateFile(Pointer(FileName1), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
    if FileHandle1 <> INVALID_HANDLE_VALUE then
    try
      FileHandle2 := CreateFile(Pointer(FileName2), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if FileHandle2 <> INVALID_HANDLE_VALUE then
      try
        MapHandle1 := CreateFileMapping(FileHandle1, nil, PAGE_READONLY, 0, 0, nil);
        if MapHandle1 <> INVALID_HANDLE_VALUE then
        try
          MapHandle2 := CreateFileMapping(FileHandle2, nil, PAGE_READONLY, 0, 0, nil);
          if MapHandle2 <> INVALID_HANDLE_VALUE then
          try
            CurSize := FileSize1 mod AllocSize;
            if CurSize = 0 then CurSize := AllocSize;
            CurPos := FileSize1 - CurSize;
            repeat
              Assert(CurPos >= 0);
              Assert(CurPos mod AllocSize = 0);
              View1 := MapViewOfFile(MapHandle1, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize);
              if View1 <> nil then
              try
                View2 := MapViewOfFile(MapHandle2, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize);
                if View2 <> nil then
                try
                  Result := CompareMem(View1, View2, CurSize);
                finally
                  UnmapViewOfFile(View2);
                end else DoError;
              finally
                UnmapViewOfFile(View1);
              end else DoError;
              CurSize := AllocSize; // 1.)
              CurPos := CurPos - CurSize; // 2.)
            until not Result or (CurPos < 0);
          finally
            CloseHandle(MapHandle2);
          end else DoError;
        finally
          CloseHandle(MapHandle1);
        end else DoError;
      finally
        CloseHandle(FileHandle2);
      end else DoError;
    finally
      CloseHandle(FileHandle1);
    end else DoError;
  end else DoError;
end;

function CompareFile1(const FileName1, FileName2: String): Boolean;

  procedure DoError;
  begin
    RaiseLastWin32Error;
// Result := False;
  end;

const
  BufferSize = 65535;
var
  FileSize1,FileSize2: Int64;
  CurSize: Integer;
  Stream1,Stream2: TStream;
  Buffer1,Buffer2: array of Byte;
begin
  Result := AnsiCompareText(FileName1, FileName2) = 0;
  if Result then Exit;
  FileSize1 := GetFileSizeInt64(FileName1);
  FileSize2 := GetFileSizeInt64(FileName2);
  if FileSize1 or FileSize2 >=0 then
  begin
    Result := FileSize1 = FileSize2;
    if not Result or (FileSize1 = 0) then Exit;
    Stream1 := TFileStream.Create(FileName1, fmOpenRead or fmShareDenyWrite);
    try
      Stream2 := TFileStream.Create(FileName2, fmOpenRead or fmShareDenyWrite);
      try
        SetLength(Buffer1, BufferSize);
        SetLength(Buffer2, BufferSize);
        while Result and (FileSize1 > 0) do
        begin
          CurSize := BufferSize;
          if CurSize > FileSize1 then CurSize := FileSize1;
          Stream1.Read(Buffer1[0], CurSize);
          Stream2.Read(Buffer2[0], CurSize);
          Result := CompareMem(@Buffer1[0], @Buffer2[0], CurSize);
          FileSize1 := FileSize1 - CurSize;
        end;
      finally
        Stream2.Free;
      end;
    finally
      Stream1.Free;
    end;
  end else DoError;
end;
So. Bei meinen Test ist CompareFile() ca. 140% schneller als CompareFile1().
Aber am wichtigsten dürfter der Fakt sein das CompareFile() umso schneller arbeitet um so öfters eine Datei als MMF in den Speicher geladen wurde, sprich schon im Cache ist. Das ist gut so denn ich nehme an das zumindestens eine der beiden Dateien kurz vorher, ebenfalls per MMF, gehasht wurde. D.h. sie ist schon im Speicher weil man ja noch den Hash per MD4 vorher ziehen wird. Die andere Datei stellt ja die Referenz die schon in der Datenbank gespeichert wurde,also auch deren Hash Wert. Somit dürften die MMF's tatsächlich einiges an Speed bringen.

Gruß Hagen
  Mit Zitat antworten Zitat