typed arrays: integrate plask's typed array implementation
diff --git a/src/node.cc b/src/node.cc
index 1e69114..be50161 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -98,6 +98,7 @@
 # include <node_crypto.h>
 #endif
 #include <node_script.h>
+#include <v8_typed_array.h>
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
@@ -2592,6 +2593,7 @@
   v8::Context::Scope context_scope(context);
 
   Handle<Object> process = SetupProcessObject(argc, argv);
+  v8_typed_array::AttachBindings(context->Global());
 
   // Create all the objects, load modules, do everything.
   // so your next reading stop should be node::Load()!
diff --git a/src/v8_typed_array.cc b/src/v8_typed_array.cc
new file mode 100644
index 0000000..d98cd1d
--- /dev/null
+++ b/src/v8_typed_array.cc
@@ -0,0 +1,762 @@
+// V8 Typed Array implementation.
+// (c) Dean McNamee <[email protected]>, 2011.
+//
+// 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.
+
+#include <stdlib.h>  // calloc, etc
+#include <string.h>  // memmove
+
+#include <v8.h>
+
+#include "v8_typed_array.h"
+
+namespace {
+
+v8::Handle<v8::Value> ThrowError(const char* msg) {
+  return v8::ThrowException(v8::Exception::Error(v8::String::New(msg)));
+}
+
+v8::Handle<v8::Value> ThrowTypeError(const char* msg) {
+  return v8::ThrowException(v8::Exception::TypeError(v8::String::New(msg)));
+}
+
+v8::Handle<v8::Value> ThrowRangeError(const char* msg) {
+  return v8::ThrowException(v8::Exception::RangeError(v8::String::New(msg)));
+}
+
+struct BatchedMethods {
+  const char* name;
+  v8::Handle<v8::Value> (*func)(const v8::Arguments& args);
+};
+
+class ArrayBuffer {
+ public:
+  static v8::Persistent<v8::FunctionTemplate> GetTemplate() {
+    static v8::Persistent<v8::FunctionTemplate> ft_cache;
+    if (!ft_cache.IsEmpty())
+      return ft_cache;
+
+    v8::HandleScope scope;
+    ft_cache = v8::Persistent<v8::FunctionTemplate>::New(
+        v8::FunctionTemplate::New(&ArrayBuffer::V8New));
+    ft_cache->SetClassName(v8::String::New("ArrayBuffer"));
+    v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate();
+    instance->SetInternalFieldCount(1);  // Buffer.
+
+    return ft_cache;
+  }
+
+  static bool HasInstance(v8::Handle<v8::Value> value) {
+    return GetTemplate()->HasInstance(value);
+  }
+
+ 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.");
+
+    // To match Chrome, we allow "new ArrayBuffer()".
+    // if (args.Length() != 1)
+    //   return ThrowError("Wrong number of arguments.");
+
+    if (args[0]->Int32Value() < 0) {
+      return ThrowRangeError("ArrayBufferView size is not a small enough "
+                             "positive integer.");
+    }
+
+    size_t num_bytes = args[0]->Uint32Value();
+    void* buf = calloc(num_bytes, 1);
+    if (!buf)
+      return ThrowError("Unable to allocate ArrayBuffer.");
+
+    args.This()->SetPointerInInternalField(0, buf);
+
+    args.This()->Set(v8::String::New("byteLength"),
+                     v8::Integer::NewFromUnsigned(num_bytes),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+
+    // NOTE(deanm): This is not in the spec, you shouldn't be able to index
+    // the ArrayBuffer.  However, it currently simplifies some handling in our
+    // implementation, so we make ArrayView operator[] act like an Uint8Array.
+    // , This allows DataView to work with both ArrayBuffers and TypedArrays.
+    args.This()->SetIndexedPropertiesToExternalArrayData(
+        buf, v8::kExternalUnsignedByteArray, num_bytes);
+
+    v8::V8::AdjustAmountOfExternalAllocatedMemory(num_bytes);
+
+    v8::Persistent<v8::Object> persistent =
+        v8::Persistent<v8::Object>::New(args.This());
+    persistent.MakeWeak(NULL, &ArrayBuffer::WeakCallback);
+
+    return args.This();
+  }
+};
+
+static bool checkAlignment(unsigned int val, unsigned int bytes) {
+  return (val & (bytes - 1)) == 0;  // Handles bytes == 0.
+}
+
+template <unsigned int TBytes, v8::ExternalArrayType TEAType>
+class TypedArray {
+ public:
+  static v8::Persistent<v8::FunctionTemplate> GetTemplate() {
+    static v8::Persistent<v8::FunctionTemplate> ft_cache;
+    if (!ft_cache.IsEmpty())
+      return ft_cache;
+
+    v8::HandleScope scope;
+    ft_cache = v8::Persistent<v8::FunctionTemplate>::New(
+        v8::FunctionTemplate::New(&TypedArray<TBytes, TEAType>::V8New));
+    v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate();
+    instance->SetInternalFieldCount(0);
+
+    ft_cache->Set(v8::String::New("BYTES_PER_ELEMENT"),
+                  v8::Uint32::New(TBytes), v8::ReadOnly);
+    instance->Set(v8::String::New("BYTES_PER_ELEMENT"),
+                  v8::Uint32::New(TBytes), v8::ReadOnly);
+
+    v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);
+
+    static BatchedMethods methods[] = {
+      { "set", &TypedArray<TBytes, TEAType>::set },
+      { "subarray", &TypedArray<TBytes, TEAType>::subarray },
+    };
+
+    for (size_t i = 0; i < sizeof(methods) / sizeof(*methods); ++i) {
+      instance->Set(v8::String::New(methods[i].name),
+                    v8::FunctionTemplate::New(methods[i].func,
+                                              v8::Handle<v8::Value>(),
+                                              default_signature));
+    }
+
+    return ft_cache;
+  }
+
+  static bool HasInstance(v8::Handle<v8::Value> value) {
+    return GetTemplate()->HasInstance(value);
+  }
+
+ private:
+  static v8::Handle<v8::Value> V8New(const v8::Arguments& args) {
+    if (!args.IsConstructCall())
+      return ThrowTypeError("Constructor cannot be called as a function.");
+
+    // To match Chrome, we allow "new Float32Array()".
+    // if (args.Length() != 1)
+    //   return ThrowError("Wrong number of arguments.");
+
+    v8::Local<v8::Object> buffer;
+    unsigned int length = 0;
+    unsigned int byte_offset = 0;
+
+    if (ArrayBuffer::HasInstance(args[0])) {  // ArrayBuffer constructor.
+      buffer = v8::Local<v8::Object>::Cast(args[0]);
+      unsigned int buflen =
+          buffer->GetIndexedPropertiesExternalArrayDataLength();
+
+      if (args[1]->Int32Value() < 0)
+        return ThrowRangeError("Byte offset out of range.");
+      byte_offset = args[1]->Uint32Value();
+
+      if (!checkAlignment(byte_offset, TBytes))
+        return ThrowRangeError("Byte offset is not aligned.");
+
+      if (args.Length() > 2) {
+        if (args[2]->Int32Value() < 0)
+          return ThrowRangeError("Length out of range.");
+        length = args[2]->Uint32Value();
+      } else {
+        if (buflen < byte_offset ||
+            !checkAlignment(buflen - byte_offset, TBytes)) {
+          return ThrowRangeError("Byte offset / length is not aligned.");
+        }
+        length = (buflen - byte_offset) / TBytes;
+      }
+
+      // NOTE(deanm): Sloppy integer overflow checks.
+      if (byte_offset > buflen || byte_offset + length > buflen ||
+          byte_offset + length * TBytes > buflen) {
+        return ThrowRangeError("Length is out of range.");
+      }
+
+      // TODO(deanm): Error check.
+      void* buf = buffer->GetPointerFromInternalField(0);
+      args.This()->SetIndexedPropertiesToExternalArrayData(
+          reinterpret_cast<char*>(buf) + byte_offset, TEAType, length);
+    } 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();
+
+      // TODO(deanm): Handle integer overflow.
+      v8::Handle<v8::Value> argv[1] = {
+          v8::Integer::NewFromUnsigned(length * TBytes)};
+      buffer = ArrayBuffer::GetTemplate()->
+                 GetFunction()->NewInstance(1, argv);
+
+      void* buf = buffer->GetPointerFromInternalField(0);
+      args.This()->SetIndexedPropertiesToExternalArrayData(
+          buf, TEAType, length);
+      // TODO(deanm): check for failure.
+      for (uint32_t i = 0; i < length; ++i) {
+        // Use the v8 setter to deal with typing.  Maybe slow?
+        args.This()->Set(i, obj->Get(i));
+      }
+    } else {  // length constructor.
+      // Try to match Chrome, Float32Array(""), Float32Array(true/false) is
+      // okay, but Float32Array(null) throws a TypeError and
+      // Float32Array(undefined) throw a RangeError.
+      if (args.Length() > 0 && (args[0]->IsUndefined() || args[0]->IsNull()))
+        return ThrowTypeError("Type error");
+
+      if (args[0]->Int32Value() < 0) {
+        return ThrowRangeError("ArrayBufferView size is not a small enough "
+                               "positive integer.");
+      }
+
+      length = args[0]->Uint32Value();
+      // TODO(deanm): Handle integer overflow.
+      v8::Handle<v8::Value> argv[1] = {
+          v8::Integer::NewFromUnsigned(length * TBytes)};
+
+      buffer = ArrayBuffer::GetTemplate()->
+                 GetFunction()->NewInstance(1, argv);
+      void* buf = buffer->GetPointerFromInternalField(0);
+
+      args.This()->SetIndexedPropertiesToExternalArrayData(
+          buf, TEAType, length);
+      // TODO(deanm): check for failure.
+    }
+
+    args.This()->Set(v8::String::New("buffer"),
+                     buffer,
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    args.This()->Set(v8::String::New("length"),
+                     v8::Integer::NewFromUnsigned(length),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    args.This()->Set(v8::String::New("byteOffset"),
+                     v8::Integer::NewFromUnsigned(byte_offset),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    args.This()->Set(v8::String::New("byteLength"),
+                     v8::Integer::NewFromUnsigned(length * TBytes),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+
+    return args.This();
+  }
+
+  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));
+      }
+    }
+
+    return v8::Undefined();
+  }
+
+  static v8::Handle<v8::Value> subarray(const v8::Arguments& args) {
+    // TODO(deanm): The unsigned / signed type mixing makes me super nervous.
+
+    unsigned int length =
+        args.This()->Get(v8::String::New("length"))->Uint32Value();
+    int begin = args[0]->Int32Value();
+    int end = length;
+    if (args.Length() > 1)
+      end = args[1]->Int32Value();
+
+    if (begin < 0) begin = length + begin;
+    if (begin < 0) begin = 0;
+    if (begin > length) begin = length;
+
+    if (end < 0) end = length + end;
+    if (end < 0) end = 0;
+    if (end > length) end = length;
+
+    if (begin > end) begin = end;
+
+    int byte_offset = begin * TBytes +
+        args.This()->Get(v8::String::New("byteOffset"))->Uint32Value();
+
+    // Call through to the ArrayBuffer, byteOffset, length constructor.
+    v8::Handle<v8::Value> argv[] = {
+        args.This()->Get(v8::String::New("buffer")),
+        v8::Integer::New(byte_offset),
+        v8::Integer::New(end - begin)};
+    return TypedArray<TBytes, TEAType>::GetTemplate()->
+        GetFunction()->NewInstance(3, argv);
+  }
+};
+
+class Int8Array : public TypedArray<1, v8::kExternalByteArray> { };
+class Uint8Array : public TypedArray<1, v8::kExternalUnsignedByteArray> { };
+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> { };
+
+template <typename T>
+v8::Handle<v8::Value> cTypeToValue(T) {
+  return v8::Undefined();
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(unsigned char val) {
+  return v8::Integer::NewFromUnsigned(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(char val) {
+  return v8::Integer::New(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(unsigned short val) {
+  return v8::Integer::NewFromUnsigned(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(short val) {
+  return v8::Integer::New(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(unsigned int val) {
+  return v8::Integer::NewFromUnsigned(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(int val) {
+  return v8::Integer::New(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(float val) {
+  return v8::Number::New(val);
+}
+
+template <>
+v8::Handle<v8::Value> cTypeToValue(double val) {
+  return v8::Number::New(val);
+}
+
+
+template <typename T>
+T valueToCType(v8::Handle<v8::Value> value) {
+  return 0;
+}
+
+template <>
+unsigned char valueToCType(v8::Handle<v8::Value> value) {
+  return value->Uint32Value();
+}
+
+template <>
+char valueToCType(v8::Handle<v8::Value> value) {
+  return value->Int32Value();
+}
+
+template <>
+unsigned short valueToCType(v8::Handle<v8::Value> value) {
+  return value->Uint32Value();
+}
+
+template <>
+short valueToCType(v8::Handle<v8::Value> value) {
+  return value->Int32Value();
+}
+
+template <>
+unsigned int valueToCType(v8::Handle<v8::Value> value) {
+  return value->Uint32Value();
+}
+
+template <>
+int valueToCType(v8::Handle<v8::Value> value) {
+  return value->Int32Value();
+}
+
+template <>
+float valueToCType(v8::Handle<v8::Value> value) {
+  return value->NumberValue();
+}
+
+template <>
+double valueToCType(v8::Handle<v8::Value> value) {
+  return value->NumberValue();
+}
+
+
+class DataView {
+ public:
+  static v8::Persistent<v8::FunctionTemplate> GetTemplate() {
+    static v8::Persistent<v8::FunctionTemplate> ft_cache;
+    if (!ft_cache.IsEmpty())
+      return ft_cache;
+
+    v8::HandleScope scope;
+    ft_cache = v8::Persistent<v8::FunctionTemplate>::New(
+        v8::FunctionTemplate::New(&DataView::V8New));
+    ft_cache->SetClassName(v8::String::New("DataView"));
+    v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate();
+    instance->SetInternalFieldCount(0);
+
+    v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);
+
+    static BatchedMethods methods[] = {
+      { "getUint8", &DataView::getUint8 },
+      { "getInt8", &DataView::getInt8 },
+      { "getUint16", &DataView::getUint16 },
+      { "getInt16", &DataView::getInt16 },
+      { "getUint32", &DataView::getUint32 },
+      { "getInt32", &DataView::getInt32 },
+      { "getFloat32", &DataView::getFloat32 },
+      { "getFloat64", &DataView::getFloat64 },
+      { "setUint8", &DataView::setUint8 },
+      { "setInt8", &DataView::setInt8 },
+      { "setUint16", &DataView::setUint16 },
+      { "setInt16", &DataView::setInt16 },
+      { "setUint32", &DataView::setUint32 },
+      { "setInt32", &DataView::setInt32 },
+      { "setFloat32", &DataView::setFloat32 },
+      { "setFloat64", &DataView::setFloat64 },
+    };
+
+    for (size_t i = 0; i < sizeof(methods) / sizeof(*methods); ++i) {
+      instance->Set(v8::String::New(methods[i].name),
+                    v8::FunctionTemplate::New(methods[i].func,
+                                              v8::Handle<v8::Value>(),
+                                              default_signature));
+    }
+
+    return ft_cache;
+  }
+
+  static bool HasInstance(v8::Handle<v8::Value> value) {
+    return GetTemplate()->HasInstance(value);
+  }
+
+ private:
+  static v8::Handle<v8::Value> V8New(const v8::Arguments& args) {
+    if (!args.IsConstructCall())
+      return ThrowTypeError("Constructor cannot be called as a function.");
+
+    if (args.Length() < 1)
+      return ThrowError("Wrong number of arguments.");
+
+    if (!args[0]->IsObject())
+      return ThrowError("Object must be an ArrayBuffer.");
+
+    v8::Handle<v8::Object> buffer = v8::Handle<v8::Object>::Cast(args[0]);
+    if (!buffer->HasIndexedPropertiesInExternalArrayData())
+      return ThrowError("Object must be an ArrayBuffer.");
+
+    unsigned int byte_length =
+        buffer->GetIndexedPropertiesExternalArrayDataLength();
+    unsigned int byte_offset = args[1]->Uint32Value();
+
+    if (args[1]->Int32Value() < 0 || byte_offset >= byte_length)
+      return ThrowRangeError("byteOffset out of range.");
+
+    if (!args[2]->IsUndefined()) {
+      if (args[2]->Int32Value() < 0)
+        return ThrowRangeError("byteLength out of range.");
+      unsigned int new_byte_length = args[2]->Uint32Value();
+      if (new_byte_length > byte_length)
+        return ThrowRangeError("byteLength out of range.");
+      if (byte_offset + new_byte_length > byte_length)
+        return ThrowRangeError("byteOffset/byteLength out of range.");
+      byte_length = new_byte_length;
+    } else {
+      // Adjust the original byte_length from total length to length to end.
+      byte_length -= byte_offset;
+    }
+
+    void* buf = buffer->GetIndexedPropertiesExternalArrayData();
+
+    // Like ArrayBuffer, we violate the spec and add an operator[].
+    args.This()->SetIndexedPropertiesToExternalArrayData(
+        reinterpret_cast<char*>(buf) + byte_offset,
+        v8::kExternalUnsignedByteArray, byte_length);
+
+    args.This()->Set(v8::String::New("buffer"),
+                     buffer,
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    args.This()->Set(v8::String::New("byteOffset"),
+                     v8::Integer::NewFromUnsigned(byte_offset),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    args.This()->Set(v8::String::New("byteLength"),
+                     v8::Integer::NewFromUnsigned(byte_length),
+                     (v8::PropertyAttribute)(v8::ReadOnly|v8::DontDelete));
+    return args.This();
+  }
+
+  // TODO(deanm): This isn't beautiful or optimal.
+  static void swizzle(char* buf, size_t len) {
+    for (int i = 0; i < len / 2; ++i) {
+      char t = buf[i];
+      buf[i] = buf[len - i - 1];
+      buf[len - i - 1] = t;
+    }
+  }
+
+  template <typename T>
+  static T getValue(void* ptr, unsigned int index, bool swiz) {
+    char buf[sizeof(T)];
+    memcpy(buf, reinterpret_cast<char*>(ptr) + index, sizeof(T));
+    if (swiz)
+      swizzle(buf, sizeof(T));
+    T val;
+    memcpy(&val, buf, sizeof(T));
+    return val;
+  }
+
+  template <typename T>
+  static void setValue(void* ptr, unsigned int index, T val, bool swiz) {
+    char buf[sizeof(T)];
+    memcpy(buf, &val, sizeof(T));
+    if (swiz)
+      swizzle(buf, sizeof(T));
+    memcpy(reinterpret_cast<char*>(ptr) + index, buf, sizeof(T));
+  }
+
+  template <typename T>
+  static v8::Handle<v8::Value> getGeneric(const v8::Arguments& args) {
+    if (args.Length() < 1)
+      return ThrowError("Wrong number of arguments.");
+
+    unsigned int index = args[0]->Uint32Value();
+    bool little_endian = args[1]->BooleanValue();
+    // TODO(deanm): All of these things should be cacheable.
+    int element_size = v8_typed_array::SizeOfArrayElementForType(
+        args.This()->GetIndexedPropertiesExternalArrayDataType());
+    int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() *
+               element_size;
+
+    if (index + sizeof(T) > size)  // TODO(deanm): integer overflow.
+      return ThrowError("Index out of range.");
+
+    void* ptr = args.This()->GetIndexedPropertiesExternalArrayData();
+    return cTypeToValue<T>(getValue<T>(ptr, index, !little_endian));
+  }
+
+  template <typename T>
+  static v8::Handle<v8::Value> setGeneric(const v8::Arguments& args) {
+    if (args.Length() < 2)
+      return ThrowError("Wrong number of arguments.");
+
+    unsigned int index = args[0]->Int32Value();
+    bool little_endian = args[2]->BooleanValue();
+    // TODO(deanm): All of these things should be cacheable.
+    int element_size = v8_typed_array::SizeOfArrayElementForType(
+        args.This()->GetIndexedPropertiesExternalArrayDataType());
+    int size = args.This()->GetIndexedPropertiesExternalArrayDataLength() *
+               element_size;
+
+    if (index + sizeof(T) > size)  // TODO(deanm): integer overflow.
+      return ThrowError("Index out of range.");
+
+    void* ptr = args.This()->GetIndexedPropertiesExternalArrayData();
+    setValue<T>(ptr, index, valueToCType<T>(args[1]), !little_endian);
+    return v8::Undefined();
+  }
+
+  static v8::Handle<v8::Value> getUint8(const v8::Arguments& args) {
+    return getGeneric<unsigned char>(args);
+  }
+
+  static v8::Handle<v8::Value> getInt8(const v8::Arguments& args) {
+    return getGeneric<char>(args);
+  }
+
+  static v8::Handle<v8::Value> getUint16(const v8::Arguments& args) {
+    return getGeneric<unsigned short>(args);
+  }
+
+  static v8::Handle<v8::Value> getInt16(const v8::Arguments& args) {
+    return getGeneric<short>(args);
+  }
+
+  static v8::Handle<v8::Value> getUint32(const v8::Arguments& args) {
+    return getGeneric<unsigned int>(args);
+  }
+
+  static v8::Handle<v8::Value> getInt32(const v8::Arguments& args) {
+    return getGeneric<int>(args);
+  }
+
+  static v8::Handle<v8::Value> getFloat32(const v8::Arguments& args) {
+    return getGeneric<float>(args);
+  }
+
+  static v8::Handle<v8::Value> getFloat64(const v8::Arguments& args) {
+    return getGeneric<double>(args);
+  }
+
+  static v8::Handle<v8::Value> setUint8(const v8::Arguments& args) {
+    return setGeneric<unsigned char>(args);
+  }
+
+  static v8::Handle<v8::Value> setInt8(const v8::Arguments& args) {
+    return setGeneric<char>(args);
+  }
+
+  static v8::Handle<v8::Value> setUint16(const v8::Arguments& args) {
+    return setGeneric<unsigned short>(args);
+  }
+
+  static v8::Handle<v8::Value> setInt16(const v8::Arguments& args) {
+    return setGeneric<short>(args);
+  }
+
+  static v8::Handle<v8::Value> setUint32(const v8::Arguments& args) {
+    return setGeneric<unsigned int>(args);
+  }
+
+  static v8::Handle<v8::Value> setInt32(const v8::Arguments& args) {
+    return setGeneric<int>(args);
+  }
+
+  static v8::Handle<v8::Value> setFloat32(const v8::Arguments& args) {
+    return setGeneric<float>(args);
+  }
+
+  static v8::Handle<v8::Value> setFloat64(const v8::Arguments& args) {
+    return setGeneric<double>(args);
+  }
+};
+
+
+}  // namespace
+
+namespace v8_typed_array {
+
+void AttachBindings(v8::Handle<v8::Object> obj) {
+  obj->Set(v8::String::New("ArrayBuffer"),
+           ArrayBuffer::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Int8Array"),
+           Int8Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Uint8Array"),
+           Uint8Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Int16Array"),
+           Int16Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Uint16Array"),
+           Uint16Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Int32Array"),
+           Int32Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Uint32Array"),
+           Uint32Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("Float32Array"),
+           Float32Array::GetTemplate()->GetFunction());
+  obj->Set(v8::String::New("DataView"),
+           DataView::GetTemplate()->GetFunction());
+}
+
+int SizeOfArrayElementForType(v8::ExternalArrayType type) {
+  switch (type) {
+    case v8::kExternalByteArray:
+    case v8::kExternalUnsignedByteArray:
+      return 1;
+    case v8::kExternalShortArray:
+    case v8::kExternalUnsignedShortArray:
+      return 2;
+    case v8::kExternalIntArray:
+    case v8::kExternalUnsignedIntArray:
+    case v8::kExternalFloatArray:
+      return 4;
+    default:
+      return 0;
+  }
+}
+
+}  // namespace v8_typed_array
diff --git a/src/v8_typed_array.h b/src/v8_typed_array.h
new file mode 100644
index 0000000..a95b29a
--- /dev/null
+++ b/src/v8_typed_array.h
@@ -0,0 +1,35 @@
+// V8 Typed Array implementation.
+// (c) Dean McNamee <[email protected]>, 2011.
+//
+// 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.
+
+#ifndef V8_TYPED_ARRAY_H_
+#define V8_TYPED_ARRAY_H_
+
+#include <v8.h>
+
+namespace v8_typed_array {
+
+void AttachBindings(v8::Handle<v8::Object> obj);
+
+int SizeOfArrayElementForType(v8::ExternalArrayType type);
+
+}  // namespace v8_typed_array
+
+#endif  // V8_TYPED_ARRAY_H_
diff --git a/wscript b/wscript
index 7c85f33..b96c3fe 100644
--- a/wscript
+++ b/wscript
@@ -869,6 +869,7 @@
     src/cares_wrap.cc
     src/stdio_wrap.cc
     src/process_wrap.cc
+    src/v8_typed_array.cc
   """
 
   if sys.platform.startswith("win32"):