Skip to content

Commit 28cf2e9

Browse files
feat: Improve performance of Uint8Array Hex functions (#1510)
* feat: Improve performance of Uint8Array Hex functions * Typo fix * op prec * Update uint8-from-hex.js * Update uint8-from-hex.js * Update uint8-from-hex.js * Fix * Improvements * Lint * Lint, cl * Requested Changes. * Cache min * Fix tests * some fixes --------- Co-authored-by: Denis Pushkarev <[email protected]>
1 parent e1a3606 commit 28cf2e9

4 files changed

Lines changed: 26 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changelog
22
### Unreleased
3-
- Improved performance of `atob` and `btoa`, [#1503](https://github.com/zloirock/core-js/issues/1503), [#1464](https://github.com/zloirock/core-js/issues/1464)
3+
- Improved performance of `atob`, `btoa`, `Uint8Array.fromHex`, `Uint8Array.prototype.setFromHex`, and `Uint8Array.prototype.toHex`, [#1503](https://github.com/zloirock/core-js/issues/1503), [#1464](https://github.com/zloirock/core-js/issues/1464), [#1510](https://github.com/zloirock/core-js/issues/1510)
44
- [`Iterator.range`](https://github.com/tc39/proposal-iterator.range) updated following the actual spec version
55
- Throw a `RangeError` on `NaN` `start` / `end` / `step`
66
- Allow `null` as `optionOrStep`

packages/core-js/internals/uint8-from-hex.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@ var uncurryThis = require('../internals/function-uncurry-this');
44

55
var Uint8Array = globalThis.Uint8Array;
66
var SyntaxError = globalThis.SyntaxError;
7-
var parseInt = globalThis.parseInt;
87
var min = Math.min;
9-
var NOT_HEX = /[^\da-f]/i;
10-
var exec = uncurryThis(NOT_HEX.exec);
11-
var stringSlice = uncurryThis(''.slice);
8+
var stringMatch = uncurryThis(''.match);
129

1310
module.exports = function (string, into) {
1411
var stringLength = string.length;
1512
if (stringLength % 2 !== 0) throw new SyntaxError('String should be an even number of characters');
1613
var maxLength = into ? min(into.length, stringLength / 2) : stringLength / 2;
1714
var bytes = into || new Uint8Array(maxLength);
18-
var read = 0;
15+
// This splitting is faster than using substrings each time.
16+
var segments = stringMatch(string, /.{2}/g);
1917
var written = 0;
20-
while (written < maxLength) {
21-
var hexits = stringSlice(string, read, read += 2);
22-
if (exec(NOT_HEX, hexits)) throw new SyntaxError('String should only contain hex characters');
23-
bytes[written++] = parseInt(hexits, 16);
18+
for (; written < maxLength; written++) {
19+
var result = +('0x' + segments[written] + '0');
20+
// eslint-disable-next-line no-self-compare -- NaN check
21+
if (result !== result) {
22+
throw new SyntaxError('String should only contain hex characters');
23+
}
24+
bytes[written] = result >> 4;
2425
}
25-
return { bytes: bytes, read: read };
26+
return { bytes: bytes, read: written << 1 };
2627
};

packages/core-js/modules/es.uint8-array.to-hex.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ var anUint8Array = require('../internals/an-uint8-array');
66
var notDetached = require('../internals/array-buffer-not-detached');
77

88
var numberToString = uncurryThis(1.1.toString);
9+
var join = uncurryThis([].join);
10+
var $Array = Array;
911

1012
var Uint8Array = globalThis.Uint8Array;
1113

@@ -24,11 +26,11 @@ if (Uint8Array) $({ target: 'Uint8Array', proto: true, forced: INCORRECT_BEHAVIO
2426
toHex: function toHex() {
2527
anUint8Array(this);
2628
notDetached(this.buffer);
27-
var result = '';
29+
var result = $Array(this.length);
2830
for (var i = 0, length = this.length; i < length; i++) {
2931
var hex = numberToString(this[i], 16);
30-
result += hex.length === 1 ? '0' + hex : hex;
32+
result[i] = hex.length === 1 ? '0' + hex : hex;
3133
}
32-
return result;
34+
return join(result, '');
3335
}
3436
});

tests/unit-global/es.uint8-array.set-from-hex.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ if (DESCRIPTORS) QUnit.test('Uint8Array.prototype.setFromHex', assert => {
3737
assert.throws(() => array5.setFromHex('48656c6c6f20576f726c64'), TypeError, 'detached');
3838
}
3939

40+
// Should not throw an error with an empty string being set. This verifies that
41+
// we aren't using the result of segments = stringMatch(string, /.{2}/g) unsafely
42+
// in cases where no matches are found, since it returns null instead of []
43+
const arrayEmpty = new Uint8Array(4);
44+
const resultEmpty = arrayEmpty.setFromHex('');
45+
46+
assert.deepEqual(arrayEmpty, new Uint8Array([0, 0, 0, 0]), 'array empty string test');
47+
assert.deepEqual(resultEmpty, { read: 0, written: 0 }, 'result empty string test');
48+
4049
// Should not throw an error on length-tracking views over ResizableArrayBuffer
4150
// https://issues.chromium.org/issues/454630441
4251
assert.notThrows(() => {

0 commit comments

Comments
 (0)