buffer-serializer
Advanced tools
Comparing version 1.0.2 to 1.1.0
Changelog | ||
========= | ||
2016-12-13 - 1.1.0 | ||
------------------ | ||
* Changed to use a class instead of a single plain object. This helps facilitate testing where you may want several serializers with different configurations in your project. | ||
2016-05-05 - 1.0.2 | ||
@@ -5,0 +11,0 @@ ------------------ |
@@ -7,118 +7,20 @@ "use strict"; | ||
/** | ||
* @typedef {Object} BufferSerializer~helper | ||
* @property {Function} checkFn Determines if a value is of this type | ||
* @property {Function} fromBufferFn | ||
* @property {string} name Name as it appears in the serialized buffer | ||
* @property {Function} toBufferFn | ||
*/ | ||
module.exports = function (BufferReader, BufferWriter) { | ||
var helpers; | ||
/** | ||
* Deserialize a buffer. Double checks the version before calling | ||
* the fromBufferInternal* methods. | ||
* | ||
* @param {Buffer} buff | ||
* @param {number} [offset=0] | ||
* @return {*} | ||
* @throws {Error} invalid version stored in the buffer | ||
*/ | ||
function fromBuffer(buff, offset) { | ||
var buffReader; | ||
offset = offset || 0; | ||
// Only version 0 is supported | ||
if (buff[offset] !== 0x00) { | ||
throw new Error("Invalid version identifier"); | ||
} | ||
buffReader = new BufferReader(buff, offset + 1); | ||
return fromBufferInternal(buffReader); | ||
} | ||
/** | ||
* Deserialize a buffer after the version checks were performed. | ||
* Reads the stored type code (single byte). From there it either | ||
* decodes the information directly or else it passes control to | ||
* another function. | ||
* | ||
* @param {BufferReader} buffReader | ||
* @return {*} | ||
* @throws {Error} invalid type code | ||
*/ | ||
function fromBufferInternal(buffReader) { | ||
var code; | ||
code = buffReader.uint8(); | ||
switch (code) { | ||
// 0x21 is a marker for ending objects and arrays | ||
case 0x2B: // + = positive 8-bit integer | ||
return buffReader.uint8(); | ||
case 0x2D: // - = negative 8-bit integer | ||
return - buffReader.uint8(); | ||
case 0x41: // A = Array object, sparse | ||
return fromBufferInternalArraySparse(buffReader); | ||
case 0x42: // B = Buffer object | ||
return buffReader.buffer(buffReader.size()); | ||
case 0x44: // D = Date object with milliseconds | ||
return fromBufferInternalObjectDateD(buffReader); | ||
case 0x49: // I = negative 32-bit integer | ||
return - buffReader.uint32(); | ||
case 0x4f: // O = object, generic | ||
return fromBufferInternalObjectGeneric(buffReader); | ||
case 0x50: // P = positive 32-bit integer | ||
return buffReader.uint32(); | ||
case 0x52: // R = RegExp | ||
return fromBufferInternalObjectRegExp(buffReader); | ||
case 0x54: // T = Date object without milliseconds | ||
return fromBufferInternalObjectDateT(buffReader); | ||
case 0x5A: // Z = custom object | ||
return fromBufferInternalObjectHelper(buffReader); | ||
case 0x61: // a = Array, dense | ||
return fromBufferInternalArrayDense(buffReader); | ||
case 0x64: // d = 8-byte double | ||
return buffReader.double(); | ||
case 0x66: // f = false | ||
return false; | ||
case 0x69: // i = negative 16-bit integer | ||
return - buffReader.uint16(); | ||
case 0x6e: // n = null | ||
return null; | ||
case 0x70: // p = positive 16-bit integer | ||
return buffReader.uint16(); | ||
case 0x73: // s = string | ||
return buffReader.string(buffReader.size()); | ||
case 0x74: // t = true | ||
return true; | ||
} | ||
throw new Error("Unable to deserialize string, unknown code: " + code); | ||
} | ||
/** | ||
* Converts a buffer to a dense array. This is a list of values | ||
* and is terminated by "!". | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {BufferReader} buffReader | ||
* @return {Array} | ||
*/ | ||
function fromBufferInternalArrayDense(buffReader) { | ||
function fromBufferInternalArrayDense(serializer, buffReader) { | ||
var result; | ||
@@ -130,3 +32,3 @@ | ||
while (buffReader.peek() !== 0x21) { | ||
result.push(fromBufferInternal(buffReader)); | ||
result.push(serializer.fromBufferInternal(buffReader)); | ||
} | ||
@@ -145,6 +47,7 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {BufferReader} buffReader | ||
* @return {Array} | ||
*/ | ||
function fromBufferInternalArraySparse(buffReader) { | ||
function fromBufferInternalArraySparse(serializer, buffReader) { | ||
var key, result; | ||
@@ -156,4 +59,4 @@ | ||
while (buffReader.peek() !== 0x21) { | ||
key = fromBufferInternal(buffReader); | ||
result[key] = fromBufferInternal(buffReader); | ||
key = serializer.fromBufferInternal(buffReader); | ||
result[key] = serializer.fromBufferInternal(buffReader); | ||
} | ||
@@ -205,6 +108,7 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {BufferReader} buffReader | ||
* @return {Object} | ||
*/ | ||
function fromBufferInternalObjectGeneric(buffReader) { | ||
function fromBufferInternalObjectGeneric(serializer, buffReader) { | ||
var key, result; | ||
@@ -216,4 +120,4 @@ | ||
while (buffReader.peek() !== 0x21) { | ||
key = fromBufferInternal(buffReader); | ||
result[key] = fromBufferInternal(buffReader); | ||
key = serializer.fromBufferInternal(buffReader); | ||
result[key] = serializer.fromBufferInternal(buffReader); | ||
} | ||
@@ -231,2 +135,3 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {BufferReader} buffReader | ||
@@ -236,3 +141,3 @@ * @return {*} | ||
*/ | ||
function fromBufferInternalObjectHelper(buffReader) { | ||
function fromBufferInternalObjectHelper(serializer, buffReader) { | ||
var i, name; | ||
@@ -242,5 +147,5 @@ | ||
for (i = 0; i < helpers.length; i += 1) { | ||
if (helpers[i].name == name) { | ||
return helpers[i].fromBufferFn(buffReader); | ||
for (i = 0; i < serializer.helpers.length; i += 1) { | ||
if (serializer.helpers[i].name == name) { | ||
return serializer.helpers[i].fromBufferFn(buffReader); | ||
} | ||
@@ -284,80 +189,2 @@ } | ||
/** | ||
* Register a custom object type for serialization. | ||
* | ||
* @param {string} name Shorter names mean smaller serialized buffers. | ||
* @param {Function(obj)} checkFn Returns true if obj is the one you want. | ||
* @param {Function(obj,BufferWriter)} toBufferFn Write to the BufferWriter | ||
* @param {Function(BufferReader)} fromBufferFn Change buffer back to object | ||
*/ | ||
function register(name, checkFn, toBufferFn, fromBufferFn) { | ||
helpers.push({ | ||
checkFn: checkFn, | ||
fromBufferFn: fromBufferFn, | ||
name: name, | ||
toBufferFn: toBufferFn | ||
}); | ||
} | ||
/** | ||
* Convert something to a buffer. Creates the new BufferWriter and | ||
* kicks off the internal functions. | ||
* | ||
* @param {*} thing | ||
* @return {Buffer} | ||
*/ | ||
function toBuffer(thing) { | ||
var buffWriter; | ||
buffWriter = new BufferWriter(); | ||
buffWriter.uint8(0); | ||
toBufferInternal(thing, buffWriter); | ||
return buffWriter.toBuffer(); | ||
} | ||
/** | ||
* Convert something to a buffer, writing it using the passed | ||
* BufferWriter instance. | ||
* | ||
* @param {*} thing | ||
* @param {BufferWriter} buffWriter | ||
* @throws {Error} when encountering an invalid type | ||
*/ | ||
function toBufferInternal(thing, buffWriter) { | ||
var type; | ||
type = typeof thing; | ||
if (type === "object") { | ||
if (!thing) { | ||
// thing is a null | ||
return buffWriter.uint8(0x6e); // n | ||
} | ||
if (Array.isArray(thing)) { | ||
return toBufferInternalArray(thing, buffWriter); | ||
} | ||
return toBufferInternalObject(thing, buffWriter); | ||
} | ||
if (type === "string") { | ||
return toBufferInternalString(thing, buffWriter); | ||
} | ||
if (type === "number") { | ||
return toBufferInternalNumber(thing, buffWriter); | ||
} | ||
if (type === "boolean") { | ||
return toBufferInternalBoolean(thing, buffWriter); | ||
} | ||
throw new Error("Invalid type: " + type) | ||
} | ||
/** | ||
* Convert an Array into a buffer. Detects if the array is dense. | ||
@@ -367,6 +194,7 @@ * If dense, use "a" and encode just the values. If sparse, encode | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {Array} thing | ||
* @param {BufferWriter} buffWriter | ||
*/ | ||
function toBufferInternalArray(thing, buffWriter) { | ||
function toBufferInternalArray(serializer, thing, buffWriter) { | ||
var i, keys, undefCount; | ||
@@ -392,3 +220,3 @@ | ||
for (i = 0; i < thing.length; i += 1) { | ||
toBufferInternal(thing[i], buffWriter); | ||
serializer.toBufferInternal(thing[i], buffWriter); | ||
} | ||
@@ -406,4 +234,4 @@ | ||
for (i = 0; i < keys.length; i += 1) { | ||
toBufferInternalKey(keys[i], buffWriter); | ||
toBufferInternal(thing[keys[i]], buffWriter); | ||
toBufferInternalKey(serializer, keys[i], buffWriter); | ||
serializer.toBufferInternal(thing[keys[i]], buffWriter); | ||
} | ||
@@ -436,6 +264,7 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {string} thing | ||
* @param {BufferWriter} buffWriter | ||
*/ | ||
function toBufferInternalKey(thing, buffWriter) { | ||
function toBufferInternalKey(serializer, thing, buffWriter) { | ||
if (/^(0|[1-9][0-9]*)$/.test(thing)) { | ||
@@ -445,3 +274,3 @@ thing = +thing; | ||
return toBufferInternal(thing, buffWriter); | ||
return serializer.toBufferInternal(thing, buffWriter); | ||
} | ||
@@ -531,14 +360,18 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {Object} thing | ||
* @param {BufferWriter} buffWriter | ||
* @return {Buffer} | ||
*/ | ||
function toBufferInternalObject(thing, buffWriter) { | ||
var i; | ||
function toBufferInternalObject(serializer, thing, buffWriter) { | ||
var helper, i; | ||
for (i = 0; i < helpers.length; i += 1) { | ||
if (helpers[i].checkFn(thing)) { | ||
for (i = 0; i < serializer.helpers.length; i += 1) { | ||
helper = serializer.helpers[i]; | ||
if (helper.checkFn(thing)) { | ||
buffWriter.uint8(0x5A); // Z | ||
buffWriter.size(helpers[i].name.length); | ||
buffWriter.string(helpers[i].name); | ||
helpers[i].toBufferFn(thing, buffWriter); | ||
buffWriter.size(helper.name.length); | ||
buffWriter.string(helper.name); | ||
helper.toBufferFn(thing, buffWriter); | ||
@@ -561,3 +394,3 @@ return; | ||
return toBufferInternalObjectGeneric(thing, buffWriter); | ||
return toBufferInternalObjectGeneric(serializer, thing, buffWriter); | ||
} | ||
@@ -609,6 +442,7 @@ | ||
* | ||
* @param {BufferSerializer} serializer | ||
* @param {Buffer} thing | ||
* @param {BufferWriter} buffWriter | ||
*/ | ||
function toBufferInternalObjectGeneric(thing, buffWriter) { | ||
function toBufferInternalObjectGeneric(serializer, thing, buffWriter) { | ||
var i, keys; | ||
@@ -620,4 +454,4 @@ | ||
for (i = 0; i < keys.length; i += 1) { | ||
toBufferInternalKey(keys[i], buffWriter); | ||
toBufferInternal(thing[keys[i]], buffWriter); | ||
toBufferInternalKey(serializer, keys[i], buffWriter); | ||
serializer.toBufferInternal(thing[keys[i]], buffWriter); | ||
} | ||
@@ -672,11 +506,198 @@ | ||
helpers = []; | ||
class BufferSerializer { | ||
/** | ||
* Creates a new buffer serializer instance. | ||
*/ | ||
constructor() { | ||
this.helpers = []; | ||
} | ||
return { | ||
fromBuffer: fromBuffer, | ||
fromBufferInternal: fromBufferInternal, | ||
register: register, | ||
toBuffer: toBuffer, | ||
toBufferInternal: toBufferInternal | ||
/** | ||
* Deserialize a buffer. Double checks the version before calling | ||
* the fromBufferInternal* methods. | ||
* | ||
* @param {Buffer} buff | ||
* @param {number} [offset=0] | ||
* @return {*} | ||
* @throws {Error} invalid version stored in the buffer | ||
*/ | ||
fromBuffer(buff, offset) { | ||
var buffReader; | ||
offset = offset || 0; | ||
// Only version 0 is supported | ||
if (buff[offset] !== 0x00) { | ||
throw new Error("Invalid version identifier"); | ||
} | ||
buffReader = new BufferReader(buff, offset + 1); | ||
return this.fromBufferInternal(buffReader); | ||
} | ||
/** | ||
* Deserialize a buffer after the version checks were performed. | ||
* Reads the stored type code (single byte). From there it either | ||
* decodes the information directly or else it passes control to | ||
* another function. | ||
* | ||
* @param {BufferReader} buffReader | ||
* @return {*} | ||
* @throws {Error} invalid type code | ||
*/ | ||
fromBufferInternal(buffReader) { | ||
var code; | ||
code = buffReader.uint8(); | ||
switch (code) { | ||
// 0x21 is a marker for ending objects and arrays | ||
case 0x2B: // + = positive 8-bit integer | ||
return buffReader.uint8(); | ||
case 0x2D: // - = negative 8-bit integer | ||
return - buffReader.uint8(); | ||
case 0x41: // A = Array object, sparse | ||
return fromBufferInternalArraySparse(this, buffReader); | ||
case 0x42: // B = Buffer object | ||
return buffReader.buffer(buffReader.size()); | ||
case 0x44: // D = Date object with milliseconds | ||
return fromBufferInternalObjectDateD(buffReader); | ||
case 0x49: // I = negative 32-bit integer | ||
return - buffReader.uint32(); | ||
case 0x4f: // O = object, generic | ||
return fromBufferInternalObjectGeneric(this, buffReader); | ||
case 0x50: // P = positive 32-bit integer | ||
return buffReader.uint32(); | ||
case 0x52: // R = RegExp | ||
return fromBufferInternalObjectRegExp(buffReader); | ||
case 0x54: // T = Date object without milliseconds | ||
return fromBufferInternalObjectDateT(buffReader); | ||
case 0x5A: // Z = custom object | ||
return fromBufferInternalObjectHelper(this, buffReader); | ||
case 0x61: // a = Array, dense | ||
return fromBufferInternalArrayDense(this, buffReader); | ||
case 0x64: // d = 8-byte double | ||
return buffReader.double(); | ||
case 0x66: // f = false | ||
return false; | ||
case 0x69: // i = negative 16-bit integer | ||
return - buffReader.uint16(); | ||
case 0x6e: // n = null | ||
return null; | ||
case 0x70: // p = positive 16-bit integer | ||
return buffReader.uint16(); | ||
case 0x73: // s = string | ||
return buffReader.string(buffReader.size()); | ||
case 0x74: // t = true | ||
return true; | ||
} | ||
throw new Error("Unable to deserialize string, unknown code: " + code); | ||
} | ||
/** | ||
* Register a custom object type for serialization. | ||
* | ||
* @param {string} name Shorter names mean smaller serialized buffers. | ||
* @param {Function(obj)} checkFn Returns true if obj is the one you want. | ||
* @param {Function(obj,BufferWriter)} toBufferFn Write to the BufferWriter | ||
* @param {Function(BufferReader)} fromBufferFn Change buffer back to object | ||
*/ | ||
register(name, checkFn, toBufferFn, fromBufferFn) { | ||
this.helpers.push({ | ||
checkFn: checkFn, | ||
fromBufferFn: fromBufferFn, | ||
name: name, | ||
toBufferFn: toBufferFn | ||
}); | ||
} | ||
/** | ||
* Convert something to a buffer. Creates the new BufferWriter and | ||
* kicks off the internal functions. | ||
* | ||
* @param {*} thing | ||
* @return {Buffer} | ||
*/ | ||
toBuffer(thing) { | ||
var buffWriter; | ||
buffWriter = new BufferWriter(); | ||
buffWriter.uint8(0); | ||
this.toBufferInternal(thing, buffWriter); | ||
return buffWriter.toBuffer(); | ||
} | ||
/** | ||
* Convert something to a buffer, writing it using the passed | ||
* BufferWriter instance. | ||
* | ||
* @param {*} thing | ||
* @param {BufferWriter} buffWriter | ||
* @throws {Error} when encountering an invalid type | ||
*/ | ||
toBufferInternal(thing, buffWriter) { | ||
var type; | ||
type = typeof thing; | ||
if (type === "object") { | ||
if (!thing) { | ||
// thing is a null | ||
return buffWriter.uint8(0x6e); // n | ||
} | ||
if (Array.isArray(thing)) { | ||
return toBufferInternalArray(this, thing, buffWriter); | ||
} | ||
return toBufferInternalObject(this, thing, buffWriter); | ||
} | ||
if (type === "string") { | ||
// Does not need the serializer | ||
return toBufferInternalString(thing, buffWriter); | ||
} | ||
if (type === "number") { | ||
// Does not need the serializer | ||
return toBufferInternalNumber(thing, buffWriter); | ||
} | ||
if (type === "boolean") { | ||
// Does not need the serializer | ||
return toBufferInternalBoolean(thing, buffWriter); | ||
} | ||
throw new Error("Invalid type: " + type) | ||
} | ||
} | ||
return BufferSerializer; | ||
} |
{ | ||
"name": "buffer-serializer", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"description": "Convert JavaScript objects into Buffers and vice-versa. Serializes objects using a compact storage mechanism. Expandable to handle your own objects.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -26,5 +26,6 @@ Buffer Serializer | ||
var aBuffer, myThing, result, serializer; | ||
var aBuffer, BufferSerializer, myThing, result, serializer; | ||
serializer = require("buffer-serializer"); | ||
BufferSerializer = require("buffer-serializer"); | ||
serializer = new BufferSerializer(); | ||
@@ -51,3 +52,3 @@ myThing = { | ||
// This example is NOT what is used inside the serializer. | ||
// This example is NOT used inside the serializer. | ||
// This code is provided only for illustrative purposes. | ||
@@ -142,3 +143,3 @@ serializer.register("Date", function checkFn(value) { | ||
Custom objects are encoded slightly differently | ||
Custom objects are encoded slightly differently. | ||
@@ -163,2 +164,8 @@ * Custom object: "z" size name data | ||
### `serializer = new BufferSerializer()` | ||
Create a new instance of the serializer. | ||
### `serializer.register(name, checkFn, toBufferFn, fromBufferFn)` | ||
@@ -165,0 +172,0 @@ |
@@ -7,3 +7,6 @@ "use strict"; | ||
beforeEach(() => { | ||
serializer = require("../"); | ||
var BufferSerializer; | ||
BufferSerializer = require("../"); | ||
serializer = new BufferSerializer(); | ||
BufferReader = require("../lib/buffer-reader")(); | ||
@@ -10,0 +13,0 @@ BufferWriter = require("../lib/buffer-writer")(); |
53301
1239
214