Permalink
Browse files

typed arrays: add Buffer -> TypedArray constructor

- create a typed array from a node::Buffer object
- update TypedArray::set() to spec
- add TypedArray::get() method
  • Loading branch information...
1 parent 55c2197 commit 5b05429bf0fa210fa8ac12ddff0d824b310da893 Mikael Bourges-Sevenier committed with bnoordhuis Dec 31, 2011
Showing with 238 additions and 64 deletions.
  1. +1 −0 src/node_extensions.h
  2. +127 −64 src/v8_typed_array.cc
  3. +110 −0 test/simple/test-typed-arrays.js
@@ -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
View
@@ -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.");
-
- 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 (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.");
-
- 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();
-
- // 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();
-
- if (offset > dst_length)
- return ThrowRangeError("Offset 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));
+ //if (!args[0]->IsObject())
+ // return ThrowTypeError("Type error.");
+
+ 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 (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.");
+
+ 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();
+
+ // 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();
+
+ if (offset > dst_length)
+ return ThrowRangeError("Offset 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));
+ }
}
}
@@ -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)
@@ -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);

0 comments on commit 5b05429

Please sign in to comment.