Pack and unpack bytes to strings I need to write a function that "packs" an array of bytes (integers between 0 and 255) into a string. I also need to be able to perform the reverse operation, to get my byte array from the string that it was packed into. This needs to be done as fast as possible. Seeing as JavaScript has 16-bit strings, I packed two bytes per character. Here is my code and tests:

function pack(bytes) {
    var str = "";
    for(var i = 0; i &lt; bytes.length; i += 2) {
        var char = bytes[i] &lt;&lt; 8;
        if (bytes[i + 1])
            char |= bytes[i + 1];
        str += String.fromCharCode(char);
    }
    return str;
}

function unpack(str) {
    var bytes = [];
    for(var i = 0; i &lt; str.length; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char &gt;&gt;&gt; 8);
        bytes.push(char &amp; 0xFF);
    }
    return bytes;
}

var tests = [
    [],
    [126, 0],
    [0, 65],
    [12, 34, 56],
    [0, 50, 100, 150, 200, 250]
];

console.log("starting tests");
tests.forEach(function(v) {
    var p = pack(v);
    console.log(v, p, unpack(p));
});


And the output to that is:

starting tests
[] "" []
[126, 0] "縀" [126, 0]
[0, 65] "A" [0, 65]
[12, 34, 56] "ఢ㠀" [12, 34, 56, 0]
[0, 50, 100, 150, 200, 250] "2撖죺" [0, 50, 100, 150, 200, 250]


I have a few things I'd like feedback on:


This was the first time I used bitwise operators. Is this how it should be done?
Are there any speed improvements that could be made?
Can you guys think of any way to discard that last 0 byte when encoding then decoding an array with an odd number of bytes? (see test #4)
function pack(bytes) {
    var str = "";
// You could make it faster by reading bytes.length once.
    for(var i = 0; i &lt; bytes.length; i += 2) {
// If you're using signed bytes, you probably need to mask here.
        var char = bytes[i] &lt;&lt; 8;
// (undefined | 0) === 0 so you can save a test here by doing
//     var char = (bytes[i] &lt;&lt; 8) | (bytes[i + 1] &amp; 0xff);
        if (bytes[i + 1])
            char |= bytes[i + 1];
// Instead of using string += you could push char onto an array
// and take advantage of the fact that String.fromCharCode can
// take any number of arguments to do
//     String.fromCharCode.apply(null, chars);
        str += String.fromCharCode(char);
    }
    return str;
}

function unpack(str) {
    var bytes = [];
    for(var i = 0; i &lt; str.length; i++) {
        var char = str.charCodeAt(i);
// You can combine both these calls into one,
//    bytes.push(char &gt;&gt;&gt; 8, char &amp; 0xff);
        bytes.push(char &gt;&gt;&gt; 8);
        bytes.push(char &amp; 0xFF);
    }
    return bytes;
}


so to put it all together

function pack(bytes) {
    var chars = [];
    for(var i = 0, n = bytes.length; i &lt; n;) {
        chars.push(((bytes[i++] &amp; 0xff) &lt;&lt; 8) | (bytes[i++] &amp; 0xff));
    }
    return String.fromCharCode.apply(null, chars);
}

function unpack(str) {
    var bytes = [];
    for(var i = 0, n = str.length; i &lt; n; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char &gt;&gt;&gt; 8, char &amp; 0xFF);
    }
    return bytes;
}