diff --git a/README.md b/README.md index c751450..b8ebff8 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ msgpack.Decoder().on("data",ondata).decode(buf); | 1000000 | 2246 | 445235 See the [benchmark.md](benchmark.md) for more benchmarks and information about benchmarking. ## Custom Extensions -You can add your own custom extensions, which can be used to encode specific types/classes in certain ways. This is done by using the `addExtension` function, and specifying the class, extension type code (should be a number from 1-100, reserving negatives for MessagePack, 101-127 for msgpackr), and your pack and unpack functions (or just the one you need). You can use msgpackr encoding and decoding within your extensions, but if you do so, you must create a separate Packr instance, otherwise you could override data in the same encoding buffer: +You can add your own custom extensions, which can be used to encode specific types/classes in certain ways. This is done by using the `addExtension` function, and specifying the class, extension `type` code (should be a number from 1-100, reserving negatives for MessagePack, 101-127 for msgpackr), and your `pack` and `unpack` functions (or just the one you need). ```js import { addExtension, Packr } from 'msgpackr'; @@ -216,16 +216,39 @@ addExtension({ type: 11, // register your own extension code (a type code from 1-100) pack(instance) { // define how your custom class should be encoded - return extPackr.pack(instance.myData); // return a buffer + return Buffer.from([instance.myData]); // return a buffer } unpack(buffer) { // define how your custom class should be decoded let instance = new MyCustomClass(); - instance.myData = extPackr.unpack(buffer); + instance.myData = buffer[0]; return instance; // decoded value from buffer } }); ``` +If you want to use msgpackr to encode and decode the data within your extensions, you can use the `read` and `write` functions and read and write data/objects that will be encoded and decoded by msgpackr, which can be easier and faster than creating and receiving separate buffers (note that you can't just return the instance from `write` or msgpackr will recursively try to use extension infinitely): +```js +import { addExtension, Packr } from 'msgpackr'; + +class MyCustomClass {...} + +let extPackr = new Packr(); +addExtension({ + Class: MyCustomClass, + type: 11, // register your own extension code (a type code from 1-100) + write(instance) { + // define how your custom class should be encoded + return instance.myData; // return some data to be encoded + } + read(data) { + // define how your custom class should be decoded, + // data will already be unpacked/decoded + let instance = new MyCustomClass(); + instance.myData = data; + return instance; // return decoded value + } +}); +``` ### Additional Performance Optimizations Msgpackr is already fast, but here are some tips for making it faster: diff --git a/pack.js b/pack.js index 46f3bbc..f593eef 100644 --- a/pack.js +++ b/pack.js @@ -331,6 +331,13 @@ class Packr extends Unpackr { let extensionClass = extensionClasses[i] if (value instanceof extensionClass) { let extension = extensions[i] + if (extension.write) { + target[position++] = 0xd4 // one byte "tag" extension + target[position++] = extension.type + target[position++] = 0 + pack(extension.write.call(this, value)) + return + } let currentTarget = target let currentTargetView = targetView let currentPosition = position @@ -748,8 +755,8 @@ function insertIds(serialized, idsToInsert) { exports.addExtension = function(extension) { if (extension.Class) { - if (!extension.pack) - throw new Error('Extension has no pack function') + if (!extension.pack && !extension.write) + throw new Error('Extension has no pack or write function') extensionClasses.unshift(extension.Class) extensions.unshift(extension) } diff --git a/tests/test.js b/tests/test.js index a356c57..032f129 100644 --- a/tests/test.js +++ b/tests/test.js @@ -145,7 +145,7 @@ suite('msgpackr basic tests', function(){ assert.deepEqual(deserialized2, data2) }) - test('extended class', function(){ + test('extended class pack/unpack', function(){ function Extended() { } @@ -181,7 +181,7 @@ suite('msgpackr basic tests', function(){ assert.deepEqual(data, deserialized) assert.equal(deserialized.extendedInstance.getDouble(), 8) }) - test('extended class', function(){ + test('extended class pack/unpack custom size', function(){ function TestClass() { } @@ -198,6 +198,43 @@ suite('msgpackr basic tests', function(){ let result = unpack(pack(new TestClass())); assert.equal(result, 256) }) + + test('extended class read/write', function(){ + function Extended() { + + } + Extended.prototype.getDouble = function() { + return this.value * 2 + } + var instance = new Extended() + instance.value = 4 + instance.string = 'decode this: ᾜ' + var data = { + prop1: 'has multi-byte: ᾜ', + extendedInstance: instance, + prop2: 'more string', + num: 3, + } + let packr = new Packr() + addExtension({ + Class: Extended, + type: 12, + read: function(data) { + let e = new Extended() + e.value = data[0] + e.string = data[1] + return e + }, + write: function(instance) { + return [instance.value, instance.string] + } + }) + var serialized = pack(data) + var deserialized = unpack(serialized) + assert.deepEqual(data, deserialized) + assert.equal(deserialized.extendedInstance.getDouble(), 8) + }) + test.skip('text decoder', function() { let td = new TextDecoder('ISO-8859-15') let b = Buffer.alloc(3) diff --git a/unpack.js b/unpack.js index de2c563..7a39d08 100644 --- a/unpack.js +++ b/unpack.js @@ -280,9 +280,14 @@ function read() { if (value == 0x72) { return recordDefinition(src[position++]) } else { - if (currentExtensions[value]) - return currentExtensions[value](src.subarray(position, ++position)) - else + let extension = currentExtensions[value] + if (extension) { + if (extension.read) { + position++ // skip filler byte + return extension.read(read()) + } else + return extension(src.subarray(position, ++position)) + } else throw new Error('Unknown extension ' + value) } case 0xd5: @@ -839,7 +844,10 @@ function clearSource() { } exports.addExtension = function(extension) { - currentExtensions[extension.type] = extension.unpack + if (extension.unpack) + currentExtensions[extension.type] = extension.unpack + else + currentExtensions[extension.type] = extension } let mult10 = new Array(147) // this is a table matching binary exponents to the multiplier to determine significant digit rounding