Search code examples
javascriptnode.jsutf-8denoucs2

Build a UCS-2 encoded HEX string from a Javascript default string encoding


How to build a UCS-2 encoded HEX string from a Javascript "basic" string, but only using web api (browser or Deno) features ?

In other words, how to convert the following node code but for the browser ?

message = "hello world 😃"
const buf_le = Buffer.from(message, "ucs2"); // 'ucs2' is as same as 'utf16le'
let buf = Buffer.alloc(buf_le.length); // convert to utf16be (ucs2)
for (let i = 0; i < buf.length; i += 2)
  buf.writeUInt16BE(buf_le.readUInt16LE(i), i);
hex = buf.toString("hex");

It will be used to encode text to PDU for SMS sendings.

Thanks!


Solution

  • You can use this solution from How to convert ArrayBuffer to and from String - Chrome Developers (usage of var updated to use const or let):

    function str2ab(str) {
      const buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
      const bufView = new Uint16Array(buf);
      for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
      }
      return buf;
    }
    

    It uses a Uint16Array as the view into the ArrayBuffer. "The Uint16Array typed array represents an array of 16-bit unsigned integers in the platform byte order. If control over byte order is needed, use DataView instead."

    To get a hex representation there are several options:

    1. view the buf using a Uint16Array instance so that you don't need to concern yourself with your platform's byte order:

      function uint16Array2hex(array) {
        return Array.from(array, (b) => b.toString(16).padStart(4, "0")).join("");
      }
      
    2. update str2ab to use a DataView instead of Uint16Array so that you can write directly to network byte order (big endian):

      function str2ab(str) {
        const buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
        const bufView = new DataView(buf);
        for (let i = 0, strLen = str.length; i < strLen; i++) {
          bufView.setUint16(2 * i, str.charCodeAt(i));
        }
        return buf;
      }
      
      function buf2hex(buf) {
        const bufView = new Uint8Array(buf);
        return Array.from(bufView, (b) => b.toString(16).padStart(2, "0")).join("");
      }
      
    3. Keep str2ab as-is from Chrome Developers and then convert from platform endianness to big-endian by reading from the buf using Uint16Array and writing to a buffer using a DataView like seen above.