Search code examples
javascriptnode.jsnode-streamstyped-arrays

How can source Int16Array values be retained though conversion to Uint8Array


I pass data through functions in this format

Int16Array > Uint8Array > Int16Array

How to make z be the same as the original x

var x = new Int16Array([17, -45]);
var y = new Uint8Array(x);
var z = new Int16Array(y);

Want to make z as [ 17, -45 ]

EDIT: FULL EXAMPLE

const Readable = require('stream').Readable;

const rs = new Readable({
    objectMode: false,
    read(){}
});

var data = new Int16Array([17, -45]);
rs.push(data);

rs.on('data', function(chunk) {
    var result = new Int16Array(chunk);
});

I want "result" to have value [17, -45]


Solution

  • Number 16 bit (BE) In binary (BE) Note
    17 0x00 0x11 00000000 00010001
    -45 0xff 0xd3 11111111 11010011 255,211 in decimal
    45 0x00 0xd3 00000000 00101101 The bits are flipped

    A 16 bit TypedArray with two values in it would need 2 * Int16Array.BYTES_PER_ELEMENT = 4 bytes to store data. You can check that yourself:

    console.log(
      arr16 = new Int16Array([17, -45]),
      arr16.buffer
    );
    
    [17, -45] // two elements (decimal)
    ...<11 00 d3 ff> // of 4 bytes total
    

    Note: 11 00 & d3 ff as opposed to 00 11 & ff d3. The order is different because the representation is Little Endian. Don't worry about that.

    If you pass the buffer to a Int8Array, you'd get:

    console.log(
      arr8 = new Uint8Array( arr16.buffer ),
      arr8.buffer
    );
    
    [17, 0, 211, 255] // four elements (decimal)
    ...<11 00 d3 ff> // but same 4 bytes total
    

    Note: In fact arr8 and arr16 both refer to the exact same bytes, so if you do arr16[0]=3, arr8[0] will change, too!

    The inverse is not that hard to imagine now:

    new Int16Array( arr8.buffer );
    [17, -45] // two elements (decimal)
    

    Thus, your sample would be:

    var x = new Int16Array([17, -45]);
    var y = new Uint8Array(x.buffer); // the .buffer is important!
    var z = new Int16Array(y.buffer);
    
    console.log(x[1] === z[1]); // true;
    

    If, rather than to a UintArray you want to convert to a Node.js Buffer:

    // Important to use .buffer here, otherwise it'll use the decimal
    // representation of your input, not the actual bytes!
    const buffer = Buffer.from(arr16.buffer);
    

    Then you'll need to add:

    new Int16Array( buffer.buffer )
    

    Because if you do this:

    new Int16Array( buffer );
    

    Then you're effectively doing this:

    const decimalRepresentationOfBytesInBuffer = [...buffer.values()];
    new Int16Array( decimalRepresentationOfBytesInBuffer );
    [17, 0, 11, 255] // four elements (decimal)
    ...<11 00 00 00 d3 00 ff 00> // of 8 bytes total