Skip to content

Commit 6f9070d

Browse files
authored
napi: Typed arrays (nodejs#90)
* napi: Separate API for external ArrayBuffer
1 parent 8803b97 commit 6f9070d

File tree

6 files changed

+409
-43
lines changed

6 files changed

+409
-43
lines changed

src/node_jsvmapi.cc

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*******************************************************************************
1+
/*******************************************************************************
22
* Experimental prototype for demonstrating VM agnostic and ABI stable API
33
* for native modules to use instead of using Nan and V8 APIs directly.
44
*
@@ -517,7 +517,7 @@ namespace v8impl {
517517
napi_ok : napi_set_last_error(napi_pending_exception))
518518

519519
// Static last error returned from napi_get_last_error_info
520-
napi_extended_error_info static_last_error;
520+
napi_extended_error_info static_last_error = {};
521521

522522
// Warning: Keep in-sync with napi_status enum
523523
const char* error_messages[] = {
@@ -1881,3 +1881,194 @@ napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result) {
18811881
*result = node::Buffer::Length(v8impl::V8LocalValueFromJsValue(v));
18821882
return GET_RETURN_STATUS();
18831883
}
1884+
1885+
napi_status napi_is_arraybuffer(napi_env e, napi_value value, bool* result) {
1886+
NAPI_PREAMBLE(e);
1887+
CHECK_ARG(result);
1888+
1889+
v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue(value);
1890+
*result = v8value->IsArrayBuffer();
1891+
1892+
return GET_RETURN_STATUS();
1893+
}
1894+
1895+
napi_status napi_create_arraybuffer(napi_env e,
1896+
size_t byte_length,
1897+
void** data,
1898+
napi_value* result) {
1899+
NAPI_PREAMBLE(e);
1900+
CHECK_ARG(result);
1901+
1902+
v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e);
1903+
v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, byte_length);
1904+
1905+
// Optionally return a pointer to the buffer's data, to avoid another call to retreive it.
1906+
if (data != nullptr) {
1907+
*data = buffer->GetContents().Data();
1908+
}
1909+
1910+
*result = v8impl::JsValueFromV8LocalValue(buffer);
1911+
return GET_RETURN_STATUS();
1912+
}
1913+
1914+
napi_status napi_create_external_arraybuffer(napi_env e,
1915+
void* external_data,
1916+
size_t byte_length,
1917+
napi_value* result) {
1918+
NAPI_PREAMBLE(e);
1919+
CHECK_ARG(result);
1920+
1921+
v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e);
1922+
v8::Local<v8::ArrayBuffer> buffer =
1923+
v8::ArrayBuffer::New(isolate, external_data, byte_length);
1924+
1925+
*result = v8impl::JsValueFromV8LocalValue(buffer);
1926+
return GET_RETURN_STATUS();
1927+
}
1928+
1929+
napi_status napi_get_arraybuffer_info(napi_env e,
1930+
napi_value arraybuffer,
1931+
void** data,
1932+
size_t* byte_length) {
1933+
NAPI_PREAMBLE(e);
1934+
1935+
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
1936+
RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg);
1937+
1938+
v8::ArrayBuffer::Contents contents = value.As<v8::ArrayBuffer>()->GetContents();
1939+
1940+
if (data != nullptr) {
1941+
*data = contents.Data();
1942+
}
1943+
1944+
if (byte_length != nullptr) {
1945+
*byte_length = contents.ByteLength();
1946+
}
1947+
1948+
return GET_RETURN_STATUS();
1949+
}
1950+
1951+
napi_status napi_is_typedarray(napi_env e, napi_value value, bool* result) {
1952+
NAPI_PREAMBLE(e);
1953+
CHECK_ARG(result);
1954+
1955+
v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue(value);
1956+
*result = v8value->IsTypedArray();
1957+
1958+
return GET_RETURN_STATUS();
1959+
}
1960+
1961+
napi_status napi_create_typedarray(napi_env e,
1962+
napi_typedarray_type type,
1963+
size_t length,
1964+
napi_value arraybuffer,
1965+
size_t byte_offset,
1966+
napi_value* result) {
1967+
NAPI_PREAMBLE(e);
1968+
CHECK_ARG(result);
1969+
1970+
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
1971+
RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg);
1972+
1973+
v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
1974+
v8::Local<v8::TypedArray> typedArray;
1975+
1976+
switch (type) {
1977+
case napi_int8:
1978+
typedArray = v8::Int8Array::New(buffer, byte_offset, length);
1979+
break;
1980+
case napi_uint8:
1981+
typedArray = v8::Uint8Array::New(buffer, byte_offset, length);
1982+
break;
1983+
case napi_uint8_clamped:
1984+
typedArray = v8::Uint8ClampedArray::New(buffer, byte_offset, length);
1985+
break;
1986+
case napi_int16:
1987+
typedArray = v8::Int16Array::New(buffer, byte_offset, length);
1988+
break;
1989+
case napi_uint16:
1990+
typedArray = v8::Uint16Array::New(buffer, byte_offset, length);
1991+
break;
1992+
case napi_int32:
1993+
typedArray = v8::Int32Array::New(buffer, byte_offset, length);
1994+
break;
1995+
case napi_uint32:
1996+
typedArray = v8::Uint32Array::New(buffer, byte_offset, length);
1997+
break;
1998+
case napi_float32:
1999+
typedArray = v8::Float32Array::New(buffer, byte_offset, length);
2000+
break;
2001+
case napi_float64:
2002+
typedArray = v8::Float64Array::New(buffer, byte_offset, length);
2003+
break;
2004+
default:
2005+
return napi_set_last_error(napi_invalid_arg);
2006+
}
2007+
2008+
*result = v8impl::JsValueFromV8LocalValue(typedArray);
2009+
return GET_RETURN_STATUS();
2010+
}
2011+
2012+
napi_status napi_get_typedarray_info(napi_env e,
2013+
napi_value typedarray,
2014+
napi_typedarray_type* type,
2015+
size_t* length,
2016+
void** data,
2017+
napi_value* arraybuffer,
2018+
size_t* byte_offset) {
2019+
NAPI_PREAMBLE(e);
2020+
2021+
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(typedarray);
2022+
RETURN_STATUS_IF_FALSE(value->IsTypedArray(), napi_invalid_arg);
2023+
2024+
v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
2025+
2026+
if (type != nullptr) {
2027+
if (value->IsInt8Array()) {
2028+
*type = napi_int8;
2029+
}
2030+
else if (value->IsUint8Array()) {
2031+
*type = napi_uint8;
2032+
}
2033+
else if (value->IsUint8ClampedArray()) {
2034+
*type = napi_uint8_clamped;
2035+
}
2036+
else if (value->IsInt16Array()) {
2037+
*type = napi_int16;
2038+
}
2039+
else if (value->IsUint16Array()) {
2040+
*type = napi_uint16;
2041+
}
2042+
else if (value->IsInt32Array()) {
2043+
*type = napi_int32;
2044+
}
2045+
else if (value->IsUint32Array()) {
2046+
*type = napi_uint32;
2047+
}
2048+
else if (value->IsFloat32Array()) {
2049+
*type = napi_float32;
2050+
}
2051+
else if (value->IsFloat64Array()) {
2052+
*type = napi_float64;
2053+
}
2054+
}
2055+
2056+
if (length != nullptr) {
2057+
*length = array->Length();
2058+
}
2059+
2060+
v8::Local<v8::ArrayBuffer> buffer = array->Buffer();
2061+
if (data != nullptr) {
2062+
*data = static_cast<uint8_t*>(buffer->GetContents().Data()) + array->ByteOffset();
2063+
}
2064+
2065+
if (arraybuffer != nullptr) {
2066+
*arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
2067+
}
2068+
2069+
if (byte_offset != nullptr) {
2070+
*byte_offset = array->ByteOffset();
2071+
}
2072+
2073+
return GET_RETURN_STATUS();
2074+
}

src/node_jsvmapi.h

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
#define SRC_NODE_JSVMAPI_H_
2020

2121
#include "node_jsvmapi_types.h"
22-
#include <stdlib.h>
23-
#include <stdint.h>
2422

2523
#ifndef NODE_EXTERN
2624
# ifdef _WIN32
@@ -51,7 +49,7 @@ struct napi_module_struct {
5149
void* nm_context_register_func;
5250
const char* nm_modname;
5351
void* nm_priv;
54-
struct node_module* nm_link;
52+
void* nm_link;
5553
};
5654

5755
NODE_EXTERN void napi_module_register(void* mod);
@@ -103,44 +101,6 @@ NODE_EXTERN void napi_module_register(void* mod);
103101
// TODO(ianhall): We're using C linkage for the API but we're also using the
104102
// bool type in these exports. Is that safe and stable?
105103
extern "C" {
106-
enum napi_valuetype {
107-
// ES6 types (corresponds to typeof)
108-
napi_undefined,
109-
napi_null,
110-
napi_boolean,
111-
napi_number,
112-
napi_string,
113-
napi_symbol,
114-
napi_object,
115-
napi_function,
116-
};
117-
118-
enum napi_status {
119-
napi_ok,
120-
napi_invalid_arg,
121-
napi_object_expected,
122-
napi_string_expected,
123-
napi_function_expected,
124-
napi_number_expected,
125-
napi_boolean_expected,
126-
napi_generic_failure,
127-
napi_pending_exception,
128-
napi_status_last
129-
};
130-
131-
struct napi_extended_error_info {
132-
const char* error_message;
133-
void* engine_reserved;
134-
uint32_t engine_error_code;
135-
napi_status error_code;
136-
137-
napi_extended_error_info() :
138-
error_message(NULL),
139-
engine_reserved(NULL),
140-
engine_error_code(0),
141-
error_code(napi_ok)
142-
{ }
143-
};
144104

145105
NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info();
146106

@@ -354,6 +314,23 @@ NODE_EXTERN napi_status napi_buffer_copy(napi_env e,
354314
NODE_EXTERN napi_status napi_buffer_has_instance(napi_env e, napi_value v, bool* result);
355315
NODE_EXTERN napi_status napi_buffer_data(napi_env e, napi_value v, char** result);
356316
NODE_EXTERN napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result);
317+
318+
// Methods to work with array buffers and typed arrays
319+
NODE_EXTERN napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result);
320+
NODE_EXTERN napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data,
321+
napi_value* result);
322+
NODE_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, void* external_data,
323+
size_t byte_length, napi_value* result);
324+
NODE_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer,
325+
void** data, size_t* byte_length);
326+
NODE_EXTERN napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result);
327+
NODE_EXTERN napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type,
328+
size_t length, napi_value arraybuffer,
329+
size_t byte_offset, napi_value* result);
330+
NODE_EXTERN napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray,
331+
napi_typedarray_type* type, size_t* length,
332+
void** data, napi_value* arraybuffer,
333+
size_t* byte_offset);
357334
} // extern "C"
358335

359336
#endif // SRC_NODE_JSVMAPI_H__

src/node_jsvmapi_types.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef SRC_NODE_JSVMAPI_TYPES_H_
22
#define SRC_NODE_JSVMAPI_TYPES_H_
33

4+
#include <stdint.h>
5+
46
// JSVM API types are all opaque pointers for ABI stability
57
// typedef undefined structs instead of void* for compile time type safety
68
typedef struct napi_env__ *napi_env;
@@ -34,4 +36,48 @@ struct napi_property_descriptor {
3436
void* data;
3537
};
3638

39+
enum napi_valuetype {
40+
// ES6 types (corresponds to typeof)
41+
napi_undefined,
42+
napi_null,
43+
napi_boolean,
44+
napi_number,
45+
napi_string,
46+
napi_symbol,
47+
napi_object,
48+
napi_function,
49+
};
50+
51+
enum napi_typedarray_type {
52+
napi_int8,
53+
napi_uint8,
54+
napi_uint8_clamped,
55+
napi_int16,
56+
napi_uint16,
57+
napi_int32,
58+
napi_uint32,
59+
napi_float32,
60+
napi_float64,
61+
};
62+
63+
enum napi_status {
64+
napi_ok,
65+
napi_invalid_arg,
66+
napi_object_expected,
67+
napi_string_expected,
68+
napi_function_expected,
69+
napi_number_expected,
70+
napi_boolean_expected,
71+
napi_generic_failure,
72+
napi_pending_exception,
73+
napi_status_last
74+
};
75+
76+
struct napi_extended_error_info {
77+
const char* error_message;
78+
void* engine_reserved;
79+
uint32_t engine_error_code;
80+
napi_status error_code;
81+
};
82+
3783
#endif // SRC_NODE_JSVMAPI_TYPES_H_
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_typedarray",
5+
"sources": [ "test_typedarray.cc" ]
6+
}
7+
]
8+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
require('../../common');
3+
var assert = require('assert');
4+
5+
// Testing api calls for arrays
6+
var test_typedarray = require('./build/Release/test_typedarray');
7+
8+
var byteArray = new Uint8Array(3);
9+
byteArray[0] = 0;
10+
byteArray[1] = 1;
11+
byteArray[2] = 2;
12+
assert.equal(byteArray.length, 3);
13+
14+
var doubleArray = new Float64Array(3);
15+
doubleArray[0] = 0.0;
16+
doubleArray[1] = 1.1;
17+
doubleArray[2] = 2.2;
18+
assert.equal(doubleArray.length, 3);
19+
20+
var byteResult = test_typedarray.Multiply(byteArray, 3);
21+
assert.equal(byteResult.length, 3);
22+
assert.equal(byteResult[0], 0);
23+
assert.equal(byteResult[1], 3);
24+
assert.equal(byteResult[2], 6);
25+
26+
var doubleResult = test_typedarray.Multiply(doubleArray, -3);
27+
assert.equal(doubleResult.length, 3);
28+
assert.equal(doubleResult[0], 0);
29+
assert.equal(Math.round(10 * doubleResult[1]) / 10, -3.3);
30+
assert.equal(Math.round(10 * doubleResult[2]) / 10, -6.6);

0 commit comments

Comments
 (0)