Search code examples
javascriptarraystyped-arrays

Initializing ArrayData from characters and byte values as in C


I have to implement a binary protocol that is supposed to go over WebSockets.

In C I'd write test data such as :

const char test_message[16] = { '/', 'f', 'o', 'o', 0, 0, 0, 0, ',', 'i', 0, 0, 123, 0, 0, 0 };

(this is a valid message in the protocol I'm implementing).

How to generate a JavaScript ArrayData containing the same bytes in the easiest way (for the programmer) ?

  • I do not want to look up ASCII codes corresponding to the bytes I want to send.

  • I do not want to index each character one by one, e.g. data[0] = '/'; data[1] = 'f'; ....


Solution

  • Assuming you want to end up with a Uint8Array or similar, and assuming the only textual characters you'll use are ASCII, you could do:

    const message = toMessage(["/", "f", "o", "o", 0, 0, 0, 0, ",", "i", 0, 0, 123, 0, 0, 0]);
    // or even
    const message = toMessage(["/foo", 0, 0, 0, 0, ",i", 0, 0, 123, 0, 0, 0]);
    

    For the first one, toMessage might be:

    function toMessage(source) {
        const array = new Uint8Array(source.length);
        source.forEach((element, index) => {
            array[index] = typeof element === "string" ? element.charCodeAt(0) : element;
        });
        return array;
    }
    

    Live example:

    function toMessage(source) {
        const array = new Uint8Array(source.length);
        source.forEach((element, index) => {
            array[index] = typeof element === "string" ? element.charCodeAt(0) : element;
        });
        return array;
    }
    
    const message = toMessage(["/", "f", "o", "o", 0, 0, 0, 0, ",", "i", 0, 0, 123, 0, 0, 0]);
    
    console.log(message);

    If you want the second one that lets you do runs of simple characters as strings, it's slightly more complicated.

    function toMessage(source) {
        let length = 0;
        for (const element of source) {
            length += typeof element === "string" ? element.length : 1;
        }
        const array = new Uint8Array(length);
        let index = 0;
        for (const element of source) {
            if (typeof element === "string") {
                for (const ch of element) {
                    array[index++] = ch.charCodeAt(0);
                }
            } else {
                array[index++] = element;
            }
        }
        source.forEach((element, index) => {
        });
        return array;
    }
    

    Live Example:

    function toMessage(source) {
        let length = 0;
        for (const element of source) {
            length += typeof element === "string" ? element.length : 1;
        }
        const array = new Uint8Array(length);
        let index = 0;
        for (const element of source) {
            if (typeof element === "string") {
                for (const ch of element) {
                    array[index++] = ch.charCodeAt(0);
                }
            } else {
                array[index++] = element;
            }
        }
        source.forEach((element, index) => {
        });
        return array;
    }
    
    const message = toMessage(["/foo", 0, 0, 0, 0, ",i", 0, 0, 123, 0, 0, 0]);
    
    console.log(message);

    Those are both off-the-cuff and can be optimized if necessary, or adapted to produce something other than a Uint8Array, but the give you the idea.


    Alternatively, if the text characters you want to use are just in tye 7-bit ASCII printable range, that's only 96 characters. You could easily have consts for them:

    const L_a = 65;
    const L_b = 66;
    // ...
    const L_A = 97;
    const L_B = 98;
    // ...
    

    (The list is easily generated, you don't have to type it out.)

    Then you wouldn't need a function at all:

    const message = UInt8Array.of([L_SLASH, L_f, L_o, L_o, 0, 0, 0, 0, L_COMMA, L_i, 0, 0, 123, 0, 0, 0]);
    

    There is no literal form for typed arrays, so it does involve a function call.