#include "napi.h"
using namespace Napi;
namespace {
const size_t testLength = 4;
uint8_t testData[testLength];
int finalizeCount = 0;
void InitData(uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) {
data[i] = static_cast(i);
}
}
bool VerifyData(uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) {
if (data[i] != static_cast(i)) {
return false;
}
}
return true;
}
Value CreateBuffer(const CallbackInfo& info) {
ArrayBuffer buffer = ArrayBuffer::New(info.Env(), testLength);
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return Value();
}
InitData(static_cast(buffer.Data()), testLength);
return buffer;
}
Value CreateExternalBuffer(const CallbackInfo& info) {
finalizeCount = 0;
ArrayBuffer buffer = ArrayBuffer::New(info.Env(), testData, testLength);
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return Value();
}
if (buffer.Data() != testData) {
Error::New(info.Env(), "Incorrect buffer data.").ThrowAsJavaScriptException();
return Value();
}
InitData(testData, testLength);
return buffer;
}
Value CreateExternalBufferWithFinalize(const CallbackInfo& info) {
finalizeCount = 0;
uint8_t* data = new uint8_t[testLength];
ArrayBuffer buffer = ArrayBuffer::New(
info.Env(),
data,
testLength,
[](Env /*env*/, void* finalizeData) {
delete[] static_cast(finalizeData);
finalizeCount++;
});
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return Value();
}
if (buffer.Data() != data) {
Error::New(info.Env(), "Incorrect buffer data.").ThrowAsJavaScriptException();
return Value();
}
InitData(data, testLength);
return buffer;
}
Value CreateExternalBufferWithFinalizeHint(const CallbackInfo& info) {
finalizeCount = 0;
uint8_t* data = new uint8_t[testLength];
char* hint = nullptr;
ArrayBuffer buffer = ArrayBuffer::New(
info.Env(),
data,
testLength,
[](Env /*env*/, void* finalizeData, char* /*finalizeHint*/) {
delete[] static_cast(finalizeData);
finalizeCount++;
},
hint);
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return Value();
}
if (buffer.Data() != data) {
Error::New(info.Env(), "Incorrect buffer data.").ThrowAsJavaScriptException();
return Value();
}
InitData(data, testLength);
return buffer;
}
void CheckBuffer(const CallbackInfo& info) {
if (!info[0].IsArrayBuffer()) {
Error::New(info.Env(), "A buffer was expected.").ThrowAsJavaScriptException();
return;
}
ArrayBuffer buffer = info[0].As();
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return;
}
if (!VerifyData(static_cast(buffer.Data()), testLength)) {
Error::New(info.Env(), "Incorrect buffer data.").ThrowAsJavaScriptException();
return;
}
}
Value GetFinalizeCount(const CallbackInfo& info) {
return Number::New(info.Env(), finalizeCount);
}
Value CreateBufferWithConstructor(const CallbackInfo& info) {
ArrayBuffer buffer = ArrayBuffer::New(info.Env(), testLength);
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return Value();
}
InitData(static_cast(buffer.Data()), testLength);
ArrayBuffer buffer2(info.Env(), buffer);
return buffer2;
}
Value CheckEmptyBuffer(const CallbackInfo& info) {
ArrayBuffer buffer;
return Boolean::New(info.Env(), buffer.IsEmpty());
}
void CheckDetachUpdatesData(const CallbackInfo& info) {
if (!info[0].IsArrayBuffer()) {
Error::New(info.Env(), "A buffer was expected.").ThrowAsJavaScriptException();
return;
}
if (!info[1].IsFunction()) {
Error::New(info.Env(), "A function was expected.").ThrowAsJavaScriptException();
return;
}
ArrayBuffer buffer = info[0].As();
Function detach = info[1].As();
// This potentially causes the buffer to cache its data pointer and length.
buffer.Data();
buffer.ByteLength();
detach.Call({});
if (buffer.Data() != nullptr) {
Error::New(info.Env(), "Incorrect data pointer.").ThrowAsJavaScriptException();
return;
}
if (buffer.ByteLength() != 0) {
Error::New(info.Env(), "Incorrect buffer length.").ThrowAsJavaScriptException();
return;
}
}
} // end anonymous namespace
Object InitArrayBuffer(Env env) {
Object exports = Object::New(env);
exports["createBuffer"] = Function::New(env, CreateBuffer);
exports["createExternalBuffer"] = Function::New(env, CreateExternalBuffer);
exports["createExternalBufferWithFinalize"] =
Function::New(env, CreateExternalBufferWithFinalize);
exports["createExternalBufferWithFinalizeHint"] =
Function::New(env, CreateExternalBufferWithFinalizeHint);
exports["checkBuffer"] = Function::New(env, CheckBuffer);
exports["getFinalizeCount"] = Function::New(env, GetFinalizeCount);
exports["createBufferWithConstructor"] = Function::New(env, CreateBufferWithConstructor);
exports["checkEmptyBuffer"] = Function::New(env, CheckEmptyBuffer);
exports["checkDetachUpdatesData"] = Function::New(env, CheckDetachUpdatesData);
return exports;
}