How do I convert the last 4 bytes of an array to an integer?
Open the base one ArrayBuffer
and create a new one TypedArray
with a chunk 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 it TypedArray
doesn't cover the entire buffer, you need a little more complex, 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 to fit into the alignment boundary of your base buffer for the data type (for example, you want a 32 bit byte value 4-8 of the base buffer), you can avoid copying the bytes with slice()
and just supply byteoffset to a constructor like in the answer @Bergi.
Below is a very easily tested function that should get a scalar value of whatever offset you want. This 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)
source to share
A bit inelegant, but if you can do it manually based on the entianna.
Little horse:
var count = 0;
// assuming the array has at least four elements
for(var i = array.length - 1; i >= array.length - 4; i--)
{
count = count << 8 + array[i];
}
Big endian:
var count = 0;
// assuming the array has at least four elements
for(var i = array.length - 4; i <= array.length - 1 ; i++)
{
count = count << 8 + array[i];
}
This can be extended to other data lengths
Edit: thanks to David for pointing out my typos
source to share
Your best bet is to create a view in the same ArrayBuffer and access the 32-bit number directly:Uint32Array
var uint8array = new Uint8Array([1,2,3,4,5,6,7,8]);
var uint32array = new Uint32Array(
uint8array.buffer,
uint8array.byteOffset + uint8array.byteLength - 4,
1 // 4Bytes long
);
return uint32array[0];
source to share
Nowadays, if you can live with IE 11+ / Chrome 49+ / Firefox 50+ , then you can use DataView to make your life almost as simple as in C #:
var u8array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); // -1
var view = new DataView(u8array.buffer)
console.log("result:" + view.getInt32());
Test it out here: https://jsfiddle.net/3udtek18/1/
source to share
Too bad there are no ways to do this. I needed to read variables of variable sizes, so based on Imortenson's answer, I wrote this little function where p
is the read position and s
is the number of bytes to read:
function readUInt(arr, p, s) {
var r = 0;
for (var i = s-1; i >= 0; i--) {
r |= arr[p + i] << (i * 8);
} return r >>> 0;
}
var iable = readUint(arr, arr.length - 4, 4);
source to share