Showing with 103 additions and 38 deletions.
  1. +70 −38 src/v8_typed_array.cc
  2. +33 −0 test/simple/test-typed-arrays.js
@@ -35,11 +35,41 @@ using node::ThrowRangeError;
using node::ThrowTypeError;
using node::ThrowError;

template <unsigned int TBytes, v8::ExternalArrayType TEAType>
class TypedArray;

typedef TypedArray<1, v8::kExternalByteArray> Int8Array;
typedef TypedArray<1, v8::kExternalUnsignedByteArray> Uint8Array;
typedef TypedArray<1, v8::kExternalPixelArray> Uint8ClampedArray;
typedef TypedArray<2, v8::kExternalShortArray> Int16Array;
typedef TypedArray<2, v8::kExternalUnsignedShortArray> Uint16Array;
typedef TypedArray<4, v8::kExternalIntArray> Int32Array;
typedef TypedArray<4, v8::kExternalUnsignedIntArray> Uint32Array;
typedef TypedArray<4, v8::kExternalFloatArray> Float32Array;
typedef TypedArray<8, v8::kExternalDoubleArray> Float64Array;

struct BatchedMethods {
const char* name;
v8::Handle<v8::Value> (*func)(const v8::Arguments& args);
};

void WeakCallback(v8::Persistent<v8::Value> value, void* data) {
v8::Object* obj = v8::Object::Cast(*value);

void* ptr = obj->GetIndexedPropertiesExternalArrayData();
int element_size = v8_typed_array::SizeOfArrayElementForType(
obj->GetIndexedPropertiesExternalArrayDataType());
int size =
obj->GetIndexedPropertiesExternalArrayDataLength() * element_size;

v8::V8::AdjustAmountOfExternalAllocatedMemory(-size);

value.ClearWeak();
value.Dispose();

free(ptr);
}

