@obsidize/tar-browserify
Version:
Browser-based tar utility for packing and unpacking tar files (stream-capable)
107 lines (106 loc) • 3.81 kB
JavaScript
import { TarUtility } from '../../common/tar-utility';
const ASCII_SPACE = 0x20;
const KEY_VALUE_SEPARATOR = '=';
/**
* Single segment in a PAX header block, represented by a text line with the format:
* ```LENGTH KEY=VALUE\n```
*/
export class PaxTarHeaderSegment {
constructor(mKey = '', mValue = '', mBytes = null) {
this.mKey = mKey;
this.mValue = mValue;
this.mBytes = mBytes;
}
static serialize(key, value) {
if (!key || !value) {
return new Uint8Array(0);
}
const segmentSuffix = ` ${key}=${value}\n`;
const preCalculatedLength = segmentSuffix.length.toString().length;
let segmentLength = segmentSuffix.length + preCalculatedLength;
// Calculation caused decimal rollover, increase combined length by 1
// (e.g. including the length part caused combined length to go from something like '99' to '101')
if (segmentLength < (segmentLength.toString().length + segmentSuffix.length)) {
segmentLength += 1;
}
const segment = segmentLength.toString() + segmentSuffix;
return TarUtility.encodeString(segment);
}
static tryParse(bytes, offset = 0) {
if (!TarUtility.isUint8Array(bytes)) {
return null;
}
const lengthEndIndex = PaxTarHeaderSegment.findNextLengthEndIndex(bytes, offset);
if (lengthEndIndex < 0) {
return null;
}
const segmentLengthStr = TarUtility.decodeString(bytes.slice(offset, lengthEndIndex));
const segmentLength = parseInt(segmentLengthStr, 10);
if (isNaN(segmentLength)) {
return null;
}
const kvpStart = lengthEndIndex + 1;
const kvpEnd = offset + segmentLength;
const kvpData = TarUtility.decodeString(bytes.slice(kvpStart, kvpEnd));
const equalsDelimiterIndex = kvpData.indexOf(KEY_VALUE_SEPARATOR);
const key = kvpData.substring(0, equalsDelimiterIndex);
const value = kvpData.substring(equalsDelimiterIndex + 1).replace(/\n$/, '');
const segmentBytes = TarUtility.cloneUint8Array(bytes, offset, offset + segmentLength);
return new PaxTarHeaderSegment(key, value, segmentBytes);
}
static findNextLengthEndIndex(bytes, offset) {
const NONE = -1;
let lengthEnd = offset + 1;
let found = false;
while (lengthEnd < bytes.byteLength && !found) {
if (bytes[lengthEnd] === ASCII_SPACE) {
found = true;
}
else {
lengthEnd += 1;
}
}
if (!found) {
return NONE;
}
return lengthEnd;
}
get key() {
return this.mKey;
}
get value() {
return this.mValue;
}
get bytes() {
return this.toUint8Array();
}
/**
* the value parsed as an integer, or undefined if the parse operation fails
*/
get intValue() {
const parsed = parseInt(this.value);
return isNaN(parsed) ? undefined : parsed;
}
/**
* the value parsed as a float, or undefined if the parse operation fails
*/
get floatValue() {
const parsed = parseFloat(this.value);
return isNaN(parsed) ? undefined : parsed;
}
toUint8Array() {
if (!TarUtility.isUint8Array(this.mBytes)) {
this.mBytes = PaxTarHeaderSegment.serialize(this.key, this.value);
}
return this.mBytes;
}
toJSON() {
const { key, value, bytes } = this;
const content = TarUtility.getDebugHexString(bytes);
return {
key,
value,
content
};
}
}