Skip to content

Commit

Permalink
added helper class for accessing contents of typedarrays
Browse files Browse the repository at this point in the history
move typedarray contents to v8 misc helper docs

fix style errors, use const and more descriptive types

more style tweaks

style tweaks, assert on alignment and missing typedarrays

remove alignof, do not assert fail on versions < 0.8

add better alignment checks for msc, g++ and c++11

fix up signature

use __cplusplus for checking if alignof available, unify msc and gnuc fallbacks, defer assigning to data_ until after assertion

add failing test case for node 0.12

access buffer property from arraybufferview before reading in order to force v8 to externalize it

change docs to force CI to rerun

remove unneeded type qualifiers which trigger spurious warnings on gcc
  • Loading branch information
mikolalysenko authored and kkoopa committed Oct 8, 2015
1 parent 9bf9b8b commit 17b5129
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ The hooks to access V8 internals—including GC and statistics—are different a
- <a href="doc/v8_misc.md#api_nan_get_current_context"><b><code>Nan::GetCurrentContext()</code></b></a>
- <a href="doc/v8_misc.md#api_nan_set_isolate_data"><b><code>Nan::SetIsolateData()</code></b></a>
- <a href="doc/v8_misc.md#api_nan_get_isolate_data"><b><code>Nan::GetIsolateData()</code></b></a>
- <a href="doc/v8_misc.md#api_nan_typedarray_contents"><b><code>Nan::TypedArrayContents</code></b></a>


### Miscellaneous Node Helpers
Expand Down
22 changes: 22 additions & 0 deletions doc/v8_misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- <a href="#api_nan_get_current_context"><b><code>Nan::GetCurrentContext()</code></b></a>
- <a href="#api_nan_set_isolate_data"><b><code>Nan::SetIsolateData()</code></b></a>
- <a href="#api_nan_get_isolate_data"><b><code>Nan::GetIsolateData()</code></b></a>
- <a href="#api_nan_typedarray_contents"><b><code>Nan::TypedArrayContents<T></code></b></a>


<a name="api_nan_utf8_string"></a>
Expand Down Expand Up @@ -61,3 +62,24 @@ Signature:
T *Nan::GetIsolateData(v8::Isolate *isolate)
```

<a name="api_nan_typedarray_contents"></a>
### Nan::TypedArrayContents<T>

A helper class for accessing the contents of an ArrayBufferView (aka a typedarray) from C++. If the input array is not a valid typedarray, then the data pointer of TypedArrayContents will default to `NULL` and the length will be 0. If the data pointer is not compatible with the alignment requirements of type, an assertion error will fail.

Note that you must store a reference to the `array` object while you are accessing its contents.

Definition:

```c++
template<typename T>
class Nan::TypedArrayContents {
public:
TypedArrayContents(v8::Local<Value> array);

size_t length() const;

T* const operator*();
const T* const operator*() const;
};
```
4 changes: 4 additions & 0 deletions nan.h
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,10 @@ MakeMaybe(MaybeMaybe<T> v) {
return imp::Maybefier<MaybeMaybe<T> >::convert(v);
}

//=== TypedArrayContents =======================================================

#include "nan_typedarray_contents.h" // NOLINT(build/include)

} // end of namespace Nan

#endif // NAN_H_
87 changes: 87 additions & 0 deletions nan_typedarray_contents.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*********************************************************************
* NAN - Native Abstractions for Node.js
*
* Copyright (c) 2015 NAN contributors
*
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
********************************************************************/

#ifndef NAN_TYPEDARRAY_CONTENTS_H_
#define NAN_TYPEDARRAY_CONTENTS_H_

template<typename T>
class TypedArrayContents {
public:
NAN_INLINE explicit TypedArrayContents(v8::Local<v8::Value> from) :
length_(0), data_(NULL) {

size_t length = 0;
void* data = NULL;

#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || \
(V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))

if (from->IsArrayBufferView()) {
v8::Local<v8::ArrayBufferView> array =
v8::Local<v8::ArrayBufferView>::Cast(from);

const size_t byte_length = array->ByteLength();
const ptrdiff_t byte_offset = array->ByteOffset();
v8::Local<v8::ArrayBuffer> buffer = array->Buffer();

length = byte_length / sizeof(T);
data = static_cast<char*>(buffer->GetContents().Data()) + byte_offset;
}

#else

if (from->IsObject() && !from->IsNull()) {
v8::Local<v8::Object> array = v8::Local<v8::Object>::Cast(from);

MaybeLocal<v8::Value> buffer = Get(array,
New<v8::String>("buffer").ToLocalChecked());
MaybeLocal<v8::Value> byte_length = Get(array,
New<v8::String>("byteLength").ToLocalChecked());
MaybeLocal<v8::Value> byte_offset = Get(array,
New<v8::String>("byteOffset").ToLocalChecked());

if (!buffer.IsEmpty() &&
!byte_length.IsEmpty() && byte_length.ToLocalChecked()->IsUint32() &&
!byte_offset.IsEmpty() && byte_offset.ToLocalChecked()->IsUint32()) {
data = array->GetIndexedPropertiesExternalArrayData();
if(data) {
length = byte_length.ToLocalChecked()->Uint32Value() / sizeof(T);
}
}
}

#endif

#if defined(_MSC_VER) || defined(__GNUC__)
assert(reinterpret_cast<uintptr_t>(data) % __alignof(T) == 0);
#elif __cplusplus >= 201103L
assert(reinterpret_cast<uintptr_t>(data) % alignof(T) == 0);
#else
assert(reinterpret_cast<uintptr_t>(data) % sizeof(T) == 0);
#endif

length_ = length;
data_ = static_cast<T*>(data);
}

NAN_INLINE size_t length() const { return length_; }
NAN_INLINE T* operator*() { return data_; }
NAN_INLINE const T* operator*() const { return data_; }

private:
NAN_DISALLOW_ASSIGN_COPY_MOVE(TypedArrayContents)

//Disable heap allocation
void *operator new(size_t size);
void operator delete(void *, size_t);

size_t length_;
T* data_;
};

#endif // NAN_TYPEDARRAY_CONTENTS_H_
4 changes: 4 additions & 0 deletions test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,8 @@
"target_name" : "setcallhandler"
, "sources" : [ "cpp/setcallhandler.cpp" ]
}
, {
"target_name" : "typedarrays"
, "sources" : [ "cpp/typedarrays.cpp" ]
}
]}
66 changes: 66 additions & 0 deletions test/cpp/typedarrays.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*********************************************************************
* NAN - Native Abstractions for Node.js
*
* Copyright (c) 2015 NAN contributors
*
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
********************************************************************/

#include <nan.h>

#include <stdint.h>

using namespace Nan; // NOLINT(build/namespaces)

NAN_METHOD(ReadU8) {
TypedArrayContents<uint8_t> data(info[0]);

v8::Local<v8::Array> result = New<v8::Array>(data.length());
for (size_t i=0; i<data.length(); i++) {
Set(result, i, New<v8::Number>((*data)[i]));
}

info.GetReturnValue().Set(result);
}

NAN_METHOD(ReadI32) {
TypedArrayContents<int32_t> data(info[0]);

v8::Local<v8::Array> result = New<v8::Array>(data.length());
for (size_t i=0; i<data.length(); i++) {
Set(result, i, New<v8::Number>((*data)[i]));
}

info.GetReturnValue().Set(result);
}

NAN_METHOD(ReadFloat) {
TypedArrayContents<float> data(info[0]);

v8::Local<v8::Array> result = New<v8::Array>(data.length());\
for (size_t i=0; i<data.length(); i++) {
Set(result, i, New<v8::Number>((*data)[i]));
}

info.GetReturnValue().Set(result);
}

NAN_METHOD(ReadDouble) {
TypedArrayContents<double> data(info[0]);

v8::Local<v8::Array> result = New<v8::Array>(data.length());
for (size_t i=0; i<data.length(); i++) {
Set(result, i, New<v8::Number>((*data)[i]));
}

info.GetReturnValue().Set(result);
}

NAN_MODULE_INIT(Init) {
NAN_EXPORT(target, ReadU8);
NAN_EXPORT(target, ReadI32);
NAN_EXPORT(target, ReadFloat);
NAN_EXPORT(target, ReadDouble);
}

NODE_MODULE(typedarrays, Init)
74 changes: 74 additions & 0 deletions test/js/typedarrays-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const test = require('tap').test
, testRoot = require('path').resolve(__dirname, '..')
, bindings = require('bindings')({ module_root: testRoot, bindings: 'typedarrays' });

test('typedarrays - simple cases', function (t) {
if (typeof Uint8Array !== 'function') {

t.pass('typedarrays not supported');
t.end();

} else {

var zeros = new Uint8Array(5)
t.same(bindings.ReadU8(zeros), [0,0,0,0,0])

var y = zeros[0]
t.equals(y, 0)
t.same(bindings.ReadU8(zeros), [0,0,0,0,0])

var u8array = new Uint8Array([1, 255, 3]);
t.same(bindings.ReadU8(u8array), [1, 255, 3]);
t.same(bindings.ReadU8(u8array.subarray(1)), [255, 3]);
t.same(bindings.ReadU8(u8array.subarray(0, 2)), [1, 255]);
t.same(bindings.ReadU8(u8array.subarray(1, 2)), [255]);

t.same(bindings.ReadU8(new Uint8Array(u8array)), [1, 255, 3]);
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(1))), [255, 3]);
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(0, 2))), [1, 255]);
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(1, 2))), [255]);

t.same(bindings.ReadU8((new Uint8Array(u8array.buffer)).subarray(1)), [255, 3]);


var i32array = new Int32Array([0, 1, -1, 1073741824, -1073741824]);
t.same(bindings.ReadI32(i32array), [0, 1, -1, 1073741824, -1073741824]);

var f32array = new Float32Array([1, -1, Infinity, -Infinity, 0, +0, -0]);
t.same(bindings.ReadFloat(f32array), [1, -1, Infinity, -Infinity, 0, +0, -0]);
t.same(bindings.ReadFloat(f32array.subarray(1)), [-1, Infinity, -Infinity, 0, +0, -0]);
t.same(bindings.ReadFloat(f32array.subarray(0,4)), [1, -1, Infinity, -Infinity]);
t.same(bindings.ReadFloat(f32array.subarray(1,3)), [-1, Infinity]);

t.end();
}
});

test('typedarrays - bad arguments', function (t) {
if (typeof Uint8Array !== 'function') {

t.pass('typedarrays not supported');
t.end();

} else {

t.same(bindings.ReadU8(0), []);
t.same(bindings.ReadU8(1), []);
t.same(bindings.ReadU8(null), []);
t.same(bindings.ReadU8(), []);
t.same(bindings.ReadU8('foobar'), []);
t.same(bindings.ReadU8([]), []);
t.same(bindings.ReadU8([1,2]), []);
t.same(bindings.ReadU8({}), []);
t.same(bindings.ReadU8(Uint8Array), []);
t.same(bindings.ReadU8(new Float32Array(0)), []);

t.same(bindings.ReadU8({
byteLength: 10000000,
byteOffset: 100000,
buffer: null
}), [])

t.end();
}
});

0 comments on commit 17b5129

Please sign in to comment.