Search code examples
javascripttyped-arrays

Can I make refference to ArrayBuffer at offset?


I convert data from JavaScript variables to ArrayBuffer and back. Think of this function:

function numberToArrayBuffer(number) {
    return new Float64Array([number]).buffer;
}

And other way around:

function numberFromArrayBuffer(buffer) { 
    // Assuming implicitly, that the buffer is long enough
    return new Float64Array(buffer, 0, 1)[0];
}

That works, but imagine you stuff more of these numbers in Blob:

var numbers = new Blob([numberToArrayBuffer(66), numberToArrayBuffer(666), numberToArrayBuffer(NaN)], {type:"binary/Float64Array"});

If you read that using file reader, you can get ArrayBuffer again:

var fl = new FileReader();
fl.onload = function() {
  console.log(Float64Array(this.result));  
}
fl.readAsArrayBuffer(numbers);

And you get something like this:

 Float64Array { 0=66,  1=666,  2=NaN}

But of course, there could be sequences od different types, like 2 unrestricted doubles and 4 uint32_t. So I would like do something like this (but there is no .offset metod):

var num1 = numberFromArrayBuffer(buffer);
// Shift by 8 bytes - if only `offset(8)` was defined
var num2 = byteFromArrayBuffer(buffer.offset(8));

Of course, I can make a "overloaded function":

function numberToArrayBuffer(number, offset) {
    return new Float64Array([number], offset||0).buffer;
}

and use it like this:

var num2 = byteFromArrayBuffer(buffer, 8);

But I'd prefer an equivalent of this C++ code:

void main()
{
    uint8_t* bytes = loadBytesFromSomewhere();
    // Read 4 bytes and make an int
    uint32_t integer = intFromBytes(bytes);
    // Read 2 bytes and make a short
    uint16_t short_integer = shortFromBytes(bytes+4);
    delete bytes;
}

I want this because it's really annoying to put offset aspect to decoding algorithms. At the same time, I really want to be consistent and use ArrayBuffer, which can be turned to any data type array at will.

Is there a trick? I tried this:

//Make 20 bytes
var buffer = new Uint8Array(20);
console.log("Original buffer: ",buffer.byteLength);
//Try to offset the buffer                off cnt
var shiftedArray = new Uint8Array(buffer, 10, 10);
var shiftedBuffer = shiftedArray.buffer;
console.log("Shifted buffer? Length:",shiftedBuffer.byteLength);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>

Not only that it doesn't work, but also exhibits possible dangerous and confusing behavior of typed arrays created with an offset - their length can be significantly smaller than their ArrayBuffer length.


Solution

  • The following may or may not exactly meet your needs, I believe it's the equivalent of what you desired in c++ (minus the delete bytes). Though, this is an old post and you may have your answer already!

    As you already know, ArrayBuffers in javascript can be accessed through a handful of different viewpoints/objects, called TypedArrays. These are objects such as Float64Array, Uint32Array, etc. There is a TypedArray object that allows you to grab data back dynamically to whatever type you would like at target byte offsets called a DataView.

    So to achieve the desired functionality you could do something like the following:

    var buffer = loadByteBufferFromSomewhere();
    
    //DataView would be the same way
    var dv = new DataView(buffer);
    //uint32 at index 0;
    dv.getUint32(0);
    //uint16 starting at byte 4
    dv.getUint16(4);
    
    //get a target byte at given indexes
    dv.getUint8(0);
    dv.getUint8(1);
    

    DataView is a wrapper around the ArrayBuffer like Float64Array, Uint32Array, etc. You can also set target types at given indexes as well. I've found JavaScripture's references really helpful TypedArrays