Skip to content

Commit 649bd1d

Browse files
committed
src: Initial implementation of DataView class
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>
1 parent daa0ad4 commit 649bd1d

File tree

7 files changed

+200
-0
lines changed

7 files changed

+200
-0
lines changed

napi-inl.h

+82
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,88 @@ inline void ArrayBuffer::EnsureInfo() const {
11711171
}
11721172
}
11731173

1174+
#if NAPI_DATA_VIEW_FEATURE
1175+
////////////////////////////////////////////////////////////////////////////////
1176+
// DataView class
1177+
////////////////////////////////////////////////////////////////////////////////
1178+
inline DataView DataView::New(napi_env env,
1179+
Napi::ArrayBuffer arrayBuffer) {
1180+
return New(env, arrayBuffer, 0, arrayBuffer.ByteLength());
1181+
}
1182+
1183+
inline DataView DataView::New(napi_env env,
1184+
Napi::ArrayBuffer arrayBuffer,
1185+
size_t byteOffset) {
1186+
if (byteOffset > arrayBuffer.ByteLength()) {
1187+
NAPI_THROW(RangeError::New(env,
1188+
"Start offset is outside the bounds of the buffer"));
1189+
return DataView();
1190+
}
1191+
return New(env, arrayBuffer, byteOffset,
1192+
arrayBuffer.ByteLength() - byteOffset);
1193+
}
1194+
1195+
inline DataView DataView::New(napi_env env,
1196+
Napi::ArrayBuffer arrayBuffer,
1197+
size_t byteOffset,
1198+
size_t byteLength) {
1199+
if (byteOffset + byteLength > arrayBuffer.ByteLength()) {
1200+
NAPI_THROW(RangeError::New(env, "Invalid DataView length"));
1201+
return DataView();
1202+
}
1203+
napi_value value;
1204+
napi_status status = napi_create_dataview(
1205+
env, byteLength, arrayBuffer, byteOffset, &value);
1206+
NAPI_THROW_IF_FAILED(env, status, DataView());
1207+
return DataView(env, value);
1208+
}
1209+
1210+
inline DataView::DataView() : Object() {
1211+
}
1212+
1213+
inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
1214+
}
1215+
1216+
inline Napi::ArrayBuffer DataView::ArrayBuffer() const {
1217+
napi_value arrayBuffer;
1218+
napi_status status = napi_get_dataview_info(
1219+
_env,
1220+
_value /* dataView */,
1221+
nullptr /* byteLength */,
1222+
nullptr /* data */,
1223+
&arrayBuffer /* arrayBuffer */,
1224+
nullptr /* byteOffset */);
1225+
NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer());
1226+
return Napi::ArrayBuffer(_env, arrayBuffer);
1227+
}
1228+
1229+
inline size_t DataView::ByteOffset() const {
1230+
size_t byteOffset;
1231+
napi_status status = napi_get_dataview_info(
1232+
_env,
1233+
_value /* dataView */,
1234+
nullptr /* byteLength */,
1235+
nullptr /* data */,
1236+
nullptr /* arrayBuffer */,
1237+
&byteOffset /* byteOffset */);
1238+
NAPI_THROW_IF_FAILED(_env, status, 0);
1239+
return byteOffset;
1240+
}
1241+
1242+
inline size_t DataView::ByteLength() const {
1243+
size_t byteLength;
1244+
napi_status status = napi_get_dataview_info(
1245+
_env,
1246+
_value /* dataView */,
1247+
&byteLength /* byteLength */,
1248+
nullptr /* data */,
1249+
nullptr /* arrayBuffer */,
1250+
nullptr /* byteOffset */);
1251+
NAPI_THROW_IF_FAILED(_env, status, 0);
1252+
return byteLength;
1253+
}
1254+
#endif
1255+
11741256
////////////////////////////////////////////////////////////////////////////////
11751257
// TypedArray class
11761258
////////////////////////////////////////////////////////////////////////////////

napi.h

+30
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,36 @@ namespace Napi {
778778
T* data);
779779
};
780780

