1

I've seen Copy data into v8::ArrayBuffer which is able to create an array buffer based on data like the following:

QByteArray data_buffer(file.readAll().data(), file.size());
v8::Handle<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(args.GetIsolate(), data_buffer.size());
memcpy(ab->GetContents().Data(), data_buffer.data(), data_buffer.size());

But he doesn't explain how to do this in the ::New constructor itself. In fact he mentions:

If I use the constructor from the documentation in which I pass a pointer to the data, I'll have to deal with the memory myself and I don't want that.

As the [documentation][1] says:

◆ New() [2/2]

static Local<ArrayBuffer> New (   Isolate *   isolate,
void *    data,
size_t    byte_length 
)     

static Create a new ArrayBuffer over an existing memory block. The created array buffer is immediately in externalized state. The memory block will not be reclaimed when a created ArrayBuffer is garbage-collected.

That basically it requires a void* to automatically make the new data.

I was a bit confused with how to do this for a simple int array, the following returns an arraybuffer with unpredictable results:

C++ code:

#include <node.h>
using namespace v8;

void Buf(const FunctionCallbackInfo<Value>& a) {
    
    Isolate* i = a.GetIsolate();
    int x[] = {6,9,8,7};
    
    void* temp = x;
    
    Local<
        ArrayBuffer
    > 
    v = 
    ArrayBuffer::New(i, temp, 5);
    a.GetReturnValue().Set(v);

}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "buf", Buf);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

And then my NodeJS file :

let a = require("./addon"),
    ab = a.buf(),
    ar = new Uint8Array(ab)
console.log(
    ar, ab
);

but the output , when run multiple times, is giving pretty unpredictable results, which don't seem to match the values from the original array:

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 122, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 7a 40
01>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 88, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 58 40 0
1>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 2, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 02 40 01
>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 216, 63, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b d8 3f
01>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 79, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 4f 40 0
1>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 54, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 36 40 0
1>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 159, 63, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 9f 3f
01>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 104, 64, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b 68 40
01>, byteLength: 5 }

C:\Users\Coby\Documents\aa\atzmus\testServer>node oy.js
Uint8Array [ 224, 107, 170, 63, 1 ] ArrayBuffer { [Uint8Contents]: <e0 6b aa 3f
01>, byteLength: 5 }

I think it's something to do with what was mentioned before about "handling the memory myself" -- but I don't know enough about this to solve it, AKA:

How do I create an ArrayBuffer in C++ from an int array, and read those exact int values back in NodeJS? [1]: https://v8docs.nodesource.com/node-0.12/d5/d6e/classv8_1_1_array_buffer.html

Daedalus
  • 7,586
  • 5
  • 36
  • 61
  • I dont see the reason for the random data yet, but in general in C++ the size of an int is 4 and not 1 bytes. You should declare your `x` array as `int8_t` or `uint8_t`. – WolverinDEV Apr 14 '20 at 23:14
  • Passing a buffer does not make a copy. You passed in a stack array, so as soon as that function loses scope, it is no longer valid. – Garr Godfrey Jul 17 '23 at 23:07

1 Answers1

0

It tells you right in the documentation:

Create a new ArrayBuffer over an existing memory block

The constructor you are using is useful if you have an existing array in memory, and you are responsible for managing that array. The ArrayBuffer will not copy it and will not free it when it is no longer needed.

But, you pass an array from the stack into this constructor:

int x[] = {6,9,8,7};

That is temporary. When the function returns, it is freed and will be overwritten soon afterwards by subsequent function calls. (Technically, this is undefined behavior, and it is. But in practice, you'll have the memory overwritten eventually)

Now, if you changed that to a static, in this example it should work:

static int x[] = {6,9,8,7};

Then the data goes in the data segment and lives forever.

How you use that constructor depends on where your real data is coming from. Will it live forever? Will it only be freed once you are sure the ArrayBuffer is no longer being used? I think, most likely, you better make a copy.

Garr Godfrey
  • 8,257
  • 2
  • 25
  • 23