Converting two Uint32Array values to Javascript number - javascript

I found a code from here that converts Javascript number to inner IEEE representation as two Uint32 values:
function DoubleToIEEE(f)
{
var buf = new ArrayBuffer(8);
(new Float64Array(buf))[0] = f;
return [ (new Uint32Array(buf))[0] ,(new Uint32Array(buf))[1] ];
}
How to convert the returned value back to Javascript number? This way:
var number = -10.3245535;
var ieee = DoubleToIEEE(number)
var number_again = IEEEtoDouble(ieee);
// number and number_again should be the same (if ever possible)

That code is ugly as hell. Use
function DoubleToIEEE(f) {
var buf = new ArrayBuffer(8);
var float = new Float64Array(buf);
var uint = new Uint32Array(buf);
float[0] = f;
return uint;
}
If you want an actual Array instead of a Uint32Array (shouldn't make a difference in the most cases), add an Array.from call. You can also reduce this to a oneliner by passing the value to the Float64Array constructor:
function DoubleToIEEE(f) {
// use either
return new Uint32Array(Float64Array.of(f).buffer);
return Array.from(new Uint32Array(Float64Array.of(f).buffer));
return Array.from(new Uint32Array((new Float64Array([f])).buffer));
}
The inverse would just write the inputs into the uint slots and return the float[0] value:
function IEEEToDouble(is) {
var buf = new ArrayBuffer(8);
var float = new Float64Array(buf);
var uint = new Uint32Array(buf);
uint[0] = is[0];
uint[1] = is[1];
return float[0];
}
which can be shortened to
function IEEEToDouble(is) {
return (new Float64Array(Uint32Array.from(is).buffer))[0];
}

I found a possible solution, which seems to work:
function IEEEToDouble(f)
{
var buffer = new ArrayBuffer(8);
(new Uint32Array(buffer))[0] = f[0];
(new Uint32Array(buffer))[1] = f[1];
return new Float64Array(buffer)[0];
}
Usage:
var a = DoubleToIEEE(-0.1234);
console.log(a); // [0, 3220176896]
var b = IEEEToDouble(a);
console.log(b); // -0.1234

Related

Convert javascript array to Float32Array to buffer and Back

I have a JS array with floats like so [0.0028808217, -0.027968751, -0.029748825] and I transform it to a Float32 Buffer with the following function:
function toFloat32Buffer(array) {
return Buffer.from(new Float32Array(array).buffer)
}
The issue I'm facing is making the conversion from the Float32 Buffer back to a JS array, so I can use the same values as the original array.
I've tried
function toJSArray(buffer) {
const newBuffer = Buffer.from(buffer)
const floatArray = new Float32Array(newBuffer)
return Array.from(floatArray)
}
function toFloat32Buffer(arr) {
return Buffer.from(new Float32Array(arr).buffer)
}
const array = [0.0028808217, -0.027968751, -0.029748825]
const bufferFromFloat32 = toFloat32Buffer(array);
const array2 = toJSArray(bufferFromFloat32);
console.log('array', array)
console.log('array2', array2)
array and array2 have different values. How should I transform back the buffer to get the same values as the original array? I'm running this in NodeJS
This is alternative solution. Considering using alloc
function bufferToArray(buffer, size) {
let arr = [];
for (let i = 0; i < buffer.length / size; i++)
arr.push(buffer.readDoubleLE(i * size));
return arr;
}
function arrayToBuffer(arr, size) {
const buffer = Buffer.allocUnsafe(size * arr.length);
arr.map((val, index) => buffer.writeDoubleLE(val, size * index));
return buffer;
}
let array = [0.0028808217, -0.027968751, -0.029748825];
const size = Float64Array.BYTES_PER_ELEMENT; // double-precision
const buffer = arrayToBuffer(array, size);
const array2 = bufferToArray(buffer, size);
console.log(array); // [0.0028808217, -0.027968751, -0.029748825]
console.log(array2); // [0.0028808217, -0.027968751, -0.029748825]
Rewrite your toJsArray function to:
function toJSArray(buffer) {
return new Float32Array(buffer.buffer);
}
The buffer property of buffer returns the underlying ArrayBuffer of the buffer.
But as #Matt mentioned in his comment:
Javascript numbers are double-precision 64-bit. I don't think you can
get back the precision that is removed converting to 32bit, short of
storing it somewhere else. – Matt
For more details: buff.buffer

Javascript combine ArrayBuffer parts

I need to combine 2 parts of 2 existing arrayBuffers into a new one.
I am building a parser and the data comes in arraybuffers of random sizes, the data will spill over the end of one, into the beginning of the other. So I need to create a new output buffer and copy in a portion of the end of one buffer and a portion of the beginning of the other. The output will just be an Arraybuffer.
Starting out with this demo, I was going to make Uint8Arrays with some offsets then use set, the problem is certain combinations throw Invalid typed array length. I will not know the length of each array or offsets beforehand.
var buffer1 = new ArrayBuffer(8);
var buffer2 = new ArrayBuffer(8);
var buffer3 = new ArrayBuffer(8);
var uint8_1 = new Uint8Array(buffer1);
var uint8_2 = new Uint8Array(buffer2);
var uint8_3 = new Uint8Array(buffer3);
uint8_1.fill(1);
uint8_2.fill(2);
var uint8_1_slice = new Uint8Array(buffer1 , 0 , 3);
var uint8_2_slice = new Uint8Array(buffer2 , 4, 7);
For this demo need to get buffer3 to be 1,1,1,1,2,2,2,2.
Cannot Use Slice
I have seen some people only use array.length. It's fine as long as the array is only 1 byte per element. It's also fine if the other typed arrays are filled up but in this example a2 isn't. That is why it's better to use byteLength this is also how Blob constructor concatenate the parts.
// Concatenate a mix of typed arrays
function concatenate(...arrays) {
// Calculate byteSize from all arrays
let size = arrays.reduce((a,b) => a + b.byteLength, 0)
// Allcolate a new buffer
let result = new Uint8Array(size)
// Build the new array
let offset = 0
for (let arr of arrays) {
result.set(arr, offset)
offset += arr.byteLength
}
return result
}
// the total length of 1-3 = 5
// the total byteLength of 1-3 = 6
let a1 = Uint8Array.of(1, 2) // [1, 2]
let a2 = Uint16Array.of(3) // [3] just for the fun of it 16 takes up 2 bytes
let a3 = Uint8Array.of(4, 5) // [4, 5]
concatenate(a1, a2, a3) // [1, 2, 3, 0, 4, 5]
/********/
var blob = new Blob([a1, a2, a3])
var res = new Response(blob)
res.arrayBuffer().then(buffer => console.log(new Uint8Array(buffer)))
// [1, 2, 3, 0, 4, 5]
For this demo need to get buffer3 to be 1,1,1,1,2,2,2,2.
You can use for loop, set uint8_3 to uint8_1 value if variable n is less than uint8_1.byteLength / 2 else set uint8_3 to value at uint8_2 .
var len = 8;
var buffer1 = new ArrayBuffer(len);
var buffer2 = new ArrayBuffer(len);
var buffer3 = new ArrayBuffer(len);
var uint8_1 = new Uint8Array(buffer1);
var uint8_2 = new Uint8Array(buffer2);
var uint8_3 = new Uint8Array(buffer3);
uint8_1.fill(1);
uint8_2.fill(2);
// `len` : uint8_1.byteLength / 2 + uint8_2.byteLength / 2
for (var n = 0; n < len; n++) {
uint8_3[n] = n < len / 2 ? uint8_1[n] : uint8_2[n];
}
console.log(uint8_3);

How to append bytes, multi-bytes and buffer to ArrayBuffer in javascript?

Javascript ArrayBuffer or TypedArrays dont have any kind of appendByte(), appendBytes(), or appendBuffer() methods. So if I want to fill an ArrayBuffer one value at a time, how do I do it?
var firstVal = 0xAB; // 1 byte
var secondVal = 0x3D7F // 2 bytes
var anotherUint8Array = someArr;
var buffer = new ArrayBuffer(); // I don't know the length yet
var bufferArr = new UInt8Array(buffer);
// following methods do not exist. What are the alternatives for each??
bufferArr.appendByte(firstVal);
bufferArr.appendBytes(secondVal);
bufferArr.appendBuffer(anotherUint8Array);
You can create a new TypedArray with a new ArrayBuffer, but you can't change the size of an existing buffer
function concatTypedArrays(a, b) { // a, b TypedArray of same type
var c = new (a.constructor)(a.length + b.length);
c.set(a, 0);
c.set(b, a.length);
return c;
}
Now can do
var a = new Uint8Array(2),
b = new Uint8Array(3);
a[0] = 1; a[1] = 2;
b[0] = 3; b[1] = 4;
concatTypedArrays(a, b); // [1, 2, 3, 4, 0] Uint8Array length 5
If you want to use different types, go via Uint8Array as the smallest unit is a byte, i.e.
function concatBuffers(a, b) {
return concatTypedArrays(
new Uint8Array(a.buffer || a),
new Uint8Array(b.buffer || b)
).buffer;
}
This means .length will work as expected, you could now convert this to your typed array of choice (make sure it's a type that would accept the .byteLength of the buffer though)
From here, you could now implement any method you like for concatenating your data, e.g.
function concatBytes(ui8a, byte) {
var b = new Uint8Array(1);
b[0] = byte;
return concatTypedArrays(ui8a, b);
}
var u8 = new Uint8Array(0);
u8 = concatBytes(u8, 0x80); // [128]
Paul's answer allows you to concatenate one TypedArray to an existing TypedArray. In ES6, you can use the following function to concatenate multiple TypedArrays:
function concatenate(resultConstructor, ...arrays) {
let totalLength = 0;
for (const arr of arrays) {
totalLength += arr.length;
}
const result = new resultConstructor(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
const ta = concatenate(Uint8Array,
Uint8Array.of(1, 2), Uint8Array.of(3, 4));
console.log(ta); // Uint8Array [1, 2, 3, 4]
console.log(ta.buffer.byteLength); // 4
To append a new byte is:
const byte = 3;
concatenate(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(byte));
This method is found in ExploringJS.

Parsing string into dictionary

i would like to transform in an elegent way this kind of string :
"{lastname=Oba, firstname=Bar}"
to
var person = [];
person["firstName"] = "Bar";
person["lastName"] = "Oba";
I try with JSON parse but not working,
i don't know how to that without dirty spliting
Algorithm:
splitting string by , you'll get a list of A=B pairs, then split each pair of the list by = and form dictionary:
Python
s = "{lastname=Oba, firstname=Bar}"
l = s[1:-1].split(",")
d = {}
for e in l:
a = e.split("=")
d[a[0].strip()] = a[1].strip()
JavaScript
var s = "{lastname=Oba, firstname=Bar}";
var l = s.slice(1,-1).split(',');
var d = {};
for (var i in l) {
var a = l[i].split('=');
d[a[0].trim()] = a[1].trim();
}
This code should easily parse this.
function myParser(str) {
return str.slice(1, -1).split(',').map(function(el) {
return el.trim()
}).map(function(el) {
return el.split('=')
}).reduce(function(result, pair) {
result[pair[0].trim()] = pair[1].trim();
return result
}, {});
}
It will work as expected only for small group of properties and datas, but figuring these cases and fixing that behaviour is leaved as exercise for reader.

What's the most straightforward way to copy an ArrayBuffer object?

I'm working with ArrayBuffer objects, and I would like to duplicate them. While this is rather easy with actual pointers and memcpy, I couldn't find any straightforward way to do it in Javascript.
Right now, this is how I copy my ArrayBuffers:
function copy(buffer)
{
var bytes = new Uint8Array(buffer);
var output = new ArrayBuffer(buffer.byteLength);
var outputBytes = new Uint8Array(output);
for (var i = 0; i < bytes.length; i++)
outputBytes[i] = bytes[i];
return output;
}
Is there a prettier way?
I prefer the following method
function copy(src) {
var dst = new ArrayBuffer(src.byteLength);
new Uint8Array(dst).set(new Uint8Array(src));
return dst;
}
It appears that simply passing in the source dataview performs a copy:
var a = new Uint8Array([2,3,4,5]);
var b = new Uint8Array(a);
a[0] = 6;
console.log(a); // [6, 3, 4, 5]
console.log(b); // [2, 3, 4, 5]
Tested in FF 33 and Chrome 36.
ArrayBuffer is supposed to support slice (http://www.khronos.org/registry/typedarray/specs/latest/) so you can try,
buffer.slice(0);
which works in Chrome 18 but not Firefox 10 or 11. As for Firefox, you need to copy it manually. You can monkey patch the slice() in Firefox because the Chrome slice() will outperform a manual copy. This would look something like,
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
var that = new Uint8Array(this);
if (end == undefined) end = that.length;
var result = new ArrayBuffer(end - start);
var resultArray = new Uint8Array(result);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i + start];
return result;
}
Then you can call,
buffer.slice(0);
to copy the array in both Chrome and Firefox.
Hmmm... if it's the Uint8Array you want to slice (which logically, it should be), this may work.
if (!Uint8Array.prototype.slice && 'subarray' in Uint8Array.prototype)
Uint8Array.prototype.slice = Uint8Array.prototype.subarray;
Faster and slightly more complicated version of chuckj's answer. Should use ~8x less copy operations on large Typed Arrays. Basically we copy as much 8-byte chunks as possible and then copy the remaining 0-7 bytes. This is especially useful in current version of IE, since it doesn't have slice method implemented for ArrayBuffer.
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
if (end == undefined) end = that.length;
var length = end - start;
var lengthDouble = Math.floor(length / Float64Array.BYTES_PER_ELEMENT);
// ArrayBuffer that will be returned
var result = new ArrayBuffer(length);
var that = new Float64Array(this, start, lengthDouble)
var resultArray = new Float64Array(result, 0, lengthDouble);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
// copying over the remaining bytes
that = new Uint8Array(this, start + lengthDouble * Float64Array.BYTES_PER_ELEMENT)
resultArray = new Uint8Array(result, lengthDouble * Float64Array.BYTES_PER_ELEMENT);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
return result;
}
Wrap a Buffer around the ArrayBuffer. This is shared memory and no copy is made. Then create a new Buffer from the wrapping Buffer. This will copy the data. Finally get a reference to the new Buffer's ArrayBuffer.
This is the most straighforward way I can find. The most efficient? Perhaps.
const wrappingBuffer = Buffer.from(arrayBuffer)
const copiedBuffer = Buffer.from(wrappingBuffer)
const copiedArrayBuffer = copiedBuffer.buffer
In some cases (like webaudio Audiobuffers) you only have a reference to the 2 arrays.
So if you have array1 as a float32Array and array2 as a float32Array,
you must do an element by element copy.
To do so you can use different methods.
var ib=z.inputBuffer.getChannelData(0);
var ob=z.outputBuffer.getChannelData(0);
this
ib.forEach((chd,i)=>ob[i]=chd);
or this nicer and probably faster
ob.set(ib);
That's because Array.set populates an existing array with multiple data (even from another array)
Some of the operations above only do "shallow" copies. When working with workers and transferable arrays, you need to do a deep copy.
function copyTypedArray(original, deep){
var copy;
var kon = original.constructor;
if(deep){
var len = original.length;
copy = new kon(len);
for (var k=len; --k;) {
copy[k] = original[k];
}
} else {
var sBuf = original.buffer;
copy = new kon(sBuf);
copy.set(original);
}
return copy;
}
HINT (for the confused): Typed Arrays contain an ArrayBuffer, which can be obtained via the "buffer" property.
var arr = new Float32Array(8);
arr.buffer <-- this is an ArrayBuffer
If you're in the browser you can do:
const copy = structuredClone(buffer);

Categories