46 lines
No EOL
1.6 KiB
TypeScript
46 lines
No EOL
1.6 KiB
TypeScript
import { open } from "fs/promises";
|
|
/**
|
|
* get hash of file or directory.
|
|
*
|
|
* Contains a hashing method that matches the hashing method described
|
|
* here: [https://pypi.org/project/oshash/](https://pypi.org/project/oshash/)
|
|
* This hashing method is particularly useful when you don't want to read
|
|
* an entire file's bytes to generate a hash, provided you trust that any
|
|
* changes to the file will cause byte differences in the first and last
|
|
* bytes of the file, or a change to its file size.
|
|
*/
|
|
export async function oshash(
|
|
path: string,
|
|
){
|
|
const chunkSize = 4096;
|
|
const minFileSize = chunkSize * 2;
|
|
|
|
const fd = await open(path, "r");
|
|
const st = await fd.stat();
|
|
let hash = BigInt(st.size);
|
|
|
|
if (st.size < minFileSize){
|
|
throw new Error("File is too small to hash");
|
|
}
|
|
|
|
// read first and last chunk
|
|
const firstChunk = new Uint8Array(chunkSize);
|
|
await fd.read(firstChunk, 0, chunkSize, 0);
|
|
const lastChunk = new Uint8Array(chunkSize);
|
|
await fd.read(lastChunk, 0, chunkSize, st.size - chunkSize);
|
|
// iterate over first and last chunk.
|
|
// for each uint64_t, add it to the hash.
|
|
const firstChunkView = new DataView(firstChunk.buffer);
|
|
for (let i = 0; i < chunkSize; i += 8){
|
|
hash += firstChunkView.getBigUint64(i, true);
|
|
// prevent overflow
|
|
hash = (hash & 0xFFFFFFFFFFFFFFFFn);
|
|
}
|
|
const lastChunkView = new DataView(lastChunk.buffer);
|
|
for (let i = 0; i < chunkSize; i += 8){
|
|
hash += lastChunkView.getBigUint64(i, true);
|
|
// prevent overflow
|
|
hash = (hash & 0xFFFFFFFFFFFFFFFFn);
|
|
}
|
|
return hash;
|
|
} |