Skip to content

Commit

Permalink
Merge pull request #3 from schl3ck/master
Browse files Browse the repository at this point in the history
Support CommonJS, reading binary files and reading text with UTF-16 surrogate pairs
  • Loading branch information
ankitrohatgi authored Jun 12, 2022
2 parents 8e70fbf + 29b6e87 commit 64ea5eb
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 48 deletions.
94 changes: 57 additions & 37 deletions tarball.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
let tarball = {};

if (typeof module === "object" && typeof module.exports === "object") {
// CommonJS
module.exports = tarball;
} else if (typeof this === "object") {
// Browser
// use this instead of window, since window might not exist and throw and error
this.tarball = tarball;
}

tarball.TarReader = class {
constructor() {
this.fileInfo = [];
Expand All @@ -19,12 +28,10 @@ tarball.TarReader = class {
}

readArrayBuffer(arrayBuffer) {
return new Promise((resolve, reject) => {
this.buffer = arrayBuffer;
this.fileInfo = [];
this._readFileInfo();
resolve(this.fileInfo);
});
this.buffer = arrayBuffer;
this.fileInfo = [];
this._readFileInfo();
return this.fileInfo;
}

_readFileInfo() {
Expand Down Expand Up @@ -61,13 +68,9 @@ tarball.TarReader = class {

_readString(str_offset, size) {
let strView = new Uint8Array(this.buffer, str_offset, size);
let i = 0;
let rtnStr = "";
while(strView[i] != 0) {
rtnStr += String.fromCharCode(strView[i]);
i++;
}
return rtnStr;
let i = strView.indexOf(0);
let td = new TextDecoder();
return td.decode(strView.slice(0, i));
}

_readFileName(header_offset) {
Expand Down Expand Up @@ -104,30 +107,37 @@ tarball.TarReader = class {
return blob;
}

_readFileBinary(file_offset, size) {
let view = new Uint8Array(this.buffer, file_offset, size);
return view;
}

_readTextFile(file_offset, size) {
let view = new Uint8Array(this.buffer, file_offset, size);
let data = "";
for(let i = 0; i < size; i++) {
data += String.fromCharCode(view[i]);
}
return data;
let td = new TextDecoder();
return td.decode(view);
}

getTextFile(file_name) {
let i = this.fileInfo.findIndex(info => info.name == file_name);
if(i >= 0) {
let info = this.fileInfo[i];
let info = this.fileInfo.find(info => info.name == file_name);
if (info) {
return this._readTextFile(info.header_offset+512, info.size);
}
}

getFileBlob(file_name, mimetype) {
let i = this.fileInfo.findIndex(info => info.name == file_name);
if(i >= 0) {
let info = this.fileInfo[i];
let info = this.fileInfo.find(info => info.name == file_name);
if (info) {
return this._readFileBlob(info.header_offset+512, info.size, mimetype);
}
}

getFileBinary(file_name) {
let info = this.fileInfo.find(info => info.name == file_name);
if (info) {
return this._readFileBinary(info.header_offset+512, info.size);
}
}
};

tarball.TarWriter = class {
Expand All @@ -136,11 +146,8 @@ tarball.TarWriter = class {
}

addTextFile(name, text, opts) {
let buf = new ArrayBuffer(text.length);
let arr = new Uint8Array(buf);
for(let i = 0; i < text.length; i++) {
arr[i] = text.charCodeAt(i);
}
let te = new TextEncoder();
let arr = te.encode(text);
this.fileData.push({
name: name,
array: arr,
Expand Down Expand Up @@ -201,7 +208,7 @@ tarball.TarWriter = class {
}

async download(filename) {
let blob = await this.write();
let blob = await this.writeBlob();
let $downloadElem = document.createElement('a');
$downloadElem.href = URL.createObjectURL(blob);
$downloadElem.download = filename;
Expand All @@ -211,17 +218,23 @@ tarball.TarWriter = class {
document.body.removeChild($downloadElem);
}

write() {
async writeBlob(onUpdate) {
return new Blob([await this.write(onUpdate)], {"type":"application/x-tar"});
}

write(onUpdate) {
return new Promise((resolve,reject) => {
this._createBuffer();
let offset = 0;
let filesAdded = 0;
let onFileDataAdded = () => {
filesAdded++;
if (onUpdate) {
onUpdate(filesAdded / this.fileData.length * 100);
}
if(filesAdded === this.fileData.length) {
let arr = new Uint8Array(this.buffer);
let blob = new Blob([arr], {"type":"application/x-tar"});
resolve(blob);
resolve(arr);
}
};
for(let fileIdx = 0; fileIdx < this.fileData.length; fileIdx++) {
Expand Down Expand Up @@ -269,12 +282,19 @@ tarball.TarWriter = class {

_writeString(str, offset, size) {
let strView = new Uint8Array(this.buffer, offset, size);
for(let i = 0; i < size; i++) {
if(i < str.length) {
strView[i] = str.charCodeAt(i);
} else {
let te = new TextEncoder();
if (te.encodeInto) {
// let the browser write directly into the buffer
let written = te.encodeInto(str, strView).written;
for (let i = written; i < size; i++) {
strView[i] = 0;
}
} else {
// browser can't write directly into the buffer, do it manually
let arr = te.encode(str);
for (let i = 0; i < size; i++) {
strView[i] = i < arr.length ? arr[i] : 0;
}
}
}

Expand Down
Binary file modified tests/files/simple.tar
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/files/simple/hello.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hello world!
hello world! 🙂
20 changes: 10 additions & 10 deletions tests/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ let testUtils = {
return new Promise((resolve, reject) => {
let tarWriter = new tarball.TarWriter();
tarWriter.addFolder("myfolder/");
tarWriter.addTextFile("myfolder/first.txt", "this is some text");
tarWriter.addTextFile("myfolder/second.txt", "some more text");
tarWriter.addTextFile("myfolder/first.txt", "this is some text 🙂");
tarWriter.addTextFile("myfolder/second.txt", "some more text with 🙃 emojis");
fetch("files/simple/tux.png").then(resp => resp.blob()).then((blob) => {
let file = blob;
file.name = "tux.png";
Expand All @@ -26,7 +26,7 @@ let testUtils = {
resolve(null);
});
} else {
tarWriter.write().then((tarBlob) => {
tarWriter.writeBlob().then((tarBlob) => {
let tarFile = tarBlob;
let tarReader = new tarball.TarReader();
tarReader.readFile(tarFile).then((fileInfo) => {
Expand All @@ -47,8 +47,8 @@ QUnit.test( "Count files", function( assert ) {
let fileInfo = tar.getFileInfo();
assert.equal(fileInfo.length, 3, "Has 3 files");
assert.equal(fileInfo[0].name, "simple/", "Has simple directory");
assert.equal(fileInfo[1].name, "simple/tux.png", "Has image file");
assert.equal(fileInfo[2].name, "simple/hello.txt", "Has text file");
assert.equal(fileInfo[1].name, "simple/hello.txt", "Has text file");
assert.equal(fileInfo[2].name, "simple/tux.png", "Has image file");
done();
});
});
Expand All @@ -57,9 +57,9 @@ QUnit.test( "Check file headers", function( assert ) {
let done = assert.async();
testUtils.fetchTar("files/simple.tar").then((tar) => {
let fileInfo = tar.getFileInfo();
assert.equal(fileInfo[1].name, "simple/tux.png", "File name is ok");
assert.equal(fileInfo[1].type, "file", "File type is ok");
assert.equal(fileInfo[1].size, 11913, "File size is ok");
assert.equal(fileInfo[2].name, "simple/tux.png", "File name is ok");
assert.equal(fileInfo[2].type, "file", "File type is ok");
assert.equal(fileInfo[2].size, 11913, "File size is ok");
done();
});
});
Expand All @@ -68,7 +68,7 @@ QUnit.test( "Check text file contents", function( assert ) {
let done = assert.async();
testUtils.fetchTar("files/simple.tar").then((tar) => {
let text = tar.getTextFile("simple/hello.txt");
assert.equal(text, "hello world!\n", "Text file contents are ok");
assert.equal(text, "hello world! 🙂\n", "Text file contents are ok");
done();
});
});
Expand Down Expand Up @@ -114,7 +114,7 @@ QUnit.test( "Check text file contents", function( assert ) {
let done = assert.async();
testUtils.generateTar().then((tar) => {
let text = tar.getTextFile("myfolder/second.txt");
assert.equal(text, "some more text", "text file contents are ok");
assert.equal(text, "some more text with 🙃 emojis", "text file contents are ok");
done();
});
});
Expand Down

0 comments on commit 64ea5eb

Please sign in to comment.