46

I want to convert a hexadecimal string like bada55 into a Uint8Array and back again.

David Braun
  • 5,573
  • 3
  • 36
  • 42

4 Answers4

56

Vanilla JS:

const fromHexString = (hexString) =>
  Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));

const toHexString = (bytes) =>
  bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');

console.log(toHexString(Uint8Array.from([0, 1, 2, 42, 100, 101, 102, 255])));
console.log(fromHexString('0001022a646566ff'));

Note: this method trusts its input. If the provided input has a length of 0, an error will be thrown. If the length of the hex-encoded buffer is not divisible by 2, the final byte will be parsed as if it were prepended with a 0 (e.g. aaa is interpreted as aa0a).

If the hex is potentially malformed or empty (e.g. user input), check its length and handle the error before calling this method, e.g.:

const isHex = (maybeHex) =>
  maybeHex.length !== 0 && maybeHex.length % 2 === 0 && !/[^a-fA-F0-9]/u.test(maybeHex);

const missingLetter = 'abc';

if (!isHex(missingLetter)) {
  console.log(`The string "${missingLetter}" is not valid hex.`)
} else {
  fromHexString(missingLetter);
}

Source: the Libauth library (hexToBin method)

Jason Dreyzehner
  • 1,049
  • 10
  • 15
34

Node.js

For JavaScript running on Node you can do this:

const hexString = 'bada55';

const hex = Uint8Array.from(Buffer.from(hexString, 'hex'));

const backToHexString = Buffer.from(hex).toString('hex');

(source: this answer by @Teneff, shared with permission)

Brian Adams
  • 43,011
  • 9
  • 113
  • 111
4

Here's a solution in native JavaScript:

var string = 'bada55';
var bytes = new Uint8Array(Math.ceil(string.length / 2));
for (var i = 0; i < bytes.length; i++) bytes[i] = parseInt(string.substr(i * 2, 2), 16);
console.log(bytes);

var convertedBack = '';
for (var i = 0; i < bytes.length; i++) {
  if (bytes[i] < 16) convertedBack += '0';
  convertedBack += bytes[i].toString(16);
}
console.log(convertedBack);
csander
  • 1,385
  • 10
  • 11
4

Browser (mimic NodeJS behavior acurately)

I had a need to mimic the NodeJS Buffer.from(x, 'hex') behavior in the browser environment and This is what I came up with.

It's pretty fast when I ran it through some benchmarks.

const HEX_STRINGS = "0123456789abcdef";
const MAP_HEX = {
  0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6,
  7: 7, 8: 8, 9: 9, a: 10, b: 11, c: 12, d: 13,
  e: 14, f: 15, A: 10, B: 11, C: 12, D: 13,
  E: 14, F: 15
};

// Fast Uint8Array to hex
function toHex(bytes) {
  return Array.from(bytes || [])
    .map((b) => HEX_STRINGS[b >> 4] + HEX_STRINGS[b & 15])
    .join("");
}

// Mimics Buffer.from(x, 'hex') logic
// Stops on first non-hex string and returns
// https://github.com/nodejs/node/blob/v14.18.1/src/string_bytes.cc#L246-L261
function fromHex(hexString) {
  const bytes = new Uint8Array(Math.floor((hexString || "").length / 2));
  let i;
  for (i = 0; i < bytes.length; i++) {
    const a = MAP_HEX[hexString[i * 2]];
    const b = MAP_HEX[hexString[i * 2 + 1]];
    if (a === undefined || b === undefined) {
      break;
    }
    bytes[i] = (a << 4) | b;
  }
  return i === bytes.length ? bytes : bytes.slice(0, i);
}
user3074620
  • 3,637
  • 1
  • 14
  • 19