diff options
Diffstat (limited to 'node_modules/bser')
| -rw-r--r-- | node_modules/bser/README.md | 81 | ||||
| -rw-r--r-- | node_modules/bser/index.js | 586 | ||||
| -rw-r--r-- | node_modules/bser/package.json | 33 | 
3 files changed, 700 insertions, 0 deletions
diff --git a/node_modules/bser/README.md b/node_modules/bser/README.md new file mode 100644 index 0000000..eab0658 --- /dev/null +++ b/node_modules/bser/README.md @@ -0,0 +1,81 @@ +# BSER Binary Serialization + +BSER is a binary serialization scheme that can be used as an alternative to JSON. +BSER uses a framed encoding that makes it simpler to use to stream a sequence of +encoded values. + +It is intended to be used for local-IPC only and strings are represented as binary +with no specific encoding; this matches the convention employed by most operating +system filename storage. + +For more details about the serialization scheme see +[Watchman's docs](https://facebook.github.io/watchman/docs/bser.html). + +## API + +```js +var bser = require('bser'); +``` + +### bser.loadFromBuffer + +The is the synchronous decoder; given an input string or buffer, +decodes a single value and returns it.  Throws an error if the +input is invalid. + +```js +var obj = bser.loadFromBuffer(buf); +``` + +### bser.dumpToBuffer + +Synchronously encodes a value as BSER. + +```js +var encoded = bser.dumpToBuffer(['hello']); +console.log(bser.loadFromBuffer(encoded)); // ['hello'] +``` + +### BunserBuf + +The asynchronous decoder API is implemented in the BunserBuf object. +You may incrementally append data to this object and it will emit the +decoded values via its `value` event. + +```js +var bunser = new bser.BunserBuf(); + +bunser.on('value', function(obj) { +  console.log(obj); +}); +``` + +Then in your socket `data` event: + +```js +bunser.append(buf); +``` + +## Example + +Read BSER from socket: + +```js +var bunser = new bser.BunserBuf(); + +bunser.on('value', function(obj) { +  console.log('data from socket', obj); +}); + +var socket = net.connect('/socket'); + +socket.on('data', function(buf) { +  bunser.append(buf); +}); +``` + +Write BSER to socket: + +```js +socket.write(bser.dumpToBuffer(obj)); +``` diff --git a/node_modules/bser/index.js b/node_modules/bser/index.js new file mode 100644 index 0000000..4ae14d3 --- /dev/null +++ b/node_modules/bser/index.js @@ -0,0 +1,586 @@ +/* Copyright 2015-present Facebook, Inc. + * Licensed under the Apache License, Version 2.0 */ + +var EE = require('events').EventEmitter; +var util = require('util'); +var os = require('os'); +var assert = require('assert'); +var Int64 = require('node-int64'); + +// BSER uses the local endianness to reduce byte swapping overheads +// (the protocol is expressly local IPC only).  We need to tell node +// to use the native endianness when reading various native values. +var isBigEndian = os.endianness() == 'BE'; + +// Find the next power-of-2 >= size +function nextPow2(size) { +  return Math.pow(2, Math.ceil(Math.log(size) / Math.LN2)); +} + +// Expandable buffer that we can provide a size hint for +function Accumulator(initsize) { +  this.buf = Buffer.alloc(nextPow2(initsize || 8192)); +  this.readOffset = 0; +  this.writeOffset = 0; +} +// For testing +exports.Accumulator = Accumulator + +// How much we can write into this buffer without allocating +Accumulator.prototype.writeAvail = function() { +  return this.buf.length - this.writeOffset; +} + +// How much we can read +Accumulator.prototype.readAvail = function() { +  return this.writeOffset - this.readOffset; +} + +// Ensure that we have enough space for size bytes +Accumulator.prototype.reserve = function(size) { +  if (size < this.writeAvail()) { +    return; +  } + +  // If we can make room by shunting down, do so +  if (this.readOffset > 0) { +    this.buf.copy(this.buf, 0, this.readOffset, this.writeOffset); +    this.writeOffset -= this.readOffset; +    this.readOffset = 0; +  } + +  // If we made enough room, no need to allocate more +  if (size < this.writeAvail()) { +    return; +  } + +  // Allocate a replacement and copy it in +  var buf = Buffer.alloc(nextPow2(this.buf.length + size - this.writeAvail())); +  this.buf.copy(buf); +  this.buf = buf; +} + +// Append buffer or string.  Will resize as needed +Accumulator.prototype.append = function(buf) { +  if (Buffer.isBuffer(buf)) { +    this.reserve(buf.length); +    buf.copy(this.buf, this.writeOffset, 0, buf.length); +    this.writeOffset += buf.length; +  } else { +    var size = Buffer.byteLength(buf); +    this.reserve(size); +    this.buf.write(buf, this.writeOffset); +    this.writeOffset += size; +  } +} + +Accumulator.prototype.assertReadableSize = function(size) { +  if (this.readAvail() < size) { +    throw new Error("wanted to read " + size + +        " bytes but only have " + this.readAvail()); +  } +} + +Accumulator.prototype.peekString = function(size) { +  this.assertReadableSize(size); +  return this.buf.toString('utf-8', this.readOffset, this.readOffset + size); +} + +Accumulator.prototype.readString = function(size) { +  var str = this.peekString(size); +  this.readOffset += size; +  return str; +} + +Accumulator.prototype.peekInt = function(size) { +  this.assertReadableSize(size); +  switch (size) { +    case 1: +      return this.buf.readInt8(this.readOffset, size); +    case 2: +      return isBigEndian ? +        this.buf.readInt16BE(this.readOffset, size) : +        this.buf.readInt16LE(this.readOffset, size); +    case 4: +      return isBigEndian ? +        this.buf.readInt32BE(this.readOffset, size) : +        this.buf.readInt32LE(this.readOffset, size); +    case 8: +        var big = this.buf.slice(this.readOffset, this.readOffset + 8); +        if (isBigEndian) { +          // On a big endian system we can simply pass the buffer directly +          return new Int64(big); +        } +        // Otherwise we need to byteswap +        return new Int64(byteswap64(big)); +    default: +      throw new Error("invalid integer size " + size); +  } +} + +Accumulator.prototype.readInt = function(bytes) { +  var ival = this.peekInt(bytes); +  if (ival instanceof Int64 && isFinite(ival.valueOf())) { +    ival = ival.valueOf(); +  } +  this.readOffset += bytes; +  return ival; +} + +Accumulator.prototype.peekDouble = function() { +  this.assertReadableSize(8); +  return isBigEndian ? +    this.buf.readDoubleBE(this.readOffset) : +    this.buf.readDoubleLE(this.readOffset); +} + +Accumulator.prototype.readDouble = function() { +  var dval = this.peekDouble(); +  this.readOffset += 8; +  return dval; +} + +Accumulator.prototype.readAdvance = function(size) { +  if (size > 0) { +    this.assertReadableSize(size); +  } else if (size < 0 && this.readOffset + size < 0) { +    throw new Error("advance with negative offset " + size + +        " would seek off the start of the buffer"); +  } +  this.readOffset += size; +} + +Accumulator.prototype.writeByte = function(value) { +  this.reserve(1); +  this.buf.writeInt8(value, this.writeOffset); +  ++this.writeOffset; +} + +Accumulator.prototype.writeInt = function(value, size) { +  this.reserve(size); +  switch (size) { +    case 1: +      this.buf.writeInt8(value, this.writeOffset); +      break; +    case 2: +      if (isBigEndian) { +        this.buf.writeInt16BE(value, this.writeOffset); +      } else { +        this.buf.writeInt16LE(value, this.writeOffset); +      } +      break; +    case 4: +      if (isBigEndian) { +        this.buf.writeInt32BE(value, this.writeOffset); +      } else { +        this.buf.writeInt32LE(value, this.writeOffset); +      } +      break; +    default: +      throw new Error("unsupported integer size " + size); +  } +  this.writeOffset += size; +} + +Accumulator.prototype.writeDouble = function(value) { +  this.reserve(8); +  if (isBigEndian) { +    this.buf.writeDoubleBE(value, this.writeOffset); +  } else { +    this.buf.writeDoubleLE(value, this.writeOffset); +  } +  this.writeOffset += 8; +} + +var BSER_ARRAY     = 0x00; +var BSER_OBJECT    = 0x01; +var BSER_STRING    = 0x02; +var BSER_INT8      = 0x03; +var BSER_INT16     = 0x04; +var BSER_INT32     = 0x05; +var BSER_INT64     = 0x06; +var BSER_REAL      = 0x07; +var BSER_TRUE      = 0x08; +var BSER_FALSE     = 0x09; +var BSER_NULL      = 0x0a; +var BSER_TEMPLATE  = 0x0b; +var BSER_SKIP      = 0x0c; + +var ST_NEED_PDU = 0; // Need to read and decode PDU length +var ST_FILL_PDU = 1; // Know the length, need to read whole content + +var MAX_INT8 = 127; +var MAX_INT16 = 32767; +var MAX_INT32 = 2147483647; + +function BunserBuf() { +  EE.call(this); +  this.buf = new Accumulator(); +  this.state = ST_NEED_PDU; +} +util.inherits(BunserBuf, EE); +exports.BunserBuf = BunserBuf; + +BunserBuf.prototype.append = function(buf, synchronous) { +  if (synchronous) { +    this.buf.append(buf); +    return this.process(synchronous); +  } + +  try { +    this.buf.append(buf); +  } catch (err) { +    this.emit('error', err); +    return; +  } +  // Arrange to decode later.  This allows the consuming +  // application to make progress with other work in the +  // case that we have a lot of subscription updates coming +  // in from a large tree. +  this.processLater(); +} + +BunserBuf.prototype.processLater = function() { +  var self = this; +  process.nextTick(function() { +    try { +      self.process(false); +    } catch (err) { +      self.emit('error', err); +    } +  }); +} + +// Do something with the buffer to advance our state. +// If we're running synchronously we'll return either +// the value we've decoded or undefined if we don't +// yet have enought data. +// If we're running asynchronously, we'll emit the value +// when it becomes ready and schedule another invocation +// of process on the next tick if we still have data we +// can process. +BunserBuf.prototype.process = function(synchronous) { +  if (this.state == ST_NEED_PDU) { +    if (this.buf.readAvail() < 2) { +      return; +    } +    // Validate BSER header +    this.expectCode(0); +    this.expectCode(1); +    this.pduLen = this.decodeInt(true /* relaxed */); +    if (this.pduLen === false) { +      // Need more data, walk backwards +      this.buf.readAdvance(-2); +      return; +    } +    // Ensure that we have a big enough buffer to read the rest of the PDU +    this.buf.reserve(this.pduLen); +    this.state = ST_FILL_PDU; +  } + +  if (this.state == ST_FILL_PDU) { +    if (this.buf.readAvail() < this.pduLen) { +      // Need more data +      return; +    } + +    // We have enough to decode it +    var val = this.decodeAny(); +    if (synchronous) { +      return val; +    } +    this.emit('value', val); +    this.state = ST_NEED_PDU; +  } + +  if (!synchronous && this.buf.readAvail() > 0) { +    this.processLater(); +  } +} + +BunserBuf.prototype.raise = function(reason) { +  throw new Error(reason + ", in Buffer of length " + +      this.buf.buf.length + " (" + this.buf.readAvail() + +      " readable) at offset " + this.buf.readOffset + " buffer: " + +      JSON.stringify(this.buf.buf.slice( +          this.buf.readOffset, this.buf.readOffset + 32).toJSON())); +} + +BunserBuf.prototype.expectCode = function(expected) { +  var code = this.buf.readInt(1); +  if (code != expected) { +    this.raise("expected bser opcode " + expected + " but got " + code); +  } +} + +BunserBuf.prototype.decodeAny = function() { +  var code = this.buf.peekInt(1); +  switch (code) { +    case BSER_INT8: +    case BSER_INT16: +    case BSER_INT32: +    case BSER_INT64: +      return this.decodeInt(); +    case BSER_REAL: +      this.buf.readAdvance(1); +      return this.buf.readDouble(); +    case BSER_TRUE: +      this.buf.readAdvance(1); +      return true; +    case BSER_FALSE: +      this.buf.readAdvance(1); +      return false; +    case BSER_NULL: +      this.buf.readAdvance(1); +      return null; +    case BSER_STRING: +      return this.decodeString(); +    case BSER_ARRAY: +      return this.decodeArray(); +    case BSER_OBJECT: +      return this.decodeObject(); +    case BSER_TEMPLATE: +      return this.decodeTemplate(); +    default: +      this.raise("unhandled bser opcode " + code); +  } +} + +BunserBuf.prototype.decodeArray = function() { +  this.expectCode(BSER_ARRAY); +  var nitems = this.decodeInt(); +  var arr = []; +  for (var i = 0; i < nitems; ++i) { +    arr.push(this.decodeAny()); +  } +  return arr; +} + +BunserBuf.prototype.decodeObject = function() { +  this.expectCode(BSER_OBJECT); +  var nitems = this.decodeInt(); +  var res = {}; +  for (var i = 0; i < nitems; ++i) { +    var key = this.decodeString(); +    var val = this.decodeAny(); +    res[key] = val; +  } +  return res; +} + +BunserBuf.prototype.decodeTemplate = function() { +  this.expectCode(BSER_TEMPLATE); +  var keys = this.decodeArray(); +  var nitems = this.decodeInt(); +  var arr = []; +  for (var i = 0; i < nitems; ++i) { +    var obj = {}; +    for (var keyidx = 0; keyidx < keys.length; ++keyidx) { +      if (this.buf.peekInt(1) == BSER_SKIP) { +        this.buf.readAdvance(1); +        continue; +      } +      var val = this.decodeAny(); +      obj[keys[keyidx]] = val; +    } +    arr.push(obj); +  } +  return arr; +} + +BunserBuf.prototype.decodeString = function() { +  this.expectCode(BSER_STRING); +  var len = this.decodeInt(); +  return this.buf.readString(len); +} + +// This is unusual compared to the other decode functions in that +// we may not have enough data available to satisfy the read, and +// we don't want to throw.  This is only true when we're reading +// the PDU length from the PDU header; we'll set relaxSizeAsserts +// in that case. +BunserBuf.prototype.decodeInt = function(relaxSizeAsserts) { +  if (relaxSizeAsserts && (this.buf.readAvail() < 1)) { +    return false; +  } else { +    this.buf.assertReadableSize(1); +  } +  var code = this.buf.peekInt(1); +  var size = 0; +  switch (code) { +    case BSER_INT8: +      size = 1; +      break; +    case BSER_INT16: +      size = 2; +      break; +    case BSER_INT32: +      size = 4; +      break; +    case BSER_INT64: +      size = 8; +      break; +    default: +      this.raise("invalid bser int encoding " + code); +  } + +  if (relaxSizeAsserts && (this.buf.readAvail() < 1 + size)) { +    return false; +  } +  this.buf.readAdvance(1); +  return this.buf.readInt(size); +} + +// synchronously BSER decode a string and return the value +function loadFromBuffer(input) { +  var buf = new BunserBuf(); +  var result = buf.append(input, true); +  if (buf.buf.readAvail()) { +    throw Error( +        'excess data found after input buffer, use BunserBuf instead'); +  } +  if (typeof result === 'undefined') { +    throw Error( +        'no bser found in string and no error raised!?'); +  } +  return result; +} +exports.loadFromBuffer = loadFromBuffer + +// Byteswap an arbitrary buffer, flipping from one endian +// to the other, returning a new buffer with the resultant data +function byteswap64(buf) { +  var swap = Buffer.alloc(buf.length); +  for (var i = 0; i < buf.length; i++) { +    swap[i] = buf[buf.length -1 - i]; +  } +  return swap; +} + +function dump_int64(buf, val) { +  // Get the raw bytes.  The Int64 buffer is big endian +  var be = val.toBuffer(); + +  if (isBigEndian) { +    // We're a big endian system, so the buffer is exactly how we +    // want it to be +    buf.writeByte(BSER_INT64); +    buf.append(be); +    return; +  } +  // We need to byte swap to get the correct representation +  var le = byteswap64(be); +  buf.writeByte(BSER_INT64); +  buf.append(le); +} + +function dump_int(buf, val) { +  var abs = Math.abs(val); +  if (abs <= MAX_INT8) { +    buf.writeByte(BSER_INT8); +    buf.writeInt(val, 1); +  } else if (abs <= MAX_INT16) { +    buf.writeByte(BSER_INT16); +    buf.writeInt(val, 2); +  } else if (abs <= MAX_INT32) { +    buf.writeByte(BSER_INT32); +    buf.writeInt(val, 4); +  } else { +    dump_int64(buf, new Int64(val)); +  } +} + +function dump_any(buf, val) { +  switch (typeof(val)) { +    case 'number': +      // check if it is an integer or a float +      if (isFinite(val) && Math.floor(val) === val) { +        dump_int(buf, val); +      } else { +        buf.writeByte(BSER_REAL); +        buf.writeDouble(val); +      } +      return; +    case 'string': +      buf.writeByte(BSER_STRING); +      dump_int(buf, Buffer.byteLength(val)); +      buf.append(val); +      return; +    case 'boolean': +      buf.writeByte(val ? BSER_TRUE : BSER_FALSE); +      return; +    case 'object': +      if (val === null) { +        buf.writeByte(BSER_NULL); +        return; +      } +      if (val instanceof Int64) { +        dump_int64(buf, val); +        return; +      } +      if (Array.isArray(val)) { +        buf.writeByte(BSER_ARRAY); +        dump_int(buf, val.length); +        for (var i = 0; i < val.length; ++i) { +          dump_any(buf, val[i]); +        } +        return; +      } +      buf.writeByte(BSER_OBJECT); +      var keys = Object.keys(val); + +      // First pass to compute number of defined keys +      var num_keys = keys.length; +      for (var i = 0; i < keys.length; ++i) { +        var key = keys[i]; +        var v = val[key]; +        if (typeof(v) == 'undefined') { +          num_keys--; +        } +      } +      dump_int(buf, num_keys); +      for (var i = 0; i < keys.length; ++i) { +        var key = keys[i]; +        var v = val[key]; +        if (typeof(v) == 'undefined') { +          // Don't include it +          continue; +        } +        dump_any(buf, key); +        try { +          dump_any(buf, v); +        } catch (e) { +          throw new Error( +            e.message + ' (while serializing object property with name `' + +              key + "')"); +        } +      } +      return; + +    default: +      throw new Error('cannot serialize type ' + typeof(val) + ' to BSER'); +  } +} + +// BSER encode value and return a buffer of the contents +function dumpToBuffer(val) { +  var buf = new Accumulator(); +  // Build out the header +  buf.writeByte(0); +  buf.writeByte(1); +  // Reserve room for an int32 to hold our PDU length +  buf.writeByte(BSER_INT32); +  buf.writeInt(0, 4); // We'll come back and fill this in at the end + +  dump_any(buf, val); + +  // Compute PDU length +  var off = buf.writeOffset; +  var len = off - 7 /* the header length */; +  buf.writeOffset = 3; // The length value to fill in +  buf.writeInt(len, 4); // write the length in the space we reserved +  buf.writeOffset = off; + +  return buf.buf.slice(0, off); +} +exports.dumpToBuffer = dumpToBuffer diff --git a/node_modules/bser/package.json b/node_modules/bser/package.json new file mode 100644 index 0000000..824f7d4 --- /dev/null +++ b/node_modules/bser/package.json @@ -0,0 +1,33 @@ +{ +  "name": "bser", +  "version": "2.1.1", +  "description": "JavaScript implementation of the BSER Binary Serialization", +  "main": "index.js", +  "directories": { +    "test": "test" +  }, +  "scripts": { +    "test": "node test/bser.js" +  }, +  "files": [ +    "index.js" +  ], +  "repository": { +    "type": "git", +    "url": "https://github.com/facebook/watchman" +  }, +  "keywords": [ +    "bser", +    "binary", +    "protocol" +  ], +  "author": "Wez Furlong <wez@fb.com> (http://wezfurlong.org)", +  "license": "Apache-2.0", +  "bugs": { +    "url": "https://github.com/facebook/watchman/issues" +  }, +  "homepage": "https://facebook.github.io/watchman/docs/bser.html", +  "dependencies": { +    "node-int64": "^0.4.0" +  } +}  | 
