chore: 更新学生档案数据
This commit is contained in:
636
node_modules/modern-tar/dist/unpacker-CPCEF5CT.js
generated
vendored
Normal file
636
node_modules/modern-tar/dist/unpacker-CPCEF5CT.js
generated
vendored
Normal file
@@ -0,0 +1,636 @@
|
||||
const FILE = "file";
|
||||
const LINK = "link";
|
||||
const SYMLINK = "symlink";
|
||||
const DIRECTORY = "directory";
|
||||
const TYPEFLAG = {
|
||||
file: "0",
|
||||
link: "1",
|
||||
symlink: "2",
|
||||
"character-device": "3",
|
||||
"block-device": "4",
|
||||
directory: "5",
|
||||
fifo: "6",
|
||||
"pax-header": "x",
|
||||
"pax-global-header": "g",
|
||||
"gnu-long-name": "L",
|
||||
"gnu-long-link-name": "K"
|
||||
};
|
||||
const FLAGTYPE = {
|
||||
"0": FILE,
|
||||
"1": LINK,
|
||||
"2": SYMLINK,
|
||||
"3": "character-device",
|
||||
"4": "block-device",
|
||||
"5": DIRECTORY,
|
||||
"6": "fifo",
|
||||
x: "pax-header",
|
||||
g: "pax-global-header",
|
||||
L: "gnu-long-name",
|
||||
K: "gnu-long-link-name"
|
||||
};
|
||||
const ZERO_BLOCK = new Uint8Array(512);
|
||||
const EMPTY = new Uint8Array(0);
|
||||
//#endregion
|
||||
//#region src/tar/encoding.ts
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
function writeString(view, offset, size, value) {
|
||||
if (value) encoder.encodeInto(value, view.subarray(offset, offset + size));
|
||||
}
|
||||
function writeOctal(view, offset, size, value) {
|
||||
if (value === void 0) return;
|
||||
const octalString = value.toString(8).padStart(size - 1, "0");
|
||||
encoder.encodeInto(octalString, view.subarray(offset, offset + size - 1));
|
||||
}
|
||||
function readString(view, offset, size) {
|
||||
const end = view.indexOf(0, offset);
|
||||
const sliceEnd = end === -1 || end > offset + size ? offset + size : end;
|
||||
return decoder.decode(view.subarray(offset, sliceEnd));
|
||||
}
|
||||
function readOctal(view, offset, size) {
|
||||
let value = 0;
|
||||
const end = offset + size;
|
||||
for (let i = offset; i < end; i++) {
|
||||
const charCode = view[i];
|
||||
if (charCode === 0) break;
|
||||
if (charCode === 32) continue;
|
||||
value = value * 8 + (charCode - 48);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function readNumeric(view, offset, size) {
|
||||
if (view[offset] & 128) {
|
||||
let result = 0;
|
||||
result = view[offset] & 127;
|
||||
for (let i = 1; i < size; i++) result = result * 256 + view[offset + i];
|
||||
if (!Number.isSafeInteger(result)) throw new Error("TAR number too large");
|
||||
return result;
|
||||
}
|
||||
return readOctal(view, offset, size);
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/body.ts
|
||||
const isBodyless = (header) => header.type === "directory" || header.type === "symlink" || header.type === "link" || header.type === "character-device" || header.type === "block-device" || header.type === "fifo";
|
||||
async function normalizeBody(body) {
|
||||
if (body === null || body === void 0) return EMPTY;
|
||||
if (body instanceof Uint8Array) return body;
|
||||
if (typeof body === "string") return encoder.encode(body);
|
||||
if (body instanceof ArrayBuffer) return new Uint8Array(body);
|
||||
if (body instanceof Blob) return new Uint8Array(await body.arrayBuffer());
|
||||
throw new TypeError("Unsupported content type for entry body.");
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/options.ts
|
||||
const stripPath = (p, n) => {
|
||||
const parts = p.split("/").filter(Boolean);
|
||||
return n >= parts.length ? "" : parts.slice(n).join("/");
|
||||
};
|
||||
function transformHeader(header, options) {
|
||||
const { strip, filter, map } = options;
|
||||
if (!strip && !filter && !map) return header;
|
||||
const h = { ...header };
|
||||
if (strip && strip > 0) {
|
||||
const newName = stripPath(h.name, strip);
|
||||
if (!newName) return null;
|
||||
h.name = h.type === "directory" && !newName.endsWith("/") ? `${newName}/` : newName;
|
||||
if (h.linkname) {
|
||||
const isAbsolute = h.linkname.startsWith("/");
|
||||
if (isAbsolute || h.type === "link") {
|
||||
const stripped = stripPath(h.linkname, strip);
|
||||
h.linkname = isAbsolute ? `/${stripped}` || "/" : stripped;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filter?.(h) === false) return null;
|
||||
const result = map ? map(h) : h;
|
||||
if (result && (!result.name || !result.name.trim() || result.name === "." || result.name === "/")) return null;
|
||||
return result;
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/checksum.ts
|
||||
const CHECKSUM_SPACE = 32;
|
||||
const ASCII_ZERO = 48;
|
||||
function validateChecksum(block) {
|
||||
const stored = readOctal(block, 148, 8);
|
||||
let sum = 0;
|
||||
for (let i = 0; i < block.length; i++) if (i >= 148 && i < 156) sum += CHECKSUM_SPACE;
|
||||
else sum += block[i];
|
||||
return stored === sum;
|
||||
}
|
||||
function writeChecksum(block) {
|
||||
block.fill(CHECKSUM_SPACE, 148, 156);
|
||||
let checksum = 0;
|
||||
for (const byte of block) checksum += byte;
|
||||
for (let i = 153; i >= 148; i--) {
|
||||
block[i] = (checksum & 7) + ASCII_ZERO;
|
||||
checksum >>= 3;
|
||||
}
|
||||
block[154] = 0;
|
||||
block[155] = CHECKSUM_SPACE;
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/pax.ts
|
||||
const USTAR_SPLIT_MAX_SIZE = 256;
|
||||
function generatePax(header) {
|
||||
const paxRecords = {};
|
||||
if (encoder.encode(header.name).length > 100) {
|
||||
if (findUstarSplit(header.name) === null) paxRecords.path = header.name;
|
||||
}
|
||||
if (header.linkname && encoder.encode(header.linkname).length > 100) paxRecords.linkpath = header.linkname;
|
||||
if (header.uname && encoder.encode(header.uname).length > 32) paxRecords.uname = header.uname;
|
||||
if (header.gname && encoder.encode(header.gname).length > 32) paxRecords.gname = header.gname;
|
||||
if (header.uid != null && header.uid > 2097151) paxRecords.uid = String(header.uid);
|
||||
if (header.gid != null && header.gid > 2097151) paxRecords.gid = String(header.gid);
|
||||
if (header.size != null && header.size > 8589934591) paxRecords.size = String(header.size);
|
||||
if (header.pax) Object.assign(paxRecords, header.pax);
|
||||
const paxEntries = Object.entries(paxRecords);
|
||||
if (paxEntries.length === 0) return null;
|
||||
const paxBody = encoder.encode(paxEntries.map(([key, value]) => {
|
||||
const record = `${key}=${value}\n`;
|
||||
const partLength = encoder.encode(record).length + 1;
|
||||
let totalLength = partLength + String(partLength).length;
|
||||
totalLength = partLength + String(totalLength).length;
|
||||
return `${totalLength} ${record}`;
|
||||
}).join(""));
|
||||
return {
|
||||
paxHeader: createTarHeader({
|
||||
name: decoder.decode(encoder.encode(`PaxHeader/${header.name}`).slice(0, 100)),
|
||||
size: paxBody.length,
|
||||
type: "pax-header",
|
||||
mode: 420,
|
||||
mtime: header.mtime,
|
||||
uname: header.uname,
|
||||
gname: header.gname,
|
||||
uid: header.uid,
|
||||
gid: header.gid
|
||||
}),
|
||||
paxBody
|
||||
};
|
||||
}
|
||||
function findUstarSplit(path) {
|
||||
const totalPathBytes = encoder.encode(path).length;
|
||||
if (totalPathBytes <= 100 || totalPathBytes > USTAR_SPLIT_MAX_SIZE) return null;
|
||||
for (let i = path.length - 1; i > 0; i--) {
|
||||
if (path[i] !== "/") continue;
|
||||
const prefix = path.slice(0, i);
|
||||
const name = path.slice(i + 1);
|
||||
if (encoder.encode(prefix).length <= 155 && encoder.encode(name).length <= 100) return {
|
||||
prefix,
|
||||
name
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/header.ts
|
||||
function createTarHeader(header) {
|
||||
const view = new Uint8Array(512);
|
||||
const size = isBodyless(header) ? 0 : header.size ?? 0;
|
||||
let name = header.name;
|
||||
let prefix = "";
|
||||
if (!header.pax?.path) {
|
||||
const split = findUstarSplit(name);
|
||||
if (split) {
|
||||
name = split.name;
|
||||
prefix = split.prefix;
|
||||
}
|
||||
}
|
||||
writeString(view, 0, 100, name);
|
||||
writeOctal(view, 100, 8, header.mode ?? (header.type === "directory" ? 493 : 420));
|
||||
writeOctal(view, 108, 8, header.uid ?? 0);
|
||||
writeOctal(view, 116, 8, header.gid ?? 0);
|
||||
writeOctal(view, 124, 12, size);
|
||||
writeOctal(view, 136, 12, Math.floor((header.mtime?.getTime() ?? Date.now()) / 1e3));
|
||||
writeString(view, 156, 1, TYPEFLAG[header.type ?? "file"]);
|
||||
writeString(view, 157, 100, header.linkname);
|
||||
writeString(view, 257, 6, "ustar\0");
|
||||
writeString(view, 263, 2, "00");
|
||||
writeString(view, 265, 32, header.uname);
|
||||
writeString(view, 297, 32, header.gname);
|
||||
writeString(view, 345, 155, prefix);
|
||||
writeChecksum(view);
|
||||
return view;
|
||||
}
|
||||
function parseUstarHeader(block, strict) {
|
||||
if (strict && !validateChecksum(block)) throw new Error("Invalid tar header checksum.");
|
||||
const typeflag = readString(block, 156, 1);
|
||||
const header = {
|
||||
name: readString(block, 0, 100),
|
||||
mode: readOctal(block, 100, 8),
|
||||
uid: readNumeric(block, 108, 8),
|
||||
gid: readNumeric(block, 116, 8),
|
||||
size: readNumeric(block, 124, 12),
|
||||
mtime: /* @__PURE__ */ new Date(readNumeric(block, 136, 12) * 1e3),
|
||||
type: FLAGTYPE[typeflag] || "file",
|
||||
linkname: readString(block, 157, 100)
|
||||
};
|
||||
const magic = readString(block, 257, 6);
|
||||
if (isBodyless(header)) header.size = 0;
|
||||
if (magic.trim() === "ustar") {
|
||||
header.uname = readString(block, 265, 32);
|
||||
header.gname = readString(block, 297, 32);
|
||||
}
|
||||
if (magic === "ustar") header.prefix = readString(block, 345, 155);
|
||||
return header;
|
||||
}
|
||||
const PAX_MAPPING = {
|
||||
path: ["name", (v) => v],
|
||||
linkpath: ["linkname", (v) => v],
|
||||
size: ["size", (v) => parseInt(v, 10)],
|
||||
mtime: ["mtime", parseFloat],
|
||||
uid: ["uid", (v) => parseInt(v, 10)],
|
||||
gid: ["gid", (v) => parseInt(v, 10)],
|
||||
uname: ["uname", (v) => v],
|
||||
gname: ["gname", (v) => v]
|
||||
};
|
||||
function parsePax(buffer) {
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
const overrides = Object.create(null);
|
||||
const pax = Object.create(null);
|
||||
let offset = 0;
|
||||
while (offset < buffer.length) {
|
||||
const spaceIndex = buffer.indexOf(32, offset);
|
||||
if (spaceIndex === -1) break;
|
||||
const length = parseInt(decoder.decode(buffer.subarray(offset, spaceIndex)), 10);
|
||||
if (Number.isNaN(length) || length === 0) break;
|
||||
const recordEnd = offset + length;
|
||||
const [key, value] = decoder.decode(buffer.subarray(spaceIndex + 1, recordEnd - 1)).split("=", 2);
|
||||
if (key && value !== void 0) {
|
||||
pax[key] = value;
|
||||
if (Object.hasOwn(PAX_MAPPING, key)) {
|
||||
const [targetKey, parser] = PAX_MAPPING[key];
|
||||
const parsedValue = parser(value);
|
||||
if (typeof parsedValue === "string" || !Number.isNaN(parsedValue)) overrides[targetKey] = parsedValue;
|
||||
}
|
||||
}
|
||||
offset = recordEnd;
|
||||
}
|
||||
if (Object.keys(pax).length > 0) overrides.pax = pax;
|
||||
return overrides;
|
||||
}
|
||||
function applyOverrides(header, overrides) {
|
||||
if (overrides.name !== void 0) header.name = overrides.name;
|
||||
if (overrides.linkname !== void 0) header.linkname = overrides.linkname;
|
||||
if (overrides.size !== void 0) header.size = overrides.size;
|
||||
if (overrides.mtime !== void 0) header.mtime = /* @__PURE__ */ new Date(overrides.mtime * 1e3);
|
||||
if (overrides.uid !== void 0) header.uid = overrides.uid;
|
||||
if (overrides.gid !== void 0) header.gid = overrides.gid;
|
||||
if (overrides.uname !== void 0) header.uname = overrides.uname;
|
||||
if (overrides.gname !== void 0) header.gname = overrides.gname;
|
||||
if (overrides.pax) header.pax = Object.assign({}, header.pax ?? {}, overrides.pax);
|
||||
}
|
||||
function getMetaParser(type) {
|
||||
switch (type) {
|
||||
case "pax-global-header":
|
||||
case "pax-header": return parsePax;
|
||||
case "gnu-long-name": return (data) => ({ name: readString(data, 0, data.length) });
|
||||
case "gnu-long-link-name": return (data) => ({ linkname: readString(data, 0, data.length) });
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
function getHeaderBlocks(header) {
|
||||
const base = createTarHeader(header);
|
||||
const pax = generatePax(header);
|
||||
if (!pax) return [base];
|
||||
const paxPadding = -pax.paxBody.length & 511;
|
||||
const paddingBlocks = paxPadding > 0 ? [ZERO_BLOCK.subarray(0, paxPadding)] : [];
|
||||
return [
|
||||
pax.paxHeader,
|
||||
pax.paxBody,
|
||||
...paddingBlocks,
|
||||
base
|
||||
];
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/packer.ts
|
||||
const EOF_BUFFER = new Uint8Array(512 * 2);
|
||||
function createTarPacker(onData, onError, onFinalize) {
|
||||
let currentHeader = null;
|
||||
let bytesWritten = 0;
|
||||
let finalized = false;
|
||||
const fail = (message) => {
|
||||
const error = new Error(message);
|
||||
onError(error);
|
||||
throw error;
|
||||
};
|
||||
return {
|
||||
add(header) {
|
||||
if (finalized) fail("No new tar entries after finalize.");
|
||||
if (currentHeader !== null) fail("Previous entry must be completed before adding a new one");
|
||||
const size = isBodyless(header) ? 0 : header.size;
|
||||
if (!Number.isSafeInteger(size) || size < 0) fail("Invalid tar entry size.");
|
||||
try {
|
||||
const headerBlocks = getHeaderBlocks({
|
||||
...header,
|
||||
size
|
||||
});
|
||||
for (const block of headerBlocks) onData(block);
|
||||
currentHeader = {
|
||||
...header,
|
||||
size
|
||||
};
|
||||
bytesWritten = 0;
|
||||
} catch (error) {
|
||||
onError(error);
|
||||
}
|
||||
},
|
||||
write(chunk) {
|
||||
if (!currentHeader) fail("No active tar entry.");
|
||||
if (finalized) fail("Cannot write data after finalize.");
|
||||
const newTotal = bytesWritten + chunk.length;
|
||||
if (newTotal > currentHeader.size) fail(`"${currentHeader.name}" exceeds given size of ${currentHeader.size} bytes.`);
|
||||
try {
|
||||
bytesWritten = newTotal;
|
||||
onData(chunk);
|
||||
} catch (error) {
|
||||
onError(error);
|
||||
}
|
||||
},
|
||||
endEntry() {
|
||||
if (!currentHeader) fail("No active entry to end.");
|
||||
if (finalized) fail("Cannot end entry after finalize.");
|
||||
try {
|
||||
if (bytesWritten !== currentHeader.size) fail(`Size mismatch for "${currentHeader.name}".`);
|
||||
const paddingSize = -currentHeader.size & 511;
|
||||
if (paddingSize > 0) onData(new Uint8Array(paddingSize));
|
||||
currentHeader = null;
|
||||
bytesWritten = 0;
|
||||
} catch (error) {
|
||||
onError(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
finalize() {
|
||||
if (finalized) fail("Archive has already been finalized");
|
||||
if (currentHeader !== null) fail("Cannot finalize while an entry is still active");
|
||||
try {
|
||||
onData(EOF_BUFFER);
|
||||
finalized = true;
|
||||
if (onFinalize) onFinalize();
|
||||
} catch (error) {
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/chunk-queue.ts
|
||||
const INITIAL_CAPACITY = 256;
|
||||
function createChunkQueue() {
|
||||
let chunks = new Array(INITIAL_CAPACITY);
|
||||
let capacityMask = chunks.length - 1;
|
||||
let head = 0;
|
||||
let tail = 0;
|
||||
let totalAvailable = 0;
|
||||
const consumeFromHead = (count) => {
|
||||
const chunk = chunks[head];
|
||||
if (count === chunk.length) {
|
||||
chunks[head] = EMPTY;
|
||||
head = head + 1 & capacityMask;
|
||||
} else chunks[head] = chunk.subarray(count);
|
||||
totalAvailable -= count;
|
||||
if (totalAvailable === 0 && chunks.length > INITIAL_CAPACITY) {
|
||||
chunks = new Array(INITIAL_CAPACITY);
|
||||
capacityMask = INITIAL_CAPACITY - 1;
|
||||
head = 0;
|
||||
tail = 0;
|
||||
}
|
||||
};
|
||||
function pull(bytes, callback) {
|
||||
if (callback) {
|
||||
let fed = 0;
|
||||
let remaining = Math.min(bytes, totalAvailable);
|
||||
while (remaining > 0) {
|
||||
const chunk = chunks[head];
|
||||
const toFeed = Math.min(remaining, chunk.length);
|
||||
const segment = toFeed === chunk.length ? chunk : chunk.subarray(0, toFeed);
|
||||
consumeFromHead(toFeed);
|
||||
remaining -= toFeed;
|
||||
fed += toFeed;
|
||||
if (!callback(segment)) break;
|
||||
}
|
||||
return fed;
|
||||
}
|
||||
if (totalAvailable < bytes) return null;
|
||||
if (bytes === 0) return EMPTY;
|
||||
const firstChunk = chunks[head];
|
||||
if (firstChunk.length >= bytes) {
|
||||
const view = firstChunk.length === bytes ? firstChunk : firstChunk.subarray(0, bytes);
|
||||
consumeFromHead(bytes);
|
||||
return view;
|
||||
}
|
||||
const result = new Uint8Array(bytes);
|
||||
let copied = 0;
|
||||
let remaining = bytes;
|
||||
while (remaining > 0) {
|
||||
const chunk = chunks[head];
|
||||
const toCopy = Math.min(remaining, chunk.length);
|
||||
result.set(toCopy === chunk.length ? chunk : chunk.subarray(0, toCopy), copied);
|
||||
copied += toCopy;
|
||||
remaining -= toCopy;
|
||||
consumeFromHead(toCopy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
push: (chunk) => {
|
||||
if (chunk.length === 0) return;
|
||||
let nextTail = tail + 1 & capacityMask;
|
||||
if (nextTail === head) {
|
||||
const oldLen = chunks.length;
|
||||
const newLen = oldLen * 2;
|
||||
const newChunks = new Array(newLen);
|
||||
const count = tail - head + oldLen & oldLen - 1;
|
||||
if (head < tail) for (let i = 0; i < count; i++) newChunks[i] = chunks[head + i];
|
||||
else if (count > 0) {
|
||||
const firstPart = oldLen - head;
|
||||
for (let i = 0; i < firstPart; i++) newChunks[i] = chunks[head + i];
|
||||
for (let i = 0; i < tail; i++) newChunks[firstPart + i] = chunks[i];
|
||||
}
|
||||
chunks = newChunks;
|
||||
capacityMask = newLen - 1;
|
||||
head = 0;
|
||||
tail = count;
|
||||
nextTail = tail + 1 & capacityMask;
|
||||
}
|
||||
chunks[tail] = chunk;
|
||||
tail = nextTail;
|
||||
totalAvailable += chunk.length;
|
||||
},
|
||||
available: () => totalAvailable,
|
||||
peek: (bytes) => {
|
||||
if (totalAvailable < bytes) return null;
|
||||
if (bytes === 0) return EMPTY;
|
||||
const firstChunk = chunks[head];
|
||||
if (firstChunk.length >= bytes) return firstChunk.length === bytes ? firstChunk : firstChunk.subarray(0, bytes);
|
||||
const result = new Uint8Array(bytes);
|
||||
let copied = 0;
|
||||
let index = head;
|
||||
while (copied < bytes) {
|
||||
const chunk = chunks[index];
|
||||
const toCopy = Math.min(bytes - copied, chunk.length);
|
||||
if (toCopy === chunk.length) result.set(chunk, copied);
|
||||
else result.set(chunk.subarray(0, toCopy), copied);
|
||||
copied += toCopy;
|
||||
index = index + 1 & capacityMask;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
discard: (bytes) => {
|
||||
if (bytes > totalAvailable) throw new Error("Too many bytes consumed");
|
||||
if (bytes === 0) return;
|
||||
let remaining = bytes;
|
||||
while (remaining > 0) {
|
||||
const chunk = chunks[head];
|
||||
const toConsume = Math.min(remaining, chunk.length);
|
||||
consumeFromHead(toConsume);
|
||||
remaining -= toConsume;
|
||||
}
|
||||
},
|
||||
pull
|
||||
};
|
||||
}
|
||||
//#endregion
|
||||
//#region src/tar/unpacker.ts
|
||||
const STATE_HEADER = 0;
|
||||
const STATE_BODY = 1;
|
||||
const truncateErr = /* @__PURE__ */ new Error("Tar archive is truncated.");
|
||||
function createUnpacker(options = {}) {
|
||||
const strict = options.strict ?? false;
|
||||
const { available, peek, push, discard, pull } = createChunkQueue();
|
||||
let state = STATE_HEADER;
|
||||
let ended = false;
|
||||
let done = false;
|
||||
let eof = false;
|
||||
let currentEntry = null;
|
||||
const paxGlobals = {};
|
||||
let nextEntryOverrides = {};
|
||||
const unpacker = {
|
||||
isEntryActive: () => state === STATE_BODY,
|
||||
isBodyComplete: () => !currentEntry || currentEntry.remaining === 0,
|
||||
canFinish: () => !currentEntry || available() >= currentEntry.remaining + currentEntry.padding,
|
||||
write(chunk) {
|
||||
if (ended) throw new Error("Archive already ended.");
|
||||
push(chunk);
|
||||
},
|
||||
end() {
|
||||
ended = true;
|
||||
},
|
||||
readHeader() {
|
||||
if (state !== STATE_HEADER) throw new Error("Cannot read header while an entry is active");
|
||||
if (done) return void 0;
|
||||
while (!done) {
|
||||
if (available() < 512) {
|
||||
if (ended) {
|
||||
if (available() > 0 && strict) throw truncateErr;
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const headerBlock = peek(512);
|
||||
if (isZeroBlock(headerBlock)) {
|
||||
if (available() < 512 * 2) {
|
||||
if (ended) {
|
||||
if (strict) throw truncateErr;
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (isZeroBlock(peek(512 * 2).subarray(512))) {
|
||||
discard(512 * 2);
|
||||
done = true;
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
if (strict) throw new Error("Invalid tar header.");
|
||||
discard(512);
|
||||
continue;
|
||||
}
|
||||
let internalHeader;
|
||||
try {
|
||||
internalHeader = parseUstarHeader(headerBlock, strict);
|
||||
} catch (err) {
|
||||
if (strict) throw err;
|
||||
discard(512);
|
||||
continue;
|
||||
}
|
||||
const metaParser = getMetaParser(internalHeader.type);
|
||||
if (metaParser) {
|
||||
const paddedSize = internalHeader.size + (-internalHeader.size & 511);
|
||||
if (available() < 512 + paddedSize) {
|
||||
if (ended && strict) throw truncateErr;
|
||||
return null;
|
||||
}
|
||||
discard(512);
|
||||
const overrides = metaParser(pull(paddedSize).subarray(0, internalHeader.size));
|
||||
const target = internalHeader.type === "pax-global-header" ? paxGlobals : nextEntryOverrides;
|
||||
for (const key in overrides) target[key] = overrides[key];
|
||||
continue;
|
||||
}
|
||||
discard(512);
|
||||
const header = internalHeader;
|
||||
if (internalHeader.prefix) header.name = `${internalHeader.prefix}/${header.name}`;
|
||||
applyOverrides(header, paxGlobals);
|
||||
applyOverrides(header, nextEntryOverrides);
|
||||
if (header.name.endsWith("/") && header.type === "file") header.type = DIRECTORY;
|
||||
nextEntryOverrides = {};
|
||||
currentEntry = {
|
||||
header,
|
||||
remaining: header.size,
|
||||
padding: -header.size & 511
|
||||
};
|
||||
state = STATE_BODY;
|
||||
return header;
|
||||
}
|
||||
},
|
||||
streamBody(callback) {
|
||||
if (state !== STATE_BODY || !currentEntry || currentEntry.remaining === 0) return 0;
|
||||
const bytesToFeed = Math.min(currentEntry.remaining, available());
|
||||
if (bytesToFeed === 0) return 0;
|
||||
const fed = pull(bytesToFeed, callback);
|
||||
currentEntry.remaining -= fed;
|
||||
return fed;
|
||||
},
|
||||
skipPadding() {
|
||||
if (state !== STATE_BODY || !currentEntry) return true;
|
||||
if (currentEntry.remaining > 0) throw new Error("Body not fully consumed");
|
||||
if (available() < currentEntry.padding) return false;
|
||||
discard(currentEntry.padding);
|
||||
currentEntry = null;
|
||||
state = STATE_HEADER;
|
||||
return true;
|
||||
},
|
||||
skipEntry() {
|
||||
if (state !== STATE_BODY || !currentEntry) return true;
|
||||
const toDiscard = Math.min(currentEntry.remaining, available());
|
||||
if (toDiscard > 0) {
|
||||
discard(toDiscard);
|
||||
currentEntry.remaining -= toDiscard;
|
||||
}
|
||||
if (currentEntry.remaining > 0) return false;
|
||||
return unpacker.skipPadding();
|
||||
},
|
||||
validateEOF() {
|
||||
if (strict) {
|
||||
if (!eof) throw truncateErr;
|
||||
if (available() > 0) {
|
||||
if (pull(available()).some((byte) => byte !== 0)) throw new Error("Invalid EOF.");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return unpacker;
|
||||
}
|
||||
function isZeroBlock(block) {
|
||||
if (block.byteOffset % 8 === 0) {
|
||||
const view = new BigUint64Array(block.buffer, block.byteOffset, block.length / 8);
|
||||
for (let i = 0; i < view.length; i++) if (view[i] !== 0n) return false;
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < block.length; i++) if (block[i] !== 0) return false;
|
||||
return true;
|
||||
}
|
||||
//#endregion
|
||||
export { normalizeBody as a, LINK as c, isBodyless as i, SYMLINK as l, createTarPacker as n, DIRECTORY as o, transformHeader as r, FILE as s, createUnpacker as t };
|
||||
Reference in New Issue
Block a user