typed arrays: add Buffer -> TypedArray constructor
- create a typed array from a node::Buffer object - update TypedArray::set() to spec - add TypedArray::get() method
This commit is contained in:
parent
55c21970fd
commit
5b05429bf0
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
NODE_EXT_LIST_START
|
NODE_EXT_LIST_START
|
||||||
NODE_EXT_LIST_ITEM(node_buffer)
|
NODE_EXT_LIST_ITEM(node_buffer)
|
||||||
|
NODE_EXT_LIST_ITEM(node_typed_array)
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
NODE_EXT_LIST_ITEM(node_crypto)
|
NODE_EXT_LIST_ITEM(node_crypto)
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <v8.h>
|
#include <v8.h>
|
||||||
|
|
||||||
#include "v8_typed_array.h"
|
#include "v8_typed_array.h"
|
||||||
|
#include "node_buffer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -151,6 +152,7 @@ class TypedArray {
|
|||||||
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);
|
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);
|
||||||
|
|
||||||
static BatchedMethods methods[] = {
|
static BatchedMethods methods[] = {
|
||||||
|
{ "get", &TypedArray<TBytes, TEAType>::get },
|
||||||
{ "set", &TypedArray<TBytes, TEAType>::set },
|
{ "set", &TypedArray<TBytes, TEAType>::set },
|
||||||
{ "slice", &TypedArray<TBytes, TEAType>::subarray },
|
{ "slice", &TypedArray<TBytes, TEAType>::subarray },
|
||||||
{ "subarray", &TypedArray<TBytes, TEAType>::subarray },
|
{ "subarray", &TypedArray<TBytes, TEAType>::subarray },
|
||||||
@ -183,14 +185,16 @@ class TypedArray {
|
|||||||
unsigned int length = 0;
|
unsigned int length = 0;
|
||||||
unsigned int byte_offset = 0;
|
unsigned int byte_offset = 0;
|
||||||
|
|
||||||
if (ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor.
|
// [m1k3] added support for Buffer constructor
|
||||||
|
if (node::Buffer::HasInstance(args[0])
|
||||||
|
|| ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor.
|
||||||
buffer = v8::Local<v8::Object>::Cast(args[0]);
|
buffer = v8::Local<v8::Object>::Cast(args[0]);
|
||||||
unsigned int buflen =
|
unsigned int buflen =
|
||||||
buffer->GetIndexedPropertiesExternalArrayDataLength();
|
buffer->GetIndexedPropertiesExternalArrayDataLength();
|
||||||
|
|
||||||
if (args[1]->Int32Value() < 0)
|
if (!args[1]->IsUndefined() && args[1]->Int32Value() < 0)
|
||||||
return ThrowRangeError("Byte offset out of range.");
|
return ThrowRangeError("Byte offset out of range.");
|
||||||
byte_offset = args[1]->Uint32Value();
|
byte_offset = args[1]->IsUndefined() ? 0 : args[1]->Uint32Value();
|
||||||
|
|
||||||
if (!checkAlignment(byte_offset, TBytes))
|
if (!checkAlignment(byte_offset, TBytes))
|
||||||
return ThrowRangeError("Byte offset is not aligned.");
|
return ThrowRangeError("Byte offset is not aligned.");
|
||||||
@ -214,10 +218,11 @@ class TypedArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(deanm): Error check.
|
// TODO(deanm): Error check.
|
||||||
void* buf = buffer->GetPointerFromInternalField(0);
|
void* buf = buffer->GetIndexedPropertiesExternalArrayData();
|
||||||
args.This()->SetIndexedPropertiesToExternalArrayData(
|
args.This()->SetIndexedPropertiesToExternalArrayData(
|
||||||
reinterpret_cast<char*>(buf) + byte_offset, TEAType, length);
|
reinterpret_cast<char*>(buf) + byte_offset, TEAType, length);
|
||||||
} else if (args[0]->IsObject()) { // TypedArray / type[] constructor.
|
}
|
||||||
|
else if (args[0]->IsObject()) { // TypedArray / type[] constructor.
|
||||||
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(args[0]);
|
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(args[0]);
|
||||||
length = obj->Get(v8::String::New("length"))->Uint32Value();
|
length = obj->Get(v8::String::New("length"))->Uint32Value();
|
||||||
|
|
||||||
@ -277,68 +282,121 @@ class TypedArray {
|
|||||||
return args.This();
|
return args.This();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static v8::Handle<v8::Value> get(const v8::Arguments& args) {
|
||||||
|
if (args.Length() < 1)
|
||||||
|
return ThrowError("Wrong number of arguments.");
|
||||||
|
|
||||||
|
if (args[0]->IsNumber()) {
|
||||||
|
unsigned int index = args[0]->Uint32Value();
|
||||||
|
void* ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
||||||
|
|
||||||
|
if (TEAType == v8::kExternalByteArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<char*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalUnsignedByteArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<unsigned char*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalShortArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<short*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalUnsignedShortArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<unsigned short*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalIntArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<int*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalUnsignedIntArray)
|
||||||
|
return v8::Integer::New(reinterpret_cast<unsigned int*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalFloatArray)
|
||||||
|
return v8::Number::New(reinterpret_cast<float*>(ptr)[index]);
|
||||||
|
else if (TEAType == v8::kExternalDoubleArray)
|
||||||
|
return v8::Number::New(reinterpret_cast<double*>(ptr)[index]);
|
||||||
|
}
|
||||||
|
return v8::Undefined();
|
||||||
|
}
|
||||||
|
|
||||||
static v8::Handle<v8::Value> set(const v8::Arguments& args) {
|
static v8::Handle<v8::Value> set(const v8::Arguments& args) {
|
||||||
if (args.Length() < 1)
|
if (args.Length() < 1)
|
||||||
return ThrowError("Wrong number of arguments.");
|
return ThrowError("Wrong number of arguments.");
|
||||||
|
|
||||||
if (!args[0]->IsObject())
|
//if (!args[0]->IsObject())
|
||||||
return ThrowTypeError("Type error.");
|
// return ThrowTypeError("Type error.");
|
||||||
|
|
||||||
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]);
|
if (args[0]->IsNumber()) {
|
||||||
|
// index, <type> value
|
||||||
|
unsigned int index = args[0]->Uint32Value();
|
||||||
|
void* ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
||||||
|
if (TEAType == v8::kExternalByteArray)
|
||||||
|
reinterpret_cast<char*>(ptr)[index] = (char) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalUnsignedByteArray)
|
||||||
|
reinterpret_cast<unsigned char*>(ptr)[index] =
|
||||||
|
(unsigned char) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalShortArray)
|
||||||
|
reinterpret_cast<short*>(ptr)[index] = (short) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalUnsignedShortArray)
|
||||||
|
reinterpret_cast<unsigned short*>(ptr)[index] =
|
||||||
|
(unsigned short) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalIntArray)
|
||||||
|
reinterpret_cast<int*>(ptr)[index] = (int) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalUnsignedIntArray)
|
||||||
|
reinterpret_cast<unsigned int*>(ptr)[index] =
|
||||||
|
(unsigned int) args[1]->Int32Value();
|
||||||
|
else if (TEAType == v8::kExternalFloatArray)
|
||||||
|
reinterpret_cast<float*>(ptr)[index] = (float) args[1]->NumberValue();
|
||||||
|
else if (TEAType == v8::kExternalDoubleArray)
|
||||||
|
reinterpret_cast<double*>(ptr)[index] = (double) args[1]->NumberValue();
|
||||||
|
} else if (args[0]->IsObject()) {
|
||||||
|
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(args[0]);
|
||||||
|
|
||||||
if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
|
if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
|
||||||
v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast(
|
v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast(
|
||||||
obj->Get(v8::String::New("buffer")));
|
obj->Get(v8::String::New("buffer")));
|
||||||
v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast(
|
v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast(
|
||||||
args.This()->Get(v8::String::New("buffer")));
|
args.This()->Get(v8::String::New("buffer")));
|
||||||
|
|
||||||
if (args[1]->Int32Value() < 0)
|
if (args[1]->Int32Value() < 0)
|
||||||
return ThrowRangeError("Offset may not be negative.");
|
return ThrowRangeError("Offset may not be negative.");
|
||||||
|
|
||||||
unsigned int offset = args[1]->Uint32Value();
|
unsigned int offset = args[1]->Uint32Value();
|
||||||
unsigned int src_length =
|
unsigned int src_length =
|
||||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||||
unsigned int dst_length =
|
unsigned int dst_length =
|
||||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||||
if (offset > dst_length)
|
if (offset > dst_length)
|
||||||
return ThrowRangeError("Offset out of range.");
|
return ThrowRangeError("Offset out of range.");
|
||||||
|
|
||||||
if (src_length > dst_length - offset)
|
if (src_length > dst_length - offset)
|
||||||
return ThrowRangeError("Offset/length out of range.");
|
return ThrowRangeError("Offset/length out of range.");
|
||||||
|
|
||||||
// We don't want to get the buffer pointer, because that means we'll have
|
// We don't want to get the buffer pointer, because that means we'll have
|
||||||
// to just do the calculations for byteOffset / byteLength again.
|
// to just do the calculations for byteOffset / byteLength again.
|
||||||
// Instead just use the pointer on the external array data.
|
// Instead just use the pointer on the external array data.
|
||||||
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
|
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
|
||||||
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
||||||
|
|
||||||
// From the spec:
|
// From the spec:
|
||||||
// If the input array is a TypedArray, the two arrays may use the same
|
// If the input array is a TypedArray, the two arrays may use the same
|
||||||
// underlying ArrayBuffer. In this situation, setting the values takes
|
// underlying ArrayBuffer. In this situation, setting the values takes
|
||||||
// place as if all the data is first copied into a temporary buffer that
|
// place as if all the data is first copied into a temporary buffer that
|
||||||
// does not overlap either of the arrays, and then the data from the
|
// does not overlap either of the arrays, and then the data from the
|
||||||
// temporary buffer is copied into the current array.
|
// temporary buffer is copied into the current array.
|
||||||
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes,
|
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, src_ptr,
|
||||||
src_ptr, src_length * TBytes);
|
src_length * TBytes);
|
||||||
} else { // type[]
|
} else { // type[]
|
||||||
if (args[1]->Int32Value() < 0)
|
if (args[1]->Int32Value() < 0)
|
||||||
return ThrowRangeError("Offset may not be negative.");
|
return ThrowRangeError("Offset may not be negative.");
|
||||||
|
|
||||||
unsigned int src_length =
|
unsigned int src_length =
|
||||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||||
unsigned int dst_length =
|
unsigned int dst_length =
|
||||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||||
unsigned int offset = args[1]->Uint32Value();
|
unsigned int offset = args[1]->Uint32Value();
|
||||||
|
|
||||||
if (offset > dst_length)
|
if (offset > dst_length)
|
||||||
return ThrowRangeError("Offset out of range.");
|
return ThrowRangeError("Offset out of range.");
|
||||||
|
|
||||||
if (src_length > dst_length - offset)
|
if (src_length > dst_length - offset)
|
||||||
return ThrowRangeError("Offset/length out of range.");
|
return ThrowRangeError("Offset/length out of range.");
|
||||||
|
|
||||||
for (uint32_t i = 0; i < src_length; ++i) {
|
for (uint32_t i = 0; i < src_length; ++i) {
|
||||||
// Use the v8 setter to deal with typing. Maybe slow?
|
// Use the v8 setter to deal with typing. Maybe slow?
|
||||||
args.This()->Set(i + offset, obj->Get(i));
|
args.This()->Set(i + offset, obj->Get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +603,8 @@ class DataView {
|
|||||||
|
|
||||||
unsigned int byte_length =
|
unsigned int byte_length =
|
||||||
buffer->GetIndexedPropertiesExternalArrayDataLength();
|
buffer->GetIndexedPropertiesExternalArrayDataLength();
|
||||||
unsigned int byte_offset = args[1]->Uint32Value();
|
unsigned int byte_offset =
|
||||||
|
args[1]->IsUndefined() ? 0 : args[1]->Uint32Value();
|
||||||
|
|
||||||
if (args[1]->Int32Value() < 0 || byte_offset >= byte_length)
|
if (args[1]->Int32Value() < 0 || byte_offset >= byte_length)
|
||||||
return ThrowRangeError("byteOffset out of range.");
|
return ThrowRangeError("byteOffset out of range.");
|
||||||
@ -724,6 +783,8 @@ class DataView {
|
|||||||
namespace v8_typed_array {
|
namespace v8_typed_array {
|
||||||
|
|
||||||
void AttachBindings(v8::Handle<v8::Object> obj) {
|
void AttachBindings(v8::Handle<v8::Object> obj) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
obj->Set(v8::String::New("ArrayBuffer"),
|
obj->Set(v8::String::New("ArrayBuffer"),
|
||||||
ArrayBuffer::GetTemplate()->GetFunction());
|
ArrayBuffer::GetTemplate()->GetFunction());
|
||||||
obj->Set(v8::String::New("Int8Array"),
|
obj->Set(v8::String::New("Int8Array"),
|
||||||
@ -766,3 +827,5 @@ int SizeOfArrayElementForType(v8::ExternalArrayType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace v8_typed_array
|
} // namespace v8_typed_array
|
||||||
|
|
||||||
|
NODE_MODULE(node_typed_array, v8_typed_array::AttachBindings)
|
||||||
|
110
test/simple/test-typed-arrays.js
Normal file
110
test/simple/test-typed-arrays.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||||
|
// following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included
|
||||||
|
// in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test to verify we are using Typed Arrays
|
||||||
|
* (http://www.khronos.org/registry/typedarray/specs/latest/) correctly Test to
|
||||||
|
* verify Buffer can used in Typed Arrays
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
var SlowBuffer = process.binding('buffer').SlowBuffer;
|
||||||
|
var ArrayBuffer = process.binding('typed_array').ArrayBuffer;
|
||||||
|
var Int32Array = process.binding('typed_array').Int32Array;
|
||||||
|
var Int16Array = process.binding('typed_array').Int16Array;
|
||||||
|
var Uint8Array = process.binding('typed_array').Uint8Array;
|
||||||
|
|
||||||
|
function test(clazz) {
|
||||||
|
var size = clazz.length;
|
||||||
|
var b = clazz;
|
||||||
|
|
||||||
|
// create a view v1 referring to b, of type Int32, starting at
|
||||||
|
// the default byte index (0) and extending until the end of the buffer
|
||||||
|
var v1 = new Int32Array(b);
|
||||||
|
assert(4, v1.BYTES_PER_ELEMENT);
|
||||||
|
|
||||||
|
// create a view v2 referring to b, of type Uint8, starting at
|
||||||
|
// byte index 2 and extending until the end of the buffer
|
||||||
|
var v2 = new Uint8Array(b, 2);
|
||||||
|
assert(1, v1.BYTES_PER_ELEMENT);
|
||||||
|
|
||||||
|
// create a view v3 referring to b, of type Int16, starting at
|
||||||
|
// byte index 2 and having a length of 2
|
||||||
|
var v3 = new Int16Array(b, 2, 2);
|
||||||
|
assert(2, v1.BYTES_PER_ELEMENT);
|
||||||
|
|
||||||
|
// The layout is now
|
||||||
|
// var index
|
||||||
|
// b = |0|1|2|3|4|5|6|7| bytes (not indexable)
|
||||||
|
// v1 = |0 |1 | indices (indexable)
|
||||||
|
// v2 = |0|1|2|3|4|5|
|
||||||
|
// v3 = |0 |1 |
|
||||||
|
|
||||||
|
// testing values
|
||||||
|
v1[0] = 0x1234;
|
||||||
|
v1[1] = 0x5678;
|
||||||
|
|
||||||
|
assert(0x1234, v1[0]);
|
||||||
|
assert(0x5678, v1[1]);
|
||||||
|
|
||||||
|
assert(0x3, v2[0]);
|
||||||
|
assert(0x4, v2[1]);
|
||||||
|
assert(0x5, v2[2]);
|
||||||
|
assert(0x6, v2[3]);
|
||||||
|
assert(0x7, v2[4]);
|
||||||
|
assert(0x8, v2[5]);
|
||||||
|
|
||||||
|
assert(0x34, v3[0]);
|
||||||
|
assert(0x56, v3[1]);
|
||||||
|
|
||||||
|
// test get/set
|
||||||
|
v2.set(1, 0x8);
|
||||||
|
v2.set(2, 0xF);
|
||||||
|
assert(0x8, v2.get(1));
|
||||||
|
assert(0xF, v2.get(2));
|
||||||
|
assert(0x38, v3.get(0));
|
||||||
|
assert(0xF6, v3.get(1));
|
||||||
|
|
||||||
|
// test subarray
|
||||||
|
var v4 = v1.subarray(1);
|
||||||
|
assert(Int32Array, typeof v4);
|
||||||
|
assert(0xF678, v4[0]);
|
||||||
|
|
||||||
|
// test set with typed array and []
|
||||||
|
v2.set([ 1, 2, 3, 4 ], 2);
|
||||||
|
assert(0x1234, v1[0]);
|
||||||
|
|
||||||
|
var sub = new Int32Array(4);
|
||||||
|
sub[0] = 0xabcd;
|
||||||
|
v2.set(sub, 1);
|
||||||
|
assert(0x3a, v3[0]);
|
||||||
|
assert(0xbc, v3[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic Typed Arrays tests
|
||||||
|
var size = 8;
|
||||||
|
var ab = new ArrayBuffer(size);
|
||||||
|
assert.equal(size, ab.byteLength);
|
||||||
|
test(ab);
|
||||||
|
|
||||||
|
// testing sharing Buffer object
|
||||||
|
var buffer = new Buffer(size);
|
||||||
|
test(buffer);
|
Loading…
x
Reference in New Issue
Block a user