Add BinaryPacker from kv-redis connector
Add a helper for encoding JavaScript values into binary Buffers. The implemenetation is based on msgpack5 format and preserves JavaScript objects like Buffers and Dates, as opposed to (binary)JSON.
This commit is contained in:
parent
2cbc1143c1
commit
6fd3ac7285
1
index.js
1
index.js
|
@ -16,3 +16,4 @@ exports.createPromiseCallback = require('./lib/utils').createPromiseCallback;
|
|||
|
||||
// KeyValue helpers
|
||||
exports.ModelKeyComposer = require('./lib/model-key-composer');
|
||||
exports.BinaryPacker = require('./lib/binary-packer');
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Node module: loopback-connector
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var createPromiseCallback = require('./utils').createPromiseCallback;
|
||||
var msgpack = require('msgpack5');
|
||||
|
||||
module.exports = BinaryPacker;
|
||||
|
||||
/**
|
||||
* Create a new Packer instance that can be used to convert between JavaScript
|
||||
* objects and a binary representation in a Buffer.
|
||||
*
|
||||
* Compared to JSON, this encoding preserves the following JavaScript types:
|
||||
* - Date
|
||||
*/
|
||||
function BinaryPacker() {
|
||||
this._packer = msgpack({ forceFloat64: true });
|
||||
this._packer.register(1, Date, encodeDate, decodeDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the provided value to a `Buffer`.
|
||||
*
|
||||
* @param {*} value Any value (string, number, object)
|
||||
* @callback {Function} cb The callback to receive the parsed result.
|
||||
* @param {Error} err
|
||||
* @param {Buffer} data The encoded value
|
||||
* @promise
|
||||
*/
|
||||
BinaryPacker.prototype.encode = function(value, cb) {
|
||||
cb = cb || createPromiseCallback();
|
||||
try {
|
||||
// msgpack5 returns https://www.npmjs.com/package/bl instead of Buffer
|
||||
// use .slice() to convert to a Buffer
|
||||
var data = this._packer.encode(value).slice();
|
||||
setImmediate(function() {
|
||||
cb(null, data);
|
||||
});
|
||||
} catch (err) {
|
||||
setImmediate(function() {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the binary value back to a JavaScript value.
|
||||
* @param {Buffer} binary The binary input.
|
||||
* @callback {Function} cb The callback to receive the composed value.
|
||||
* @param {Error} err
|
||||
* @param {*} value Decoded value.
|
||||
* @promise
|
||||
*/
|
||||
BinaryPacker.prototype.decode = function(binary, cb) {
|
||||
cb = cb || createPromiseCallback();
|
||||
try {
|
||||
var value = this._packer.decode(binary);
|
||||
setImmediate(function() {
|
||||
cb(null, value);
|
||||
});
|
||||
} catch (err) {
|
||||
setImmediate(function() {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
function encodeDate(obj) {
|
||||
return new Buffer(obj.toISOString(), 'utf8');
|
||||
}
|
||||
|
||||
function decodeDate(buf) {
|
||||
return new Date(buf.toString('utf8'));
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
"async": "^1.0.0",
|
||||
"bluebird": "^3.4.6",
|
||||
"debug": "^2.2.0",
|
||||
"msgpack5": "^3.4.1",
|
||||
"strong-globalize": "^2.5.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Node module: loopback-connector
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var BinaryPacker = require('../lib/binary-packer');
|
||||
var expect = require('chai').expect;
|
||||
|
||||
describe('BinaryPacker', function() {
|
||||
var packer;
|
||||
|
||||
beforeEach(function createPacker() {
|
||||
packer = new BinaryPacker();
|
||||
});
|
||||
|
||||
describe('encode()', function() {
|
||||
it('supports invocation with a callback', function(done) {
|
||||
packer.encode('a-value', done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode()', function() {
|
||||
it('supports invocation with a callback', function(done) {
|
||||
packer.encode('a-value', function(err, binary) {
|
||||
if (err) return done(err);
|
||||
packer.decode(binary, function(err, result) {
|
||||
if (err) return done(err);
|
||||
expect(result).to.eql('a-value');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('roundtrip', function() {
|
||||
var TEST_CASES = {
|
||||
String: 'a-value',
|
||||
Object: { a: 1, b: 2 },
|
||||
Buffer: new Buffer([1, 2, 3]),
|
||||
Date: new Date('2016-08-03T11:53:03.470Z'),
|
||||
Integer: 12345,
|
||||
Float: 12.345,
|
||||
Boolean: false,
|
||||
};
|
||||
|
||||
Object.keys(TEST_CASES).forEach(function(tc) {
|
||||
it('works for ' + tc + ' values', function() {
|
||||
var value = TEST_CASES[tc];
|
||||
return encodeAndDecode(value)
|
||||
.then(function(result) {
|
||||
expect(result).to.eql(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('works for nested properties', function() {
|
||||
return encodeAndDecode(TEST_CASES)
|
||||
.then(function(result) {
|
||||
expect(result).to.eql(TEST_CASES);
|
||||
});
|
||||
});
|
||||
|
||||
function encodeAndDecode(value) {
|
||||
return packer.encode(value)
|
||||
.then(function(binary) {
|
||||
return packer.decode(binary);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue