Skip to content

Commit 9c4d321

Browse files
romandevmhdawson
authored andcommitted
Implement data manipulation methods for dataview
This change implements additional methods for manipulating data in the dataview object. This change includes the following things: - Get/Set${type} methods to manipulate data - Tests for the methods PR-URL: #218 Reviewed-By: Michael Dawson <[email protected]>
1 parent 5a39fdc commit 9c4d321

File tree

7 files changed

+336
-15
lines changed

7 files changed

+336
-15
lines changed

napi-inl.h

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,14 @@ inline DataView::DataView() : Object() {
12111211
}
12121212

12131213
inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
1214+
napi_status status = napi_get_dataview_info(
1215+
_env,
1216+
_value /* dataView */,
1217+
&_length /* byteLength */,
1218+
&_data /* data */,
1219+
nullptr /* arrayBuffer */,
1220+
nullptr /* byteOffset */);
1221+
NAPI_THROW_IF_FAILED(_env, status);
12141222
}
12151223

12161224
inline Napi::ArrayBuffer DataView::ArrayBuffer() const {
@@ -1240,16 +1248,99 @@ inline size_t DataView::ByteOffset() const {
12401248
}
12411249

12421250
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;
1251+
return _length;
1252+
}
1253+
1254+
inline void* DataView::Data() const {
1255+
return _data;
1256+
}
1257+
1258+
inline float DataView::GetFloat32(size_t byteOffset) const {
1259+
return ReadData<float>(byteOffset);
1260+
}
1261+
1262+
inline double DataView::GetFloat64(size_t byteOffset) const {
1263+
return ReadData<double>(byteOffset);
1264+
}
1265+
1266+
inline int8_t DataView::GetInt8(size_t byteOffset) const {
1267+
return ReadData<int8_t>(byteOffset);
1268+
}
1269+
1270+
inline int16_t DataView::GetInt16(size_t byteOffset) const {
1271+
return ReadData<int16_t>(byteOffset);
1272+
}
1273+
1274+
inline int32_t DataView::GetInt32(size_t byteOffset) const {
1275+
return ReadData<int32_t>(byteOffset);
1276+
}
1277+
1278+
inline uint8_t DataView::GetUint8(size_t byteOffset) const {
1279+
return ReadData<uint8_t>(byteOffset);
1280+
}
1281+
1282+
inline uint16_t DataView::GetUint16(size_t byteOffset) const {
1283+
return ReadData<uint16_t>(byteOffset);
1284+
}
1285+
1286+
inline uint32_t DataView::GetUint32(size_t byteOffset) const {
1287+
return ReadData<uint32_t>(byteOffset);
1288+
}
1289+
1290+
inline void DataView::SetFloat32(size_t byteOffset, float value) const {
1291+
WriteData<float>(byteOffset, value);
1292+
}
1293+
1294+
inline void DataView::SetFloat64(size_t byteOffset, double value) const {
1295+
WriteData<double>(byteOffset, value);
1296+
}
1297+
1298+
inline void DataView::SetInt8(size_t byteOffset, int8_t value) const {
1299+
WriteData<int8_t>(byteOffset, value);
1300+
}
1301+
1302+
inline void DataView::SetInt16(size_t byteOffset, int16_t value) const {
1303+
WriteData<int16_t>(byteOffset, value);
1304+
}
1305+
1306+
inline void DataView::SetInt32(size_t byteOffset, int32_t value) const {
1307+
WriteData<int32_t>(byteOffset, value);
1308+
}
1309+
1310+
inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const {
1311+
WriteData<uint8_t>(byteOffset, value);
1312+
}
1313+
1314+
inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const {
1315+
WriteData<uint16_t>(byteOffset, value);
1316+
}
1317+
1318+
inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const {
1319+
WriteData<uint32_t>(byteOffset, value);
1320+
}
1321+
1322+
template <typename T>
1323+
inline T DataView::ReadData(size_t byteOffset) const {
1324+
if (byteOffset + sizeof(T) > _length ||
1325+
byteOffset + sizeof(T) < byteOffset) { // overflow
1326+
NAPI_THROW(RangeError::New(_env,
1327+
"Offset is outside the bounds of the DataView"));
1328+
return 0;
1329+
}
1330+
1331+
return *reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset);
1332+
}
1333+
1334+
template <typename T>
1335+
inline void DataView::WriteData(size_t byteOffset, T value) const {
1336+
if (byteOffset + sizeof(T) > _length ||
1337+
byteOffset + sizeof(T) < byteOffset) { // overflow
1338+
NAPI_THROW(RangeError::New(_env,
1339+
"Offset is outside the bounds of the DataView"));
1340+
return;
1341+
}
1342+
1343+
*reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset) = value;
12531344
}
12541345
#endif
12551346

