mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
5.3 KiB
200 lines
5.3 KiB
|
|
#ifndef SRC_ALIASED_BUFFER_H_
|
|
#define SRC_ALIASED_BUFFER_H_
|
|
|
|
#include "v8.h"
|
|
#include "util.h"
|
|
#include "util-inl.h"
|
|
|
|
namespace node {
|
|
|
|
/**
|
|
* This class encapsulates the technique of having a native buffer mapped to
|
|
* a JS object. Writes to the native buffer can happen efficiently without
|
|
* going through JS, and the data is then available to user's via the exposed
|
|
* JS object.
|
|
*
|
|
* While this technique is computationaly efficient, it is effectively a
|
|
* write to JS program state w/out going through the standard
|
|
* (monitored) API. Thus any VM capabilities to detect the modification are
|
|
* circumvented.
|
|
*
|
|
* The encapsulation herein provides a placeholder where such writes can be
|
|
* observed. Any notification APIs will be left as a future exercise.
|
|
*/
|
|
template <class NativeT, class V8T>
|
|
class AliasedBuffer {
|
|
public:
|
|
AliasedBuffer(v8::Isolate* isolate, const size_t count)
|
|
: isolate_(isolate),
|
|
count_(count),
|
|
byte_offset_(0),
|
|
free_buffer_(true) {
|
|
CHECK_GT(count, 0);
|
|
const v8::HandleScope handle_scope(isolate_);
|
|
|
|
const size_t sizeInBytes = sizeof(NativeT) * count;
|
|
|
|
// allocate native buffer
|
|
buffer_ = Calloc<NativeT>(count);
|
|
|
|
// allocate v8 ArrayBuffer
|
|
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
|
|
isolate_, buffer_, sizeInBytes);
|
|
|
|
// allocate v8 TypedArray
|
|
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
|
|
js_array_ = v8::Global<V8T>(isolate, js_array);
|
|
}
|
|
|
|
/**
|
|
* Create an AliasedBuffer over a sub-region of another aliased buffer.
|
|
* The two will share a v8::ArrayBuffer instance &
|
|
* a native buffer, but will each read/write to different sections of the
|
|
* native buffer.
|
|
*
|
|
* Note that byte_offset must by aligned by sizeof(NativeT).
|
|
*/
|
|
AliasedBuffer(v8::Isolate* isolate,
|
|
const size_t byte_offset,
|
|
const size_t count,
|
|
const AliasedBuffer<uint8_t,
|
|
v8::Uint8Array>& backing_buffer)
|
|
: isolate_(isolate),
|
|
count_(count),
|
|
byte_offset_(byte_offset),
|
|
free_buffer_(false) {
|
|
const v8::HandleScope handle_scope(isolate_);
|
|
|
|
v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();
|
|
|
|
// validate that the byte_offset is aligned with sizeof(NativeT)
|
|
CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0);
|
|
// validate this fits inside the backing buffer
|
|
CHECK_LE(sizeof(NativeT) * count, ab->ByteLength() - byte_offset);
|
|
|
|
buffer_ = reinterpret_cast<NativeT*>(
|
|
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));
|
|
|
|
v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count);
|
|
js_array_ = v8::Global<V8T>(isolate, js_array);
|
|
}
|
|
|
|
AliasedBuffer(const AliasedBuffer& that)
|
|
: isolate_(that.isolate_),
|
|
count_(that.count_),
|
|
byte_offset_(that.byte_offset_),
|
|
buffer_(that.buffer_),
|
|
free_buffer_(false) {
|
|
js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray());
|
|
}
|
|
|
|
~AliasedBuffer() {
|
|
if (free_buffer_ && buffer_ != nullptr) {
|
|
free(buffer_);
|
|
}
|
|
js_array_.Reset();
|
|
}
|
|
|
|
/**
|
|
* Helper class that is returned from operator[] to support assignment into
|
|
* a specified location.
|
|
*/
|
|
class Reference {
|
|
public:
|
|
Reference(AliasedBuffer<NativeT, V8T>* aliased_buffer, size_t index)
|
|
: aliased_buffer_(aliased_buffer),
|
|
index_(index) {
|
|
}
|
|
|
|
Reference(const Reference& that)
|
|
: aliased_buffer_(that.aliased_buffer_),
|
|
index_(that.index_) {
|
|
}
|
|
|
|
inline Reference& operator=(const NativeT &val) {
|
|
aliased_buffer_->SetValue(index_, val);
|
|
return *this;
|
|
}
|
|
|
|
operator NativeT() const {
|
|
return aliased_buffer_->GetValue(index_);
|
|
}
|
|
|
|
private:
|
|
AliasedBuffer<NativeT, V8T>* aliased_buffer_;
|
|
size_t index_;
|
|
};
|
|
|
|
/**
|
|
* Get the underlying v8 TypedArray overlayed on top of the native buffer
|
|
*/
|
|
v8::Local<V8T> GetJSArray() const {
|
|
return js_array_.Get(isolate_);
|
|
}
|
|
|
|
/**
|
|
* Get the underlying v8::ArrayBuffer underlying the TypedArray and
|
|
* overlaying the native buffer
|
|
*/
|
|
v8::Local<v8::ArrayBuffer> GetArrayBuffer() const {
|
|
return GetJSArray()->Buffer();
|
|
}
|
|
|
|
/**
|
|
* Get the underlying native buffer. Note that all reads/writes should occur
|
|
* through the GetValue/SetValue/operator[] methods
|
|
*/
|
|
inline const NativeT* GetNativeBuffer() const {
|
|
return buffer_;
|
|
}
|
|
|
|
/**
|
|
* Synonym for GetBuffer()
|
|
*/
|
|
inline const NativeT* operator * () const {
|
|
return GetNativeBuffer();
|
|
}
|
|
|
|
/**
|
|
* Set position index to given value.
|
|
*/
|
|
inline void SetValue(const size_t index, NativeT value) {
|
|
#if defined(DEBUG) && DEBUG
|
|
CHECK_LT(index, count_);
|
|
#endif
|
|
buffer_[index] = value;
|
|
}
|
|
|
|
/**
|
|
* Get value at position index
|
|
*/
|
|
inline const NativeT GetValue(const size_t index) const {
|
|
#if defined(DEBUG) && DEBUG
|
|
CHECK_LT(index, count_);
|
|
#endif
|
|
return buffer_[index];
|
|
}
|
|
|
|
/**
|
|
* Effectively, a synonym for GetValue/SetValue
|
|
*/
|
|
Reference operator[](size_t index) {
|
|
return Reference(this, index);
|
|
}
|
|
|
|
NativeT operator[](size_t index) const {
|
|
return GetValue(index);
|
|
}
|
|
|
|
private:
|
|
v8::Isolate* const isolate_;
|
|
size_t count_;
|
|
size_t byte_offset_;
|
|
NativeT* buffer_;
|
|
v8::Global<V8T> js_array_;
|
|
bool free_buffer_;
|
|
};
|
|
} // namespace node
|
|
|
|
#endif // SRC_ALIASED_BUFFER_H_
|
|
|