diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d5fc0..5c5ab80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## [1.2.2] - 2021-07-05 -* Improve [browser compatibility][pr#26] by eliminating a dependence on +* Improve [browser compatibility][pr#27] by eliminating a dependence on the Node assert module. ## [1.2.1] - 2021-04-29 diff --git a/lib/Layout.js b/lib/Layout.js index f4b8b1a..4667258 100644 --- a/lib/Layout.js +++ b/lib/Layout.js @@ -22,7 +22,7 @@ */ /** - * Support for translating between Buffer instances and JavaScript + * Support for translating between Uint8Array instances and JavaScript * native types. * * {@link module:Layout~Layout|Layout} is the basis of a class @@ -139,6 +139,25 @@ if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { Buffer = require('buffer').Buffer; } +/* Check if a value is a Uint8Array. + * + * @ignore */ +function checkUint8Array(b) { + if (!(b instanceof Uint8Array)) { + throw new TypeError('b must be a Uint8Array'); + } +} +exports.checkUint8Array = checkUint8Array; + +/* Create a Buffer instance from a Uint8Array. + * + * @ignore */ +function uint8ArrayToBuffer(b) { + checkUint8Array(b); + return Buffer.from(b.buffer, b.byteOffset, b.length); +} +exports.uint8ArrayToBuffer = uint8ArrayToBuffer; + /** * Base class for layout objects. * @@ -203,9 +222,9 @@ class Layout { } /** - * Decode from a Buffer into an JavaScript value. + * Decode from a Uint8Array into a JavaScript value. * - * @param {Buffer} b - the buffer from which encoded data is read. + * @param {Uint8Array} b - the buffer from which encoded data is read. * * @param {Number} [offset] - the offset at which the encoded data * starts. If absent a zero offset is inferred. @@ -219,13 +238,13 @@ class Layout { } /** - * Encode a JavaScript value into a Buffer. + * Encode a JavaScript value into a Uint8Array. * * @param {(Number|Array|Object)} src - the value to be encoded into * the buffer. The type accepted depends on the (sub-)type of {@link * Layout}. * - * @param {Buffer} b - the buffer into which encoded data will be + * @param {Uint8Array} b - the buffer into which encoded data will be * written. * * @param {Number} [offset] - the offset at which the encoded data @@ -247,7 +266,7 @@ class Layout { /** * Calculate the span of a specific instance of a layout. * - * @param {Buffer} b - the buffer that contains an encoded instance. + * @param {Uint8Array} b - the buffer that contains an encoded instance. * * @param {Number} [offset] - the offset at which the encoded instance * starts. If absent a zero offset is inferred. @@ -463,6 +482,7 @@ class GreedyCount extends ExternalLayout { /** @override */ decode(b, offset) { + checkUint8Array(b); if (undefined === offset) { offset = 0; } @@ -517,7 +537,7 @@ class OffsetLayout extends ExternalLayout { * start of another layout. * * The value may be positive or negative, but an error will thrown - * if at the point of use it goes outside the span of the Buffer + * if at the point of use it goes outside the span of the Uint8Array * being accessed. */ this.offset = offset; } @@ -574,6 +594,7 @@ class UInt extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readUIntLE(offset, this.span); } @@ -582,6 +603,7 @@ class UInt extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeUIntLE(src, offset, this.span); return this.span; } @@ -616,6 +638,7 @@ class UIntBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readUIntBE(offset, this.span); } @@ -624,6 +647,7 @@ class UIntBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeUIntBE(src, offset, this.span); return this.span; } @@ -658,6 +682,7 @@ class Int extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readIntLE(offset, this.span); } @@ -666,6 +691,7 @@ class Int extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeIntLE(src, offset, this.span); return this.span; } @@ -700,6 +726,7 @@ class IntBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readIntBE(offset, this.span); } @@ -708,6 +735,7 @@ class IntBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeIntBE(src, offset, this.span); return this.span; } @@ -748,6 +776,7 @@ class NearUInt64 extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); const lo32 = b.readUInt32LE(offset); const hi32 = b.readUInt32LE(offset + 4); return roundedInt64(hi32, lo32); @@ -759,6 +788,7 @@ class NearUInt64 extends Layout { offset = 0; } const split = divmodInt64(src); + b = uint8ArrayToBuffer(b); b.writeUInt32LE(split.lo32, offset); b.writeUInt32LE(split.hi32, offset + 4); return 8; @@ -786,6 +816,7 @@ class NearUInt64BE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); const hi32 = b.readUInt32BE(offset); const lo32 = b.readUInt32BE(offset + 4); return roundedInt64(hi32, lo32); @@ -797,6 +828,7 @@ class NearUInt64BE extends Layout { offset = 0; } const split = divmodInt64(src); + b = uint8ArrayToBuffer(b); b.writeUInt32BE(split.hi32, offset); b.writeUInt32BE(split.lo32, offset + 4); return 8; @@ -824,6 +856,7 @@ class NearInt64 extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); const lo32 = b.readUInt32LE(offset); const hi32 = b.readInt32LE(offset + 4); return roundedInt64(hi32, lo32); @@ -835,6 +868,7 @@ class NearInt64 extends Layout { offset = 0; } const split = divmodInt64(src); + b = uint8ArrayToBuffer(b); b.writeUInt32LE(split.lo32, offset); b.writeInt32LE(split.hi32, offset + 4); return 8; @@ -862,6 +896,7 @@ class NearInt64BE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); const hi32 = b.readInt32BE(offset); const lo32 = b.readUInt32BE(offset + 4); return roundedInt64(hi32, lo32); @@ -873,6 +908,7 @@ class NearInt64BE extends Layout { offset = 0; } const split = divmodInt64(src); + b = uint8ArrayToBuffer(b); b.writeInt32BE(split.hi32, offset); b.writeUInt32BE(split.lo32, offset + 4); return 8; @@ -899,6 +935,7 @@ class Float extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readFloatLE(offset); } @@ -907,6 +944,7 @@ class Float extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeFloatLE(src, offset); return 4; } @@ -932,6 +970,7 @@ class FloatBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readFloatBE(offset); } @@ -940,6 +979,7 @@ class FloatBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeFloatBE(src, offset); return 4; } @@ -965,6 +1005,7 @@ class Double extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readDoubleLE(offset); } @@ -973,6 +1014,7 @@ class Double extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeDoubleLE(src, offset); return 8; } @@ -998,6 +1040,7 @@ class DoubleBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); return b.readDoubleBE(offset); } @@ -1006,6 +1049,7 @@ class DoubleBE extends Layout { if (undefined === offset) { offset = 0; } + b = uint8ArrayToBuffer(b); b.writeDoubleBE(src, offset); return 8; } @@ -1232,6 +1276,7 @@ class Structure extends Layout { /** @override */ decode(b, offset) { + checkUint8Array(b); if (undefined === offset) { offset = 0; } @@ -1753,17 +1798,17 @@ class Union extends Layout { * If `vb` does not produce a registered variant the function returns * `undefined`. * - * @param {(Number|Buffer)} vb - either the variant number, or a + * @param {(Number|Uint8Array)} vb - either the variant number, or a * buffer from which the discriminator is to be read. * * @param {Number} offset - offset into `vb` for the start of the - * union. Used only when `vb` is an instance of {Buffer}. + * union. Used only when `vb` is an instance of {Uint8Array}. * * @return {({VariantLayout}|undefined)} */ getVariant(vb, offset) { let variant = vb; - if (Buffer.isBuffer(vb)) { + if (vb instanceof Uint8Array) { if (undefined === offset) { offset = 0; } @@ -2258,7 +2303,7 @@ class Boolean extends BitField { /** * Contain a fixed-length block of arbitrary data, represented as a - * Buffer. + * Uint8Array. * * *Factory*: {@link module:Layout.blob|blob} * @@ -2310,6 +2355,7 @@ class Blob extends Layout { if (0 > span) { span = this.length.decode(b, offset); } + b = uint8ArrayToBuffer(b); return b.slice(offset, offset + span); } @@ -2323,14 +2369,15 @@ class Blob extends Layout { if (this.length instanceof ExternalLayout) { span = src.length; } - if (!(Buffer.isBuffer(src) - && (span === src.length))) { + if (!(src instanceof Uint8Array && span === src.length)) { throw new TypeError(nameWithProperty('Blob.encode', this) - + ' requires (length ' + span + ') Buffer as src'); + + ' requires (length ' + span + ') Uint8Array as src'); } if ((offset + span) > b.length) { - throw new RangeError('encoding overruns Buffer'); + throw new RangeError('encoding overruns Uint8Array'); } + b = uint8ArrayToBuffer(b); + src = uint8ArrayToBuffer(src); b.write(src.toString('hex'), offset, span, 'hex'); if (this.length instanceof ExternalLayout) { this.length.encode(span, b, offset); @@ -2359,9 +2406,7 @@ class CString extends Layout { /** @override */ getSpan(b, offset) { - if (!Buffer.isBuffer(b)) { - throw new TypeError('b must be a Buffer'); - } + checkUint8Array(b); if (undefined === offset) { offset = 0; } @@ -2378,6 +2423,7 @@ class CString extends Layout { offset = 0; } const span = this.getSpan(b, offset); + b = uint8ArrayToBuffer(b); return b.slice(offset, offset + span - 1).toString('utf-8'); } @@ -2394,6 +2440,7 @@ class CString extends Layout { } const srcb = Buffer.from(src, 'utf8'); const span = srcb.length; + b = uint8ArrayToBuffer(b); if ((offset + span) > b.length) { throw new RangeError('encoding overruns Buffer'); } @@ -2450,9 +2497,7 @@ class UTF8 extends Layout { /** @override */ getSpan(b, offset) { - if (!Buffer.isBuffer(b)) { - throw new TypeError('b must be a Buffer'); - } + checkUint8Array(b); if (undefined === offset) { offset = 0; } @@ -2469,6 +2514,7 @@ class UTF8 extends Layout { && (this.maxSpan < span)) { throw new RangeError('text length exceeds maxSpan'); } + b = uint8ArrayToBuffer(b); return b.slice(offset, offset + span).toString('utf-8'); } @@ -2489,6 +2535,7 @@ class UTF8 extends Layout { && (this.maxSpan < span)) { throw new RangeError('text length exceeds maxSpan'); } + b = uint8ArrayToBuffer(b); if ((offset + span) > b.length) { throw new RangeError('encoding overruns Buffer'); } diff --git a/test/LayoutTest.js b/test/LayoutTest.js index 802abba..63a3061 100644 --- a/test/LayoutTest.js +++ b/test/LayoutTest.js @@ -1762,6 +1762,24 @@ suite('Layout', function() { assert.equal(bl.property, 'bl'); }); test('basics', function() { + const bl = new lo.Blob(3, 'bl'); + const a = new Uint8Array([1, 2, 3, 4, 5]); + let bv = bl.decode(a); + assert(bv instanceof Buffer); + assert.equal(bv.length, bl.span); + assert.equal(Buffer.from('010203', 'hex').compare(bv), 0); + bv = bl.decode(a, 2); + assert.equal(bl.getSpan(a), bl.span); + const src = new Uint8Array(Buffer.from('112233', 'hex')); + assert.equal(Buffer.from('030405', 'hex').compare(bv), 0); + assert.equal(bl.encode(src, a, 1), 3); + const b = lo.uint8ArrayToBuffer(a) + assert.equal(Buffer.from('0111223305', 'hex').compare(b), 0); + assert.throws(() => bl.encode('ABC', a), Error); + assert.throws(() => bl.encode(Buffer.from('0102', 'hex'), a), + Error); + }); + test('Buffer', function() { const bl = new lo.Blob(3, 'bl'); const b = Buffer.from('0102030405', 'hex'); let bv = bl.decode(b);