napi.h

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -800,11 +800,35 @@ namespace Napi {
800800
size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts.
801801
size_t ByteLength() const; ///< Gets the length of the array in bytes.
802802

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.
803+
void* Data() const;
804+
805+
float GetFloat32(size_t byteOffset) const;
806+
double GetFloat64(size_t byteOffset) const;
807+
int8_t GetInt8(size_t byteOffset) const;
808+
int16_t GetInt16(size_t byteOffset) const;
809+
int32_t GetInt32(size_t byteOffset) const;
810+
uint8_t GetUint8(size_t byteOffset) const;
811+
uint16_t GetUint16(size_t byteOffset) const;
812+
uint32_t GetUint32(size_t byteOffset) const;
813+
814+
void SetFloat32(size_t byteOffset, float value) const;
815+
void SetFloat64(size_t byteOffset, double value) const;
816+
void SetInt8(size_t byteOffset, int8_t value) const;
817+
void SetInt16(size_t byteOffset, int16_t value) const;
818+
void SetInt32(size_t byteOffset, int32_t value) const;
819+
void SetUint8(size_t byteOffset, uint8_t value) const;
820+
void SetUint16(size_t byteOffset, uint16_t value) const;
821+
void SetUint32(size_t byteOffset, uint32_t value) const;
822+
823+
private:
824+
template <typename T>
825+
T ReadData(size_t byteOffset) const;
826+
827+
template <typename T>
828+
void WriteData(size_t byteOffset, T value) const;
829+
830+
void* _data;
831+
size_t _length;
808832
};
809833
#endif
810834

