| 1879 | |
| 1880 | === GO === |
| 1881 | https://github.com/oz/osdb/blob/6a89d7f831a6a3874260fe4677e546d551cad79d/osdb.go#L42 |
| 1882 | {{{ |
| 1883 | import ( |
| 1884 | "bytes" |
| 1885 | "encoding/binary" |
| 1886 | "fmt" |
| 1887 | "os" |
| 1888 | ) |
| 1889 | |
| 1890 | const ( |
| 1891 | ChunkSize = 65536 // 64k |
| 1892 | ) |
| 1893 | |
| 1894 | // Generate an OSDB hash for an *os.File. |
| 1895 | func HashFile(file *os.File) (hash uint64, err error) { |
| 1896 | fi, err := file.Stat() |
| 1897 | if err != nil { |
| 1898 | return |
| 1899 | } |
| 1900 | if fi.Size() < ChunkSize { |
| 1901 | return 0, fmt.Errorf("File is too small") |
| 1902 | } |
| 1903 | |
| 1904 | // Read head and tail blocks. |
| 1905 | buf := make([]byte, ChunkSize*2) |
| 1906 | err = readChunk(file, 0, buf[:ChunkSize]) |
| 1907 | if err != nil { |
| 1908 | return |
| 1909 | } |
| 1910 | err = readChunk(file, fi.Size()-ChunkSize, buf[ChunkSize:]) |
| 1911 | if err != nil { |
| 1912 | return |
| 1913 | } |
| 1914 | |
| 1915 | // Convert to uint64, and sum. |
| 1916 | var nums [(ChunkSize * 2) / 8]uint64 |
| 1917 | reader := bytes.NewReader(buf) |
| 1918 | err = binary.Read(reader, binary.LittleEndian, &nums) |
| 1919 | if err != nil { |
| 1920 | return 0, err |
| 1921 | } |
| 1922 | for _, num := range nums { |
| 1923 | hash += num |
| 1924 | } |
| 1925 | |
| 1926 | return hash + uint64(fi.Size()), nil |
| 1927 | } |
| 1928 | |
| 1929 | // Read a chunk of a file at `offset` so as to fill `buf`. |
| 1930 | func readChunk(file *os.File, offset int64, buf []byte) (err error) { |
| 1931 | n, err := file.ReadAt(buf, offset) |
| 1932 | if err != nil { |
| 1933 | return |
| 1934 | } |
| 1935 | if n != ChunkSize { |
| 1936 | return fmt.Errorf("Invalid read %v", n) |
| 1937 | } |
| 1938 | return |
| 1939 | } |
| 1940 | }}} |