Skip to content

Commit 17b5129

Browse files
mikolalysenkokkoopa
authored andcommitted
added helper class for accessing contents of typedarrays
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
1 parent 9bf9b8b commit 17b5129

File tree

7 files changed

+258
-0
lines changed

7 files changed

+258
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ The hooks to access V8 internals—including GC and statistics—are different a
277277
- <a href="doc/v8_misc.md#api_nan_get_current_context"><b><code>Nan::GetCurrentContext()</code></b></a>
278278
- <a href="doc/v8_misc.md#api_nan_set_isolate_data"><b><code>Nan::SetIsolateData()</code></b></a>
279279
- <a href="doc/v8_misc.md#api_nan_get_isolate_data"><b><code>Nan::GetIsolateData()</code></b></a>
280+
- <a href="doc/v8_misc.md#api_nan_typedarray_contents"><b><code>Nan::TypedArrayContents</code></b></a>
280281

281282

282283
### Miscellaneous Node Helpers

doc/v8_misc.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- <a href="#api_nan_get_current_context"><b><code>Nan::GetCurrentContext()</code></b></a>
55
- <a href="#api_nan_set_isolate_data"><b><code>Nan::SetIsolateData()</code></b></a>
66
- <a href="#api_nan_get_isolate_data"><b><code>Nan::GetIsolateData()</code></b></a>
7+
- <a href="#api_nan_typedarray_contents"><b><code>Nan::TypedArrayContents<T></code></b></a>
78

89

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

65+
<a name="api_nan_typedarray_contents"></a>
66+
### Nan::TypedArrayContents<T>
67+
68+
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.
69+
70+
Note that you must store a reference to the `array` object while you are accessing its contents.
71+
72+
Definition:
73+
74+
```c++
75+
template<typename T>
76+
class Nan::TypedArrayContents {
77+
public:
78+
TypedArrayContents(v8::Local<Value> array);
79+
80+
size_t length() const;
81+
82+
T* const operator*();
83+
const T* const operator*() const;
84+
};
85+
```

nan.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,6 +2228,10 @@ MakeMaybe(MaybeMaybe<T> v) {
22282228
return imp::Maybefier<MaybeMaybe<T> >::convert(v);
22292229
}
22302230

2231+
//=== TypedArrayContents =======================================================
2232+
2233+
#include "nan_typedarray_contents.h" // NOLINT(build/include)
2234+
22312235
} // end of namespace Nan
22322236

22332237
#endif // NAN_H_

