diff --git a/README.md b/README.md index f25d6e5..06d9720 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ You may define your own codecs that handle encoding and decoding values. There i ```js class YourCodec { - // return the number of bytes read and the value - decode(view: DataView, byteOffset = 0): {read: number, value: any} + // return the value + decode(view: DataView, target: {byteOffset: number}): any // return the number of bytes written encode(value: any, view: DataView, byteOffset = 0): number @@ -175,11 +175,11 @@ import {Codecs} from 'crunches'; // will coerce strings to `Date`s class MyDateCodec extends Codecs.string { - decode(view, byteOffset = 0) { + decode(view, target) { // let the `string` codec decode the string - const decoded = super.decode(view, byteOffset); + const decoded = super.decode(view, target); // pass it to the `Date` constructor - return {read: decoded.read, value: new Date(decoded.value)}; + return new Date(decoded); } encode(value, view, byteOffset = 0) { @@ -203,6 +203,8 @@ Codecs.myDate = MyDateCodec; All this codec does is coerce `Date`s to and from strings. It leans on the built-in `string` codec to handle the actual over-the-wire encoding and string size calculation. +Inside your codec, you must increment `target.byteOffset` as you decode bytes. + Just set a key on the `Codecs` object and go. Too easy! ## Schema types @@ -335,3 +337,6 @@ SchemaPack's `varint` types only work up to $2^{30}-1$ whereas `crunches` uses m **Q**: Why no TypeScript support? **A**: Feel free to create an issue. + +**Q**: This library isn't **always** faster than SchemaPack! +**A**: That's true, at least for now. SchemaPack does a lot of black magic and compiles codecs on-the-fly. However, it is consistently faster when using larger aggregate types even in its current state. diff --git a/src/codecs/array.js b/src/codecs/array.js index 7ece00a..24637a7 100644 --- a/src/codecs/array.js +++ b/src/codecs/array.js @@ -13,17 +13,14 @@ class ArrayCodec { this.$$elementCodec = new Codecs[blueprint.element.type](blueprint.element); } - decode(view, byteOffset = 0) { - let read = 0; - const length = view.getUint32(byteOffset); - read += 4; + decode(view, target = {byteOffset: 0}) { + const length = view.getUint32(target.byteOffset); + target.byteOffset += 4; const value = Array(length); for (let i = 0; i < length; ++i) { - const decoded = this.$$elementCodec.decode(view, byteOffset + read); - value[i] = decoded.value; - read += decoded.read; + value[i] = this.$$elementCodec.decode(view, target); } - return {read, value}; + return value; } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/array.test.js b/src/codecs/array.test.js index a569fb1..0a0797a 100644 --- a/src/codecs/array.test.js +++ b/src/codecs/array.test.js @@ -12,6 +12,6 @@ test('array', async () => { }); const value = [1, 2, 3, 4]; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/bool.js b/src/codecs/bool.js index 05d930f..9b9294a 100644 --- a/src/codecs/bool.js +++ b/src/codecs/bool.js @@ -1,6 +1,8 @@ class BoolCodec { - decode(view, byteOffset = 0) { - return {read: 1, value: !!view.getUint8(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = !!view.getUint8(target.byteOffset); + target.byteOffset += 1; + return value; } encode(value, view, byteOffset = 0) { view.setUint8(byteOffset, !!value); diff --git a/src/codecs/bool.test.js b/src/codecs/bool.test.js index 0279de9..d833a46 100644 --- a/src/codecs/bool.test.js +++ b/src/codecs/bool.test.js @@ -6,14 +6,14 @@ test('bool', async () => { const codec = new Codec(); const value = true; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('coerced bool', async () => { const codec = new Codec(); const value = 'truthy value'; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: !!value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(!!value); }); diff --git a/src/codecs/buffer.js b/src/codecs/buffer.js index a1967cf..d23cc74 100644 --- a/src/codecs/buffer.js +++ b/src/codecs/buffer.js @@ -1,13 +1,15 @@ class BufferCodec { - decode(view, byteOffset = 0) { - const length = view.getUint32(byteOffset); + decode(view, target = {byteOffset: 0}) { + const length = view.getUint32(target.byteOffset); + target.byteOffset += 4; const value = new ArrayBuffer(length); if (0 === length) { - return {read: 4, value}; + return value; } - new Uint8Array(value).set(new Uint8Array(view.buffer, 4 + view.byteOffset + byteOffset)); - return {read: 4 + length, value}; + new Uint8Array(value).set(new Uint8Array(view.buffer, view.byteOffset + target.byteOffset)); + target.byteOffset += length; + return value; } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/buffer.test.js b/src/codecs/buffer.test.js index 966eb7f..8485772 100644 --- a/src/codecs/buffer.test.js +++ b/src/codecs/buffer.test.js @@ -21,10 +21,10 @@ test('buffer', async () => { }); const value = new Map([[1, 'one'], [2, 'two']]); const mapView = new DataView(new ArrayBuffer(mapSchema.size(value))); - const written = mapSchema.encode(value, mapView); + mapSchema.encode(value, mapView); const bufferSchema = new Codec(); const bufferView = new DataView(new ArrayBuffer(bufferSchema.size(mapView.buffer))); bufferSchema.encode(mapView.buffer, bufferView); - const newMapView = new DataView(bufferSchema.decode(bufferView).value); - expect(mapSchema.decode(newMapView)).to.deep.equal({read: written, value}); + const newMapView = new DataView(bufferSchema.decode(bufferView)); + expect(mapSchema.decode(newMapView)).to.deep.equal(value); }); diff --git a/src/codecs/date.js b/src/codecs/date.js index a3619a3..8a57190 100644 --- a/src/codecs/date.js +++ b/src/codecs/date.js @@ -3,9 +3,8 @@ import StringCodec from './string.js'; // will coerce strings to `Date`s class DateCodec extends StringCodec { - decode(view, byteOffset = 0) { - const decoded = super.decode(view, byteOffset); - return {read: decoded.read, value: new Date(decoded.value)}; + decode(view, target = {byteOffset: 0}) { + return new Date(super.decode(view, target)); } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/date.test.js b/src/codecs/date.test.js index a873b8b..0db38c5 100644 --- a/src/codecs/date.test.js +++ b/src/codecs/date.test.js @@ -6,18 +6,18 @@ test('date', async () => { const codec = new Codec(); const value = new Date('2024-11-24T18:58:48.912Z'); const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); + codec.encode(value, view); const decoded = codec.decode(view); - expect(decoded.value).to.be.instanceOf(Date); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + expect(decoded).to.be.instanceOf(Date); + expect(codec.decode(view)).to.deep.equal(value); }); test('coerce date', async () => { const codec = new Codec(); const value = '2024-11-24T18:58:48.912Z'; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); + codec.encode(value, view); const decoded = codec.decode(view); - expect(decoded.value).to.be.instanceOf(Date); - expect(codec.decode(view)).to.deep.equal({read: written, value: new Date(value)}); + expect(decoded).to.be.instanceOf(Date); + expect(codec.decode(view)).to.deep.equal(new Date(value)); }); diff --git a/src/codecs/float32.js b/src/codecs/float32.js index 59a63e0..1273918 100644 --- a/src/codecs/float32.js +++ b/src/codecs/float32.js @@ -1,6 +1,8 @@ class Float32Codec { - decode(view, byteOffset = 0) { - return {read: 4, value: view.getFloat32(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getFloat32(target.byteOffset); + target.byteOffset += 4; + return value; } encode(value, view, byteOffset = 0) { view.setFloat32(byteOffset, value); diff --git a/src/codecs/float32.test.js b/src/codecs/float32.test.js index 8530f7f..6a69d6c 100644 --- a/src/codecs/float32.test.js +++ b/src/codecs/float32.test.js @@ -6,14 +6,14 @@ test('float32', async () => { const codec = new Codec(); const value = 1 / 4; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('float32 infinity', async () => { const codec = new Codec(); const value = Infinity; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/float64.js b/src/codecs/float64.js index 866447b..1bfe1d8 100644 --- a/src/codecs/float64.js +++ b/src/codecs/float64.js @@ -1,6 +1,8 @@ class Float64Codec { - decode(view, byteOffset = 0) { - return {read: 8, value: view.getFloat64(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getFloat64(target.byteOffset); + target.byteOffset += 8; + return value; } encode(value, view, byteOffset = 0) { view.setFloat64(byteOffset, value); diff --git a/src/codecs/float64.test.js b/src/codecs/float64.test.js index ea0d732..ec473d2 100644 --- a/src/codecs/float64.test.js +++ b/src/codecs/float64.test.js @@ -6,14 +6,14 @@ test('float64', async () => { const codec = new Codec(); const value = 1 / 7; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('float64 infinity', async () => { const codec = new Codec(); const value = Infinity; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/int16.js b/src/codecs/int16.js index 91af039..89f5847 100644 --- a/src/codecs/int16.js +++ b/src/codecs/int16.js @@ -1,6 +1,8 @@ class Int16Codec { - decode(view, byteOffset = 0) { - return {read: 2, value: view.getInt16(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getInt16(target.byteOffset); + target.byteOffset += 2; + return value; } encode(value, view, byteOffset = 0) { view.setInt16(byteOffset, value); diff --git a/src/codecs/int16.test.js b/src/codecs/int16.test.js index 08f8107..7215abe 100644 --- a/src/codecs/int16.test.js +++ b/src/codecs/int16.test.js @@ -6,6 +6,6 @@ test('int16', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/int32.js b/src/codecs/int32.js index 9516e06..e0f890c 100644 --- a/src/codecs/int32.js +++ b/src/codecs/int32.js @@ -1,6 +1,8 @@ class Int32Codec { - decode(view, byteOffset = 0) { - return {read: 4, value: view.getInt32(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getInt32(target.byteOffset); + target.byteOffset += 4; + return value; } encode(value, view, byteOffset = 0) { view.setInt32(byteOffset, value); diff --git a/src/codecs/int32.test.js b/src/codecs/int32.test.js index 52f11e0..af956c0 100644 --- a/src/codecs/int32.test.js +++ b/src/codecs/int32.test.js @@ -6,6 +6,6 @@ test('int32', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/int8.js b/src/codecs/int8.js index 013fbe8..a608131 100644 --- a/src/codecs/int8.js +++ b/src/codecs/int8.js @@ -1,6 +1,8 @@ class Int8Codec { - decode(view, byteOffset = 0) { - return {read: 1, value: view.getInt8(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getInt8(target.byteOffset); + target.byteOffset += 1; + return value; } encode(value, view, byteOffset = 0) { view.setInt8(byteOffset, value); diff --git a/src/codecs/int8.test.js b/src/codecs/int8.test.js index 6b6ef6a..2543725 100644 --- a/src/codecs/int8.test.js +++ b/src/codecs/int8.test.js @@ -6,6 +6,6 @@ test('int8', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/map.js b/src/codecs/map.js index 42ed542..a11ee81 100644 --- a/src/codecs/map.js +++ b/src/codecs/map.js @@ -15,13 +15,12 @@ class MapCodec extends ArrayCodec { }); } - decode(view, byteOffset = 0) { - const decoded = super.decode(view, byteOffset); + decode(view, target = {byteOffset: 0}) { const value = new Map(); - for (const {key, value: mapValue} of decoded.value) { + for (const {key, value: mapValue} of super.decode(view, target)) { value.set(key, mapValue); } - return {read: decoded.read, value}; + return value; } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/map.test.js b/src/codecs/map.test.js index 7457875..952693f 100644 --- a/src/codecs/map.test.js +++ b/src/codecs/map.test.js @@ -19,8 +19,8 @@ test('map', async () => { }); const value = new Map([[1, 'one'], [2, 'two']]); const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('coerce map', async () => { @@ -30,6 +30,6 @@ test('coerce map', async () => { }); const value = [[1, 'one'], [2, 'two']]; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: new Map(value)}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(new Map(value)); }); diff --git a/src/codecs/object.js b/src/codecs/object.js index 5fa7852..de70f78 100644 --- a/src/codecs/object.js +++ b/src/codecs/object.js @@ -23,19 +23,18 @@ class ObjectCodec { } } - decode(view, byteOffset = 0) { + decode(view, target = {byteOffset: 0}) { const booleanFlags = []; const optionalFlags = []; let currentBoolean = 0; let currentOptional = 0; - let read = 0; let {$$booleans} = this; const booleanBackpatches = []; const optionalCount = Math.ceil(this.$$optionals / 8); for (let i = 0; i < optionalCount; ++i) { - optionalFlags.push(view.getUint8(byteOffset + i)); + optionalFlags.push(view.getUint8(target.byteOffset)); + target.byteOffset += 1; } - read += optionalCount; const value = {}; for (const {codec, key, property} of this.$$codecs) { if (property.optional) { @@ -57,22 +56,20 @@ class ObjectCodec { booleanBackpatches.push({bit, index, key}); } else { - const decoded = codec.decode(view, byteOffset + read); - value[key] = decoded.value; - read += decoded.read; + value[key] = codec.decode(view, target); } } const booleanCount = Math.ceil($$booleans / 8); if (booleanCount > 0) { for (let i = 0; i < booleanCount; ++i) { - booleanFlags.push(view.getUint8(byteOffset + read + i)); + booleanFlags.push(view.getUint8(target.byteOffset)); + target.byteOffset += 1; } for (const {bit, index, key} of booleanBackpatches) { value[key] = !!(booleanFlags[index] & (1 << bit)); } - read += booleanCount; } - return {read, value}; + return value; } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/object.test.js b/src/codecs/object.test.js index 1561337..f38ac05 100644 --- a/src/codecs/object.test.js +++ b/src/codecs/object.test.js @@ -17,8 +17,8 @@ test('object', async () => { }, }); const view = new DataView(new ArrayBuffer(codec.size({1: 32, 2: 32}))); - const written = codec.encode({1: 32, 2: 32}, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: {1: 32, 2: 32}}); + codec.encode({1: 32, 2: 32}, view); + expect(codec.decode(view)).to.deep.equal({1: 32, 2: 32}); }); test('object boolean coalescence', async () => { @@ -73,15 +73,14 @@ test('object optional property', async () => { }); expect(codec.size({1: 32, 2: 32, 3: {4: 32}})).to.equal(5); const view = new DataView(new ArrayBuffer(codec.size({1: 32, 2: 32, 3: {4: 32}}))); - let written; - written = codec.encode({1: 32, 2: 32, 3: {4: 32}}, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: {1: 32, 2: 32, 3: {4: 32}}}); - written = codec.encode({1: 32, 2: 32, 3: {}}, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: {1: 32, 2: 32, 3: {}}}); - written = codec.encode({1: 32, 2: 32}, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: {1: 32, 2: 32}}); - written = codec.encode({1: 32}, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: {1: 32}}); + codec.encode({1: 32, 2: 32, 3: {4: 32}}, view); + expect(codec.decode(view)).to.deep.equal({1: 32, 2: 32, 3: {4: 32}}); + codec.encode({1: 32, 2: 32, 3: {}}, view); + expect(codec.decode(view)).to.deep.equal({1: 32, 2: 32, 3: {}}); + codec.encode({1: 32, 2: 32}, view); + expect(codec.decode(view)).to.deep.equal({1: 32, 2: 32}); + codec.encode({1: 32}, view); + expect(codec.decode(view)).to.deep.equal({1: 32}); }); test('object boolean properties', async () => { @@ -94,8 +93,8 @@ test('object boolean properties', async () => { const codec = new Codec(blueprint); expect(codec.size(value)).to.equal(1); const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('object boolean optional interaction', async () => { @@ -109,5 +108,5 @@ test('object boolean optional interaction', async () => { const view = new DataView(new ArrayBuffer(codec.size(value))); const written = codec.encode(value, view); expect(written).to.equal(1); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/set.js b/src/codecs/set.js index 510d156..92a8eff 100644 --- a/src/codecs/set.js +++ b/src/codecs/set.js @@ -9,9 +9,8 @@ class SetCodec extends ArrayCodec { }); } - decode(view, byteOffset = 0) { - const decoded = super.decode(view, byteOffset); - return {read: decoded.read, value: new Set(decoded.value)}; + decode(view, target = {byteOffset: 0}) { + return new Set(super.decode(view, target)); } } diff --git a/src/codecs/set.test.js b/src/codecs/set.test.js index 5aa4f1b..011e0a0 100644 --- a/src/codecs/set.test.js +++ b/src/codecs/set.test.js @@ -12,8 +12,8 @@ test('set', async () => { }); const value = new Set([1, 2, 3, 4]); const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('coerce set', async () => { @@ -22,6 +22,6 @@ test('coerce set', async () => { }); const value = [1, 2, 3, 4]; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value: new Set(value)}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(new Set(value)); }); diff --git a/src/codecs/string.js b/src/codecs/string.js index 3158c98..0564ccb 100644 --- a/src/codecs/string.js +++ b/src/codecs/string.js @@ -3,14 +3,15 @@ class StringCodec { static decoder = new TextDecoder(); static encoder = new TextEncoder(); - decode(view, byteOffset = 0) { - const length = view.getUint32(byteOffset); + decode(view, target = {byteOffset: 0}) { + const length = view.getUint32(target.byteOffset); + target.byteOffset += 4; if (0 === length) { - return {read: 4, value: ''}; + return ''; } - const stringView = new DataView(view.buffer, view.byteOffset + byteOffset + 4, length); - const value = this.constructor.decoder.decode(stringView); - return {read: 4 + length, value}; + const stringView = new DataView(view.buffer, view.byteOffset + target.byteOffset, length); + target.byteOffset += length; + return this.constructor.decoder.decode(stringView); } encode(value, view, byteOffset = 0) { diff --git a/src/codecs/string.test.js b/src/codecs/string.test.js index c54e547..17c35ba 100644 --- a/src/codecs/string.test.js +++ b/src/codecs/string.test.js @@ -6,14 +6,14 @@ test('string', async () => { const codec = new Codec(); const value = 'hello world'; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); test('unicode', async () => { const codec = new Codec(); const value = 'hαllo world'; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/uint16.js b/src/codecs/uint16.js index 4ad1185..021e0c1 100644 --- a/src/codecs/uint16.js +++ b/src/codecs/uint16.js @@ -1,6 +1,8 @@ class Uint16Codec { - decode(view, byteOffset = 0) { - return {read: 2, value: view.getUint16(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getUint16(target.byteOffset); + target.byteOffset += 2; + return value; } encode(value, view, byteOffset = 0) { view.setUint16(byteOffset, value); diff --git a/src/codecs/uint16.test.js b/src/codecs/uint16.test.js index 6511de7..4ff2819 100644 --- a/src/codecs/uint16.test.js +++ b/src/codecs/uint16.test.js @@ -6,6 +6,6 @@ test('uint16', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/uint32.js b/src/codecs/uint32.js index d62d5d5..59305c6 100644 --- a/src/codecs/uint32.js +++ b/src/codecs/uint32.js @@ -1,6 +1,8 @@ class Uint32Codec { - decode(view, byteOffset = 0) { - return {read: 4, value: view.getUint32(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getUint32(target.byteOffset); + target.byteOffset += 4; + return value; } encode(value, view, byteOffset = 0) { view.setUint32(byteOffset, value); diff --git a/src/codecs/uint32.test.js b/src/codecs/uint32.test.js index 4611b5e..1dec64c 100644 --- a/src/codecs/uint32.test.js +++ b/src/codecs/uint32.test.js @@ -6,6 +6,6 @@ test('uint32', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/uint8.js b/src/codecs/uint8.js index 7e5d2e1..d6cced6 100644 --- a/src/codecs/uint8.js +++ b/src/codecs/uint8.js @@ -1,6 +1,8 @@ class Uint8Codec { - decode(view, byteOffset = 0) { - return {read: 1, value: view.getUint8(byteOffset)}; + decode(view, target = {byteOffset: 0}) { + const value = view.getUint8(target.byteOffset); + target.byteOffset += 1; + return value; } encode(value, view, byteOffset = 0) { view.setUint8(byteOffset, value); diff --git a/src/codecs/uint8.test.js b/src/codecs/uint8.test.js index 11b3422..b9f4bc4 100644 --- a/src/codecs/uint8.test.js +++ b/src/codecs/uint8.test.js @@ -6,6 +6,6 @@ test('uint8', async () => { const codec = new Codec(); const value = 32; const view = new DataView(new ArrayBuffer(codec.size(value))); - const written = codec.encode(value, view); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + codec.encode(value, view); + expect(codec.decode(view)).to.deep.equal(value); }); diff --git a/src/codecs/varint.js b/src/codecs/varint.js index 46dcabd..0f4e0de 100644 --- a/src/codecs/varint.js +++ b/src/codecs/varint.js @@ -1,10 +1,10 @@ import VarUintCodec from './varuint.js'; class VarIntCodec extends VarUintCodec { - decode(view, byteOffset = 0) { - const decoded = super.decode(view, byteOffset); - const half = Math.floor(decoded.value / 2); - return {read: decoded.read, value: decoded.value & 1 ? -half - 1 : half}; + decode(view, target = {byteOffset: 0}) { + const varuint = super.decode(view, target); + const half = Math.floor(varuint / 2); + return varuint & 1 ? -half - 1 : half; } encode(value, view, byteOffset = 0) { value *= 2; diff --git a/src/codecs/varint.test.js b/src/codecs/varint.test.js index 266f5a5..cdefaf2 100644 --- a/src/codecs/varint.test.js +++ b/src/codecs/varint.test.js @@ -12,12 +12,12 @@ test('varint', async () => { view = new DataView(new ArrayBuffer(codec.size(value))); written = codec.encode(value, view); expect(written).to.equal(i + 1); - expect(codec.decode(view)).to.deep.equal({read: written, value: value}); + expect(codec.decode(view)).to.deep.equal(value); value = -value - 1; view = new DataView(new ArrayBuffer(codec.size(value))); written = codec.encode(value, view); expect(written).to.equal(i + 1); - expect(codec.decode(view)).to.deep.equal({read: written, value: value}); + expect(codec.decode(view)).to.deep.equal(value); } }); diff --git a/src/codecs/varuint.js b/src/codecs/varuint.js index 78e27cd..1dd32fe 100644 --- a/src/codecs/varuint.js +++ b/src/codecs/varuint.js @@ -1,14 +1,15 @@ class VarUintCodec { - decode(view, byteOffset = 0) { + decode(view, target = {byteOffset: 0}) { let byte; let read = 0; let value = 0; do { - byte = view.getUint8(byteOffset + read); + byte = view.getUint8(target.byteOffset); value += (byte & 127) * Math.pow(2, read * 7); read += 1; + target.byteOffset += 1; } while (byte & 128); - return {read, value}; + return value; } encode(value, view, byteOffset = 0) { let written = 0; diff --git a/src/codecs/varuint.test.js b/src/codecs/varuint.test.js index 47af346..3a24a69 100644 --- a/src/codecs/varuint.test.js +++ b/src/codecs/varuint.test.js @@ -11,7 +11,7 @@ test('varuint', async () => { view = new DataView(new ArrayBuffer(codec.size(value))); written = codec.encode(value, view); expect(written).to.equal(i + 1); - expect(codec.decode(view)).to.deep.equal({read: written, value}); + expect(codec.decode(view)).to.deep.equal(value); } }); diff --git a/src/schema.js b/src/schema.js index 6cabd63..3c72b19 100644 --- a/src/schema.js +++ b/src/schema.js @@ -12,8 +12,8 @@ class Schema { this.$$codec = new Codecs[blueprint.type](blueprint); } - decode(view, byteOffset = 0) { - return this.$$codec.decode(view, byteOffset); + decode(view, target = {byteOffset: 0}) { + return this.$$codec.decode(view, target); } encode(value, view, byteOffset = 0) {