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_ITEM(node_buffer)
|
||||
NODE_EXT_LIST_ITEM(node_typed_array)
|
||||
#if HAVE_OPENSSL
|
||||
NODE_EXT_LIST_ITEM(node_crypto)
|
||||
#endif
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <v8.h>
|
||||
|
||||
#include "v8_typed_array.h"
|
||||
#include "node_buffer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -151,6 +152,7 @@ class TypedArray {
|
||||
v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);
|
||||
|
||||
static BatchedMethods methods[] = {
|
||||
{ "get", &TypedArray<TBytes, TEAType>::get },
|
||||
{ "set", &TypedArray<TBytes, TEAType>::set },
|
||||
{ "slice", &TypedArray<TBytes, TEAType>::subarray },
|
||||
{ "subarray", &TypedArray<TBytes, TEAType>::subarray },
|
||||
@ -183,14 +185,16 @@ class TypedArray {
|
||||
unsigned int length = 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]);
|
||||
unsigned int buflen =
|
||||
buffer->GetIndexedPropertiesExternalArrayDataLength();
|
||||
|
||||
if (args[1]->Int32Value() < 0)
|
||||
if (!args[1]->IsUndefined() && args[1]->Int32Value() < 0)
|
||||
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))
|
||||
return ThrowRangeError("Byte offset is not aligned.");
|
||||
@ -214,10 +218,11 @@ class TypedArray {
|
||||
}
|
||||
|
||||
// TODO(deanm): Error check.
|
||||
void* buf = buffer->GetPointerFromInternalField(0);
|
||||
void* buf = buffer->GetIndexedPropertiesExternalArrayData();
|
||||
args.This()->SetIndexedPropertiesToExternalArrayData(
|
||||
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]);
|
||||
length = obj->Get(v8::String::New("length"))->Uint32Value();
|
||||
|
||||
@ -277,68 +282,121 @@ class TypedArray {
|
||||
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) {
|
||||
if (args.Length() < 1)
|
||||
return ThrowError("Wrong number of arguments.");
|
||||
|
||||
if (!args[0]->IsObject())
|
||||
return ThrowTypeError("Type error.");
|
||||
//if (!args[0]->IsObject())
|
||||
// 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.
|
||||
v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast(
|
||||
obj->Get(v8::String::New("buffer")));
|
||||
v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast(
|
||||
args.This()->Get(v8::String::New("buffer")));
|
||||
if (TypedArray<TBytes, TEAType>::HasInstance(obj)) { // ArrayBufferView.
|
||||
v8::Handle<v8::Object> src_buffer = v8::Handle<v8::Object>::Cast(
|
||||
obj->Get(v8::String::New("buffer")));
|
||||
v8::Handle<v8::Object> dst_buffer = v8::Handle<v8::Object>::Cast(
|
||||
args.This()->Get(v8::String::New("buffer")));
|
||||
|
||||
if (args[1]->Int32Value() < 0)
|
||||
return ThrowRangeError("Offset may not be negative.");
|
||||
if (args[1]->Int32Value() < 0)
|
||||
return ThrowRangeError("Offset may not be negative.");
|
||||
|
||||
unsigned int offset = args[1]->Uint32Value();
|
||||
unsigned int src_length =
|
||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int dst_length =
|
||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||
if (offset > dst_length)
|
||||
return ThrowRangeError("Offset out of range.");
|
||||
unsigned int offset = args[1]->Uint32Value();
|
||||
unsigned int src_length =
|
||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int dst_length =
|
||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||
if (offset > dst_length)
|
||||
return ThrowRangeError("Offset out of range.");
|
||||
|
||||
if (src_length > dst_length - offset)
|
||||
return ThrowRangeError("Offset/length out of range.");
|
||||
if (src_length > dst_length - offset)
|
||||
return ThrowRangeError("Offset/length out of range.");
|
||||
|
||||
// We don't want to get the buffer pointer, because that means we'll have
|
||||
// to just do the calculations for byteOffset / byteLength again.
|
||||
// Instead just use the pointer on the external array data.
|
||||
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
|
||||
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
||||
// We don't want to get the buffer pointer, because that means we'll have
|
||||
// to just do the calculations for byteOffset / byteLength again.
|
||||
// Instead just use the pointer on the external array data.
|
||||
void* src_ptr = obj->GetIndexedPropertiesExternalArrayData();
|
||||
void* dst_ptr = args.This()->GetIndexedPropertiesExternalArrayData();
|
||||
|
||||
// From the spec:
|
||||
// If the input array is a TypedArray, the two arrays may use the same
|
||||
// underlying ArrayBuffer. In this situation, setting the values takes
|
||||
// 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
|
||||
// temporary buffer is copied into the current array.
|
||||
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes,
|
||||
src_ptr, src_length * TBytes);
|
||||
} else { // type[]
|
||||
if (args[1]->Int32Value() < 0)
|
||||
return ThrowRangeError("Offset may not be negative.");
|
||||
// From the spec:
|
||||
// If the input array is a TypedArray, the two arrays may use the same
|
||||
// underlying ArrayBuffer. In this situation, setting the values takes
|
||||
// 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
|
||||
// temporary buffer is copied into the current array.
|
||||
memmove(reinterpret_cast<char*>(dst_ptr) + offset * TBytes, src_ptr,
|
||||
src_length * TBytes);
|
||||
} else { // type[]
|
||||
if (args[1]->Int32Value() < 0)
|
||||
return ThrowRangeError("Offset may not be negative.");
|
||||
|
||||
unsigned int src_length =
|
||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int dst_length =
|
||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int offset = args[1]->Uint32Value();
|
||||
unsigned int src_length =
|
||||
obj->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int dst_length =
|
||||
args.This()->Get(v8::String::New("length"))->Uint32Value();
|
||||
unsigned int offset = args[1]->Uint32Value();
|
||||
|
||||
if (offset > dst_length)
|
||||
return ThrowRangeError("Offset out of range.");
|
||||
if (offset > dst_length)
|
||||
return ThrowRangeError("Offset out of range.");
|
||||
|
||||
if (src_length > dst_length - offset)
|
||||
return ThrowRangeError("Offset/length out of range.");
|
||||
if (src_length > dst_length - offset)
|
||||
return ThrowRangeError("Offset/length out of range.");
|
||||
|
||||
for (uint32_t i = 0; i < src_length; ++i) {
|
||||
// Use the v8 setter to deal with typing. Maybe slow?
|
||||
args.This()->Set(i + offset, obj->Get(i));
|
||||
for (uint32_t i = 0; i < src_length; ++i) {
|
||||
// Use the v8 setter to deal with typing. Maybe slow?
|
||||
args.This()->Set(i + offset, obj->Get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,7 +603,8 @@ class DataView {
|
||||
|
||||
unsigned int byte_length =
|
||||
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)
|
||||
return ThrowRangeError("byteOffset out of range.");
|
||||
@ -724,6 +783,8 @@ class DataView {
|
||||
namespace v8_typed_array {
|
||||
|
||||
void AttachBindings(v8::Handle<v8::Object> obj) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
obj->Set(v8::String::New("ArrayBuffer"),
|
||||
ArrayBuffer::GetTemplate()->GetFunction());
|
||||
obj->Set(v8::String::New("Int8Array"),
|
||||
@ -766,3 +827,5 @@ int SizeOfArrayElementForType(v8::ExternalArrayType type) {
|
||||
}
|
||||
|
||||
} // 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