From b7ddd2ac24e65cc47befc1e0eb5026422f8ab037 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Wed, 20 Nov 2024 19:43:28 +0400 Subject: [PATCH] [Fix] io.js 3.0 - Node.js 5.3 typed array support --- index.js | 83 +++++++++++++++++++++++++++++++++------------------ test/index.js | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index 741d913..804c108 100644 --- a/index.js +++ b/index.js @@ -28,42 +28,67 @@ var useArrayBuffer = typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && (Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT); -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - var bufferData; +function toBuffer(data, encoding) { + /* + * No need to do anything for exact instance + * This is only valid when safe-buffer.Buffer === buffer.Buffer, i.e. when Buffer.from/Buffer.alloc existed + */ if (data instanceof Buffer) { - // No need to do anything - bufferData = data; - } else if (typeof data === 'string') { - // Convert strings to Buffer - bufferData = Buffer.from(data, inputEnc); - } else if (useArrayBuffer && ArrayBuffer.isView(data)) { - /* - * Wrap any TypedArray instances and DataViews - * Makes sense only on engines with full TypedArray support -- let Buffer detect that - */ - bufferData = Buffer.from(data.buffer, data.byteOffset, data.byteLength); - } else if (useUint8Array && data instanceof Uint8Array) { + return data; + } + + // Convert strings to Buffer + if (typeof data === 'string') { + return Buffer.from(data, encoding); + } + + /* + * Wrap any TypedArray instances and DataViews + * Makes sense only on engines with full TypedArray support -- let Buffer detect that + */ + if (useArrayBuffer && ArrayBuffer.isView(data)) { + // Bug in Node.js <6.3.1, which treats this as out-of-bounds + if (data.byteLength === 0) { + return Buffer.alloc(0); + } + + var res = Buffer.from(data.buffer, data.byteOffset, data.byteLength); /* - * Uint8Array in engines where Buffer.from might not work with ArrayBuffer, just copy over - * Doesn't make sense with other TypedArray instances + * Recheck result size, as offset/length doesn't work on Node.js <5.10 + * We just go to Uint8Array case if this fails */ - bufferData = Buffer.from(data); - } else if ( + if (res.byteLength === data.byteLength) { + return res; + } + } + + /* + * Uint8Array in engines where Buffer.from might not work with ArrayBuffer, just copy over + * Doesn't make sense with other TypedArray instances + */ + if (useUint8Array && data instanceof Uint8Array) { + return Buffer.from(data); + } + + /* + * Old Buffer polyfill on an engine that doesn't have TypedArray support + * Also, this is from a different Buffer polyfill implementation then we have, as instanceof check failed + * Convert to our current Buffer implementation + */ + if ( Buffer.isBuffer(data) - && data.constructor - && data.constructor.isBuffer - && data.constructor.isBuffer(data) + && data.constructor + && typeof data.constructor.isBuffer === 'function' + && data.constructor.isBuffer(data) ) { - /* - * Old Buffer polyfill on an engine that doesn't have TypedArray support - * Also, this is from a different Buffer polyfill implementation then we have, as instanceof check failed - * Convert to our current Buffer implementation - */ - bufferData = Buffer.from(data); - } else { - throw new Error('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.'); + return Buffer.from(data); } + throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.'); +} + +CipherBase.prototype.update = function (data, inputEnc, outputEnc) { + var bufferData = toBuffer(data, inputEnc); // asserts correct input type var outData = this._update(bufferData); if (this.hashMode) { return this; diff --git a/test/index.js b/test/index.js index ea2c651..8a3ceeb 100644 --- a/test/index.js +++ b/test/index.js @@ -129,6 +129,72 @@ test('encodings', function (t) { }); }); +test('handle SafeBuffer instances', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var cipher = new Cipher(); + var final = cipher.update(Buffer.from('a0c1', 'hex')).finalName('hex'); + t.equals(final, 'a0c1'); + + t.end(); +}); + +test('handle Uint8Array view', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var buf = new Uint8Array([0, 1, 2, 3, 4, 5]); + var uarr = new Uint8Array(buf.buffer, 2, 3); + + var cipher = new Cipher(); + var final = cipher.update(uarr).finalName('hex'); + t.equals(final, '020304'); + + t.end(); +}); + +test('handle empty Uint8Array instances', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var cipher = new Cipher(); + var final = cipher.update(new Uint8Array(0)).finalName('hex'); + t.equals(final, ''); + + t.end(); +}); + test('handle UInt16Array', function (t) { function Cipher() { CipherBase.call(this, 'finalName');