mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-09-21 05:21:43 +00:00
feat(worker): CRC32 archive fingerprint compare
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,35 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { crcFingerprint, fingerprintsMatch } from "./fingerprint.js";
|
||||||
|
import type { FileEntry } from "./zip-reader.js";
|
||||||
|
|
||||||
describe("harness", () => {
|
const fe = (crc: string | null): FileEntry => ({
|
||||||
it("runs", () => {
|
path: "a", fileName: "a", extension: null,
|
||||||
expect(1 + 1).toBe(2);
|
compressedSize: 0n, uncompressedSize: 0n, crc32: crc,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("crcFingerprint", () => {
|
||||||
|
it("sorts crcs and marks complete", () => {
|
||||||
|
expect(crcFingerprint([fe("00ff"), fe("00aa")])).toEqual({ crcs: ["00aa", "00ff"], complete: true });
|
||||||
|
});
|
||||||
|
it("is incomplete when any crc is null", () => {
|
||||||
|
expect(crcFingerprint([fe("00aa"), fe(null)]).complete).toBe(false);
|
||||||
|
});
|
||||||
|
it("is incomplete when empty", () => {
|
||||||
|
expect(crcFingerprint([]).complete).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fingerprintsMatch", () => {
|
||||||
|
it("matches identical crc multisets regardless of order", () => {
|
||||||
|
expect(fingerprintsMatch([fe("01"), fe("02")], [fe("02"), fe("01")])).toBe(true);
|
||||||
|
});
|
||||||
|
it("rejects different counts", () => {
|
||||||
|
expect(fingerprintsMatch([fe("01")], [fe("01"), fe("02")])).toBe(false);
|
||||||
|
});
|
||||||
|
it("rejects disjoint sets", () => {
|
||||||
|
expect(fingerprintsMatch([fe("01")], [fe("09")])).toBe(false);
|
||||||
|
});
|
||||||
|
it("rejects when either side is incomplete", () => {
|
||||||
|
expect(fingerprintsMatch([fe("01"), fe(null)], [fe("01"), fe("02")])).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import type { FileEntry } from "./zip-reader.js";
|
||||||
|
|
||||||
|
export function crcFingerprint(entries: FileEntry[]): { crcs: string[]; complete: boolean } {
|
||||||
|
if (entries.length === 0) return { crcs: [], complete: false };
|
||||||
|
const crcs: string[] = [];
|
||||||
|
let complete = true;
|
||||||
|
for (const e of entries) {
|
||||||
|
if (e.crc32 == null) { complete = false; continue; }
|
||||||
|
crcs.push(e.crc32.toLowerCase());
|
||||||
|
}
|
||||||
|
crcs.sort();
|
||||||
|
return { crcs, complete };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fingerprintsMatch(a: FileEntry[], b: FileEntry[]): boolean {
|
||||||
|
const fa = crcFingerprint(a);
|
||||||
|
const fb = crcFingerprint(b);
|
||||||
|
if (!fa.complete || !fb.complete) return false;
|
||||||
|
if (fa.crcs.length !== fb.crcs.length) return false;
|
||||||
|
return fa.crcs.every((c, i) => c === fb.crcs[i]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user