Tons of Solutions Engineering work done today for the rest of the CS team! Headway, Howard Hanna, Engels, Brighton, etc. Also completed Datasnippers auth flow and worked on Anthology's script. Cloned Anthology's courses (900..) and will clone Full Story on Monday.
This commit is contained in:
406
Scripts/node_modules/tar-stream/extract.js
generated
vendored
Normal file
406
Scripts/node_modules/tar-stream/extract.js
generated
vendored
Normal file
@ -0,0 +1,406 @@
|
||||
const { Writable, Readable, getStreamError } = require('streamx')
|
||||
const FIFO = require('fast-fifo')
|
||||
const b4a = require('b4a')
|
||||
const headers = require('./headers')
|
||||
|
||||
const EMPTY = b4a.alloc(0)
|
||||
|
||||
class BufferList {
|
||||
constructor () {
|
||||
this.buffered = 0
|
||||
this.shifted = 0
|
||||
this.queue = new FIFO()
|
||||
|
||||
this._offset = 0
|
||||
}
|
||||
|
||||
push (buffer) {
|
||||
this.buffered += buffer.byteLength
|
||||
this.queue.push(buffer)
|
||||
}
|
||||
|
||||
shiftFirst (size) {
|
||||
return this._buffered === 0 ? null : this._next(size)
|
||||
}
|
||||
|
||||
shift (size) {
|
||||
if (size > this.buffered) return null
|
||||
if (size === 0) return EMPTY
|
||||
|
||||
let chunk = this._next(size)
|
||||
|
||||
if (size === chunk.byteLength) return chunk // likely case
|
||||
|
||||
const chunks = [chunk]
|
||||
|
||||
while ((size -= chunk.byteLength) > 0) {
|
||||
chunk = this._next(size)
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
return b4a.concat(chunks)
|
||||
}
|
||||
|
||||
_next (size) {
|
||||
const buf = this.queue.peek()
|
||||
const rem = buf.byteLength - this._offset
|
||||
|
||||
if (size >= rem) {
|
||||
const sub = this._offset ? buf.subarray(this._offset, buf.byteLength) : buf
|
||||
this.queue.shift()
|
||||
this._offset = 0
|
||||
this.buffered -= rem
|
||||
this.shifted += rem
|
||||
return sub
|
||||
}
|
||||
|
||||
this.buffered -= size
|
||||
this.shifted += size
|
||||
|
||||
return buf.subarray(this._offset, (this._offset += size))
|
||||
}
|
||||
}
|
||||
|
||||
class Source extends Readable {
|
||||
constructor (self, header, offset) {
|
||||
super()
|
||||
|
||||
this.header = header
|
||||
this.offset = offset
|
||||
|
||||
this._parent = self
|
||||
}
|
||||
|
||||
_read (cb) {
|
||||
if (this.header.size === 0) {
|
||||
this.push(null)
|
||||
}
|
||||
if (this._parent._stream === this) {
|
||||
this._parent._update()
|
||||
}
|
||||
cb(null)
|
||||
}
|
||||
|
||||
_predestroy () {
|
||||
this._parent.destroy(getStreamError(this))
|
||||
}
|
||||
|
||||
_detach () {
|
||||
if (this._parent._stream === this) {
|
||||
this._parent._stream = null
|
||||
this._parent._missing = overflow(this.header.size)
|
||||
this._parent._update()
|
||||
}
|
||||
}
|
||||
|
||||
_destroy (cb) {
|
||||
this._detach()
|
||||
cb(null)
|
||||
}
|
||||
}
|
||||
|
||||
class Extract extends Writable {
|
||||
constructor (opts) {
|
||||
super(opts)
|
||||
|
||||
if (!opts) opts = {}
|
||||
|
||||
this._buffer = new BufferList()
|
||||
this._offset = 0
|
||||
this._header = null
|
||||
this._stream = null
|
||||
this._missing = 0
|
||||
this._longHeader = false
|
||||
this._callback = noop
|
||||
this._locked = false
|
||||
this._finished = false
|
||||
this._pax = null
|
||||
this._paxGlobal = null
|
||||
this._gnuLongPath = null
|
||||
this._gnuLongLinkPath = null
|
||||
this._filenameEncoding = opts.filenameEncoding || 'utf-8'
|
||||
this._allowUnknownFormat = !!opts.allowUnknownFormat
|
||||
this._unlockBound = this._unlock.bind(this)
|
||||
}
|
||||
|
||||
_unlock (err) {
|
||||
this._locked = false
|
||||
|
||||
if (err) {
|
||||
this.destroy(err)
|
||||
this._continueWrite(err)
|
||||
return
|
||||
}
|
||||
|
||||
this._update()
|
||||
}
|
||||
|
||||
_consumeHeader () {
|
||||
if (this._locked) return false
|
||||
|
||||
this._offset = this._buffer.shifted
|
||||
|
||||
try {
|
||||
this._header = headers.decode(this._buffer.shift(512), this._filenameEncoding, this._allowUnknownFormat)
|
||||
} catch (err) {
|
||||
this._continueWrite(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this._header) return true
|
||||
|
||||
switch (this._header.type) {
|
||||
case 'gnu-long-path':
|
||||
case 'gnu-long-link-path':
|
||||
case 'pax-global-header':
|
||||
case 'pax-header':
|
||||
this._longHeader = true
|
||||
this._missing = this._header.size
|
||||
return true
|
||||
}
|
||||
|
||||
this._locked = true
|
||||
this._applyLongHeaders()
|
||||
|
||||
if (this._header.size === 0 || this._header.type === 'directory') {
|
||||
this.emit('entry', this._header, this._createStream(), this._unlockBound)
|
||||
return true
|
||||
}
|
||||
|
||||
this._stream = this._createStream()
|
||||
this._missing = this._header.size
|
||||
|
||||
this.emit('entry', this._header, this._stream, this._unlockBound)
|
||||
return true
|
||||
}
|
||||
|
||||
_applyLongHeaders () {
|
||||
if (this._gnuLongPath) {
|
||||
this._header.name = this._gnuLongPath
|
||||
this._gnuLongPath = null
|
||||
}
|
||||
|
||||
if (this._gnuLongLinkPath) {
|
||||
this._header.linkname = this._gnuLongLinkPath
|
||||
this._gnuLongLinkPath = null
|
||||
}
|
||||
|
||||
if (this._pax) {
|
||||
if (this._pax.path) this._header.name = this._pax.path
|
||||
if (this._pax.linkpath) this._header.linkname = this._pax.linkpath
|
||||
if (this._pax.size) this._header.size = parseInt(this._pax.size, 10)
|
||||
this._header.pax = this._pax
|
||||
this._pax = null
|
||||
}
|
||||
}
|
||||
|
||||
_decodeLongHeader (buf) {
|
||||
switch (this._header.type) {
|
||||
case 'gnu-long-path':
|
||||
this._gnuLongPath = headers.decodeLongPath(buf, this._filenameEncoding)
|
||||
break
|
||||
case 'gnu-long-link-path':
|
||||
this._gnuLongLinkPath = headers.decodeLongPath(buf, this._filenameEncoding)
|
||||
break
|
||||
case 'pax-global-header':
|
||||
this._paxGlobal = headers.decodePax(buf)
|
||||
break
|
||||
case 'pax-header':
|
||||
this._pax = this._paxGlobal === null
|
||||
? headers.decodePax(buf)
|
||||
: Object.assign({}, this._paxGlobal, headers.decodePax(buf))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_consumeLongHeader () {
|
||||
this._longHeader = false
|
||||
this._missing = overflow(this._header.size)
|
||||
|
||||
const buf = this._buffer.shift(this._header.size)
|
||||
|
||||
try {
|
||||
this._decodeLongHeader(buf)
|
||||
} catch (err) {
|
||||
this._continueWrite(err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
_consumeStream () {
|
||||
const buf = this._buffer.shiftFirst(this._missing)
|
||||
if (buf === null) return false
|
||||
|
||||
this._missing -= buf.byteLength
|
||||
const drained = this._stream.push(buf)
|
||||
|
||||
if (this._missing === 0) {
|
||||
this._stream.push(null)
|
||||
if (drained) this._stream._detach()
|
||||
return drained && this._locked === false
|
||||
}
|
||||
|
||||
return drained
|
||||
}
|
||||
|
||||
_createStream () {
|
||||
return new Source(this, this._header, this._offset)
|
||||
}
|
||||
|
||||
_update () {
|
||||
while (this._buffer.buffered > 0 && !this.destroying) {
|
||||
if (this._missing > 0) {
|
||||
if (this._stream !== null) {
|
||||
if (this._consumeStream() === false) return
|
||||
continue
|
||||
}
|
||||
|
||||
if (this._longHeader === true) {
|
||||
if (this._missing > this._buffer.buffered) break
|
||||
if (this._consumeLongHeader() === false) return false
|
||||
continue
|
||||
}
|
||||
|
||||
const ignore = this._buffer.shiftFirst(this._missing)
|
||||
if (ignore !== null) this._missing -= ignore.byteLength
|
||||
continue
|
||||
}
|
||||
|
||||
if (this._buffer.buffered < 512) break
|
||||
if (this._stream !== null || this._consumeHeader() === false) return
|
||||
}
|
||||
|
||||
this._continueWrite(null)
|
||||
}
|
||||
|
||||
_continueWrite (err) {
|
||||
const cb = this._callback
|
||||
this._callback = noop
|
||||
cb(err)
|
||||
}
|
||||
|
||||
_write (data, cb) {
|
||||
this._callback = cb
|
||||
this._buffer.push(data)
|
||||
this._update()
|
||||
}
|
||||
|
||||
_final (cb) {
|
||||
this._finished = this._missing === 0 && this._buffer.buffered === 0
|
||||
cb(this._finished ? null : new Error('Unexpected end of data'))
|
||||
}
|
||||
|
||||
_predestroy () {
|
||||
this._continueWrite(null)
|
||||
}
|
||||
|
||||
_destroy (cb) {
|
||||
if (this._stream) this._stream.destroy(getStreamError(this))
|
||||
cb(null)
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator] () {
|
||||
let error = null
|
||||
|
||||
let promiseResolve = null
|
||||
let promiseReject = null
|
||||
|
||||
let entryStream = null
|
||||
let entryCallback = null
|
||||
|
||||
const extract = this
|
||||
|
||||
this.on('entry', onentry)
|
||||
this.on('error', (err) => { error = err })
|
||||
this.on('close', onclose)
|
||||
|
||||
return {
|
||||
[Symbol.asyncIterator] () {
|
||||
return this
|
||||
},
|
||||
next () {
|
||||
return new Promise(onnext)
|
||||
},
|
||||
return () {
|
||||
return destroy(null)
|
||||
},
|
||||
throw (err) {
|
||||
return destroy(err)
|
||||
}
|
||||
}
|
||||
|
||||
function consumeCallback (err) {
|
||||
if (!entryCallback) return
|
||||
const cb = entryCallback
|
||||
entryCallback = null
|
||||
cb(err)
|
||||
}
|
||||
|
||||
function onnext (resolve, reject) {
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
|
||||
if (entryStream) {
|
||||
resolve({ value: entryStream, done: false })
|
||||
entryStream = null
|
||||
return
|
||||
}
|
||||
|
||||
promiseResolve = resolve
|
||||
promiseReject = reject
|
||||
|
||||
consumeCallback(null)
|
||||
|
||||
if (extract._finished && promiseResolve) {
|
||||
promiseResolve({ value: undefined, done: true })
|
||||
promiseResolve = promiseReject = null
|
||||
}
|
||||
}
|
||||
|
||||
function onentry (header, stream, callback) {
|
||||
entryCallback = callback
|
||||
stream.on('error', noop) // no way around this due to tick sillyness
|
||||
|
||||
if (promiseResolve) {
|
||||
promiseResolve({ value: stream, done: false })
|
||||
promiseResolve = promiseReject = null
|
||||
} else {
|
||||
entryStream = stream
|
||||
}
|
||||
}
|
||||
|
||||
function onclose () {
|
||||
consumeCallback(error)
|
||||
if (!promiseResolve) return
|
||||
if (error) promiseReject(error)
|
||||
else promiseResolve({ value: undefined, done: true })
|
||||
promiseResolve = promiseReject = null
|
||||
}
|
||||
|
||||
function destroy (err) {
|
||||
extract.destroy(err)
|
||||
consumeCallback(err)
|
||||
return new Promise((resolve, reject) => {
|
||||
if (extract.destroyed) return resolve({ value: undefined, done: true })
|
||||
extract.once('close', function () {
|
||||
if (err) reject(err)
|
||||
else resolve({ value: undefined, done: true })
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function extract (opts) {
|
||||
return new Extract(opts)
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
||||
function overflow (size) {
|
||||
size &= 511
|
||||
return size && 512 - size
|
||||
}
|
||||
Reference in New Issue
Block a user