1 #ifndef SRC_ALIASED_BUFFER_H_ 2 #define SRC_ALIASED_BUFFER_H_ 3 4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 5 6 #include <cinttypes> 7 #include "util-inl.h" 8 #include "v8.h" 9 10 namespace node { 11 12 /** 13 * Do not use this class directly when creating instances of it - use the 14 * Aliased*Array defined at the end of this file instead. 15 * 16 * This class encapsulates the technique of having a native buffer mapped to 17 * a JS object. Writes to the native buffer can happen efficiently without 18 * going through JS, and the data is then available to user's via the exposed 19 * JS object. 20 * 21 * While this technique is computationally efficient, it is effectively a 22 * write to JS program state w/out going through the standard 23 * (monitored) API. Thus any VM capabilities to detect the modification are 24 * circumvented. 25 * 26 * The encapsulation herein provides a placeholder where such writes can be 27 * observed. Any notification APIs will be left as a future exercise. 28 */ 29 template <class NativeT, 30 class V8T, 31 // SFINAE NativeT to be scalar 32 typename = std::enable_if_t<std::is_scalar<NativeT>::value>> 33 class AliasedBufferBase { 34 public: AliasedBufferBase(v8::Isolate * isolate,const size_t count)35 AliasedBufferBase(v8::Isolate* isolate, const size_t count) 36 : isolate_(isolate), count_(count), byte_offset_(0) { 37 CHECK_GT(count, 0); 38 const v8::HandleScope handle_scope(isolate_); 39 const size_t size_in_bytes = 40 MultiplyWithOverflowCheck(sizeof(NativeT), count); 41 42 // allocate v8 ArrayBuffer 43 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( 44 isolate_, size_in_bytes); 45 buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data()); 46 47 // allocate v8 TypedArray 48 v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count); 49 js_array_ = v8::Global<V8T>(isolate, js_array); 50 } 51 52 /** 53 * Create an AliasedBufferBase over a sub-region of another aliased buffer. 54 * The two will share a v8::ArrayBuffer instance & 55 * a native buffer, but will each read/write to different sections of the 56 * native buffer. 57 * 58 * Note that byte_offset must by aligned by sizeof(NativeT). 59 */ 60 // TODO(refack): refactor into a non-owning `AliasedBufferBaseView` AliasedBufferBase(v8::Isolate * isolate,const size_t byte_offset,const size_t count,const AliasedBufferBase<uint8_t,v8::Uint8Array> & backing_buffer)61 AliasedBufferBase( 62 v8::Isolate* isolate, 63 const size_t byte_offset, 64 const size_t count, 65 const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer) 66 : isolate_(isolate), count_(count), byte_offset_(byte_offset) { 67 const v8::HandleScope handle_scope(isolate_); 68 69 v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer(); 70 71 // validate that the byte_offset is aligned with sizeof(NativeT) 72 CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0); 73 // validate this fits inside the backing buffer 74 CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count), 75 ab->ByteLength() - byte_offset); 76 77 buffer_ = reinterpret_cast<NativeT*>( 78 const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset)); 79 80 v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count); 81 js_array_ = v8::Global<V8T>(isolate, js_array); 82 } 83 AliasedBufferBase(const AliasedBufferBase & that)84 AliasedBufferBase(const AliasedBufferBase& that) 85 : isolate_(that.isolate_), 86 count_(that.count_), 87 byte_offset_(that.byte_offset_), 88 buffer_(that.buffer_) { 89 js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray()); 90 } 91 92 AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept { 93 this->~AliasedBufferBase(); 94 isolate_ = that.isolate_; 95 count_ = that.count_; 96 byte_offset_ = that.byte_offset_; 97 buffer_ = that.buffer_; 98 99 js_array_.Reset(isolate_, that.js_array_.Get(isolate_)); 100 101 that.buffer_ = nullptr; 102 that.js_array_.Reset(); 103 return *this; 104 } 105 106 /** 107 * Helper class that is returned from operator[] to support assignment into 108 * a specified location. 109 */ 110 class Reference { 111 public: Reference(AliasedBufferBase<NativeT,V8T> * aliased_buffer,size_t index)112 Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index) 113 : aliased_buffer_(aliased_buffer), index_(index) {} 114 Reference(const Reference & that)115 Reference(const Reference& that) 116 : aliased_buffer_(that.aliased_buffer_), 117 index_(that.index_) { 118 } 119 120 inline Reference& operator=(const NativeT& val) { 121 aliased_buffer_->SetValue(index_, val); 122 return *this; 123 } 124 125 inline Reference& operator=(const Reference& val) { 126 return *this = static_cast<NativeT>(val); 127 } 128 NativeT()129 operator NativeT() const { 130 return aliased_buffer_->GetValue(index_); 131 } 132 133 inline Reference& operator+=(const NativeT& val) { 134 const NativeT current = aliased_buffer_->GetValue(index_); 135 aliased_buffer_->SetValue(index_, current + val); 136 return *this; 137 } 138 139 inline Reference& operator+=(const Reference& val) { 140 return this->operator+=(static_cast<NativeT>(val)); 141 } 142 143 inline Reference& operator-=(const NativeT& val) { 144 const NativeT current = aliased_buffer_->GetValue(index_); 145 aliased_buffer_->SetValue(index_, current - val); 146 return *this; 147 } 148 149 private: 150 AliasedBufferBase<NativeT, V8T>* aliased_buffer_; 151 size_t index_; 152 }; 153 154 /** 155 * Get the underlying v8 TypedArray overlayed on top of the native buffer 156 */ GetJSArray()157 v8::Local<V8T> GetJSArray() const { 158 return js_array_.Get(isolate_); 159 } 160 161 /** 162 * Get the underlying v8::ArrayBuffer underlying the TypedArray and 163 * overlaying the native buffer 164 */ GetArrayBuffer()165 v8::Local<v8::ArrayBuffer> GetArrayBuffer() const { 166 return GetJSArray()->Buffer(); 167 } 168 169 /** 170 * Get the underlying native buffer. Note that all reads/writes should occur 171 * through the GetValue/SetValue/operator[] methods 172 */ GetNativeBuffer()173 inline const NativeT* GetNativeBuffer() const { 174 return buffer_; 175 } 176 177 /** 178 * Synonym for GetBuffer() 179 */ 180 inline const NativeT* operator * () const { 181 return GetNativeBuffer(); 182 } 183 184 /** 185 * Set position index to given value. 186 */ SetValue(const size_t index,NativeT value)187 inline void SetValue(const size_t index, NativeT value) { 188 DCHECK_LT(index, count_); 189 buffer_[index] = value; 190 } 191 192 /** 193 * Get value at position index 194 */ GetValue(const size_t index)195 inline const NativeT GetValue(const size_t index) const { 196 DCHECK_LT(index, count_); 197 return buffer_[index]; 198 } 199 200 /** 201 * Effectively, a synonym for GetValue/SetValue 202 */ 203 Reference operator[](size_t index) { 204 return Reference(this, index); 205 } 206 207 NativeT operator[](size_t index) const { 208 return GetValue(index); 209 } 210 Length()211 size_t Length() const { 212 return count_; 213 } 214 215 // Should only be used to extend the array. 216 // Should only be used on an owning array, not one created as a sub array of 217 // an owning `AliasedBufferBase`. reserve(size_t new_capacity)218 void reserve(size_t new_capacity) { 219 DCHECK_GE(new_capacity, count_); 220 DCHECK_EQ(byte_offset_, 0); 221 const v8::HandleScope handle_scope(isolate_); 222 223 const size_t old_size_in_bytes = sizeof(NativeT) * count_; 224 const size_t new_size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT), 225 new_capacity); 226 227 // allocate v8 new ArrayBuffer 228 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( 229 isolate_, new_size_in_bytes); 230 231 // allocate new native buffer 232 NativeT* new_buffer = static_cast<NativeT*>(ab->GetBackingStore()->Data()); 233 // copy old content 234 memcpy(new_buffer, buffer_, old_size_in_bytes); 235 236 // allocate v8 TypedArray 237 v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity); 238 239 // move over old v8 TypedArray 240 js_array_ = std::move(v8::Global<V8T>(isolate_, js_array)); 241 242 buffer_ = new_buffer; 243 count_ = new_capacity; 244 } 245 246 private: 247 v8::Isolate* isolate_; 248 size_t count_; 249 size_t byte_offset_; 250 NativeT* buffer_; 251 v8::Global<V8T> js_array_; 252 }; 253 254 typedef AliasedBufferBase<int32_t, v8::Int32Array> AliasedInt32Array; 255 typedef AliasedBufferBase<uint8_t, v8::Uint8Array> AliasedUint8Array; 256 typedef AliasedBufferBase<uint32_t, v8::Uint32Array> AliasedUint32Array; 257 typedef AliasedBufferBase<double, v8::Float64Array> AliasedFloat64Array; 258 typedef AliasedBufferBase<uint64_t, v8::BigUint64Array> AliasedBigUint64Array; 259 } // namespace node 260 261 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 262 263 #endif // SRC_ALIASED_BUFFER_H_ 264