@loaders.gl/zip
Version:
Zip Archive Loader
136 lines (135 loc) • 4.32 kB
JavaScript
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
// This file is derived from the tar-js code base under MIT license
// See https://github.com/beatgammit/tar-js/blob/master/LICENSE
/*
* tar-js
* MIT (c) 2011 T. Jameson Little
*/
import { clean, pad, stringToUint8 } from "./utils.js";
import { format } from "./header.js";
let blockSize;
let headerLength;
let inputLength;
const recordSize = 512;
class Tar {
written;
out;
blocks = [];
length;
/**
* @param [recordsPerBlock]
*/
constructor(recordsPerBlock) {
this.written = 0;
blockSize = (recordsPerBlock || 20) * recordSize;
this.out = clean(blockSize);
this.blocks = [];
this.length = 0;
this.save = this.save.bind(this);
this.clear = this.clear.bind(this);
this.append = this.append.bind(this);
}
/**
* Append a file to the tar archive
* @param filepath
* @param input
* @param [opts]
*/
// eslint-disable-next-line complexity
append(filepath, input, opts) {
let checksum;
if (typeof input === 'string') {
input = stringToUint8(input);
}
else if (input.constructor && input.constructor !== Uint8Array.prototype.constructor) {
// @ts-ignore
const errorInputMatch = /function\s*([$A-Za-z_][0-9A-Za-z_]*)\s*\(/.exec(input.constructor.toString());
const errorInput = errorInputMatch && errorInputMatch[1];
const errorMessage = `Invalid input type. You gave me: ${errorInput}`;
throw errorMessage;
}
opts = opts || {};
const mode = opts.mode || parseInt('777', 8) & 0xfff;
const mtime = opts.mtime || Math.floor(Number(new Date()) / 1000);
const uid = opts.uid || 0;
const gid = opts.gid || 0;
const data = {
fileName: filepath,
fileMode: pad(mode, 7),
uid: pad(uid, 7),
gid: pad(gid, 7),
fileSize: pad(input.length, 11),
mtime: pad(mtime, 11),
checksum: ' ',
// 0 = just a file
type: '0',
ustar: 'ustar ',
owner: opts.owner || '',
group: opts.group || ''
};
// calculate the checksum
checksum = 0;
Object.keys(data).forEach((key) => {
let i;
const value = data[key];
let length;
for (i = 0, length = value.length; i < length; i += 1) {
checksum += value.charCodeAt(i);
}
});
data.checksum = `${pad(checksum, 6)}\u0000 `;
const headerArr = format(data);
headerLength = Math.ceil(headerArr.length / recordSize) * recordSize;
inputLength = Math.ceil(input.length / recordSize) * recordSize;
this.blocks.push({
header: headerArr,
input,
headerLength,
inputLength
});
}
/**
* Compiling data to a Blob object
* @returns {Blob}
*/
save() {
const buffers = [];
const chunks = new Array();
let length = 0;
const max = Math.pow(2, 20);
let chunk = new Array();
this.blocks.forEach((b = []) => {
if (length + b.headerLength + b.inputLength > max) {
chunks.push({ blocks: chunk, length });
chunk = [];
length = 0;
}
chunk.push(b);
length += b.headerLength + b.inputLength;
});
chunks.push({ blocks: chunk, length });
chunks.forEach((c = []) => {
const buffer = new Uint8Array(c.length);
let written = 0;
c.blocks.forEach((b = []) => {
buffer.set(b.header, written);
written += b.headerLength;
buffer.set(b.input, written);
written += b.inputLength;
});
buffers.push(buffer);
});
buffers.push(new Uint8Array(2 * recordSize));
return new Blob(buffers, { type: 'octet/stream' });
}
/**
* Clear the data by its blocksize
*/
clear() {
this.written = 0;
this.out = clean(blockSize);
}
}
export default Tar;