From 8fd136432ca298a664f5637629cf2b42a6c7f294 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Sat, 16 Nov 2024 09:19:57 +0400 Subject: [PATCH] [Fix] return valid values on multi-byte-wide TypedArray input --- .eslintrc | 5 +++++ index.js | 41 ++++++++++++++++++++++++++++++++++++++++- test/index.js | 25 +++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index fb62a75..ff010df 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,6 +7,11 @@ "func-style": "off", }, + "globals": { + "Uint8Array": false, + "Uint16Array": false, + }, + "overrides": [ { "files": [ diff --git a/index.js b/index.js index 39c8bcc..741d913 100644 --- a/index.js +++ b/index.js @@ -22,8 +22,47 @@ function CipherBase(hashMode) { } inherits(CipherBase, Transform); +var useUint8Array = typeof Uint8Array !== 'undefined'; +var useArrayBuffer = typeof ArrayBuffer !== 'undefined' + && typeof Uint8Array !== 'undefined' + && ArrayBuffer.isView + && (Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT); + CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - var bufferData = typeof data === 'string' ? Buffer.from(data, inputEnc) : data; + var bufferData; + 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) { + /* + * Uint8Array in engines where Buffer.from might not work with ArrayBuffer, just copy over + * Doesn't make sense with other TypedArray instances + */ + bufferData = Buffer.from(data); + } else if ( + Buffer.isBuffer(data) + && data.constructor + && data.constructor.isBuffer + && 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.'); + } var outData = this._update(bufferData); if (this.hashMode) { diff --git a/test/index.js b/test/index.js index b612701..ea2c651 100644 --- a/test/index.js +++ b/test/index.js @@ -128,3 +128,28 @@ test('encodings', function (t) { st.equals(txt, enc); }); }); + +test('handle UInt16Array', 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); + }; + + if (ArrayBuffer.isView && (Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)) { + var cipher = new Cipher(); + var final = cipher.update(new Uint16Array([1234, 512])).finalName('hex'); + t.equals(final, 'd2040002'); + } else { + t.skip('ArrayBuffer.isView and/or TypedArray not fully supported'); + } + + t.end(); +});