Skip to content

[Fix] return valid values on multi-byte-wide TypedArray input #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ var Buffer = require('safe-buffer').Buffer
var Transform = require('stream').Transform
var inherits = require('inherits')

function throwIfNotStringOrBuffer (val, prefix) {
if (!Buffer.isBuffer(val) && typeof val !== 'string') {
throw new TypeError(prefix + ' must be a string or a buffer')
}
}

function HashBase (blockSize) {
Transform.call(this)

Expand Down Expand Up @@ -44,10 +38,59 @@ HashBase.prototype._flush = function (callback) {
callback(error)
}

var useUint8Array = typeof Uint8Array !== 'undefined'
var useArrayBuffer = typeof ArrayBuffer !== 'undefined' &&
typeof Uint8Array !== 'undefined' &&
ArrayBuffer.isView &&
(Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)

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) 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)) {
if (data.byteLength === 0) return Buffer.alloc(0) // Bug in Node.js <6.3.1, which treats this as out-of-bounds
var res = Buffer.from(data.buffer, data.byteOffset, data.byteLength)
// Recheck result size, as offset/length doesn't work on Node.js <5.10
// We just go to Uint8Array case if this fails
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 &&
typeof data.constructor.isBuffer === 'function' &&
data.constructor.isBuffer(data)
) {
return Buffer.from(data)
}

throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.')
}

HashBase.prototype.update = function (data, encoding) {
throwIfNotStringOrBuffer(data, 'Data')
if (this._finalized) throw new Error('Digest already called')
if (!Buffer.isBuffer(data)) data = Buffer.from(data, encoding)

data = toBuffer(data, encoding) // asserts correct input type

// consume data
var block = this._block
Expand Down
21 changes: 20 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ test('HashBase#update', function (t) {
var base = new HashBase(64)
t.throws(function () {
base.update(null)
}, /^TypeError: Data must be a string or a buffer$/)
}, /^TypeError: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.$/)
t.end()
})

Expand Down Expand Up @@ -132,6 +132,25 @@ test('HashBase#update', function (t) {
t.end()
})

t.test(
'handle UInt16Array',
{
skip: !(
ArrayBuffer.isView &&
(Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)
) && 'ArrayBuffer.isView and TypedArray fully supported'
},
function (t) {
var base = new HashBase(64)

base._update = noop
base.update(new Uint16Array([1234, 512]))
t.same(base._block.slice(0, base._blockOffset), Buffer.from('d2040002', 'hex'))

t.end()
}
)

t.end()
})

Expand Down