class ArrayBuffer {
public:
static v8::Persistent<v8::FunctionTemplate> GetTemplate() {
@@ -69,23 +99,6 @@ class ArrayBuffer {
}

private:
static void WeakCallback(v8::Persistent<v8::Value> value, void* data) {
v8::Object* obj = v8::Object::Cast(*value);

void* ptr = obj->GetIndexedPropertiesExternalArrayData();
int element_size = v8_typed_array::SizeOfArrayElementForType(
obj->GetIndexedPropertiesExternalArrayDataType());
int size =
obj->GetIndexedPropertiesExternalArrayDataLength() * element_size;

v8::V8::AdjustAmountOfExternalAllocatedMemory(-size);

value.ClearWeak();
value.Dispose();

free(ptr);
}

static v8::Handle<v8::Value> V8New(const v8::Arguments& args) {
if (!args.IsConstructCall())
return ThrowTypeError("Constructor cannot be called as a function.");
@@ -125,7 +138,7 @@ class ArrayBuffer {

v8::Persistent<v8::Object> persistent =
v8::Persistent<v8::Object>::New(args.This());
persistent.MakeWeak(NULL, &ArrayBuffer::WeakCallback);
persistent.MakeWeak(NULL, WeakCallback);

return args.This();
}
@@ -251,12 +264,23 @@ class TypedArray {
unsigned int length = 0;
unsigned int byte_offset = 0;

// [m1k3] added support for Buffer constructor
if (node::Buffer::HasInstance(args[0])
|| ArrayBuffer::HasInstance(args[0])) { // ArrayBuffer constructor.
if (node::Buffer::HasInstance(args[0]) ||
ArrayBuffer::HasInstance(args[0])) {
buffer = v8::Local<v8::Object>::Cast(args[0]);
size_t buflen =
buffer->GetIndexedPropertiesExternalArrayDataLength();
size_t buflen = buffer->GetIndexedPropertiesExternalArrayDataLength();

// Try to short-circuit as early as possible. Assume that in most
// `new TypedArray(other_array)` calls, the old and the new array
// have the same type.
bool make_copy = HasInstance(buffer) ||
Int8Array::HasInstance(buffer) ||
Uint8Array::HasInstance(buffer) ||
Int16Array::HasInstance(buffer) ||
Uint16Array::HasInstance(buffer) ||
Int32Array::HasInstance(buffer) ||
Uint32Array::HasInstance(buffer) ||
Float32Array::HasInstance(buffer) ||
Float64Array::HasInstance(buffer);

if (!args[1]->IsUndefined() && args[1]->Int32Value() < 0)
return ThrowRangeError("Byte offset out of range.");
@@ -281,13 +305,31 @@ class TypedArray {
}

void* buf = buffer->GetIndexedPropertiesExternalArrayData();
char* begin = reinterpret_cast<char*>(buf) + byte_offset;

if (!checkAlignment(reinterpret_cast<uintptr_t>(begin), TBytes))
return ThrowRangeError("Byte offset is not aligned.");
if (make_copy) {
void* dst = malloc(length);

args.This()->SetIndexedPropertiesToExternalArrayData(
begin, TEAType, length);
if (dst == NULL)
return ThrowError("Out of memory.");

v8::V8::AdjustAmountOfExternalAllocatedMemory(length);
memcpy(dst, static_cast<const char*>(buf) + byte_offset, length);
v8::Persistent<v8::Object> persistent =
v8::Persistent<v8::Object>::New(args.This());
persistent->SetIndexedPropertiesToExternalArrayData(dst,
TEAType,
length);
persistent.MakeWeak(NULL, WeakCallback);
}
else {
char* begin = reinterpret_cast<char*>(buf) + byte_offset;

if (!checkAlignment(reinterpret_cast<uintptr_t>(begin), TBytes))
return ThrowRangeError("Byte offset is not aligned.");

args.This()->SetIndexedPropertiesToExternalArrayData(
begin, TEAType, length);
}
}
else if (args[0]->IsObject()) { // TypedArray / type[] constructor.
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(args[0]);
@@ -461,16 +503,6 @@ class TypedArray {
}
};

class Int8Array : public TypedArray<1, v8::kExternalByteArray> { };
class Uint8Array : public TypedArray<1, v8::kExternalUnsignedByteArray> { };
class Uint8ClampedArray : public TypedArray<1, v8::kExternalPixelArray> { };
class Int16Array : public TypedArray<2, v8::kExternalShortArray> { };
class Uint16Array : public TypedArray<2, v8::kExternalUnsignedShortArray> { };
class Int32Array : public TypedArray<4, v8::kExternalIntArray> { };
class Uint32Array : public TypedArray<4, v8::kExternalUnsignedIntArray> { };
class Float32Array : public TypedArray<4, v8::kExternalFloatArray> { };
class Float64Array : public TypedArray<8, v8::kExternalDoubleArray> { };

template <typename T>
v8::Handle<v8::Value> cTypeToValue(T) {
return v8::Undefined();
@@ -201,3 +201,36 @@ assert.throws(function() {
view.setUint16(0, 1);
assert.equal(view.getUint16(0), 1);
})();

(function() {
// Backing store should not be shared.
var a = new Uint8Array(1);
var b = new Uint8Array(a);
a[0] = 0;
b[0] = 1;
assert.equal(a[0], 0);
assert.equal(b[0], 1);
})();

(function() {
// Backing store should not be shared.
var a = new Uint8Array(2);
var b = new Uint16Array(a);
a[0] = 0;
a[1] = 0;
b[0] = 257;
assert.equal(a[0], 0);
assert.equal(a[1], 0);
assert.equal(b[0], 257);
})();

(function() {
// Backing store should be shared.
var abuf = new ArrayBuffer(32);
var a = new Uint8Array(abuf);
var b = new Uint8Array(abuf);
a[0] = 0;
b[0] = 1;
assert.equal(a[0], 1);
assert.equal(b[0], 1);
})();