Skip to content

Commit

Permalink
feat: decode
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Aug 1, 2024
1 parent b639d4a commit 2f5e435
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 22 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "dist/index.d.ts",
"module": "dist/index.mjs",
"scripts": {
"dev": "rescript -w",
"dev": "rollup --config rollup.config.mts --configPlugin swc3 --watch",
"build": "rollup --config rollup.config.mts --configPlugin swc3",
"pretest": "sh -c '[ -d ./dist ] && echo \"Directory exists. Skipping build.\" || (echo \"Directory does not exist. Running build...\" && pnpm run build)'",
"test": "vitest"
Expand Down
68 changes: 51 additions & 17 deletions src/head.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-labels */
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
// https://www.gnu.org/software/tar/manual/html_node/Portability.html#Portability

type uint8 = number

Expand Down Expand Up @@ -60,7 +61,11 @@ export type TypeFlag = typeof TypeFlag[keyof typeof TypeFlag]

export const Magic = {
T_MAGIC: 'ustar',
T_VERSION: '00'
T_VERSION: '00',
WHITE_SPACE: 32, // ascii code
NULL_CHAR: 0, // ascii code
NEGATIVE_256: 0xFF,
POSITIVE_256: 0x80
}

export interface EncodingHeadOptions {
Expand All @@ -86,30 +91,28 @@ export interface DecodingHeadOptions {
export const ERROR_MESSAGES = {
INVALID_ENCODING_NAME: 'Invalid name. Invalid name. Please check \'name\' is a direcotry type.',
INVALID_ENCODING_NAME_LEN: 'Invalid name. Please check \'name\' length is less than 255 byte.',
INVALID_ENCODING_LINKNAME: 'Invalid linkname. Please check \'linkname\' length is less than 100 byte.'
INVALID_ENCODING_LINKNAME: 'Invalid linkname. Please check \'linkname\' length is less than 100 byte.',
INVALID_BASE256: 'Invalid base256 format'
}

const enc =/* @__PURE__ */ new TextEncoder()
// For most scens. format ustar is useful, but when we meet the large file, we should fallback to the old gnu format.

const dec =/* @__PURE__ */ new TextDecoder()
const enc =/* @__PURE__ */ new TextEncoder()

/* @__NO_SIDE_EFFECTS__ */
function encodeString(s: string) {
return enc.encode(s)
}
/* @__NO_SIDE_EFFECTS__ */
function indexOf(b: Uint8Array, c: number, start: number) {
//
}

/* @__NO_SIDE_EFFECTS__ */
function decodeString(b: Uint8Array, offset: number, length: number, encoding = 'utf-8') {
// return dec.decode(b)
b.subarray(offset, offset + length)
// filter null character
while (b[offset + length - 1] === Magic.NULL_CHAR) {
length--
}
const dec = new TextDecoder(encoding)
return dec.decode(b.subarray(offset, offset + length))
}

/* @__NO_SIDE_EFFECTS__ */
function encodeOctal(b: number, fixed?: number) {
export function encodeOctal(b: number, fixed?: number) {
const o = b.toString(8)
if (fixed) {
if (o.length <= fixed) {
Expand All @@ -121,6 +124,36 @@ function encodeOctal(b: number, fixed?: number) {
return o
}

// https://www.gnu.org/software/tar/manual/html_node/Extensions.html
function parse256(b: Uint8Array) {
const positive = b[0] === Magic.POSITIVE_256 ? true : false
return b.reduceRight((acc, cur, i) => {
return acc += cur * Math.pow(256, b.length - i - 1)
}, 0) * (positive ? 1 : -1)
}

export function decodeOctal(b: Uint8Array, offset: number, length: number) {
const range = b.subarray(offset, offset + length)
// for old gnu format
if (range[0] & Magic.POSITIVE_256) {
if (range[0] === Magic.POSITIVE_256 || range[0] === Magic.NEGATIVE_256) {
return parse256(range)
}
throw new Error(ERROR_MESSAGES.INVALID_BASE256)
}

// [48...48, 32, 0] // len = 8
let pos = 0
console.log(range)
for (;;) {
if (range[pos] === Magic.WHITE_SPACE) {
break
}
pos++
}
return parseInt(decodeString(range, 0, pos), 8)
}

function chksum(b: Uint8Array) {
return b.reduce((acc, cur, i) => {
if (i >= 148 && i < 156) {
Expand All @@ -130,7 +163,6 @@ function chksum(b: Uint8Array) {
}, 0)
}

/* @__NO_SIDE_EFFECTS__ */
export function encode(options: EncodingHeadOptions) {
const block = new Uint8Array(512)
let name = options.name
Expand Down Expand Up @@ -211,10 +243,12 @@ const defaultDecodeOptions = {
allowUnknownFormat: false
}

/* @__NO_SIDE_EFFECTS__ */
export function decode(b: Uint8Array, options?: DecodingHeadOptions) {
const opts = options = { ...defaultDecodeOptions, ...options }
const { filenameEncoding, allowUnknownFormat } = opts
// const name = decodeString(b.slice(0, 100))
const name = decodeString(b, 0, 100, filenameEncoding)
const mode = decodeOctal(b, 100, 8)

// const mode =
// const mode = parseInt(decodeString(b.slice(100, 108)), 8)
}
18 changes: 14 additions & 4 deletions src/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ function createWriteableStream(options?: WritableOptions) {
return new Writable(options)
}

const PACK_ERROR_MESSAGES = {
HAS_DONE: 'Can\'t add new entry after calling done()'
}

// New archives should be created using REGTYPE.
const defaultPackOptions = {
mode: F_MODE,
Expand All @@ -29,7 +33,9 @@ export class Pack {
private reader: Readable
private finished: boolean
constructor() {
this.reader = createReadbleStream()
this.reader = createReadbleStream({
read() {}
})
this.finished = false
}

Expand All @@ -40,15 +46,16 @@ export class Pack {
}

add(binary: Uint8Array, options: PackOptions) {
if (this.finished) {
throw new Error(PACK_ERROR_MESSAGES.HAS_DONE)
}
const resolved = this.resolveHeadOptions(binary.length, options)
this.transport(binary, resolved)
}

done() {
if (this.finished) return
this.finished = true
// The end of tar is two 512 byte blocks of zeros
// For performance reasone, we should call push as less as possible
this.reader.push(new Uint8Array(1024))
this.reader.push(null)
}
Expand All @@ -68,7 +75,10 @@ export class Pack {
writer.write(binary)
}

receiver() {
get receiver() {
if (!this.finished) {
this.done()
}
return this.reader
}
}
Expand Down

0 comments on commit 2f5e435

Please sign in to comment.