UNPKG

diffusion

Version:

Diffusion JavaScript client

274 lines (231 loc) 7.54 kB
var BufferOutputStream = require('io/buffer-output-stream'); var Int64Impl = require('data/primitive/int64-impl'); var consts = require('cbor/consts'); var additional = consts.additional, types = consts.types; function Encoder(initial) { var bos = initial || new BufferOutputStream(); var self = this; function writeByte(b) { bos.write(b); } function writeBuffer(buf, offset, length) { offset = offset || 0; length = length === undefined ? buf.length : length; bos.writeMany(buf, offset, length); } function writeUint16(v) { writeByte((v >> 8) & 0xff); writeByte(v & 0xff); } function writeUint32(v) { writeUint16((v >> 16) & 0xffff); writeUint16(v & 0xffff); } function writeUint64(v) { writeUint32(Math.floor(v / 4294967296)); writeUint32(v % 4294967296); } function writeBreakHeader(type) { writeByte(type << 5 | additional.BREAK); } function writeToken(type, val) { var first = type << 5; if (val < 24) { writeByte(first | val); } else if (val < 256) { writeByte(first | 24); writeByte(val); } else if (val < 65536) { writeByte(first | 25); writeUint16(val); } else if (val < 4294967296) { writeByte(first | 26); writeUint32(val); } else { writeByte(first | 27); writeUint64(val); } } function writeNumber(val) { // Determine if the number is int or not if (val.toString().indexOf(".") > -1) { // Because Numbers are 53 bits, always write non-integers as doubles // Can't bit-twiddle to figure out how to encode, as bitwise operations // will coerce to a 32-bit integer under the hood. var buf = new Buffer(8); buf.writeDoubleBE(val, 0); writeByte((types.SIMPLE << 5) | additional.DOUBLE_PRECISION); writeBuffer(buf); } else { if (val < 0) { writeToken(types.INT, -1 - val); } else { writeToken(types.UINT, val); } } } function writeString(val) { var buf = new Buffer(val, 'utf-8'); writeToken(types.STRING, buf.length); writeBuffer(buf); } function writeBinary(val, offset, length) { length = length === undefined ? val.length : length; writeToken(types.BYTES, length); writeBuffer(val, offset, length); } function writeBoolean(val) { if (val) { writeToken(types.SIMPLE, additional.TRUE); } else { writeToken(types.SIMPLE, additional.FALSE); } } function writeUndefined() { writeToken(types.SIMPLE, additional.UNDEFINED); } function writeNull() { writeToken(types.SIMPLE, additional.NULL); } function writeObject(val) { var keys = Object.keys(val); self.startObject(keys.length); for (var i = 0; i < keys.length; ++i) { self.encode(keys[i]); self.encode(val[keys[i]]); } } function writeArray(val) { self.startArray(val.length); for (var i = 0; i < val.length; ++i) { self.encode(val[i]); } } /** * Write a BREAK stop code. This is not constrained by any pre-written strucutural context. * * @returns {Encoder} This encoder */ this.break = function () { writeBreakHeader(types.SIMPLE); return self; }; /** * Write the header for an array. * <P> * Data may be subsequently written. No constraints are applied to subsequent data, i.e the header for a * fixed-length array may be immediately followed by a break flag * * @param {Number} [length] - The length of the array. If not provided, this array will be considered * indefinite-length * * @returns {Encoder} This encoder */ this.startArray = function (length) { if (length === undefined) { writeBreakHeader(types.ARRAY); } else { writeToken(types.ARRAY, length); } return self; }; /** * Write the header for an object. * <P> * Data may be subsequently written. No constraints are applied to subsequent data, i.e the header for a * fixed-length object may be immediately followed by a break flag * * @param {Number} [length] - The number of keys in the object. If not provided, this object will be considered * indefinite-length * * @returns {Encoder} This encoder */ this.startObject = function (length) { if (length === undefined) { writeBreakHeader(types.MAP); } else { writeToken(types.MAP, length); } return self; }; /** * Write an {@link Int64Impl} value. * * @param {Int64Impl} val - the int64 value to encode * @returns {Encoder} this encoder */ this.writeInt64 = function(val) { if (val.isNegative()) { writeByte((types.INT << 5) | 27); } else { writeByte((types.UINT << 5) | 27); } writeBuffer(val.toBuffer()); return self; }; /** Write a {@link Double} value. * * @param {Double} val - the double value to encode * @return {Encoder} this encoder */ this.writeDouble = function(val) { var buf = new Buffer(8); buf.writeDoubleBE(val, 0); writeByte((types.SIMPLE << 5) | additional.DOUBLE_PRECISION); writeBuffer(buf); return self; }; /** * Encode a value to a CBOR representation. * <P> * If a buffer is provided, optional offset & length parameters may be provided to * dictate which bytes are to be copied. * * @param {*} value - The value to encode * @param {Number} [offset=0] - The offset to use if encoding a buffer * @param {Number} [length=value.length] - The length to use if encoding a buffer * * @returns {Encoder} This encoder */ this.encode = function (value, offset, length) { if (value instanceof Date) { writeString(value.toJSON()); } else if (value === null) { writeNull(); } else if (value === undefined) { writeUndefined(); } else if (value === true) { writeBoolean(true); } else if (value === false) { writeBoolean(false); } else if (Int64Impl.isPrototypeOf(value)) { self.writeInt64(value); } else { switch (typeof value) { case 'string' : writeString(value); break; case 'number' : writeNumber(value); break; case 'object' : if (Buffer.isBuffer(value)) { writeBinary(value, offset, length); } else if (Array.isArray(value)) { writeArray(value); } else { writeObject(value); } } } return self; }; this.flush = function () { var res = bos.getBuffer(); bos = new BufferOutputStream(); return res; }; } module.exports = Encoder;