UNPKG

@pgxsinkit/pgwasm

Version:
234 lines (230 loc) • 8.93 kB
// @bun // packages/pgwasm/src/tar/gzip.ts var GZIP_MAGIC_0 = 31; var GZIP_MAGIC_1 = 139; function isGzip2(bytes) { return bytes.byteLength >= 2 && bytes[0] === GZIP_MAGIC_0 && bytes[1] === GZIP_MAGIC_1; } async function gzip2(bytes) { return await pipe(bytes, new CompressionStream("gzip")); } async function gunzip2(bytes) { return await pipe(bytes, new DecompressionStream("gzip")); } async function gunzipIfCompressed2(bytes) { return isGzip2(bytes) ? await gunzip2(bytes) : bytes; } async function pipe(bytes, transform) { const input = new Blob([bytes.slice()]).stream().pipeThrough(transform); return new Uint8Array(await new Response(input).arrayBuffer()); } // packages/pgwasm/src/tar/tar.ts class TarFormatError2 extends Error { name = "TarFormatError"; } var RECORD = 512; var NAME_LENGTH = 100; var PREFIX_LENGTH = 155; var encoder = new TextEncoder; var decoder = new TextDecoder; function writeTar2(entries) { let total = RECORD * 2; for (const entry of entries) total += RECORD + padded(entry.type === "file" ? entry.data.byteLength : 0); const archive = new Uint8Array(total); let offset = 0; for (const entry of entries) { const size = entry.type === "file" ? entry.data.byteLength : 0; writeHeader(archive, offset, entry, size); offset += RECORD; if (size > 0) archive.set(entry.data, offset); offset += padded(size); } return archive; } function readTar2(archive) { const entries = []; let offset = 0; let pendingName; let pendingSize; for (;; ) { if (offset === archive.byteLength) { throw new TarFormatError2(`tar archive ends at byte ${offset} without its end-of-archive records: it is truncated`); } if (offset + RECORD > archive.byteLength) { throw new TarFormatError2(`tar archive ends inside the header at byte ${offset}: it is truncated`); } const header = archive.subarray(offset, offset + RECORD); if (isZeroRecord(header)) { const second = archive.subarray(offset + RECORD, offset + 2 * RECORD); if (second.byteLength < RECORD || !isZeroRecord(second)) { throw new TarFormatError2(`tar archive has one end-of-archive record at byte ${offset}, not two: it is truncated or damaged`); } if (pendingName !== undefined || pendingSize !== undefined) { throw new TarFormatError2(`tar archive ends after an extended header with no member for it`); } return entries; } verifyChecksum(header, offset); const typeflag = String.fromCharCode(header[156] ?? 0); const declaredSize = readOctal(header, 124, 12, offset); const size = pendingSize ?? declaredSize; const dataStart = offset + RECORD; if (dataStart + padded(size) > archive.byteLength) { throw new TarFormatError2(`tar member at byte ${offset} runs past the end of the archive: it is truncated`); } const data = archive.subarray(dataStart, dataStart + size); offset = dataStart + padded(size); if (typeflag === "x") { const pax = readPax(data, offset); pendingName = pax.path ?? pendingName; pendingSize = pax.size; continue; } if (typeflag === "g") continue; if (typeflag === "L") { pendingName = readCString(data, 0, data.byteLength); continue; } const name = pendingName ?? joinPrefix(header); pendingName = undefined; pendingSize = undefined; const mode = readOctal(header, 100, 8, offset) & 4095; const mtimeSeconds = readOctal(header, 136, 12, offset); if (typeflag === "0" || typeflag === "\x00" || typeflag === "7") { entries.push({ name, type: "file", mode, mtimeSeconds, data: data.slice() }); } else if (typeflag === "5") { entries.push({ name, type: "directory", mode, mtimeSeconds, data: new Uint8Array(0) }); } else { throw new TarFormatError2(`tar member "${name}" has unsupported type "${typeflag}"`); } } } function padded(size) { return Math.ceil(size / RECORD) * RECORD; } function writeHeader(archive, offset, entry, size) { const [prefix, rest] = splitName(entry.name); writeString(archive, offset + 0, NAME_LENGTH, rest); writeOctal(archive, offset + 100, 8, entry.mode & 4095); writeOctal(archive, offset + 108, 8, 0); writeOctal(archive, offset + 116, 8, 0); writeOctal(archive, offset + 124, 12, size); writeOctal(archive, offset + 136, 12, Math.max(0, Math.floor(entry.mtimeSeconds))); archive[offset + 156] = (entry.type === "directory" ? "5" : "0").charCodeAt(0); writeString(archive, offset + 257, 6, "ustar"); writeString(archive, offset + 263, 2, "00", false); writeString(archive, offset + 345, PREFIX_LENGTH, prefix); archive.fill(32, offset + 148, offset + 156); let sum = 0; for (let index = offset;index < offset + RECORD; index++) sum += archive[index] ?? 0; archive.set(encoder.encode(sum.toString(8).padStart(6, "0")), offset + 148); archive[offset + 154] = 0; } function splitName(name) { if (encoder.encode(name).byteLength < NAME_LENGTH) return ["", name]; for (let cut = name.indexOf("/");cut !== -1; cut = name.indexOf("/", cut + 1)) { const prefix = name.slice(0, cut); const rest = name.slice(cut + 1); if (encoder.encode(prefix).byteLength < PREFIX_LENGTH && encoder.encode(rest).byteLength < NAME_LENGTH) { return [prefix, rest]; } } throw new TarFormatError2(`tar member name is too long to store: "${name}"`); } function joinPrefix(header) { const name = readCString(header, 0, NAME_LENGTH); const magic = readCString(header, 257, 6); if (magic !== "ustar") return name; const prefix = readCString(header, 345, PREFIX_LENGTH); return prefix === "" ? name : `${prefix}/${name}`; } function writeString(archive, at, length, value, terminate = true) { const bytes = encoder.encode(value); if (bytes.byteLength > (terminate ? length - 1 : length)) { throw new TarFormatError2(`tar header field overflow: "${value}"`); } archive.set(bytes, at); } function writeOctal(archive, at, length, value) { const digits = value.toString(8); if (!Number.isSafeInteger(value) || value < 0 || digits.length > length - 1) { throw new TarFormatError2(`tar header number out of range: ${value}`); } writeString(archive, at, length, digits.padStart(length - 1, "0")); } function readCString(bytes, at, length) { let end = at; while (end < at + length && bytes[end] !== 0) end++; return decoder.decode(bytes.subarray(at, end)); } function readOctal(header, at, length, recordOffset) { if (((header[at] ?? 0) & 128) !== 0) { throw new TarFormatError2(`tar header at byte ${recordOffset} uses base-256 numbers, which are not supported`); } const text = readCString(header, at, length).trim(); if (text === "") return 0; if (!/^[0-7]+$/.test(text)) { throw new TarFormatError2(`tar header at byte ${recordOffset} has a malformed number: "${text}"`); } return Number.parseInt(text, 8); } function verifyChecksum(header, recordOffset) { const stored = readOctal(header, 148, 8, recordOffset); let sum = 0; for (let index = 0;index < RECORD; index++) { sum += index >= 148 && index < 156 ? 32 : header[index] ?? 0; } if (sum !== stored) throw new TarFormatError2(`tar header at byte ${recordOffset} fails its checksum`); } function isZeroRecord(header) { for (const byte of header) if (byte !== 0) return false; return true; } function readPax(data, recordOffset) { const result = {}; const malformed = (what) => new TarFormatError2(`tar PAX header before byte ${recordOffset} has a malformed ${what}`); let cursor = 0; while (cursor < data.byteLength) { let space = cursor; while (space < data.byteLength && data[space] !== 32) space++; const lengthText = decoder.decode(data.subarray(cursor, space)); if (space >= data.byteLength || !/^[0-9]+$/.test(lengthText)) throw malformed("record"); const length = Number.parseInt(lengthText, 10); if (!Number.isSafeInteger(length) || length <= space - cursor + 1 || cursor + length > data.byteLength) { throw malformed("record"); } if (data[cursor + length - 1] !== 10) throw malformed("record"); const record = decoder.decode(data.subarray(space + 1, cursor + length - 1)); const equals = record.indexOf("="); if (equals === -1) throw malformed("record"); const key = record.slice(0, equals); const value = record.slice(equals + 1); if (key === "path") result.path = value; if (key === "size") { const size = /^[0-9]+$/.test(value) ? Number.parseInt(value, 10) : Number.NaN; if (!Number.isSafeInteger(size)) throw malformed(`size ("${value}")`); result.size = size; } cursor += length; } return result; } export { isGzip2, gzip2, gunzip2, gunzipIfCompressed2, TarFormatError2, writeTar2, readTar2 }; //# debugId=BA947B826380513C64756E2164756E21