snappy-stream
Version:
Compress data over a Stream using the snappy framing format
97 lines (77 loc) • 2.52 kB
JavaScript
var Transform = require('stream').Transform
, util = require('util')
, bufferEqual = require('buffer-equal')
, bufferFrom = require('buffer-from')
, BufferList = require('bl')
, snappy = require('snappy')
, IDENTIFIER = bufferFrom([
0x73, 0x4e, 0x61, 0x50, 0x70, 0x59
])
, frameSize = function (buffer, offset) {
return buffer.get(offset) + (buffer.get(offset + 1) << 8) + (buffer.get(offset + 2) << 16)
}
, getType = function (value) {
if (value === 0xff)
return 'identifier'
if (value === 0x00)
return 'compressed'
if (value === 0x01)
return 'uncompressed'
if (value === 0xfe)
return 'padding'
// TODO: Handle the other cases described in the spec
}
, UncompressStream = function (opts) {
var asBuffer = (opts && typeof(opts.asBuffer) === 'boolean') ? opts.asBuffer : true
Transform.call(this, { objectMode: !asBuffer })
this.asBuffer = asBuffer
this.foundIdentifier = false
this.buffer = new BufferList()
}
util.inherits(UncompressStream, Transform)
UncompressStream.prototype._parse = function (callback) {
if (this.buffer.length < 4)
return callback()
var self = this
, size = frameSize(this.buffer, 1)
, type = getType(this.buffer.get(0))
, data = this.buffer.slice(4, 4 + size)
if (this.buffer.length - 4 < size)
return callback()
this.buffer.consume(4 + size)
if (!this.foundIdentifier && type !== 'identifier')
return callback(new Error('malformed input: must begin with an identifier'))
if (type === 'identifier') {
if(!bufferEqual(data, IDENTIFIER))
return callback(new Error('malformed input: bad identifier'))
this.foundIdentifier = true
return this._parse(callback)
}
if (type === 'compressed') {
// TODO: check that the checksum matches
snappy.uncompress(data.slice(4), { asBuffer: this.asBuffer }, function (err, raw) {
if(err) {
return callback(err)
}
self.push(raw)
self._parse(callback)
})
return
}
if (type === 'uncompressed') {
// TODO: check that the checksum matches
data = data.slice(4)
if (!this.asBuffer)
data = data.toString()
this.push(data)
this._parse(callback)
}
if (type === 'padding') {
return this._parse(callback)
}
}
UncompressStream.prototype._transform = function (chunk, enc, callback) {
this.buffer.append(chunk)
this._parse(callback)
}
module.exports = UncompressStream