Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement data manipulation methods for dataview #218

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 101 additions & 10 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,14 @@ inline DataView::DataView() : Object() {
}

inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
napi_status status = napi_get_dataview_info(
_env,
_value /* dataView */,
&_length /* byteLength */,
&_data /* data */,
nullptr /* arrayBuffer */,
nullptr /* byteOffset */);
NAPI_THROW_IF_FAILED(_env, status);
}

inline Napi::ArrayBuffer DataView::ArrayBuffer() const {
Expand Down Expand Up @@ -1240,16 +1248,99 @@ inline size_t DataView::ByteOffset() const {
}

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;
return _length;
}

inline void* DataView::Data() const {
return _data;
}

inline float DataView::GetFloat32(size_t byteOffset) const {
return ReadData<float>(byteOffset);
}

inline double DataView::GetFloat64(size_t byteOffset) const {
return ReadData<double>(byteOffset);
}

inline int8_t DataView::GetInt8(size_t byteOffset) const {
return ReadData<int8_t>(byteOffset);
}

inline int16_t DataView::GetInt16(size_t byteOffset) const {
return ReadData<int16_t>(byteOffset);
}

inline int32_t DataView::GetInt32(size_t byteOffset) const {
return ReadData<int32_t>(byteOffset);
}

inline uint8_t DataView::GetUint8(size_t byteOffset) const {
return ReadData<uint8_t>(byteOffset);
}

inline uint16_t DataView::GetUint16(size_t byteOffset) const {
return ReadData<uint16_t>(byteOffset);
}

inline uint32_t DataView::GetUint32(size_t byteOffset) const {
return ReadData<uint32_t>(byteOffset);
}

inline void DataView::SetFloat32(size_t byteOffset, float value) const {
WriteData<float>(byteOffset, value);
}

inline void DataView::SetFloat64(size_t byteOffset, double value) const {
WriteData<double>(byteOffset, value);
}

inline void DataView::SetInt8(size_t byteOffset, int8_t value) const {
WriteData<int8_t>(byteOffset, value);
}

inline void DataView::SetInt16(size_t byteOffset, int16_t value) const {
WriteData<int16_t>(byteOffset, value);
}

inline void DataView::SetInt32(size_t byteOffset, int32_t value) const {
WriteData<int32_t>(byteOffset, value);
}

inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const {
WriteData<uint8_t>(byteOffset, value);
}

inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const {
WriteData<uint16_t>(byteOffset, value);
}

inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const {
WriteData<uint32_t>(byteOffset, value);
}

template <typename T>
inline T DataView::ReadData(size_t byteOffset) const {
if (byteOffset + sizeof(T) > _length ||
byteOffset + sizeof(T) < byteOffset) { // overflow
NAPI_THROW(RangeError::New(_env,
"Offset is outside the bounds of the DataView"));
return 0;
}

return *reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset);
}

template <typename T>
inline void DataView::WriteData(size_t byteOffset, T value) const {
if (byteOffset + sizeof(T) > _length ||
byteOffset + sizeof(T) < byteOffset) { // overflow
NAPI_THROW(RangeError::New(_env,
"Offset is outside the bounds of the DataView"));
return;
}

*reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset) = value;
}
#endif

Expand Down
34 changes: 29 additions & 5 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,11 +800,35 @@ namespace Napi {
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.
void* Data() const;

float GetFloat32(size_t byteOffset) const;
double GetFloat64(size_t byteOffset) const;
int8_t GetInt8(size_t byteOffset) const;
int16_t GetInt16(size_t byteOffset) const;
int32_t GetInt32(size_t byteOffset) const;
uint8_t GetUint8(size_t byteOffset) const;
uint16_t GetUint16(size_t byteOffset) const;
uint32_t GetUint32(size_t byteOffset) const;

void SetFloat32(size_t byteOffset, float value) const;
void SetFloat64(size_t byteOffset, double value) const;
void SetInt8(size_t byteOffset, int8_t value) const;
void SetInt16(size_t byteOffset, int16_t value) const;
void SetInt32(size_t byteOffset, int32_t value) const;
void SetUint8(size_t byteOffset, uint8_t value) const;
void SetUint16(size_t byteOffset, uint16_t value) const;
void SetUint32(size_t byteOffset, uint32_t value) const;

private:
template <typename T>
T ReadData(size_t byteOffset) const;

template <typename T>
void WriteData(size_t byteOffset, T value) const;

void* _data;
size_t _length;
};
#endif

