Skip to content

Instantly share code, notes, and snippets.

@deepak1556
Created March 13, 2023 10:04
Show Gist options
  • Save deepak1556/b734e972f3871c1e4c5b5ce3aa3b892c to your computer and use it in GitHub Desktop.
Save deepak1556/b734e972f3871c1e4c5b5ce3aa3b892c to your computer and use it in GitHub Desktop.
External buffer allocation
#include <napi.h>
#include <mach/mach.h>
#include <malloc/malloc.h>
using namespace Napi;
namespace {
static inline void find_registered_zone(const void *ptr) {
vm_address_t* zones;
unsigned int count;
kern_return_t kr =
malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count);
if (kr != KERN_SUCCESS)
return;
for (unsigned int i = 0; i < count; ++i) {
malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);
if (zone->size(zone, ptr)) {
printf("ptr %p in registered zone %s\n", ptr, zone->zone_name);
break;
}
}
}
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<uint8_t>(i);
}
}
bool VerifyData(uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) {
if (data[i] != static_cast<uint8_t>(i)) {
return false;
}
}
return true;
}
Value CreateExternalBufferWithFinalize(const CallbackInfo& info) {
finalizeCount = 0;
uint8_t* data = new uint8_t[testLength];
find_registered_zone((void*)data);
ArrayBuffer buffer = ArrayBuffer::New(
info.Env(), data, testLength, [](Env /*env*/, void* finalizeData) {
delete[] static_cast<uint8_t*>(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;
}
void CheckBuffer(const CallbackInfo& info) {
if (!info[0].IsArrayBuffer()) {
Error::New(info.Env(), "A buffer was expected.")
.ThrowAsJavaScriptException();
return;
}
ArrayBuffer buffer = info[0].As<ArrayBuffer>();
if (buffer.ByteLength() != testLength) {
Error::New(info.Env(), "Incorrect buffer length.")
.ThrowAsJavaScriptException();
return;
}
if (!VerifyData(static_cast<uint8_t*>(buffer.Data()), testLength)) {
Error::New(info.Env(), "Incorrect buffer data.")
.ThrowAsJavaScriptException();
return;
}
}
Value GetFinalizeCount(const CallbackInfo& info) {
return Number::New(info.Env(), finalizeCount);
}
} // end anonymous namespace
Object InitArrayBuffer(Env env) {
Object exports = Object::New(env);
exports["createExternalBufferWithFinalize"] =
Function::New(env, CreateExternalBufferWithFinalize);
exports["checkBuffer"] = Function::New(env, CheckBuffer);
return exports;
}
Object Init(Env env, Object exports) {
exports.Set("arraybuffer", InitArrayBuffer(env));
return exports;
}
NODE_API_MODULE(addon, Init)
'use strict';
const binding = require('./build/Release/binding.node');
{
const test = binding.arraybuffer.createExternalBufferWithFinalize();
binding.arraybuffer.checkBuffer(test);
console.log(binding.arraybuffer.getFinalizeCount());
}
{
'variables': {
'NAPI_VERSION%': "<!(node -p \"process.versions.napi\")",
'disable_deprecated': "<!(node -p \"process.env['npm_config_disable_deprecated']\")"
},
'targets': [{
'target_name': 'binding',
'conditions': [
['NAPI_VERSION!=""', { 'defines': ['NAPI_VERSION=<@(NAPI_VERSION)'] } ],
['OS=="mac"', {
'cflags+': ['-fvisibility=hidden'],
'xcode_settings': {
'OTHER_CFLAGS': ['-fvisibility=hidden'],
'CLANG_CXX_LIBRARY': 'libc++',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
}
}]
],
'defines': [ 'NAPI_CPP_EXCEPTIONS' ],
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions' ],
'include_dirs': ["<!(node -p \"require('node-addon-api').include_dir\")"],
'cflags': [ '-Werror', '-Wall', '-Wextra', '-Wpedantic', '-Wunused-parameter' ],
'cflags_cc': [ '-Werror', '-Wall', '-Wextra', '-Wpedantic', '-Wunused-parameter' ],
'sources': [
'array-buffer.cc',
],
}],
}
% node ./array-buffer.js
ptr 0x6000008602d0 in registered zone DefaultMallocZone
0
% ELECTRON_RUN_AS_NODE=1 ./out/Testing/Electron.app/Contents/MacOS/Electron ../array-buffer.js
ptr 0x120002f53a0 in registered zone PartitionAlloc
/Users/demohan/github/test-arraybuffer/array-buffer.js:6
const test = binding.arraybuffer.createExternalBufferWithFinalize();
^
Error: External buffers are not allowed
at Object.<anonymous> (/Users/demohan/github/test-arraybuffer/array-buffer.js:6:36)
at Module._compile (node:internal/modules/cjs/loader:1269:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1324:10)
at Module.load (node:internal/modules/cjs/loader:1124:32)
at Module._load (node:internal/modules/cjs/loader:965:12)
at Module._load (node:electron/js2c/asar_bundle:757:32)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47
Node.js v18.14.2
{
"name": "test-arraybuffer",
"version": "1.0.0",
"description": "",
"main": "array-buffer.js",
"scripts": {
"install": "node-gyp rebuild"
},
"author": "",
"license": "ISC",
"gypfile": true,
"devDependencies": {
"node-addon-api": "^6.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment