Something went wrong. Try again.
archived: streaming zip archiver/extractor jsr.io/@mary/zip
typescript jsr
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198import { TruncatedArchiveError } from '../errors.ts';
const fromCharCode = String.fromCharCode;
export const textEncoder = new TextEncoder();export const textDecoder = new TextDecoder();
export function concat(arrays: Uint8Array[], size?: number): Uint8Array<ArrayBuffer> { let written = 0;
// deno-lint-ignore prefer-const let len = arrays.length; let idx: number;
if (size === undefined) { for (idx = size = 0; idx < len; idx++) { const chunk = arrays[idx]; size += chunk.length; } }
const buffer = new Uint8Array(size);
for (idx = 0; idx < len; idx++) { const chunk = arrays[idx];
buffer.set(chunk, written); written += chunk.length; }
return buffer;}
export async function read( stream: ReadableStream<Uint8Array>, size: number,): Promise<Uint8Array<ArrayBuffer>> { // fast path: a byte-type stream lets us request bytes straight into a // pre-allocated buffer with no producer-side memcpy. DecompressionStream // pipes and user-supplied non-byte sources (fromUint8Array, fromFile via // Blob) don't support BYOB; fall back to the default-reader copy loop // when getReader throws. // // the BYOB reader setup (and the TypeError throw when the stream isn't // byte-typed) carries ~µs of fixed overhead. for tiny reads — header // probes, signature checks — that swamps the per-byte savings, so only // attempt BYOB once the request is large enough to amortize the setup. // the threshold is empirical, matching the pkg-rar measurement. let byob: ReadableStreamBYOBReader | undefined; if (size >= 4096) { try { byob = stream.getReader({ mode: 'byob' }); } catch { // fall through to default-reader path below } }
if (byob !== undefined) { let buffer = new ArrayBuffer(size); let pos = 0; let drained = false; try { while (pos < size) { const view = new Uint8Array(buffer, pos, size - pos); const { done, value } = await byob.read(view); if (done) { drained = true; break; } // BYOB read transfers the input view's buffer into `value`; // reassign so the next iteration's view sees the live buffer. buffer = value.buffer as ArrayBuffer; pos += value.byteLength; } } finally { if (!drained) { await byob.cancel(); } else { byob.releaseLock(); } } if (pos < size) { throw new TruncatedArchiveError(`unexpected end of stream: expected ${size} bytes, got ${pos}`); } return new Uint8Array(buffer, 0, size); }
const buffer = new Uint8Array(size); const reader = stream.getReader(); let pos = 0; let drained = false;
try { while (pos < size) { const { done, value } = await reader.read(); if (done) { drained = true; break; } const length = value.length; const room = size - pos; if (length <= room) { buffer.set(value, pos); pos += length; } else { buffer.set(value.subarray(0, room), pos); pos = size; } } } finally { // when the stream is already at end, cancel is a no-op; when we have // enough bytes but the producer might still deliver more (e.g. a // decompress stream flushing tail bytes past the declared size), // cancel releases its resources without the costly async-iterator // `return()` dance. if (!drained) { await reader.cancel(); } else { reader.releaseLock(); } }
if (pos < size) { throw new TruncatedArchiveError(`unexpected end of stream: expected ${size} bytes, got ${pos}`); }
return buffer;}
// IBM Code Page 437 — the encoding zip archives use for filenames and// comments when the EFS bit (general purpose flag 0x0800) is not set.// bytes 0x00–0x7f are ASCII-compatible; bytes 0x80–0xff map to specific// Unicode codepoints, packed into this 128-char string in order.const CP437_HIGH = 'ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒ' + 'áíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐' + '└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀' + 'αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ';
export const decodeCp437From = (from: Uint8Array): string => { let str = ''; for (let idx = 0, len = from.length; idx < len; idx++) { const byte = from[idx]; if (byte < 0x80) { str += fromCharCode(byte); } else { str += CP437_HIGH[byte - 0x80]; } } return str;};
export const decodeUtf8From = (from: Uint8Array, offset?: number, length?: number): string => { let buffer: Uint8Array;
if (offset === undefined) { buffer = from; } else if (length === undefined) { buffer = from.subarray(offset); } else { buffer = from.subarray(offset, offset + length); }
const end = buffer.length; if (end > 24) { return textDecoder.decode(buffer); }
{ let str = ''; let idx = 0;
for (; idx + 3 < end; idx += 4) { const a = buffer[idx]; const b = buffer[idx + 1]; const c = buffer[idx + 2]; const d = buffer[idx + 3];
if ((a | b | c | d) & 0x80) { return str + textDecoder.decode(buffer.subarray(idx)); }
str += fromCharCode(a, b, c, d); }
for (; idx < end; idx++) { const x = buffer[idx];
if (x & 0x80) { return str + textDecoder.decode(buffer.subarray(idx)); }
str += fromCharCode(x); }
return str; }};