Expand Down
3 changes: 3 additions & 0 deletions test/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Object InitBasicTypesNumber(Env env);
Object InitBasicTypesValue(Env env);
Object InitBuffer(Env env);
Object InitDataView(Env env);
Object InitDataViewReadWrite(Env env);
Object InitError(Env env);
Object InitExternal(Env env);
Object InitFunction(Env env);
Expand All @@ -24,6 +25,8 @@ Object Init(Env env, Object exports) {
exports.Set("basic_types_value", InitBasicTypesValue(env));
exports.Set("buffer", InitBuffer(env));
exports.Set("dataview", InitDataView(env));
exports.Set("dataview_read_write", InitDataView(env));
exports.Set("dataview_read_write", InitDataViewReadWrite(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 @@ -8,6 +8,7 @@
'binding.cc',
'buffer.cc',
'dataview/dataview.cc',
'dataview/dataview_read_write.cc',
'error.cc',
'external.cc',
'function.cc',
Expand Down
115 changes: 115 additions & 0 deletions test/dataview/dataview_read_write.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "napi.h"

using namespace Napi;

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

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

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

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

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

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

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

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

static void SetFloat32(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().FloatValue();
info[0].As<DataView>().SetFloat32(byteOffset, value);
}

static void SetFloat64(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().DoubleValue();
info[0].As<DataView>().SetFloat64(byteOffset, value);
}

static void SetInt8(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Int32Value();
info[0].As<DataView>().SetInt8(byteOffset, value);
}

static void SetInt16(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Int32Value();
info[0].As<DataView>().SetInt16(byteOffset, value);
}

static void SetInt32(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Int32Value();
info[0].As<DataView>().SetInt32(byteOffset, value);
}

static void SetUint8(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Uint32Value();
info[0].As<DataView>().SetUint8(byteOffset, value);
}

static void SetUint16(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Uint32Value();
info[0].As<DataView>().SetUint16(byteOffset, value);
}

static void SetUint32(const CallbackInfo& info) {
size_t byteOffset = info[1].As<Number>().Uint32Value();
auto value = info[2].As<Number>().Uint32Value();
info[0].As<DataView>().SetUint32(byteOffset, value);
}

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

exports["getFloat32"] = Function::New(env, GetFloat32);
exports["getFloat64"] = Function::New(env, GetFloat64);
exports["getInt8"] = Function::New(env, GetInt8);
exports["getInt16"] = Function::New(env, GetInt16);
exports["getInt32"] = Function::New(env, GetInt32);
exports["getUint8"] = Function::New(env, GetUint8);
exports["getUint16"] = Function::New(env, GetUint16);
exports["getUint32"] = Function::New(env, GetUint32);

exports["setFloat32"] = Function::New(env, SetFloat32);
exports["setFloat64"] = Function::New(env, SetFloat64);
exports["setInt8"] = Function::New(env, SetInt8);
exports["setInt16"] = Function::New(env, SetInt16);
exports["setInt32"] = Function::New(env, SetInt32);
exports["setUint8"] = Function::New(env, SetUint8);
exports["setUint16"] = Function::New(env, SetUint16);
exports["setUint32"] = Function::New(env, SetUint32);

return exports;
}
86 changes: 86 additions & 0 deletions test/dataview/dataview_read_write.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'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 expected(type, value) {
return eval(`(new ${type}Array([${value}]))[0]`);
}

function nativeReadDataView(dataview, type, offset, value) {
return eval(`binding.dataview_read_write.get${type}(dataview, offset)`);
}

function nativeWriteDataView(dataview, type, offset, value) {
eval(`binding.dataview_read_write.set${type}(dataview, offset, value)`);
}

function jsReadDataView(dataview, type, offset, value) {
return eval(`dataview.get${type}(offset, true)`);
}

function jsWriteDataView(dataview, type, offset, value) {
eval(`dataview.set${type}(offset, value, true)`);
}

function testReadData(dataview, type, offset, value) {
jsWriteDataView(dataview, type, offset, 0);
assert.strictEqual(jsReadDataView(dataview, type, offset), 0);

jsWriteDataView(dataview, type, offset, value);
assert.strictEqual(
nativeReadDataView(dataview, type, offset), expected(type, value));
}

function testWriteData(dataview, type, offset, value) {
jsWriteDataView(dataview, type, offset, 0);
assert.strictEqual(jsReadDataView(dataview, type, offset), 0);

nativeWriteDataView(dataview, type, offset, value);
assert.strictEqual(
jsReadDataView(dataview, type, offset), expected(type, value));
}

function testInvalidOffset(dataview, type, offset, value) {
assert.throws(() => {
nativeReadDataView(dataview, type, offset);
}, RangeError);

assert.throws(() => {
nativeWriteDataView(dataview, type, offset, value);
}, RangeError);
}

const dataview = new DataView(new ArrayBuffer(22));

testReadData(dataview, 'Float32', 0, 10.2);
testReadData(dataview, 'Float64', 4, 20.3);
testReadData(dataview, 'Int8', 5, 120);
testReadData(dataview, 'Int16', 7, 15000);
testReadData(dataview, 'Int32', 11, 200000);
testReadData(dataview, 'Uint8', 12, 128);
testReadData(dataview, 'Uint16', 14, 32768);
testReadData(dataview, 'Uint32', 18, 1000000);

testWriteData(dataview, 'Float32', 0, 10.2);
testWriteData(dataview, 'Float64', 4, 20.3);
testWriteData(dataview, 'Int8', 5, 120);
testWriteData(dataview, 'Int16', 7, 15000);
testWriteData(dataview, 'Int32', 11, 200000);
testWriteData(dataview, 'Uint8', 12, 128);
testWriteData(dataview, 'Uint16', 14, 32768);
testWriteData(dataview, 'Uint32', 18, 1000000);

testInvalidOffset(dataview, 'Float32', 22, 10.2);
testInvalidOffset(dataview, 'Float64', 22, 20.3);
testInvalidOffset(dataview, 'Int8', 22, 120);
testInvalidOffset(dataview, 'Int16', 22, 15000);
testInvalidOffset(dataview, 'Int32', 22, 200000);
testInvalidOffset(dataview, 'Uint8', 22, 128);
testInvalidOffset(dataview, 'Uint16', 22, 32768);
testInvalidOffset(dataview, 'Uint32', 22, 1000000);
}
Loading