Skip to content

Instantly share code, notes, and snippets.

@ZReC
Created April 20, 2022 10:03
Show Gist options
  • Save ZReC/36bdac682b0780fe3ece425c1da372c9 to your computer and use it in GitHub Desktop.
Save ZReC/36bdac682b0780fe3ece425c1da372c9 to your computer and use it in GitHub Desktop.
It compares directories and outputs homonymous files bytes that aren't equal
/**
* The program compares same name files of different directories and
* outputs differences. Stops comparing if there's no bytes left in any file
*/
import { join, resolve } from "https://deno.land/std@0.134.0/path/mod.ts";
const szCHUNK = 512;
if (Deno.args.length < 2) {
throw new Error("you should provide at least two paths");
}
interface File {
path: string,
descriptor: Deno.FsFile
}
const fnames: { [fname: string]: File[] } = {};
for (const path of Deno.args) {
for await (const entry of Deno.readDir(path)) {
if (entry.isFile) {
const val: File = { path: resolve(path), descriptor: await Deno.open(join(path, entry.name)) };
fnames[entry.name]
? fnames[entry.name].push(val)
: fnames[entry.name] = new Array(val);
}
}
}
for (const fname in fnames) {
if (fnames[fname].length > 1) {
const paths: Map<File, { buff: Uint8Array, byteLength: number }> = new Map();
for (const v of fnames[fname]) {
paths.set(v, { buff: new Uint8Array(szCHUNK), byteLength: 0 });
}
const diffArr: Array<[number, number | null]> = [];
const pointers: Set<number> = new Set();
let chunk_offset = 0;
while (paths.size > 0) {
// await chunks
await Promise.all(
(() => {
const promises = [];
for (const path of paths) {
promises.push(path[0].descriptor.read(path[1].buff).then(v => {
if (v == null) {
paths.delete(path[0]);
} else {
path[1].byteLength = v;
}
}));
}
return promises;
})()
);
const pathsCopy = new Map(paths);
for (let i = 0; i < szCHUNK; i++) {
const entries = pathsCopy.entries();
for (let [a, b] = entries; b != undefined; [b] = entries) {
if (i < a[1].byteLength && i < b[1].byteLength) {
if (a[1].buff[i] != b[1].buff[i]) {
const offset = i + chunk_offset * szCHUNK;
pointers.add(offset);
if (pointers.delete(offset - 1)) {
diffArr[diffArr.length - 1][1] = offset;
} else {
diffArr[diffArr.length] = [offset, null];
}
break;
}
}
}
}
chunk_offset++;
}
if (diffArr.length > 0) {
const files = fnames[fname].map(v => v.descriptor);
console.log(`'${fname}' [${fnames[fname].map(v => v.path).join(', ')}]`);
for (const v of diffArr) {
const byteLength = 1 + (v[1] || v[0]) - v[0], pos = v[0].toString(16);
console.log(` $${pos.length & 1 ? '0' + pos : pos}`);
for (const f of files) {
const buffer = new Uint8Array(byteLength);
await f.seek(v[0], Deno.SeekMode.Start);
await f.read(buffer);
let str = ' ';
for (let i = 0; i < buffer.byteLength; i++) {
str += buffer[i].toString(16).padStart(2, '0') + (i % 32 == 31 ? '\n ' : ' ');
}
console.log(`${str.slice(0, -1)}\n`);
}
}
}
}
delete fnames[fname];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment