@pgxsinkit/pgwasm
Version:
234 lines (230 loc) • 8.93 kB
JavaScript
// @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