diff --git a/src/binding.cc b/src/binding.cc index abd43ae..6441816 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -333,6 +333,45 @@ Value ReadPointer(const CallbackInfo& args) { return WrapPointer(env, val, size); } +/** + * Creates an ArrayBuffer from the given address without allocating new memory, + * using napi_create_external_arraybuffer. + * + * args[0] - Number/String - the memory address + * args[1] - Number - the length in bytes of the returned ArrayBuffer instance + */ + +Value ReadExternalArrayBuffer(const CallbackInfo& args) { + Env env = args.Env(); + Value in = args[0]; + int64_t address; + if (in.IsNumber()) { + address = in.As(); + } else if (in.IsString()) { + char* endptr; + char* str; + int base = 0; + std::string _str = in.As(); + str = &_str[0]; + + errno = 0; /* To distinguish success/failure after call */ + address = strtoll(str, &endptr, base); + + if (endptr == str) { + throw TypeError::New(env, "readExternalArrayBuffer: no digits we found in input String"); + } else if (errno == ERANGE && (address == INT64_MAX || address == INT64_MIN)) { + throw TypeError::New(env, "readExternalArrayBuffer: input String numerical value out of range"); + } else if (errno != 0 && address == 0) { + char errmsg[200]; + snprintf(errmsg, sizeof(errmsg), "readExternalArrayBuffer: %s", strerror(errno)); + throw TypeError::New(env, errmsg); + } + } + int64_t length = args[1].ToNumber(); + + return ArrayBuffer::New(env, (void*) address, (size_t) length); +} + /** * Writes the memory address of the "input" buffer (and optional offset) to the * specified "buf" buffer and offset. Essentially making "buf" hold a reference @@ -699,6 +738,7 @@ Object Init(Env env, Object exports) { exports["readCString"] = Function::New(env, ReadCString); exports["_reinterpret"] = Function::New(env, ReinterpretBuffer); exports["_reinterpretUntilZeros"] = Function::New(env, ReinterpretBufferUntilZeros); + exports["readExternalArrayBuffer"] = Function::New(env, ReadExternalArrayBuffer); return exports; } diff --git a/test/external_arraybuffer.js b/test/external_arraybuffer.js new file mode 100644 index 0000000..2219559 --- /dev/null +++ b/test/external_arraybuffer.js @@ -0,0 +1,35 @@ +'use strict'; +const assert = require('assert'); +const ref = require('../'); +const inspect = require('util').inspect; + +describe('external_arraybuffer', function() { + + it('should get an array buffer with number address', function() { + const buf = Buffer.from('hello' + '\0'); + const address = ref.address(buf); + const ab = ref.readExternalArrayBuffer(address, 6); + assert.strictEqual(typeof ab, 'object'); + const buffer = Buffer.from(ab); + const address2 = ref.address(buffer); + assert.strictEqual(address, address2); + assert.strictEqual('hello', buffer.readCString()); + // when buffer gets changed, the original buf should be changed too + buffer.writeCString('olleh'); + assert.strictEqual('olleh', buf.readCString()); + }); + + it('should get an array buffer with string address', function() { + const buf = Buffer.from('hello' + '\0'); + const hexAddress = ref.hexAddress(buf); + const ab = ref.readExternalArrayBuffer('0x' + hexAddress, 6); + assert.strictEqual(typeof ab, 'object'); + const buffer = Buffer.from(ab); + const address2 = ref.hexAddress(buffer); + assert.strictEqual(hexAddress, address2); + assert.strictEqual('hello', buffer.readCString()); + // when buffer gets changed, the original buf should be changed too + buffer.writeCString('olleh'); + assert.strictEqual('olleh', buf.readCString()); + }); +}); \ No newline at end of file