mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-09-21 13:31:42 +00:00
fix(worker): fetch packed header region for encoded-header 7z ranged listing
This commit is contained in:
@@ -63,3 +63,67 @@ describe("readSevenZListingRanged", () => {
|
|||||||
expect(entries === null || Array.isArray(entries)).toBe(true);
|
expect(entries === null || Array.isArray(entries)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
import { read7zNumber, locate7zEncodedHeaderPack } from "./sevenz-ranged.js";
|
||||||
|
|
||||||
|
describe("read7zNumber", () => {
|
||||||
|
it("reads a single-byte number", () => {
|
||||||
|
expect(read7zNumber(Buffer.from([0x2a]), 0)).toEqual({ value: 42, next: 1 });
|
||||||
|
});
|
||||||
|
it("reads a two-byte number (first-byte length mask + LE trailing byte)", () => {
|
||||||
|
// 1000 = 0x03E8: low byte 0xE8, high nibble 0x03 -> first = 0x80|0x03 = 0x83, trailing 0xE8
|
||||||
|
expect(read7zNumber(Buffer.from([0x83, 0xe8]), 0)).toEqual({ value: 1000, next: 2 });
|
||||||
|
// 500 = 0x01F4 -> first 0x81, trailing 0xF4
|
||||||
|
expect(read7zNumber(Buffer.from([0x81, 0xf4]), 0)).toEqual({ value: 500, next: 2 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("locate7zEncodedHeaderPack", () => {
|
||||||
|
it("parses PackPos and summed PackSize from an encoded header", () => {
|
||||||
|
// kEncodedHeader, kPackInfo, PackPos=1000([0x83,0xe8]), NumStreams=1([0x01]),
|
||||||
|
// kSize, PackSize=500([0x81,0xf4])
|
||||||
|
const enc = Buffer.from([0x17, 0x06, 0x83, 0xe8, 0x01, 0x09, 0x81, 0xf4]);
|
||||||
|
expect(locate7zEncodedHeaderPack(enc)).toEqual({ packPos: 1000, packSize: 500 });
|
||||||
|
});
|
||||||
|
it("returns null for a plain (kHeader 0x01) header", () => {
|
||||||
|
expect(locate7zEncodedHeaderPack(Buffer.from([0x01, 0x04]))).toBeNull();
|
||||||
|
});
|
||||||
|
it("sums multiple pack streams", () => {
|
||||||
|
// PackPos=0([0x00]), NumStreams=2([0x02]), kSize, sizes 10([0x0a]) + 20([0x14])
|
||||||
|
const enc = Buffer.from([0x17, 0x06, 0x00, 0x02, 0x09, 0x0a, 0x14]);
|
||||||
|
expect(locate7zEncodedHeaderPack(enc)).toEqual({ packPos: 0, packSize: 30 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("readSevenZListingRanged (encoded header)", () => {
|
||||||
|
it("fetches the mid-file packed-header region as a third read", async () => {
|
||||||
|
const size = 5_000_000;
|
||||||
|
const nextHeaderOffset = 4_000_000; // relative to end of 32-byte sig header
|
||||||
|
const endStart = 32 + nextHeaderOffset; // absolute
|
||||||
|
const packPos = 1000; // relative to end of sig header
|
||||||
|
const packStart = 32 + packPos; // absolute
|
||||||
|
const packSize = 500;
|
||||||
|
const sig = Buffer.alloc(32);
|
||||||
|
Buffer.from([0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]).copy(sig, 0);
|
||||||
|
sig.writeBigUInt64LE(BigInt(nextHeaderOffset), 12);
|
||||||
|
sig.writeBigUInt64LE(8n, 20); // NextHeaderSize = 8 (the encoded-header descriptor below)
|
||||||
|
const encHeader = Buffer.from([0x17, 0x06, 0x83, 0xe8, 0x01, 0x09, 0x81, 0xf4]);
|
||||||
|
|
||||||
|
const reads: { offset: number; length: number }[] = [];
|
||||||
|
const read = async (_id: string, offset: number, length: number) => {
|
||||||
|
reads.push({ offset, length });
|
||||||
|
if (offset === 0) return sig.subarray(0, length);
|
||||||
|
if (offset === endStart) return encHeader.subarray(0, length);
|
||||||
|
return Buffer.alloc(length, 0xcd); // stand-in packed-header bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
const entries = await readSevenZListingRanged(
|
||||||
|
[{ fileId: "1", fileSize: BigInt(size), fileName: "a.7z" }],
|
||||||
|
read,
|
||||||
|
);
|
||||||
|
expect(reads[0]).toEqual({ offset: 0, length: 32 });
|
||||||
|
expect(reads[1]).toEqual({ offset: endStart, length: 8 });
|
||||||
|
expect(reads[2]).toEqual({ offset: packStart, length: packSize });
|
||||||
|
expect(entries === null || Array.isArray(entries)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -8,6 +8,55 @@ const log = childLogger("sevenz-ranged");
|
|||||||
|
|
||||||
const SEVENZ_MAGIC = Buffer.from([0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]);
|
const SEVENZ_MAGIC = Buffer.from([0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]);
|
||||||
|
|
||||||
|
const K_HEADER = 0x01;
|
||||||
|
const K_ENCODED_HEADER = 0x17;
|
||||||
|
const K_PACK_INFO = 0x06;
|
||||||
|
const K_SIZE = 0x09;
|
||||||
|
|
||||||
|
/** Read a 7z variable-length number: first byte is a length mask, followed by
|
||||||
|
* little-endian bytes. Math.pow keeps values exact above 2^31. */
|
||||||
|
export function read7zNumber(buf: Buffer, pos: number): { value: number; next: number } {
|
||||||
|
const first = buf[pos];
|
||||||
|
let mask = 0x80;
|
||||||
|
let value = 0;
|
||||||
|
let p = pos + 1;
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
if ((first & mask) === 0) {
|
||||||
|
value += (first & (mask - 1)) * Math.pow(2, 8 * i);
|
||||||
|
return { value, next: p };
|
||||||
|
}
|
||||||
|
if (p >= buf.length) throw new RangeError("7z number overruns buffer");
|
||||||
|
value += buf[p] * Math.pow(2, 8 * i);
|
||||||
|
p++;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
return { value, next: p };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For an encoded (kEncodedHeader) 7z next-header, return the absolute-ish
|
||||||
|
* location of the packed header stream(s): PackPos (relative to end of the
|
||||||
|
* 32-byte signature header) and the summed PackSize. Null if not encoded or
|
||||||
|
* the StreamsInfo doesn't start with PackInfo as expected. */
|
||||||
|
export function locate7zEncodedHeaderPack(
|
||||||
|
nextHeader: Buffer,
|
||||||
|
): { packPos: number; packSize: number } | null {
|
||||||
|
let p = 0;
|
||||||
|
if (nextHeader[p] !== K_ENCODED_HEADER) return null;
|
||||||
|
p++;
|
||||||
|
if (nextHeader[p] !== K_PACK_INFO) return null;
|
||||||
|
p++;
|
||||||
|
const packPos = read7zNumber(nextHeader, p); p = packPos.next;
|
||||||
|
const numStreams = read7zNumber(nextHeader, p); p = numStreams.next;
|
||||||
|
if (nextHeader[p] !== K_SIZE) return null;
|
||||||
|
p++;
|
||||||
|
let total = 0;
|
||||||
|
for (let i = 0; i < numStreams.value; i++) {
|
||||||
|
const s = read7zNumber(nextHeader, p); p = s.next;
|
||||||
|
total += s.value;
|
||||||
|
}
|
||||||
|
return { packPos: packPos.value, packSize: total };
|
||||||
|
}
|
||||||
|
|
||||||
export function parseSevenZSignatureHeader(
|
export function parseSevenZSignatureHeader(
|
||||||
buf: Buffer,
|
buf: Buffer,
|
||||||
): { nextHeaderOffset: number; nextHeaderSize: number } | null {
|
): { nextHeaderOffset: number; nextHeaderSize: number } | null {
|
||||||
@@ -35,17 +84,26 @@ export async function readSevenZListingRanged(
|
|||||||
const endStart = 32 + parsed.nextHeaderOffset;
|
const endStart = 32 + parsed.nextHeaderOffset;
|
||||||
if (endStart < 0 || endStart + parsed.nextHeaderSize > size) return null;
|
if (endStart < 0 || endStart + parsed.nextHeaderSize > size) return null;
|
||||||
const endHeader = await read(part.fileId, endStart, parsed.nextHeaderSize, part.fileSize);
|
const endHeader = await read(part.fileId, endStart, parsed.nextHeaderSize, part.fileSize);
|
||||||
return listFromSparse(
|
|
||||||
[{
|
const regions = [
|
||||||
fileName: part.fileName,
|
|
||||||
size,
|
|
||||||
regions: [
|
|
||||||
{ offset: 0, bytes: sig },
|
{ offset: 0, bytes: sig },
|
||||||
{ offset: endStart, bytes: endHeader },
|
{ offset: endStart, bytes: endHeader },
|
||||||
],
|
];
|
||||||
}],
|
|
||||||
read7zContents,
|
const headerType = endHeader[0];
|
||||||
);
|
if (headerType === K_ENCODED_HEADER) {
|
||||||
|
// Compressed header: its packed bytes live mid-file, not at EOF. Fetch them.
|
||||||
|
const pack = locate7zEncodedHeaderPack(endHeader);
|
||||||
|
if (!pack) return null;
|
||||||
|
const packStart = 32 + pack.packPos;
|
||||||
|
if (packStart < 0 || packStart + pack.packSize > size) return null;
|
||||||
|
const packBytes = await read(part.fileId, packStart, pack.packSize, part.fileSize);
|
||||||
|
regions.push({ offset: packStart, bytes: packBytes });
|
||||||
|
} else if (headerType !== K_HEADER) {
|
||||||
|
return null; // unknown next-header type
|
||||||
|
}
|
||||||
|
|
||||||
|
return listFromSparse([{ fileName: part.fileName, size, regions }], read7zContents);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.warn({ err, fileId: part.fileId }, "ranged 7z listing failed");
|
log.warn({ err, fileId: part.fileId }, "ranged 7z listing failed");
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user