Changes between Initial Version and Version 1 of HashSourceCodes


Ignore:
Timestamp:
Jul 14, 2008, 8:07:32 AM (16 years ago)
Author:
os
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • HashSourceCodes

    v1 v1  
     1[[PageOutline(2, Programming Languages)]]
     2Hash code is based on [http://sourceforge.net/projects/guliverkli/ Media Player Classic]. In natural language it calculates: size + 64bit chksum of the first and last 64k (even if they overlap because the file is smaller than 128k).
     3
     4Feel free to edit/add source-codes if you have faster/better implementation. Also don't forget to check, if hash is right for test. Test these 2 files please to ensure your algo is completely OK: 
     5 * [http://www.opensubtitles.org/addons/avi/breakdance.avi AVI file] (12 909 756 bytes)
     6   * hash: '''8e245d9679d31e12'''
     7 * [http://www.opensubtitles.org/addons/avi/dummy.rar DUMMY RAR file] (2 565 922 bytes, 4 295 033 890 after RAR unpacking)
     8   * hash: '''61f7751fc2a72bfb'''
     9
     10
     11== C ==
     12{{{
     13
     14#include <stdio.h>
     15#include <stdlib.h>
     16
     17#define MAX(x,y) (((x) > (y)) ? (x) : (y))
     18#ifndef uint64_t
     19#define uint64_t unsigned long long
     20#endif
     21
     22uint64_t compute_hash(FILE * handle)
     23{
     24        uint64_t hash, fsize;
     25
     26        fseek(handle, 0, SEEK_END);
     27        fsize = ftell(handle);
     28        fseek(handle, 0, SEEK_SET);
     29
     30        hash = fsize;
     31
     32        for(uint64_t tmp = 0, i = 0; i < 65536/sizeof(tmp) && fread((char*)&tmp, sizeof(tmp), 1, handle); hash += tmp, i++);
     33        fseek(handle, (long)MAX(0, fsize - 65536), SEEK_SET);
     34        for(uint64_t tmp = 0, i = 0; i < 65536/sizeof(tmp) && fread((char*)&tmp, sizeof(tmp), 1, handle); hash += tmp, i++);
     35       
     36        return hash;
     37}
     38
     39int main(int argc, char *argv)
     40{
     41        FILE * handle;
     42        uint64_t myhash;
     43
     44        handle = fopen("breakdance.avi", "rb");
     45       
     46        if (!handle)
     47        {
     48                printf("Error openning file!");
     49                return 1;
     50        }
     51
     52        myhash = compute_hash(handle); 
     53        printf("%I64x", myhash);
     54
     55        fclose(handle);
     56        return 0;
     57}
     58
     59}}}
     60
     61
     62== C++ ==
     63{{{
     64 #include <iostream>
     65 #include <fstream>
     66 
     67 typedef unsigned __int64 uint64_t;
     68 using namespace std;
     69 
     70 int MAX(int x, int y)
     71 { 
     72        if((x) > (y))
     73                return x;
     74        else   
     75                return y;
     76 }
     77 
     78 uint64_t compute_hash(ifstream& f)
     79 {
     80        uint64_t hash, fsize;
     81 
     82        f.seekg(0, ios::end);
     83        fsize = f.tellg();
     84        f.seekg(0, ios::beg);
     85 
     86        hash = fsize;
     87        for(uint64_t tmp = 0, i = 0; i < 65536/sizeof(tmp) && f.read((char*)&tmp, sizeof(tmp)); i++, hash += tmp);
     88        f.seekg(MAX(0, (uint64_t)fsize - 65536), ios::beg);
     89        for(tmp = 0, i = 0; i < 65536/sizeof(tmp) && f.read((char*)&tmp, sizeof(tmp)); i++, hash += tmp);
     90        return hash;
     91 }
     92 
     93 int main(int argc, char *argv)
     94 {
     95        ifstream f;
     96        uint64_t myhash;
     97 
     98        f.open("c:\\test.avi", ios::in|ios::binary|ios::ate);
     99        if (!f.is_open()) {
     100           cerr << "Error opening file" << endl;
     101           return 1;
     102        }
     103 
     104        myhash = compute_hash(f);
     105        cout << setw(16) << setfill('0') << hex << myhash;
     106 
     107        f.close();
     108        return 0;
     109 }
     110
     111}}}
     112
     113
     114== Java ==
     115{{{
     116import java.io.File;
     117import java.io.FileInputStream;
     118import java.io.IOException;
     119import java.nio.ByteOrder;
     120import java.nio.LongBuffer;
     121import java.nio.MappedByteBuffer;
     122import java.nio.channels.FileChannel;
     123import java.nio.channels.FileChannel.MapMode;
     124
     125
     126/**
     127 * Hash code is based on Media Player Classic. In natural language it calculates: size + 64bit
     128 * checksum of the first and last 64k (even if they overlap because the file is smaller than
     129 * 128k).
     130 */
     131public class OpenSubtitlesHasher {
     132       
     133        /**
     134         * Size of the chunks that will be hashed in bytes (64 KB)
     135         */
     136        private static final int HASH_CHUNK_SIZE = 64 * 1024;
     137       
     138       
     139        public static String computeHash(File file) throws IOException {
     140                long size = file.length();
     141                long chunkSizeForFile = Math.min(HASH_CHUNK_SIZE, size);
     142               
     143                FileChannel fileChannel = new FileInputStream(file).getChannel();
     144               
     145                long head = computeHashForChunk(fileChannel, 0, chunkSizeForFile);
     146                long tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile);
     147               
     148                fileChannel.close();
     149               
     150                return String.format("%016x", size + head + tail);
     151        }
     152       
     153
     154        private static long computeHashForChunk(FileChannel fileChannel, long start, long size) throws IOException {
     155                MappedByteBuffer byteBuffer = fileChannel.map(MapMode.READ_ONLY, start, size);
     156               
     157                LongBuffer longBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer();
     158                long hash = 0;
     159               
     160                while (longBuffer.hasRemaining()) {
     161                        hash += longBuffer.get();
     162                }
     163               
     164                return hash;
     165        }
     166       
     167}
     168}}}
     169
     170== C# ==
     171{{{
     172using System;
     173using System.Text;
     174using System.IO;
     175   
     176namespace MovieHasher
     177{
     178    class Program
     179    {
     180        private static byte[] ComputeMovieHash(string filename)
     181        {
     182            byte[] result;
     183            using (Stream input = File.OpenRead(filename))
     184            {
     185                result = ComputeMovieHash(input);
     186            }
     187            return result;
     188        }
     189 
     190        private static byte[] ComputeMovieHash(Stream input)
     191        {
     192            long lhash, streamsize;
     193            streamsize = input.Length;
     194            lhash = streamsize;
     195 
     196            long i = 0;
     197            byte[] buffer = new byte[sizeof(long)];
     198            while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
     199            {
     200                i++;
     201                lhash += BitConverter.ToInt64(buffer, 0);
     202            }
     203 
     204            input.Position = Math.Max(0, streamsize - 65536);
     205            i = 0;
     206            while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
     207            {
     208                i++;
     209                lhash += BitConverter.ToInt64(buffer, 0);
     210            }
     211            input.Close();
     212            byte[] result = BitConverter.GetBytes(lhash);
     213            Array.Reverse(result);
     214            return result;
     215        }
     216 
     217        private static string ToHexadecimal(byte[] bytes)
     218        {
     219            StringBuilder hexBuilder = new StringBuilder();
     220            for(int i = 0; i < bytes.Length; i++)
     221            {
     222                hexBuilder.Append(bytes[i].ToString("x2"));
     223            }
     224            return hexBuilder.ToString();
     225        }
     226 
     227        static void Main(string[] args)
     228        {
     229            byte[] moviehash = ComputeMovieHash(@"C:\test.avi");
     230            Console.WriteLine("The hash of the movie-file is: {0}", ToHexadecimal(moviehash));
     231        }
     232    }
     233}
     234}}}
     235
     236== VB.Net ==
     237{{{
     238Imports System
     239Imports System.Text
     240Imports System.IO
     241'Note: you must remove integer overflow checking.
     242
     243Namespace MovieHasher
     244        Class Program
     245                Private Shared Function ComputeMovieHash(ByVal filename As String) As Byte()
     246                        Dim result As Byte()
     247                        Using input As Stream = File.OpenRead(filename)
     248                                result = ComputeMovieHash(input)
     249                        End Using
     250                        Return result
     251                End Function
     252               
     253                Private Function ComputeMovieHash(ByVal input As Stream) As Byte()
     254                        Dim lhash As System.Int64, streamsize As Long
     255                        streamsize = input.Length
     256                        lhash = streamsize
     257                       
     258                        Dim i As Long = 0
     259                        Dim buffer As Byte() = New Byte(Marshal.SizeOf(GetType(Long)) - 1) {}
     260                        While i < 65536 / Marshal.SizeOf(GetType(Long)) AndAlso (input.Read(buffer, 0, Marshal.SizeOf(GetType(Long))) > 0)
     261                                i += 1
     262                               
     263                                lhash += BitConverter.ToInt64(buffer, 0)
     264                        End While
     265                       
     266                        input.Position = Math.Max(0, streamsize - 65536)
     267                        i = 0
     268                        While i < 65536 / Marshal.SizeOf(GetType(Long)) AndAlso (input.Read(buffer, 0, Marshal.SizeOf(GetType(Long))) > 0)
     269                                i += 1
     270                                lhash += BitConverter.ToInt64(buffer, 0)
     271                        End While
     272                        input.Close()
     273                        Dim result As Byte() = BitConverter.GetBytes(lhash)
     274                        Array.Reverse(result)
     275                        Return result
     276                End Function
     277               
     278                Private Shared Function ToHexadecimal(ByVal bytes As Byte()) As String
     279                        Dim hexBuilder As New StringBuilder()
     280                        For i As Integer = 0 To bytes.Length - 1
     281                                hexBuilder.Append(bytes(i).ToString("x2"))
     282                        Next
     283                        Return hexBuilder.ToString()
     284                End Function
     285               
     286                Private Shared Sub Main(ByVal args As String())
     287                        Dim moviehash As Byte() = ComputeMovieHash("C:\test.avi")
     288                        Console.WriteLine("The hash of the movie-file is: {0}", ToHexadecimal(moviehash))
     289                End Sub
     290        End Class
     291End Namespace
     292}}}
     293
     294== Python ==
     295{{{
     296def hashFile(name):
     297      try:
     298                 
     299                longlongformat = 'q'  # long long
     300                bytesize = struct.calcsize(longlongformat)
     301                   
     302                f = file(name, "rb")
     303                   
     304                filesize = os.path.getsize(name)
     305                hash = filesize
     306                   
     307                if filesize < 65536 * 2:
     308                       return "SizeError"
     309                 
     310                for x in range(65536/bytesize):
     311                        buffer = f.read(bytesize)
     312                        (l_value,)= struct.unpack(longlongformat, buffer) 
     313                        hash += l_value
     314                        hash = hash & 0xFFFFFFFFFFFFFFFF #to remain as 64bit number 
     315                         
     316   
     317                f.seek(max(0,filesize-65536),0)
     318                for x in range(65536/bytesize):
     319                        buffer = f.read(bytesize)
     320                        (l_value,)= struct.unpack(longlongformat, buffer) 
     321                        hash += l_value
     322                        hash = hash & 0xFFFFFFFFFFFFFFFF
     323                 
     324                f.close()
     325                returnedhash =  "%016x" % hash
     326                return returnedhash
     327   
     328      except(IOError):
     329                return "IOError"
     330}}}
     331
     332
     333== Delphi ==
     334This is just a quick conversion of Gabest's original C code. Anyone who can come up with a cleaner code, please feel free to do so and post here.
     335
     336{{{
     337function CalcGabestHash(const Filename: string): string;
     338var
     339  i: Integer;
     340  f: File;
     341  s: Array[1..8] of char;
     342  tmp: Int64 absolute s;
     343  hash: Int64;
     344  readed: Integer;
     345  OldFM: Byte;
     346begin
     347  Result := '';
     348  if FileExists(Filename) then begin
     349    { Open file }
     350    OldFM := FileMode;
     351    try
     352      FileMode := fmShareDenyNone;
     353      AssignFile(f,Filename);
     354      Reset(f,1);
     355      try
     356        { Start hashing }
     357        hash := filesize(f);
     358        { Read from begining }
     359        for i := 0 to 8191 do begin
     360          blockread(f,s,sizeof(s),readed);
     361          if readed > 0 then hash := hash + tmp else break;
     362        end;
     363        { Read from the end }
     364        seek(f,Max(0,filesize(f)-65536));
     365        for i := 0 to 8191 do begin
     366          blockread(f,s,sizeof(s),readed);
     367          if readed > 0 then hash := hash + tmp else break;
     368        end;
     369        { Finished }
     370        Result := Format('%.16x',[hash]);
     371      finally
     372        CloseFile(f);
     373      end;
     374    finally
     375      FileMode := OldFM;
     376    end;
     377  end;
     378end;
     379}}}
     380
     381== !RealBasic ==
     382Combined routine that will calculate a fast hash for videofiles over 65K and a normal md5 for subtitles
     383
     384{{{
     385    dim b as BinaryStream
     386    dim mb as MemoryBlock
     387   
     388    dim hash,bytesize as UINT64
     389    dim i, x, chunksize, filelen, difference as integer
     390   
     391    hash = 0 //Reset Hash
     392    difference = 0
     393   
     394    if f <> nil and f.Exists then
     395      b= f.OpenAsBinaryFile
     396      hash = b.Length
     397      bytesize = b.Length
     398      bytesizestr = str(bytesize)
     399     
     400      if bytesize >= 65536 and routine = "video" then
     401        chunksize = 65536
     402        mb = b.Read(65536)
     403        mb.LittleEndian = True
     404       
     405        for i= 0 to chunksize -1 step 8
     406          hash = hash+ mb.UINT64Value(i)
     407        next
     408       
     409        b.Position = max(b.Length-chunksize, 0)
     410        mb= b.Read(chunksize)
     411        mb.LittleEndian = True
     412       
     413        for i= 0 to chunksize -1 step 8
     414          hash = hash+ mb.UINT64Value(i)
     415        next
     416       
     417        myhash = Lowercase(str(hex(hash)))
     418       
     419      elseif routine = "subtitle" then
     420       
     421        dim c,result as string
     422        mb = md5(b.Read(b.Length))
     423        mb.LittleEndian = True
     424       
     425        for i = 0 to mb.size-1
     426          x = mb.byte( i )
     427          c = right( "00"+hex( x ), 2 )
     428          result = result + c
     429        next
     430        result = lowercase( result )
     431        myhash = result
     432       
     433      end
     434}}}
     435
     436== PHP 4/5 ==
     437
     438{{{
     439function OpenSubtitlesHash($file)
     440{
     441    $handle = fopen($file, "rb");
     442    $fsize = filesize($file);
     443   
     444    $hash = array(3 => 0,
     445                  2 => 0,
     446                  1 => ($fsize >> 16) & 0xFFFF,
     447                  0 => $fsize & 0xFFFF);
     448       
     449    for ($i = 0; $i < 8192; $i++)
     450    {
     451        $tmp = ReadUINT64($handle);
     452        $hash = AddUINT64($hash, $tmp);
     453    }
     454   
     455    $offset = $fsize - 65536;
     456    fseek($handle, $offset > 0 ? $offset : 0, SEEK_SET);
     457   
     458    for ($i = 0; $i < 8192; $i++)
     459    {
     460        $tmp = ReadUINT64($handle);
     461        $hash = AddUINT64($hash, $tmp);         
     462    }
     463   
     464    fclose($handle);
     465        return UINT64FormatHex($hash);
     466}
     467
     468function ReadUINT64($handle)
     469{
     470    $u = unpack("va/vb/vc/vd", fread($handle, 8));
     471    return array(0 => $u["a"], 1 => $u["b"], 2 => $u["c"], 3 => $u["d"]);
     472}
     473
     474function AddUINT64($a, $b)
     475{
     476    $o = array(0 => 0, 1 => 0, 2 => 0, 3 => 0);
     477
     478    $carry = 0;
     479    for ($i = 0; $i < 4; $i++)
     480    {
     481        if (($a[$i] + $b[$i] + $carry) > 0xffff )
     482        {
     483            $o[$i] += ($a[$i] + $b[$i] + $carry) & 0xffff;
     484            $carry = 1;
     485        }
     486        else
     487        {
     488            $o[$i] += ($a[$i] + $b[$i] + $carry);
     489            $carry = 0;
     490        }
     491    }
     492   
     493    return $o;   
     494}
     495
     496function UINT64FormatHex($n)
     497{   
     498    return sprintf("%04x%04x%04x%04x", $n[3], $n[2], $n[1], $n[0]);
     499}
     500}}}
     501
     502== Perl ==
     503
     504{{{
     505#!/usr/bin/perl
     506use strict;
     507use warnings;
     508
     509print OpenSubtitlesHash('breakdance.avi');
     510
     511sub OpenSubtitlesHash {
     512        my $filename = shift or die("Need video filename");
     513
     514        open my $handle, "<", $filename or die $!;
     515        my $fsize = -s $filename;
     516
     517        my $hash = [$fsize & 0xFFFF, ($fsize >> 16) & 0xFFFF, 0, 0];
     518
     519        $hash = AddUINT64($hash, ReadUINT64($handle)) for (1..8192);
     520
     521    my $offset = $fsize - 65536;
     522    seek($handle, $offset > 0 ? $offset : 0, 0) or die $!;
     523
     524    $hash = AddUINT64($hash, ReadUINT64($handle)) for (1..8192);
     525
     526    close $handle or die $!;
     527    return UINT64FormatHex($hash);
     528}
     529
     530sub ReadUINT64 {
     531        read($_[0], my $u, 8);
     532        return [unpack("vvvv", $u)];
     533}
     534
     535sub AddUINT64 {
     536    my $o = [0,0,0,0];
     537    my $carry = 0;
     538    for my $i (0..3) {
     539        if (($_[0]->[$i] + $_[1]->[$i] + $carry) > 0xffff ) {
     540                        $o->[$i] += ($_[0]->[$i] + $_[1]->[$i] + $carry) & 0xffff;
     541                        $carry = 1;
     542                } else {
     543                        $o->[$i] += ($_[0]->[$i] + $_[1]->[$i] + $carry);
     544                        $carry = 0;
     545                }
     546        }
     547    return $o;
     548}
     549
     550sub UINT64FormatHex {
     551    return sprintf("%04x%04x%04x%04x", $_[0]->[3], $_[0]->[2], $_[0]->[1], $_[0]->[0]);
     552}
     553
     554}}}
     555
     556
     557
     558== Ruby ==
     559This is a quick translation/transliteration of the Perl script.
     560{{{
     561
     562class Hasher
     563
     564  def open_subtitles_hash(filename)
     565    raise "Need video filename" unless filename
     566
     567    fh = File.open(filename)
     568    fsize = File.size(filename)
     569
     570    hash = [fsize & 0xffff, (fsize >> 16) & 0xffff, 0, 0]
     571
     572    8192.times { hash = add_unit_64(hash, read_uint_64(fh)) }
     573
     574    offset = fsize - 65536
     575    fh.seek([0,offset].max, 0)
     576
     577    8192.times { hash = add_unit_64(hash, read_uint_64(fh)) }
     578
     579    fh.close
     580
     581    return uint_64_format_hex(hash)
     582  end
     583
     584  def read_uint_64(stream)
     585    stream.read(8).unpack("vvvv")
     586  end
     587
     588  def add_unit_64(hash, input)
     589    res = [0,0,0,0]
     590    carry = 0
     591
     592    hash.zip(input).each_with_index do |(h,i),n|
     593      sum = h + i + carry
     594      if sum > 0xffff
     595        res[n] += sum & 0xffff
     596        carry = 1
     597      else
     598        res[n] += sum
     599        carry = 0
     600      end
     601    end
     602    return res
     603  end
     604
     605  def uint_64_format_hex(hash)
     606    sprintf("%04x%04x%04x%04x", *hash.reverse)
     607  end
     608end
     609
     610if __FILE__ == $0
     611  require 'test/unit'
     612
     613  class HashTester < Test::Unit::TestCase
     614    def setup
     615      @h = Hasher.new
     616    end
     617
     618    def test_test_file_hash
     619      assert_equal("8e245d9679d31e12", @h.open_subtitles_hash('breakdance.avi'))
     620    end
     621  end
     622end
     623
     624
     625}}}
     626
     627Another more "rubyesque" implementation.
     628{{{
     629
     630class MovieHasher
     631
     632  HASH_CHUNK_SIZE = 64 * 1024 # in bytes
     633  HASH_SIZE = 8 # in bytes
     634 
     635  def self.compute_hash(filename)
     636    file = File.open(filename, "rb")
     637    filesize = File.size(filename)
     638   
     639    hash = filesize
     640   
     641    # Read 64 kbytes, divide up into 64 bits and add each
     642    # to hash. Do for beginning and end of file.
     643   
     644    # Q = unsigned long long (64 bit = HASH_SIZE)
     645    file.read(HASH_CHUNK_SIZE).unpack("Q*").each { |n| hash += n }
     646    file.seek([0, filesize - HASH_CHUNK_SIZE].max, IO::SEEK_SET)
     647    file.read(HASH_CHUNK_SIZE).unpack("Q*").each { |n| hash += n }
     648   
     649    file.close
     650   
     651    hash &= 0xffffffffffffffff # keep the first 8 bytes / 64 bits
     652   
     653    sprintf("%0" + (HASH_SIZE * 2).to_s +  "x", hash)
     654  end
     655end
     656
     657if __FILE__ == $0
     658  require 'test/unit'
     659
     660  class MovieHasherTest < Test::Unit::TestCase
     661    def test_compute_hash
     662      assert_equal("8e245d9679d31e12", MovieHasher::compute_hash('breakdance.avi'))
     663    end
     664  end
     665end
     666
     667}}}