var saltVal = crypto.getRandomValues(new Uint8Array(16));
function arrayBufferToHexString(bytes) {
bytes = new Uint8Array(bytes);
var hex = new Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
hex[i] = ("0" + bytes[i].toString(16)).slice(-2);
}
return hex.join("");
}
function bytesToHexString(bytes) {
bytes = new Uint8Array(bytes);
var hexBytes = [];
for (var i = 0; i < bytes.length; i++) {
var byteString = bytes[i].toString(16);
if (byteString.length < 2) byteString = "0" + byteString;
hexBytes.push(byteString);
}
return hexBytes.join("");
}
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
function hex(arrayBuffer)
{
const asciiCodes = new Uint8Array(
Array.prototype.map.call(
"0123456789abcdef",
char => char.charCodeAt()
)
);
const buff = new Uint8Array(arrayBuffer);
const charCodes = new Uint8Array(buff.length * 2);
for (let i = 0; i < buff.length; ++i)
{
charCodes[i * 2] = asciiCodes[buff[i] >>> 4];
charCodes[i * 2 + 1] = asciiCodes[buff[i] & 0xf];
}
return String.fromCharCode(charCodes);
}
function hex2(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => n.toString(16).padStart(2, "0")
).join("");
}
function arrayBufferToBase64(buffer) {
const byteArray = new Uint8Array(buffer);
const binaryString = byteArray.reduce((acc, byte) => acc + String.fromCharCode(byte), "");
return btoa(binaryString);
}
function arrayBufferToBase64loop( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function bytesToHexString2(bytes) {
bytes = new Uint8Array(bytes);
var hexBytes = '';
for (var i = 0; i < bytes.length; i++) {
var byteString = bytes[i].toString(16);
if (byteString.length < 2) byteString = "0" + byteString;
hexBytes += byteString;
}
return hexBytes;
}
arrayBufferToHexString(saltVal);
bytesToHexString(saltVal)
buf2hex(saltVal)
hex(saltVal)
hex2(saltVal)
arrayBufferToBase64(saltVal)
arrayBufferToBase64loop(saltVal)
bytesToHexString2(saltVal)
--enable-precise-memory-info
flag.
Test case name | Result |
---|---|
directly set | |
push values | |
buf2hex() | |
precomputed ASCII with shift | |
array prototype map | |
base64 | |
base64 loop | |
string + |
Test name | Executions per second |
---|---|
directly set | 161614.0 Ops/sec |
push values | 196980.7 Ops/sec |
buf2hex() | 67684.2 Ops/sec |
precomputed ASCII with shift | 44176.9 Ops/sec |
array prototype map | 168050.5 Ops/sec |
base64 | 334801.2 Ops/sec |
base64 loop | 436318.1 Ops/sec |
string + | 305059.6 Ops/sec |
The benchmark in question evaluates the performance of different methods for converting an ArrayBuffer to hexadecimal and Base64 string formats. Here's a breakdown of the various approaches that are tested:
Hex Conversion Methods:
arrayBufferToHexString(saltVal);
: Directly constructs a hexadecimal string from each byte in the ArrayBuffer.bytesToHexString(saltVal)
: Similar to the first method but approaches the task using a push
operation in the loop.buf2hex(saltVal)
: Uses modern JavaScript syntax and spreads the Uint8Array to create the hex string.hex(arrayBuffer)
: Uses precomputed ASCII values for hexadecimal characters, efficiently mapping the bytes to their hex equivalents.hex2(arrayBuffer)
: Employs the Array.prototype.map
method to iterate over each byte for conversion.Base64 Conversion Methods:
arrayBufferToBase64(buffer)
: Converts an ArrayBuffer to a Base64 string by creating a binary string from the byte values and using btoa()
for conversion.arrayBufferToBase64loop(buffer)
: Similar to the previous method but constructs the binary string using a loop instead of a reduce
method.A New Hex Conversion Method:
bytesToHexString2(saltVal)
: This method constructs a hex string by concatenating results as an operation, showcasing a different string-building strategy.Directly Set vs. Push Values:
arrayBufferToHexString
: May offer better performance due to no intermediate storage. bytesToHexString
: Slightly less efficient due to operations on arrays but may lead to clearer code.Using Modern Syntax with Spread (buf2hex
):
Precomputed ASCII Mapping (hex
):
Array.prototype.map()
Approach (hex2
):
reduce
(in arrayBufferToBase64
) might be more readable and can leverage functional programming paradigms, while using a loop (in arrayBufferToBase64loop
) is often simpler to grasp and might have different performance characteristics. Generally, loops can outperform higher-order functions in performance-critical paths.The performance measured by Executions Per Second indicates that the simplest concatenation methods (bytesToHexString2
and arrayBufferToBase64loop
) tend to perform the best. This could be attributed to lower computational overhead, as these methods avoid pooling intermediate results. On the other hand, precomputed ASCII with shift
and array prototype map
methods rank lower in speed, likely due to their added complexity and functional overhead.
Beyond the methods explicitly outlined in the benchmark, there are other libraries and approaches that could be considered:
By taking these considerations into account, software engineers can select the appropriate method based on their performance needs, code clarity, and the specific requirements of their project.