Skip to content

Commit c8f6626

Browse files
ChALkeRljharb
authored andcommitted
[Fix] support multi-byte wide typed arrays
1 parent 8befb1e commit c8f6626

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

Diff for: .eslintrc

+12
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,17 @@
6060
"max-lines-per-function": "off",
6161
},
6262
},
63+
{
64+
"files": "hash.js",
65+
"globals": {
66+
"Uint8Array": false,
67+
},
68+
},
69+
{
70+
"files": "test/test.js",
71+
"globals": {
72+
"Uint16Array": false,
73+
},
74+
},
6375
],
6476
}

Diff for: hash.js

+33-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
var Buffer = require('safe-buffer').Buffer;
44

5+
var useArrayBuffer = typeof ArrayBuffer !== 'undefined'
6+
&& typeof Uint8Array !== 'undefined'
7+
&& ArrayBuffer.isView;
8+
59
// prototype class for hash functions
610
function Hash(blockSize, finalSize) {
711
this._block = Buffer.alloc(blockSize);
@@ -12,9 +16,37 @@ function Hash(blockSize, finalSize) {
1216

1317
Hash.prototype.update = function (data, enc) {
1418
/* eslint no-param-reassign: 0 */
15-
if (typeof data === 'string') {
19+
if (data instanceof Uint8Array) {
20+
/*
21+
* Fast path
22+
* Already single-byte wide and 0-255
23+
*/
24+
} else if (typeof data === 'string') {
1625
enc = enc || 'utf8';
1726
data = Buffer.from(data, enc);
27+
} else if (useArrayBuffer && ArrayBuffer.isView(data)) {
28+
// Convert all TypedArray and DataView instances to single-byte-wide Uint8Array views
29+
var oldSize = data.byteLength;
30+
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
31+
32+
if (!(data.byteLength === oldSize && data.byteLength === data.length)) {
33+
throw new Error('Unexpected: broken Uint8Array');
34+
}
35+
} else {
36+
if (!data || typeof data !== 'object' || typeof data.length !== 'number') {
37+
throw new TypeError('Not an array-like');
38+
}
39+
40+
// non-negative 32-bit integer
41+
if ((data.length >>> 0) !== data.length) {
42+
throw new RangeError('Invalid length');
43+
}
44+
45+
for (var j = 0; j < data.length; j++) {
46+
if ((data[j] & 255) !== data[j]) {
47+
throw new TypeError('Not a byte array');
48+
}
49+
}
1850
}
1951

2052
var block = this._block;

Diff for: test/test.js

+28-4
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ var inputs = [
1515
['123456789abcdef123456789abcdef123456789abcdef123456789ab', 'ascii'],
1616
['0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde', 'ascii'],
1717
['0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', 'ascii'],
18-
['foobarbaz', 'ascii']
19-
];
18+
['foobarbaz', 'ascii'],
19+
typeof Uint16Array === 'function' ? [new Uint16Array([1, 2, 3])] : null
20+
].filter(Boolean);
2021

2122
tape("hash is the same as node's crypto", function (t) {
2223
inputs.forEach(function (v) {
@@ -35,7 +36,7 @@ tape('call update multiple times', function (t) {
3536
var sha1hash = crypto.createHash('sha1');
3637

3738
for (var i = 0; i < v[0].length; i = (i + 1) * 2) {
38-
var s = v[0].substring(i, (i + 1) * 2);
39+
var s = v[0].slice(i, (i + 1) * 2);
3940
hash.update(s, v[1]);
4041
sha1hash.update(s, v[1]);
4142
}
@@ -74,7 +75,7 @@ tape('hex encoding', function (t) {
7475
var sha1hash = crypto.createHash('sha1');
7576

7677
for (var i = 0; i < v[0].length; i = (i + 1) * 2) {
77-
var s = v[0].substring(i, (i + 1) * 2);
78+
var s = v[0].slice(i, (i + 1) * 2);
7879
hash.update(Buffer.from(s, 'ascii').toString('hex'), 'hex');
7980
sha1hash.update(Buffer.from(s, 'ascii').toString('hex'), 'hex');
8081
}
@@ -88,6 +89,29 @@ tape('hex encoding', function (t) {
8889
t.end();
8990
});
9091

92+
tape('throws on invalid input', function (t) {
93+
var invalid = [
94+
{}, // non-arrayish
95+
{ length: 20 }, // undefined values
96+
[NaN], // non-numbers
97+
[[]], // non-numbers
98+
[1, 1.5], // non-integers
99+
[1, 256], // out of bounds
100+
[-1, 0] // out of bounds
101+
];
102+
103+
invalid.forEach(function (input) {
104+
var hash = new Sha1();
105+
106+
t['throws'](function () {
107+
hash.update(input);
108+
hash.digest('hex');
109+
});
110+
});
111+
112+
t.end();
113+
});
114+
91115
tape('call digest for more than MAX_UINT32 bits of data', function (t) {
92116
var sha1hash = crypto.createHash('sha1');
93117
var hash = new Sha1();

0 commit comments

Comments
 (0)