Skip to content

Commit

Permalink
src: Initial implementation of DataView class
Browse files Browse the repository at this point in the history
This is an initial implementation of DataView class. This change
includes the following things:
  - DataView::New() methods
  - Getters for DataView (ArrayBuffer(), ByteOffset(), ByteLength())
  - Tests for them

PR-URL: nodejs/node-addon-api#205
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Kyle Farnung <kfarnung@microsoft.com>
  • Loading branch information
kevindavies8 committed Jan 18, 2018
1 parent daa0ad4 commit 649bd1d
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 0 deletions.
82 changes: 82 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,88 @@ inline void ArrayBuffer::EnsureInfo() const {
}
}

#if NAPI_DATA_VIEW_FEATURE
////////////////////////////////////////////////////////////////////////////////
// DataView class
////////////////////////////////////////////////////////////////////////////////
inline DataView DataView::New(napi_env env,
Napi::ArrayBuffer arrayBuffer) {
return New(env, arrayBuffer, 0, arrayBuffer.ByteLength());
}

inline DataView DataView::New(napi_env env,
Napi::ArrayBuffer arrayBuffer,
size_t byteOffset) {
if (byteOffset > arrayBuffer.ByteLength()) {
NAPI_THROW(RangeError::New(env,
"Start offset is outside the bounds of the buffer"));
return DataView();
}
return New(env, arrayBuffer, byteOffset,
arrayBuffer.ByteLength() - byteOffset);
}

inline DataView DataView::New(napi_env env,
Napi::ArrayBuffer arrayBuffer,
size_t byteOffset,
size_t byteLength) {
if (byteOffset + byteLength > arrayBuffer.ByteLength()) {
NAPI_THROW(RangeError::New(env, "Invalid DataView length"));
return DataView();
}
napi_value value;
napi_status status = napi_create_dataview(
env, byteLength, arrayBuffer, byteOffset, &value);
NAPI_THROW_IF_FAILED(env, status, DataView());
return DataView(env, value);
}

inline DataView::DataView() : Object() {
}

inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
}

inline Napi::ArrayBuffer DataView::ArrayBuffer() const {
napi_value arrayBuffer;
napi_status status = napi_get_dataview_info(
_env,
_value /* dataView */,
nullptr /* byteLength */,
nullptr /* data */,
&arrayBuffer /* arrayBuffer */,
nullptr /* byteOffset */);
NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer());
return Napi::ArrayBuffer(_env, arrayBuffer);
}

inline size_t DataView::ByteOffset() const {
size_t byteOffset;
napi_status status = napi_get_dataview_info(
_env,
_value /* dataView */,
nullptr /* byteLength */,
nullptr /* data */,
nullptr /* arrayBuffer */,
&byteOffset /* byteOffset */);
NAPI_THROW_IF_FAILED(_env, status, 0);
return byteOffset;
}

inline size_t DataView::ByteLength() const {
size_t byteLength;
napi_status status = napi_get_dataview_info(
_env,
_value /* dataView */,
&byteLength /* byteLength */,
nullptr /* data */,
nullptr /* arrayBuffer */,
nullptr /* byteOffset */);
NAPI_THROW_IF_FAILED(_env, status, 0);
return byteLength;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// TypedArray class
////////////////////////////////////////////////////////////////////////////////
Expand Down
30 changes: 30 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,36 @@ namespace Napi {
T* data);
};

#if NAPI_DATA_VIEW_FEATURE
/// The DataView provides a low-level interface for reading/writing multiple
/// number types in an ArrayBuffer irrespective of the platform's endianness.
class DataView : public Object {
public:
static DataView New(napi_env env,
Napi::ArrayBuffer arrayBuffer);
static DataView New(napi_env env,
Napi::ArrayBuffer arrayBuffer,
size_t byteOffset);
static DataView New(napi_env env,
Napi::ArrayBuffer arrayBuffer,
size_t byteOffset,
size_t byteLength);

DataView(); ///< Creates a new _empty_ DataView instance.
DataView(napi_env env, napi_value value); ///< Wraps a N-API value primitive.

Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer.
size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts.
size_t ByteLength() const; ///< Gets the length of the array in bytes.

// TODO: This class isn't a complete implementation yet, and will
// incrementally add additional methods to read/write data into buffer.
// Currently, this class is wrapped by the NAPI_DATA_VIEW_FEATURE macro flag
// and this should be enabled only in the tests until the implementation is
// completed.
};
#endif