nan_typedarray_contents.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2015 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
#ifndef NAN_TYPEDARRAY_CONTENTS_H_
10+
#define NAN_TYPEDARRAY_CONTENTS_H_
11+
12+
template<typename T>
13+
class TypedArrayContents {
14+
public:
15+
NAN_INLINE explicit TypedArrayContents(v8::Local<v8::Value> from) :
16+
length_(0), data_(NULL) {
17+
18+
size_t length = 0;
19+
void* data = NULL;
20+
21+
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || \
22+
(V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
23+
24+
if (from->IsArrayBufferView()) {
25+
v8::Local<v8::ArrayBufferView> array =
26+
v8::Local<v8::ArrayBufferView>::Cast(from);
27+
28+
const size_t byte_length = array->ByteLength();
29+
const ptrdiff_t byte_offset = array->ByteOffset();
30+
v8::Local<v8::ArrayBuffer> buffer = array->Buffer();
31+
32+
length = byte_length / sizeof(T);
33+
data = static_cast<char*>(buffer->GetContents().Data()) + byte_offset;
34+
}
35+
36+
#else
37+
38+
if (from->IsObject() && !from->IsNull()) {
39+
v8::Local<v8::Object> array = v8::Local<v8::Object>::Cast(from);
40+
41+
MaybeLocal<v8::Value> buffer = Get(array,
42+
New<v8::String>("buffer").ToLocalChecked());
43+
MaybeLocal<v8::Value> byte_length = Get(array,
44+
New<v8::String>("byteLength").ToLocalChecked());
45+
MaybeLocal<v8::Value> byte_offset = Get(array,
46+
New<v8::String>("byteOffset").ToLocalChecked());
47+
48+
if (!buffer.IsEmpty() &&
49+
!byte_length.IsEmpty() && byte_length.ToLocalChecked()->IsUint32() &&
50+
!byte_offset.IsEmpty() && byte_offset.ToLocalChecked()->IsUint32()) {
51+
data = array->GetIndexedPropertiesExternalArrayData();
52+
if(data) {
53+
length = byte_length.ToLocalChecked()->Uint32Value() / sizeof(T);
54+
}
55+
}
56+
}
57+
58+
#endif
59+
60+
#if defined(_MSC_VER) || defined(__GNUC__)
61+
assert(reinterpret_cast<uintptr_t>(data) % __alignof(T) == 0);
62+
#elif __cplusplus >= 201103L
63+
assert(reinterpret_cast<uintptr_t>(data) % alignof(T) == 0);
64+
#else
65+
assert(reinterpret_cast<uintptr_t>(data) % sizeof(T) == 0);
66+
#endif
67+
68+
length_ = length;
69+
data_ = static_cast<T*>(data);
70+
}
71+
72+
NAN_INLINE size_t length() const { return length_; }
73+
NAN_INLINE T* operator*() { return data_; }
74+
NAN_INLINE const T* operator*() const { return data_; }
75+
76+
private:
77+
NAN_DISALLOW_ASSIGN_COPY_MOVE(TypedArrayContents)
78+
79+
//Disable heap allocation
80+
void *operator new(size_t size);
81+
void operator delete(void *, size_t);
82+
83+
size_t length_;
84+
T* data_;
85+
};
86+
87+
#endif // NAN_TYPEDARRAY_CONTENTS_H_

test/binding.gyp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,8 @@
140140
"target_name" : "setcallhandler"
141141
, "sources" : [ "cpp/setcallhandler.cpp" ]
142142
}
143+
, {
144+
"target_name" : "typedarrays"
145+
, "sources" : [ "cpp/typedarrays.cpp" ]
146+
}
143147
]}

test/cpp/typedarrays.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2015 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
#include <nan.h>
10+
11+
#include <stdint.h>
12+
13+
using namespace Nan; // NOLINT(build/namespaces)
14+
15+
NAN_METHOD(ReadU8) {
16+
TypedArrayContents<uint8_t> data(info[0]);
17+
18+
v8::Local<v8::Array> result = New<v8::Array>(data.length());
19+
for (size_t i=0; i<data.length(); i++) {
20+
Set(result, i, New<v8::Number>((*data)[i]));
21+
}
22+
23+
info.GetReturnValue().Set(result);
24+
}
25+
26+
NAN_METHOD(ReadI32) {
27+
TypedArrayContents<int32_t> data(info[0]);
28+
29+
v8::Local<v8::Array> result = New<v8::Array>(data.length());
30+
for (size_t i=0; i<data.length(); i++) {
31+
Set(result, i, New<v8::Number>((*data)[i]));
32+
}
33+
34+
info.GetReturnValue().Set(result);
35+
}
36+
37+
NAN_METHOD(ReadFloat) {
38+
TypedArrayContents<float> data(info[0]);
39+
40+
v8::Local<v8::Array> result = New<v8::Array>(data.length());\
41+
for (size_t i=0; i<data.length(); i++) {
42+
Set(result, i, New<v8::Number>((*data)[i]));
43+
}
44+
45+
info.GetReturnValue().Set(result);
46+
}
47+
48+
NAN_METHOD(ReadDouble) {
49+
TypedArrayContents<double> data(info[0]);
50+
51+
v8::Local<v8::Array> result = New<v8::Array>(data.length());
52+
for (size_t i=0; i<data.length(); i++) {
53+
Set(result, i, New<v8::Number>((*data)[i]));
54+
}
55+
56+
info.GetReturnValue().Set(result);
57+
}
58+
59+
NAN_MODULE_INIT(Init) {
60+
NAN_EXPORT(target, ReadU8);
61+
NAN_EXPORT(target, ReadI32);
62+
NAN_EXPORT(target, ReadFloat);
63+
NAN_EXPORT(target, ReadDouble);
64+
}
65+
66+
NODE_MODULE(typedarrays, Init)

test/js/typedarrays-test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const test = require('tap').test
2+
, testRoot = require('path').resolve(__dirname, '..')
3+
, bindings = require('bindings')({ module_root: testRoot, bindings: 'typedarrays' });
4+
5+
test('typedarrays - simple cases', function (t) {
6+
if (typeof Uint8Array !== 'function') {
7+
8+
t.pass('typedarrays not supported');
9+
t.end();
10+
11+
} else {
12+
13+
var zeros = new Uint8Array(5)
14+
t.same(bindings.ReadU8(zeros), [0,0,0,0,0])
15+
16+
var y = zeros[0]
17+
t.equals(y, 0)
18+
t.same(bindings.ReadU8(zeros), [0,0,0,0,0])
19+
20+
var u8array = new Uint8Array([1, 255, 3]);
21+
t.same(bindings.ReadU8(u8array), [1, 255, 3]);
22+
t.same(bindings.ReadU8(u8array.subarray(1)), [255, 3]);
23+
t.same(bindings.ReadU8(u8array.subarray(0, 2)), [1, 255]);
24+
t.same(bindings.ReadU8(u8array.subarray(1, 2)), [255]);
25+
26+
t.same(bindings.ReadU8(new Uint8Array(u8array)), [1, 255, 3]);
27+
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(1))), [255, 3]);
28+
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(0, 2))), [1, 255]);
29+
t.same(bindings.ReadU8(new Uint8Array(u8array.subarray(1, 2))), [255]);
30+
31+
t.same(bindings.ReadU8((new Uint8Array(u8array.buffer)).subarray(1)), [255, 3]);
32+
33+
34+
var i32array = new Int32Array([0, 1, -1, 1073741824, -1073741824]);
35+
t.same(bindings.ReadI32(i32array), [0, 1, -1, 1073741824, -1073741824]);
36+
37+
var f32array = new Float32Array([1, -1, Infinity, -Infinity, 0, +0, -0]);
38+
t.same(bindings.ReadFloat(f32array), [1, -1, Infinity, -Infinity, 0, +0, -0]);
39+
t.same(bindings.ReadFloat(f32array.subarray(1)), [-1, Infinity, -Infinity, 0, +0, -0]);
40+
t.same(bindings.ReadFloat(f32array.subarray(0,4)), [1, -1, Infinity, -Infinity]);
41+
t.same(bindings.ReadFloat(f32array.subarray(1,3)), [-1, Infinity]);
42+
43+
t.end();
44+
}
45+
});
46+
47+
test('typedarrays - bad arguments', function (t) {
48+
if (typeof Uint8Array !== 'function') {
49+
50+
t.pass('typedarrays not supported');
51+
t.end();
52+
53+
} else {
54+
55+
t.same(bindings.ReadU8(0), []);
56+
t.same(bindings.ReadU8(1), []);
57+
t.same(bindings.ReadU8(null), []);
58+
t.same(bindings.ReadU8(), []);
59+
t.same(bindings.ReadU8('foobar'), []);
60+
t.same(bindings.ReadU8([]), []);
61+
t.same(bindings.ReadU8([1,2]), []);
62+
t.same(bindings.ReadU8({}), []);
63+
t.same(bindings.ReadU8(Uint8Array), []);
64+
t.same(bindings.ReadU8(new Float32Array(0)), []);
65+
66+
t.same(bindings.ReadU8({
67+
byteLength: 10000000,
68+
byteOffset: 100000,
69+
buffer: null
70+
}), [])
71+
72+
t.end();
73+
}
74+
});

0 commit comments

Comments
 (0)