Skip to content

Instantly share code, notes, and snippets.

@bmeurer
Last active May 31, 2016 18:43
Show Gist options
  • Save bmeurer/e7f61c7dd2d0fe92ae74da3b99f8d5fa to your computer and use it in GitHub Desktop.
Save bmeurer/e7f61c7dd2d0fe92ae74da3b99f8d5fa to your computer and use it in GitHub Desktop.

Converting an array of char codes to a String

There's currently String.fromCharCode and String.fromCodePoint in EcmaScript to convert a (sequence of) character code(s)/code point(s) to a String, but this only works if the input codes are passed as parameters to these String builtins. So a common pattern in JavaScript to construct a String from the bytes in an ArrayBuffer (that was read from a file or fetched from some server) is to either use

let array = new Uint8Array(buffer);
String.fromCharCode.apply(void 0, array)

or

let result = '';
let array = new Uint8Array(buffer);
for (let i = 0; i < array.length; ++i) {
  result += String.fromCharCode(array[i]);
}

and both of these solutions have their own set of problems associated with them. The first solution can easily hit the implementation specific stack limit due to the use of Function.prototype.apply, plus it is probably not very efficient across different VMs (even tho particular VMs could try to detect this combination and replace it with an optimized version). The second solution looks better on the surface, but might consume a lot more memory for the resulting string than expected, because of the use of the string addition operator. For example, V8 has this concept of a ConsString, which is used to represent the concatenation of two individual strings, whose combined length exceeds a certain limit. And even if the VM doesn't have ConsStrings, the repeated string addition will be fairly slow.

So from a users perspective, what you really want instead is just some builtin where you can pass an array-like object that contains string char codes or code points, and the builtin turns that into a proper String (in the most efficient way), something along the lines of:

// Apply String.fromCharCode to all elements in an Array
String.fromCharCodes([66, 67, 68, 69])

// Or to all bytes in an ArrayBuffer
String.fromCharCodes(new Uint8Array(buffer))

This could be polyfilled to something like:

String.fromCharCodes = function(source) {
  let arraylike = Object(source);
  let length = arraylike.length;  // Should apply ToLength here.
  let result = '';
  for (let k = 0; k < length; ++k) {
    result += String.fromCharCode(arraylike[k]);
  }
  return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment