Skip to content

Commit 1af18ee

Browse files
ChALkeRljharb
authored andcommitted
[Fix] return valid values on multi-byte-wide TypedArray input
1 parent ae228cd commit 1af18ee

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

index.js

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ var Buffer = require('safe-buffer').Buffer
33
var Transform = require('stream').Transform
44
var inherits = require('inherits')
55

6-
function throwIfNotStringOrBuffer (val, prefix) {
7-
if (!Buffer.isBuffer(val) && typeof val !== 'string') {
8-
throw new TypeError(prefix + ' must be a string or a buffer')
9-
}
10-
}
11-
126
function HashBase (blockSize) {
137
Transform.call(this)
148

@@ -44,10 +38,59 @@ HashBase.prototype._flush = function (callback) {
4438
callback(error)
4539
}
4640

41+
var useUint8Array = typeof Uint8Array !== 'undefined'
42+
var useArrayBuffer = typeof ArrayBuffer !== 'undefined' &&
43+
typeof Uint8Array !== 'undefined' &&
44+
ArrayBuffer.isView &&
45+
(Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)
46+
47+
function toBuffer (data, encoding) {
48+
// No need to do anything for exact instance
49+
// This is only valid when safe-buffer.Buffer === buffer.Buffer, i.e. when Buffer.from/Buffer.alloc existed
50+
if (data instanceof Buffer) return data
51+
52+
// Convert strings to Buffer
53+
if (typeof data === 'string') return Buffer.from(data, encoding)
54+
55+
/*
56+
* Wrap any TypedArray instances and DataViews
57+
* Makes sense only on engines with full TypedArray support -- let Buffer detect that
58+
*/
59+
if (useArrayBuffer && ArrayBuffer.isView(data)) {
60+
if (data.byteLength === 0) return Buffer.alloc(0) // Bug in Node.js <6.3.1, which treats this as out-of-bounds
61+
var res = Buffer.from(data.buffer, data.byteOffset, data.byteLength)
62+
// Recheck result size, as offset/length doesn't work on Node.js <5.10
63+
// We just go to Uint8Array case if this fails
64+
if (res.byteLength === data.byteLength) return res
65+
}
66+
67+
/*
68+
* Uint8Array in engines where Buffer.from might not work with ArrayBuffer, just copy over
69+
* Doesn't make sense with other TypedArray instances
70+
*/
71+
if (useUint8Array && data instanceof Uint8Array) return Buffer.from(data)
72+
73+
/*
74+
* Old Buffer polyfill on an engine that doesn't have TypedArray support
75+
* Also, this is from a different Buffer polyfill implementation then we have, as instanceof check failed
76+
* Convert to our current Buffer implementation
77+
*/
78+
if (
79+
Buffer.isBuffer(data) &&
80+
data.constructor &&
81+
typeof data.constructor.isBuffer === 'function' &&
82+
data.constructor.isBuffer(data)
83+
) {
84+
return Buffer.from(data)
85+
}
86+
87+
throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.')
88+
}
89+
4790
HashBase.prototype.update = function (data, encoding) {
48-
throwIfNotStringOrBuffer(data, 'Data')
4991
if (this._finalized) throw new Error('Digest already called')
50-
if (!Buffer.isBuffer(data)) data = Buffer.from(data, encoding)
92+
93+
data = toBuffer(data, encoding) // asserts correct input type
5194

5295
// consume data
5396
var block = this._block

test/index.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ test('HashBase#update', function (t) {
6868
var base = new HashBase(64)
6969
t.throws(function () {
7070
base.update(null)
71-
}, /^TypeError: Data must be a string or a buffer$/)
71+
}, /^TypeError: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.$/)
7272
t.end()
7373
})
7474

@@ -132,6 +132,25 @@ test('HashBase#update', function (t) {
132132
t.end()
133133
})
134134

135+
t.test(
136+
'handle UInt16Array',
137+
{
138+
skip: !(
139+
ArrayBuffer.isView &&
140+
(Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)
141+
) && 'ArrayBuffer.isView and TypedArray fully supported'
142+
},
143+
function (t) {
144+
var base = new HashBase(64)
145+
146+
base._update = noop
147+
base.update(new Uint16Array([1234, 512]))
148+
t.same(base._block.slice(0, base._blockOffset), Buffer.from('d2040002', 'hex'))
149+
150+
t.end()
151+
}
152+
)
153+
135154
t.end()
136155
})
137156

0 commit comments

Comments
 (0)