Something went wrong. Try again.
archived: streaming 7-Zip archive extractor
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185import { TruncatedArchiveError } from '../errors.ts';
const fromCharCode = String.fromCharCode;
export const textEncoder = new TextEncoder();export const textDecoder = new TextDecoder();
export function concat(arrays: readonly 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. user-supplied // non-byte sources 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, etc. — that swamps the per-byte savings, so // only attempt BYOB once the request is large enough to amortize the // setup. 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; } 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 { 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;}
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; }};
/** * reads exactly `length` bytes from `reader` at `offset`. * * @param reader the random-access source * @param offset starting byte position within the reader * @param length number of bytes to read * @returns the requested bytes as a fresh `Uint8Array` * @throws if the reader returns fewer than `length` bytes */export const readBytes = async ( reader: { read(offset: number, length?: number): Promise<ReadableStream<Uint8Array<ArrayBuffer>>> }, offset: number, length: number,): Promise<Uint8Array<ArrayBuffer>> => { return await read(await reader.read(offset, length), length);};