javascriptintegerarraysbitconverter

How to convert last 4 bytes in an array to an integer?


If I have an Uint8Array array in JavaScript, how would I get the last four bytes and then convert that to an int? Using C# I would do something like this:

int count = BitConverter.ToInt32(array, array.Length - 4);

Is there an inequivalent way to do this using JavaScript?


Solution

  • Access the underlying ArrayBuffer and create a new TypedArray with a slice of its bytes:

    var u8 = new Uint8Array([1,2,3,4,5,6]); // original array
    var u32bytes = u8.buffer.slice(-4); // last four bytes as a new `ArrayBuffer`
    var uint = new Uint32Array(u32bytes)[0];
    

    If the TypedArray does not cover the entire buffer, you need to be a little trickier, but not much:

    var startbyte = u8.byteOffset + u8.byteLength - Uint32Array.BYTES_PER_ELEMENT;
    var u32bytes = u8.buffer.slice(startbyte, startbyte + Uint32Array.BYTES_PER_ELEMENT);
    

    This works in both cases.

    If the bytes you want fit in the alignment boundary of your underlying buffer for the datatype (e.g., you want the 32-bit value of bytes 4-8 of the underlying buffer), you can avoid copying the bytes with slice() and just supply a byteoffset to the view constructor, as in @Bergi's answer.

    Below is a very-lightly-tested function that should get the scalar value of any offset you want. It will avoid copying if possible.

    function InvalidArgument(msg) {
        this.message = msg | null;
    }
    
    function scalarValue(buf_or_view, byteOffset, type) {
        var buffer, bufslice, view, sliceLength = type.BYTES_PER_ELEMENT;
        if (buf_or_view instanceof ArrayBuffer) {
            buffer = buf_or_view;
            if (byteOffset < 0) {
                byteOffset = buffer.byteLength - byteOffset;
            }
        } else if (buf_or_view.buffer instanceof ArrayBuffer) {
            view = buf_or_view;
            buffer = view.buffer;
            if (byteOffset < 0) {
                byteOffset = view.byteOffset + view.byteLength + byteOffset;
            } else {
                byteOffset = view.byteOffset + byteOffset;
            }
            return scalarValue(buffer, view.byteOffset + byteOffset, type);
        } else {
            throw new InvalidArgument('buf_or_view must be ArrayBuffer or have a .buffer property');
        }
        // assert buffer instanceof ArrayBuffer
        // assert byteOffset > 0
        // assert byteOffset relative to entire buffer
        try {
            // try in-place first
            // only works if byteOffset % slicelength === 0
            return (new type(buffer, byteOffset, 1))[0]
        } catch (e) {
            // if this doesn't work, we need to copy the bytes (slice them out)
            bufslice = buffer.slice(byteOffset, byteOffset + sliceLength);
            return (new type(bufslice, 0, 1))[0]
        }
    }
    

    You would use it like this:

    // positive or negative byte offset
    // relative to beginning or end *of a view*
    100992003 === scalarValueAs(u8, -4, Uint32Array)
    // positive or negative byte offset
    // relative to the beginning or end *of a buffer*
    100992003 === scalarValue(u8.buffer, -4, Uint32Array)