Index: gin/array_buffer.cc |
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e8236fc520511e0cc0e700b7260374fbefbddcaa |
--- /dev/null |
+++ b/gin/array_buffer.cc |
@@ -0,0 +1,150 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "gin/array_buffer.h" |
+ |
+#include <stdlib.h> |
+ |
+namespace gin { |
+ |
+COMPILE_ASSERT(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2, |
+ array_buffers_must_have_two_internal_fields); |
+ |
+static const int kBufferViewPrivateIndex = 0; |
+ |
+// ArrayBufferAllocator ------------------------------------------------------- |
+ |
+void* ArrayBufferAllocator::Allocate(size_t length) { |
+ return calloc(1, length); |
+} |
+ |
+void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { |
+ return malloc(length); |
+} |
+ |
+void ArrayBufferAllocator::Free(void* data, size_t length) { |
+ free(data); |
+} |
+ |
+ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() { |
+ static ArrayBufferAllocator* instance = new ArrayBufferAllocator(); |
+ return instance; |
+} |
+ |
+// BufferView::Private -------------------------------------------------------- |
+ |
+// This class exists to solve a tricky lifetime problem. The V8 API doesn't |
+// want to expose a direct view into the memory behind an array buffer because |
+// V8 might deallocate that memory during garbage collection. Instead, the V8 |
+// API forces us to externalize the buffer and take ownership of the memory. |
+// In order to know when to free the memory, we need to figure out both when |
+// we're done with it and when V8 is done with it. |
+// |
+// To determine whether we're done with the memory, every view we have into |
+// the array buffer takes a reference to the BufferView::Private object that |
+// actually owns the memory. To determine when V8 is done with the memory, we |
+// open a weak handle to the ArrayBuffer object. When we receive the weak |
+// callback, we know the object is about to be garbage collected and we can |
+// drop V8's implied reference to the memory. |
+// |
+// The final subtlety is that we need every BufferView into the same array |
+// buffer to AddRef the same BufferView::Private. To make that work, we store a |
+// pointer to the BufferView::Private object in an internal field of the |
+// ArrayBuffer object. |
+// |
+class BufferView::Private { |
+ public: |
+ static scoped_refptr<Private> From(v8::Isolate* isolate, |
+ v8::Handle<v8::ArrayBuffer> array); |
+ |
+ void AddRef(); |
+ void Release(); |
+ |
+ void* buffer() const { return buffer_; } |
+ size_t length() const { return length_; } |
+ |
+ private: |
+ Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array); |
+ ~Private(); |
+ |
+ static void WeakCallback( |
+ const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data); |
+ |
+ size_t ref_count_; |
+ v8::Persistent<v8::ArrayBuffer> array_buffer_; |
+ void* buffer_; |
+ size_t length_; |
+}; |
+ |
+scoped_refptr<BufferView::Private> BufferView::Private::From( |
+ v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) { |
+ if (array->IsExternal()) { |
+ return make_scoped_refptr(static_cast<Private*>( |
+ array->GetAlignedPointerFromInternalField(kBufferViewPrivateIndex))); |
+ } |
+ return make_scoped_refptr(new Private(isolate, array)); |
+} |
+ |
+void BufferView::Private::AddRef() { |
+ ++ref_count_; |
+} |
+ |
+void BufferView::Private::Release() { |
+ if (--ref_count_) |
+ return; |
+ delete this; |
+} |
+ |
+BufferView::Private::Private(v8::Isolate* isolate, |
+ v8::Handle<v8::ArrayBuffer> array) |
+ : ref_count_(0), |
+ array_buffer_(isolate, array) { |
+ // Take ownership of the array buffer. |
+ v8::ArrayBuffer::Contents contents = array->Externalize(); |
+ buffer_ = contents.Data(); |
+ length_ = contents.ByteLength(); |
+ |
+ array->SetAlignedPointerInInternalField(kBufferViewPrivateIndex, this); |
+ |
+ AddRef(); // Balanced in WeakCallback. |
+ array_buffer_.SetWeak(this, WeakCallback); |
+} |
+ |
+BufferView::Private::~Private() { |
+ ArrayBufferAllocator::SharedInstance()->Free(buffer_, length_); |
+} |
+ |
+void BufferView::Private::WeakCallback( |
+ const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) { |
+ Private* parameter = data.GetParameter(); |
+ parameter->array_buffer_.Reset(); |
+ parameter->Release(); // Balanced in BufferView::Private::Private. |
+} |
+ |
+// BufferView ----------------------------------------------------------------- |
+ |
+BufferView::BufferView(v8::Isolate* isolate, |
+ v8::Handle<v8::ArrayBufferView> view) { |
+ Initialize(isolate, view->Buffer()); |
+ uint8_t* ptr = static_cast<uint8_t*>(bytes_); |
+ bytes_ = static_cast<void*>(ptr + view->ByteOffset()); |
+ num_bytes_ = view->ByteLength(); |
+} |
+ |
+BufferView::BufferView(v8::Isolate* isolate, |
+ v8::Handle<v8::ArrayBuffer> array) { |
+ Initialize(isolate, array); |
+} |
+ |
+BufferView::~BufferView() { |
+} |
+ |
+void BufferView::Initialize(v8::Isolate* isolate, |
+ v8::Handle<v8::ArrayBuffer> array) { |
+ private_ = BufferView::Private::From(isolate, array); |
+ bytes_ = private_->buffer(); |
+ num_bytes_ = private_->length(); |
+} |
+ |
+} // namespace gin |