test/binding.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Object InitBasicTypesNumber(Env env);
88
Object InitBasicTypesValue(Env env);
99
Object InitBuffer(Env env);
1010
Object InitDataView(Env env);
11+
Object InitDataViewReadWrite(Env env);
1112
Object InitError(Env env);
1213
Object InitExternal(Env env);
1314
Object InitFunction(Env env);
@@ -24,6 +25,8 @@ Object Init(Env env, Object exports) {
2425
exports.Set("basic_types_value", InitBasicTypesValue(env));
2526
exports.Set("buffer", InitBuffer(env));
2627
exports.Set("dataview", InitDataView(env));
28+
exports.Set("dataview_read_write", InitDataView(env));
29+
exports.Set("dataview_read_write", InitDataViewReadWrite(env));
2730
exports.Set("error", InitError(env));
2831
exports.Set("external", InitExternal(env));
2932
exports.Set("function", InitFunction(env));

test/binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
'binding.cc',
99
'buffer.cc',
1010
'dataview/dataview.cc',
11+
'dataview/dataview_read_write.cc',
1112
'error.cc',
1213
'external.cc',
1314
'function.cc',

test/dataview/dataview_read_write.cc

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include "napi.h"
2+
3+
using namespace Napi;
4+
5+
static Value GetFloat32(const CallbackInfo& info) {
6+
size_t byteOffset = info[1].As<Number>().Uint32Value();
7+
return Number::New(info.Env(), info[0].As<DataView>().GetFloat32(byteOffset));
8+
}
9+
10+
static Value GetFloat64(const CallbackInfo& info) {
11+
size_t byteOffset = info[1].As<Number>().Uint32Value();
12+
return Number::New(info.Env(), info[0].As<DataView>().GetFloat64(byteOffset));
13+
}
14+
15+
static Value GetInt8(const CallbackInfo& info) {
16+
size_t byteOffset = info[1].As<Number>().Uint32Value();
17+
return Number::New(info.Env(), info[0].As<DataView>().GetInt8(byteOffset));
18+
}
19+
20+
static Value GetInt16(const CallbackInfo& info) {
21+
size_t byteOffset = info[1].As<Number>().Uint32Value();
22+
return Number::New(info.Env(), info[0].As<DataView>().GetInt16(byteOffset));
23+
}
24+
25+
static Value GetInt32(const CallbackInfo& info) {
26+
size_t byteOffset = info[1].As<Number>().Uint32Value();
27+
return Number::New(info.Env(), info[0].As<DataView>().GetInt32(byteOffset));
28+
}
29+
30+
static Value GetUint8(const CallbackInfo& info) {
31+
size_t byteOffset = info[1].As<Number>().Uint32Value();
32+
return Number::New(info.Env(), info[0].As<DataView>().GetUint8(byteOffset));
33+
}
34+
35+
static Value GetUint16(const CallbackInfo& info) {
36+
size_t byteOffset = info[1].As<Number>().Uint32Value();
37+
return Number::New(info.Env(), info[0].As<DataView>().GetUint16(byteOffset));
38+
}
39+
40+
static Value GetUint32(const CallbackInfo& info) {
41+
size_t byteOffset = info[1].As<Number>().Uint32Value();
42+
return Number::New(info.Env(), info[0].As<DataView>().GetUint32(byteOffset));
43+
}
44+
45+
static void SetFloat32(const CallbackInfo& info) {
46+
size_t byteOffset = info[1].As<Number>().Uint32Value();
47+
auto value = info[2].As<Number>().FloatValue();
48+
info[0].As<DataView>().SetFloat32(byteOffset, value);
49+
}
50+
51+
static void SetFloat64(const CallbackInfo& info) {
52+
size_t byteOffset = info[1].As<Number>().Uint32Value();
53+
auto value = info[2].As<Number>().DoubleValue();
54+
info[0].As<DataView>().SetFloat64(byteOffset, value);
55+
}
56+
57+
static void SetInt8(const CallbackInfo& info) {
58+
size_t byteOffset = info[1].As<Number>().Uint32Value();
59+
auto value = info[2].As<Number>().Int32Value();
60+
info[0].As<DataView>().SetInt8(byteOffset, value);
61+
}
62+
63+
static void SetInt16(const CallbackInfo& info) {
64+
size_t byteOffset = info[1].As<Number>().Uint32Value();
65+
auto value = info[2].As<Number>().Int32Value();
66+
info[0].As<DataView>().SetInt16(byteOffset, value);
67+
}
68+
69+
static void SetInt32(const CallbackInfo& info) {
70+
size_t byteOffset = info[1].As<Number>().Uint32Value();
71+
auto value = info[2].As<Number>().Int32Value();
72+
info[0].As<DataView>().SetInt32(byteOffset, value);
73+
}
74+
75+
static void SetUint8(const CallbackInfo& info) {
76+
size_t byteOffset = info[1].As<Number>().Uint32Value();
77+
auto value = info[2].As<Number>().Uint32Value();
78+
info[0].As<DataView>().SetUint8(byteOffset, value);
79+
}
80+
81+
static void SetUint16(const CallbackInfo& info) {
82+
size_t byteOffset = info[1].As<Number>().Uint32Value();
83+
auto value = info[2].As<Number>().Uint32Value();
84+
info[0].As<DataView>().SetUint16(byteOffset, value);
85+
}
86+
87+
static void SetUint32(const CallbackInfo& info) {
88+
size_t byteOffset = info[1].As<Number>().Uint32Value();
89+
auto value = info[2].As<Number>().Uint32Value();
90+
info[0].As<DataView>().SetUint32(byteOffset, value);
91+
}
92+
93+
Object InitDataViewReadWrite(Env env) {
94+
Object exports = Object::New(env);
95+
96+
exports["getFloat32"] = Function::New(env, GetFloat32);
97+
exports["getFloat64"] = Function::New(env, GetFloat64);
98+
exports["getInt8"] = Function::New(env, GetInt8);
99+
exports["getInt16"] = Function::New(env, GetInt16);
100+
exports["getInt32"] = Function::New(env, GetInt32);
101+
exports["getUint8"] = Function::New(env, GetUint8);
102+
exports["getUint16"] = Function::New(env, GetUint16);
103+
exports["getUint32"] = Function::New(env, GetUint32);
104+
105+
exports["setFloat32"] = Function::New(env, SetFloat32);
106+
exports["setFloat64"] = Function::New(env, SetFloat64);
107+
exports["setInt8"] = Function::New(env, SetInt8);
108+
exports["setInt16"] = Function::New(env, SetInt16);
109+
exports["setInt32"] = Function::New(env, SetInt32);
110+
exports["setUint8"] = Function::New(env, SetUint8);
111+
exports["setUint16"] = Function::New(env, SetUint16);
112+
exports["setUint32"] = Function::New(env, SetUint32);
113+
114+
return exports;
115+
}

test/dataview/dataview_read_write.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 expected(type, value) {
11+
return eval(`(new ${type}Array([${value}]))[0]`);
12+
}
13+
14+
function nativeReadDataView(dataview, type, offset, value) {
15+
return eval(`binding.dataview_read_write.get${type}(dataview, offset)`);
16+
}
17+
18+
function nativeWriteDataView(dataview, type, offset, value) {
19+
eval(`binding.dataview_read_write.set${type}(dataview, offset, value)`);
20+
}
21+
22+
function jsReadDataView(dataview, type, offset, value) {
23+
return eval(`dataview.get${type}(offset, true)`);
24+
}
25+
26+
function jsWriteDataView(dataview, type, offset, value) {
27+
eval(`dataview.set${type}(offset, value, true)`);
28+
}
29+
30+
function testReadData(dataview, type, offset, value) {
31+
jsWriteDataView(dataview, type, offset, 0);
32+
assert.strictEqual(jsReadDataView(dataview, type, offset), 0);
33+
34+
jsWriteDataView(dataview, type, offset, value);
35+
assert.strictEqual(
36+
nativeReadDataView(dataview, type, offset), expected(type, value));
37+
}
38+
39+
function testWriteData(dataview, type, offset, value) {
40+
jsWriteDataView(dataview, type, offset, 0);
41+
assert.strictEqual(jsReadDataView(dataview, type, offset), 0);
42+
43+
nativeWriteDataView(dataview, type, offset, value);
44+
assert.strictEqual(
45+
jsReadDataView(dataview, type, offset), expected(type, value));
46+
}
47+
48+
function testInvalidOffset(dataview, type, offset, value) {
49+
assert.throws(() => {
50+
nativeReadDataView(dataview, type, offset);
51+
}, RangeError);
52+
53+
assert.throws(() => {
54+
nativeWriteDataView(dataview, type, offset, value);
55+
}, RangeError);
56+
}
57+
58+
const dataview = new DataView(new ArrayBuffer(22));
59+
60+
testReadData(dataview, 'Float32', 0, 10.2);
61+
testReadData(dataview, 'Float64', 4, 20.3);
62+
testReadData(dataview, 'Int8', 5, 120);
63+
testReadData(dataview, 'Int16', 7, 15000);
64+
testReadData(dataview, 'Int32', 11, 200000);
65+
testReadData(dataview, 'Uint8', 12, 128);
66+
testReadData(dataview, 'Uint16', 14, 32768);
67+
testReadData(dataview, 'Uint32', 18, 1000000);
68+
69+
testWriteData(dataview, 'Float32', 0, 10.2);
70+
testWriteData(dataview, 'Float64', 4, 20.3);
71+
testWriteData(dataview, 'Int8', 5, 120);
72+
testWriteData(dataview, 'Int16', 7, 15000);
73+
testWriteData(dataview, 'Int32', 11, 200000);
74+
testWriteData(dataview, 'Uint8', 12, 128);
75+
testWriteData(dataview, 'Uint16', 14, 32768);
76+
testWriteData(dataview, 'Uint32', 18, 1000000);
77+
78+
testInvalidOffset(dataview, 'Float32', 22, 10.2);
79+
testInvalidOffset(dataview, 'Float64', 22, 20.3);
80+
testInvalidOffset(dataview, 'Int8', 22, 120);
81+
testInvalidOffset(dataview, 'Int16', 22, 15000);
82+
testInvalidOffset(dataview, 'Int32', 22, 200000);
83+
testInvalidOffset(dataview, 'Uint8', 22, 128);
84+
testInvalidOffset(dataview, 'Uint16', 22, 32768);
85+
testInvalidOffset(dataview, 'Uint32', 22, 1000000);
86+
}

0 commit comments

Comments
 (0)