UNPKG

s2-tools

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

130 lines 4.66 kB
import { concatUint8Arrays } from '.'; /** * A Browser compatible Gzip compression function * @param bytes - the data to decompress * @param format - the format of the data. Defaults to 'gzip' * @returns - the decompressed data */ export async function compressStream(bytes, format) { // Convert the string to a byte stream. const stream = new Blob([bytes]).stream(); // Create a compressed stream. const compressedStream = stream.pipeThrough(new CompressionStream(format ?? 'gzip')); // Read all the bytes from this stream. const chunks = []; for await (const chunk of compressedStream) chunks.push(chunk); return await concatUint8Arrays(chunks); } /** * A Browser compatible Gzip decompression function * @param bytes - the data to decompress * @param format - the format of the data. Defaults to 'gzip' * @returns - the decompressed data */ export async function decompressStream(bytes, format) { if (format === undefined) format = bytes[0] === 0x1f && bytes[1] === 0x8b ? 'gzip' : 'deflate'; // Convert the bytes to a stream. const stream = new Blob([bytes]).stream(); // Create a decompressed stream. const decompressedStream = stream.pipeThrough(new DecompressionStream(format)); // Read all the bytes from this stream. const chunks = []; for await (const chunk of decompressedStream) chunks.push(chunk); return await concatUint8Arrays(chunks); } /** * Find the end of the central directory * @param raw - the raw data * @returns - the end of the central directory */ function findEndCentralDirectory(raw) { let search = raw.length - 20; const bounds = Math.max(search - 65516, 2); // sub 2**256 - 20 (max comment length) while ((search = raw.lastIndexOf(0x50, search - 1)) !== -1 && !(raw[search + 1] === 0x4b && raw[search + 2] === 0x05 && raw[search + 3] === 0x06) && search > bounds) { // no-op } return search; } /** * Iterate over the items in a zip file * @param raw - the raw data to read * @yields - {@link ZipItem} */ export function* iterItems(raw) { const d = new TextDecoder(); let at = findEndCentralDirectory(raw); if (at === -1) { throwCode(2); // bad zip format } /** * @param startBy - starting index * @param endBy - ending index * @returns - the subarray */ const subarrayMove = (startBy, endBy) => raw.subarray((at += startBy), (at += endBy)); const dataView = new DataView(raw.buffer, raw.byteOffset); // we don't need byteLength, could be a longer buffer :shrug: /** * @param offset - the offset to read from * @returns - the u16 value */ const u16 = (offset) => dataView.getUint16(offset + at, true); /** * @param offset - the offset to read from * @returns - the u32 value */ const u32 = (offset) => dataView.getUint32(offset + at, true); // read end central directory let fileCount = u16(10); if (fileCount !== u16(8)) { throwCode(3); // no multi-disk support } const centralDirectoryStart = u32(16); at = centralDirectoryStart; // read central directory while (fileCount-- > 0) { const compressionMethod = u16(10); const filenameLength = u16(28); const extraFieldsLength = u16(30); const commentLength = u16(32); const compressedSize = u32(20); // find local entry location const localEntryAt = u32(42); // read buffers, move at to after entry, and store where we were const filename = d.decode(subarrayMove(46, filenameLength)); // we skip extraFields here const comment = d.decode(subarrayMove(extraFieldsLength, commentLength)); const nextCentralDirectoryEntry = at; // >> start reading entry at = localEntryAt; // this is the local entry (filename + extra fields) length, which we skip const bytes = subarrayMove(30 + u16(26) + u16(28), compressedSize); yield { filename, comment, /** * @returns - the decompressed bytes */ read: async () => { return (compressionMethod & 8) > 0 ? await decompressStream(bytes, 'deflate-raw') : compressionMethod > 0 ? throwCode(1) : bytes; }, }; at = nextCentralDirectoryEntry; } } /** * @param code - error code * @throws - an error */ function throwCode(code) { throw new Error(`Error code: ${code}`); } //# sourceMappingURL=gzip.js.map