class Function : public Object {
public:
/// Callable must implement operator() accepting a const CallbackInfo&
Expand Down
2 changes: 2 additions & 0 deletions test/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Object InitAsyncWorker(Env env);
Object InitBasicTypesNumber(Env env);
Object InitBasicTypesValue(Env env);
Object InitBuffer(Env env);
Object InitDataView(Env env);
Object InitError(Env env);
Object InitExternal(Env env);
Object InitFunction(Env env);
Expand All @@ -22,6 +23,7 @@ Object Init(Env env, Object exports) {
exports.Set("basic_types_number", InitBasicTypesNumber(env));
exports.Set("basic_types_value", InitBasicTypesValue(env));
exports.Set("buffer", InitBuffer(env));
exports.Set("dataview", InitDataView(env));
exports.Set("error", InitError(env));
exports.Set("external", InitExternal(env));
exports.Set("function", InitFunction(env));
Expand Down
1 change: 1 addition & 0 deletions test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
'basic_types/value.cc',
'binding.cc',
'buffer.cc',
'dataview/dataview.cc',
'error.cc',
'external.cc',
'function.cc',
Expand Down
46 changes: 46 additions & 0 deletions test/dataview/dataview.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "napi.h"

using namespace Napi;

static Value CreateDataView1(const CallbackInfo& info) {
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
return DataView::New(info.Env(), arrayBuffer);
}

static Value CreateDataView2(const CallbackInfo& info) {
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
size_t byteOffset = info[1].As<Number>().Uint32Value();
return DataView::New(info.Env(), arrayBuffer, byteOffset);
}

static Value CreateDataView3(const CallbackInfo& info) {
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
size_t byteOffset = info[1].As<Number>().Uint32Value();
size_t byteLength = info[2].As<Number>().Uint32Value();
return DataView::New(info.Env(), arrayBuffer, byteOffset, byteLength);
}

static Value GetArrayBuffer(const CallbackInfo& info) {
return info[0].As<DataView>().ArrayBuffer();
}

static Value GetByteOffset(const CallbackInfo& info) {
return Number::New(info.Env(), info[0].As<DataView>().ByteOffset());
}

static Value GetByteLength(const CallbackInfo& info) {
return Number::New(info.Env(), info[0].As<DataView>().ByteLength());
}

Object InitDataView(Env env) {
Object exports = Object::New(env);

exports["createDataView1"] = Function::New(env, CreateDataView1);
exports["createDataView2"] = Function::New(env, CreateDataView2);
exports["createDataView3"] = Function::New(env, CreateDataView3);
exports["getArrayBuffer"] = Function::New(env, GetArrayBuffer);
exports["getByteOffset"] = Function::New(env, GetByteOffset);
exports["getByteLength"] = Function::New(env, GetByteLength);

return exports;
}
38 changes: 38 additions & 0 deletions test/dataview/dataview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

const buildType = process.config.target_defaults.default_configuration;
const assert = require('assert');

test(require(`../build/${buildType}/binding.node`));
test(require(`../build/${buildType}/binding_noexcept.node`));

function test(binding) {
function testDataViewCreation(factory, arrayBuffer, offset, length) {
const view = factory(arrayBuffer, offset, length);
offset = offset ? offset : 0;
assert.ok(dataview.getArrayBuffer(view) instanceof ArrayBuffer);
assert.strictEqual(dataview.getArrayBuffer(view), arrayBuffer);
assert.strictEqual(dataview.getByteOffset(view), offset);
assert.strictEqual(dataview.getByteLength(view),
length ? length : arrayBuffer.byteLength - offset);
}

function testInvalidRange(factory, arrayBuffer, offset, length) {
assert.throws(() => {
factory(arrayBuffer, offset, length);
}, RangeError);
}

const dataview = binding.dataview;
const arrayBuffer = new ArrayBuffer(10);

testDataViewCreation(dataview.createDataView1, arrayBuffer);
testDataViewCreation(dataview.createDataView2, arrayBuffer, 2);
testDataViewCreation(dataview.createDataView2, arrayBuffer, 10);
testDataViewCreation(dataview.createDataView3, arrayBuffer, 2, 4);
testDataViewCreation(dataview.createDataView3, arrayBuffer, 10, 0);

testInvalidRange(dataview.createDataView2, arrayBuffer, 11);
testInvalidRange(dataview.createDataView3, arrayBuffer, 11, 0);
testInvalidRange(dataview.createDataView3, arrayBuffer, 6, 5);
}
1 change: 1 addition & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let testModules = [
'basic_types/number',
'basic_types/value',
'buffer',
'dataview/dataview',
'error',
'external',
'function',
Expand Down

0 comments on commit 649bd1d

Please sign in to comment.