781+
#if NAPI_DATA_VIEW_FEATURE
782+
/// The DataView provides a low-level interface for reading/writing multiple
783+
/// number types in an ArrayBuffer irrespective of the platform's endianness.
784+
class DataView : public Object {
785+
public:
786+
static DataView New(napi_env env,
787+
Napi::ArrayBuffer arrayBuffer);
788+
static DataView New(napi_env env,
789+
Napi::ArrayBuffer arrayBuffer,
790+
size_t byteOffset);
791+
static DataView New(napi_env env,
792+
Napi::ArrayBuffer arrayBuffer,
793+
size_t byteOffset,
794+
size_t byteLength);
795+
796+
DataView(); ///< Creates a new _empty_ DataView instance.
797+
DataView(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
798+
799+
Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer.
800+
size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts.
801+
size_t ByteLength() const; ///< Gets the length of the array in bytes.
802+
803+
// TODO: This class isn't a complete implementation yet, and will
804+
// incrementally add additional methods to read/write data into buffer.
805+
// Currently, this class is wrapped by the NAPI_DATA_VIEW_FEATURE macro flag
806+
// and this should be enabled only in the tests until the implementation is
807+
// completed.
808+
};
809+
#endif
810+
781811
class Function : public Object {
782812
public:
783813
/// Callable must implement operator() accepting a const CallbackInfo&

test/binding.cc

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Object InitAsyncWorker(Env env);
77
Object InitBasicTypesNumber(Env env);
88
Object InitBasicTypesValue(Env env);
99
Object InitBuffer(Env env);
10+
Object InitDataView(Env env);
1011
Object InitError(Env env);
1112
Object InitExternal(Env env);
1213
Object InitFunction(Env env);
@@ -22,6 +23,7 @@ Object Init(Env env, Object exports) {
2223
exports.Set("basic_types_number", InitBasicTypesNumber(env));
2324
exports.Set("basic_types_value", InitBasicTypesValue(env));
2425
exports.Set("buffer", InitBuffer(env));
26+
exports.Set("dataview", InitDataView(env));
2527
exports.Set("error", InitError(env));
2628
exports.Set("external", InitExternal(env));
2729
exports.Set("function", InitFunction(env));

test/binding.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
'basic_types/value.cc',
88
'binding.cc',
99
'buffer.cc',
10+
'dataview/dataview.cc',
1011
'error.cc',
1112
'external.cc',
1213
'function.cc',

test/dataview/dataview.cc

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "napi.h"
2+
3+
using namespace Napi;
4+
5+
static Value CreateDataView1(const CallbackInfo& info) {
6+
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
7+
return DataView::New(info.Env(), arrayBuffer);
8+
}
9+
10+
static Value CreateDataView2(const CallbackInfo& info) {
11+
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
12+
size_t byteOffset = info[1].As<Number>().Uint32Value();
13+
return DataView::New(info.Env(), arrayBuffer, byteOffset);
14+
}
15+
16+
static Value CreateDataView3(const CallbackInfo& info) {
17+
ArrayBuffer arrayBuffer = info[0].As<ArrayBuffer>();
18+
size_t byteOffset = info[1].As<Number>().Uint32Value();
19+
size_t byteLength = info[2].As<Number>().Uint32Value();
20+
return DataView::New(info.Env(), arrayBuffer, byteOffset, byteLength);
21+
}
22+
23+
static Value GetArrayBuffer(const CallbackInfo& info) {
24+
return info[0].As<DataView>().ArrayBuffer();
25+
}
26+
27+
static Value GetByteOffset(const CallbackInfo& info) {
28+
return Number::New(info.Env(), info[0].As<DataView>().ByteOffset());
29+
}
30+
31+
static Value GetByteLength(const CallbackInfo& info) {
32+
return Number::New(info.Env(), info[0].As<DataView>().ByteLength());
33+
}
34+
35+
Object InitDataView(Env env) {
36+
Object exports = Object::New(env);
37+
38+
exports["createDataView1"] = Function::New(env, CreateDataView1);
39+
exports["createDataView2"] = Function::New(env, CreateDataView2);
40+
exports["createDataView3"] = Function::New(env, CreateDataView3);
41+
exports["getArrayBuffer"] = Function::New(env, GetArrayBuffer);
42+
exports["getByteOffset"] = Function::New(env, GetByteOffset);
43+
exports["getByteLength"] = Function::New(env, GetByteLength);
44+
45+
return exports;
46+
}

test/dataview/dataview.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const buildType = process.config.target_defaults.default_configuration;
4+
const assert = require('assert');
5+
6+
test(require(`../build/${buildType}/binding.node`));
7+
test(require(`../build/${buildType}/binding_noexcept.node`));
8+
9+
function test(binding) {
10+
function testDataViewCreation(factory, arrayBuffer, offset, length) {
11+
const view = factory(arrayBuffer, offset, length);
12+
offset = offset ? offset : 0;
13+
assert.ok(dataview.getArrayBuffer(view) instanceof ArrayBuffer);
14+
assert.strictEqual(dataview.getArrayBuffer(view), arrayBuffer);
15+
assert.strictEqual(dataview.getByteOffset(view), offset);
16+
assert.strictEqual(dataview.getByteLength(view),
17+
length ? length : arrayBuffer.byteLength - offset);
18+
}
19+
20+
function testInvalidRange(factory, arrayBuffer, offset, length) {
21+
assert.throws(() => {
22+
factory(arrayBuffer, offset, length);
23+
}, RangeError);
24+
}
25+
26+
const dataview = binding.dataview;
27+
const arrayBuffer = new ArrayBuffer(10);
28+
29+
testDataViewCreation(dataview.createDataView1, arrayBuffer);
30+
testDataViewCreation(dataview.createDataView2, arrayBuffer, 2);
31+
testDataViewCreation(dataview.createDataView2, arrayBuffer, 10);
32+
testDataViewCreation(dataview.createDataView3, arrayBuffer, 2, 4);
33+
testDataViewCreation(dataview.createDataView3, arrayBuffer, 10, 0);
34+
35+
testInvalidRange(dataview.createDataView2, arrayBuffer, 11);
36+
testInvalidRange(dataview.createDataView3, arrayBuffer, 11, 0);
37+
testInvalidRange(dataview.createDataView3, arrayBuffer, 6, 5);
38+
}

test/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let testModules = [
1313
'basic_types/number',
1414
'basic_types/value',
1515
'buffer',
16+
'dataview/dataview',
1617
'error',
1718
'external',
1819
'function',

0 commit comments

Comments
 (0)