GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 <iostream> |
||
8 |
#include "util-inl.h" |
||
9 |
#include "v8.h" |
||
10 |
|||
11 |
namespace node { |
||
12 |
|||
13 |
typedef size_t AliasedBufferIndex; |
||
14 |
|||
15 |
/** |
||
16 |
* Do not use this class directly when creating instances of it - use the |
||
17 |
* Aliased*Array defined at the end of this file instead. |
||
18 |
* |
||
19 |
* This class encapsulates the technique of having a native buffer mapped to |
||
20 |
* a JS object. Writes to the native buffer can happen efficiently without |
||
21 |
* going through JS, and the data is then available to user's via the exposed |
||
22 |
* JS object. |
||
23 |
* |
||
24 |
* While this technique is computationally efficient, it is effectively a |
||
25 |
* write to JS program state w/out going through the standard |
||
26 |
* (monitored) API. Thus any VM capabilities to detect the modification are |
||
27 |
* circumvented. |
||
28 |
* |
||
29 |
* The encapsulation herein provides a placeholder where such writes can be |
||
30 |
* observed. Any notification APIs will be left as a future exercise. |
||
31 |
*/ |
||
32 |
template <class NativeT, |
||
33 |
class V8T, |
||
34 |
// SFINAE NativeT to be scalar |
||
35 |
typename = std::enable_if_t<std::is_scalar<NativeT>::value>> |
||
36 |
123294 |
class AliasedBufferBase { |
|
37 |
public: |
||
38 |
55473 |
AliasedBufferBase(v8::Isolate* isolate, |
|
39 |
const size_t count, |
||
40 |
const AliasedBufferIndex* index = nullptr) |
||
41 |
110946 |
: isolate_(isolate), count_(count), byte_offset_(0), index_(index) { |
|
42 |
✗✓✗✓ ✗✓✗✓ ✗✓✗✓ ✗✓✗✓ |
55473 |
CHECK_GT(count, 0); |
43 |
✓✓✓✓ ✓✓✓✓ ✗✓✗✓ ✗✓✗✓ |
55473 |
if (index != nullptr) { |
44 |
// Will be deserialized later. |
||
45 |
36904 |
return; |
|
46 |
} |
||
47 |
37138 |
const v8::HandleScope handle_scope(isolate_); |
|
48 |
const size_t size_in_bytes = |
||
49 |
18569 |
MultiplyWithOverflowCheck(sizeof(NativeT), count); |
|
50 |
|||
51 |
// allocate v8 ArrayBuffer |
||
52 |
18569 |
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( |
|
53 |
18569 |
isolate_, size_in_bytes); |
|
54 |
18569 |
buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data()); |
|
55 |
|||
56 |
// allocate v8 TypedArray |
||
57 |
18569 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count); |
|
58 |
37138 |
js_array_ = v8::Global<V8T>(isolate, js_array); |
|
59 |
} |
||
60 |
|||
61 |
/** |
||
62 |
* Create an AliasedBufferBase over a sub-region of another aliased buffer. |
||
63 |
* The two will share a v8::ArrayBuffer instance & |
||
64 |
* a native buffer, but will each read/write to different sections of the |
||
65 |
* native buffer. |
||
66 |
* |
||
67 |
* Note that byte_offset must by aligned by sizeof(NativeT). |
||
68 |
*/ |
||
69 |
// TODO(refack): refactor into a non-owning `AliasedBufferBaseView` |
||
70 |
11613 |
AliasedBufferBase( |
|
71 |
v8::Isolate* isolate, |
||
72 |
const size_t byte_offset, |
||
73 |
const size_t count, |
||
74 |
const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer, |
||
75 |
const AliasedBufferIndex* index = nullptr) |
||
76 |
: isolate_(isolate), |
||
77 |
count_(count), |
||
78 |
byte_offset_(byte_offset), |
||
79 |
23226 |
index_(index) { |
|
80 |
✓✓✓✓ ✗✓✗✓ |
11613 |
if (index != nullptr) { |
81 |
// Will be deserialized later. |
||
82 |
9226 |
return; |
|
83 |
} |
||
84 |
4775 |
const v8::HandleScope handle_scope(isolate_); |
|
85 |
2388 |
v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer(); |
|
86 |
|||
87 |
// validate that the byte_offset is aligned with sizeof(NativeT) |
||
88 |
✗✓✗✓ ✗✓ |
2382 |
CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0); |
89 |
// validate this fits inside the backing buffer |
||
90 |
✗✗✓✗ ✓✗✓✓ |
4776 |
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count), |
91 |
ab->ByteLength() - byte_offset); |
||
92 |
|||
93 |
2388 |
buffer_ = reinterpret_cast<NativeT*>( |
|
94 |
2388 |
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset)); |
|
95 |
|||
96 |
2388 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count); |
|
97 |
4776 |
js_array_ = v8::Global<V8T>(isolate, js_array); |
|
98 |
} |
||
99 |
|||
100 |
8 |
AliasedBufferBase(const AliasedBufferBase& that) |
|
101 |
8 |
: isolate_(that.isolate_), |
|
102 |
8 |
count_(that.count_), |
|
103 |
8 |
byte_offset_(that.byte_offset_), |
|
104 |
32 |
buffer_(that.buffer_) { |
|
105 |
DCHECK_NULL(index_); |
||
106 |
24 |
js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray()); |
|
107 |
8 |
} |
|
108 |
|||
109 |
80 |
AliasedBufferIndex Serialize(v8::Local<v8::Context> context, |
|
110 |
v8::SnapshotCreator* creator) { |
||
111 |
DCHECK_NULL(index_); |
||
112 |
160 |
return creator->AddData(context, GetJSArray()); |
|
113 |
} |
||
114 |
|||
115 |
46130 |
inline void Deserialize(v8::Local<v8::Context> context) { |
|
116 |
DCHECK_NOT_NULL(index_); |
||
117 |
v8::Local<V8T> arr = |
||
118 |
184520 |
context->GetDataFromSnapshotOnce<V8T>(*index_).ToLocalChecked(); |
|
119 |
// These may not hold true for AliasedBuffers that have grown, so should |
||
120 |
// be removed when we expand the snapshot support. |
||
121 |
DCHECK_EQ(count_, arr->Length()); |
||
122 |
DCHECK_EQ(byte_offset_, arr->ByteOffset()); |
||
123 |
uint8_t* raw = |
||
124 |
92260 |
static_cast<uint8_t*>(arr->Buffer()->GetBackingStore()->Data()); |
|
125 |
46130 |
buffer_ = reinterpret_cast<NativeT*>(raw + byte_offset_); |
|
126 |
46130 |
js_array_.Reset(isolate_, arr); |
|
127 |
46130 |
index_ = nullptr; |
|
128 |
46130 |
} |
|
129 |
|||
130 |
AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept { |
||
131 |
DCHECK_NULL(index_); |
||
132 |
this->~AliasedBufferBase(); |
||
133 |
isolate_ = that.isolate_; |
||
134 |
count_ = that.count_; |
||
135 |
byte_offset_ = that.byte_offset_; |
||
136 |
buffer_ = that.buffer_; |
||
137 |
|||
138 |
js_array_.Reset(isolate_, that.js_array_.Get(isolate_)); |
||
139 |
|||
140 |
that.buffer_ = nullptr; |
||
141 |
that.js_array_.Reset(); |
||
142 |
return *this; |
||
143 |
} |
||
144 |
|||
145 |
/** |
||
146 |
* Helper class that is returned from operator[] to support assignment into |
||
147 |
* a specified location. |
||
148 |
*/ |
||
149 |
class Reference { |
||
150 |
public: |
||
151 |
32946588 |
Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index) |
|
152 |
32946588 |
: aliased_buffer_(aliased_buffer), index_(index) {} |
|
153 |
|||
154 |
1 |
Reference(const Reference& that) |
|
155 |
1 |
: aliased_buffer_(that.aliased_buffer_), |
|
156 |
1 |
index_(that.index_) { |
|
157 |
1 |
} |
|
158 |
|||
159 |
11301567 |
inline Reference& operator=(const NativeT& val) { |
|
160 |
11301567 |
aliased_buffer_->SetValue(index_, val); |
|
161 |
11301268 |
return *this; |
|
162 |
} |
||
163 |
|||
164 |
5066086 |
inline Reference& operator=(const Reference& val) { |
|
165 |
5066086 |
return *this = static_cast<NativeT>(val); |
|
166 |
} |
||
167 |
|||
168 |
19639703 |
operator NativeT() const { |
|
169 |
19639703 |
return aliased_buffer_->GetValue(index_); |
|
170 |
} |
||
171 |
|||
172 |
1517649 |
inline Reference& operator+=(const NativeT& val) { |
|
173 |
1517649 |
const NativeT current = aliased_buffer_->GetValue(index_); |
|
174 |
1517676 |
aliased_buffer_->SetValue(index_, current + val); |
|
175 |
1517678 |
return *this; |
|
176 |
} |
||
177 |
|||
178 |
1 |
inline Reference& operator+=(const Reference& val) { |
|
179 |
1 |
return this->operator+=(static_cast<NativeT>(val)); |
|
180 |
} |
||
181 |
|||
182 |
495326 |
inline Reference& operator-=(const NativeT& val) { |
|
183 |
495326 |
const NativeT current = aliased_buffer_->GetValue(index_); |
|
184 |
495330 |
aliased_buffer_->SetValue(index_, current - val); |
|
185 |
495330 |
return *this; |
|
186 |
} |
||
187 |
|||
188 |
private: |
||
189 |
AliasedBufferBase<NativeT, V8T>* aliased_buffer_; |
||
190 |
size_t index_; |
||
191 |
}; |
||
192 |
|||
193 |
/** |
||
194 |
* Get the underlying v8 TypedArray overlayed on top of the native buffer |
||
195 |
*/ |
||
196 |
196599 |
v8::Local<V8T> GetJSArray() const { |
|
197 |
DCHECK_NULL(index_); |
||
198 |
393198 |
return js_array_.Get(isolate_); |
|
199 |
} |
||
200 |
|||
201 |
/** |
||
202 |
* Get the underlying v8::ArrayBuffer underlying the TypedArray and |
||
203 |
* overlaying the native buffer |
||
204 |
*/ |
||
205 |
2388 |
v8::Local<v8::ArrayBuffer> GetArrayBuffer() const { |
|
206 |
4776 |
return GetJSArray()->Buffer(); |
|
207 |
} |
||
208 |
|||
209 |
/** |
||
210 |
* Get the underlying native buffer. Note that all reads/writes should occur |
||
211 |
* through the GetValue/SetValue/operator[] methods |
||
212 |
*/ |
||
213 |
2484 |
inline const NativeT* GetNativeBuffer() const { |
|
214 |
DCHECK_NULL(index_); |
||
215 |
2484 |
return buffer_; |
|
216 |
} |
||
217 |
|||
218 |
/** |
||
219 |
* Synonym for GetBuffer() |
||
220 |
*/ |
||
221 |
48 |
inline const NativeT* operator * () const { |
|
222 |
48 |
return GetNativeBuffer(); |
|
223 |
} |
||
224 |
|||
225 |
/** |
||
226 |
* Set position index to given value. |
||
227 |
*/ |
||
228 |
16221656 |
inline void SetValue(const size_t index, NativeT value) { |
|
229 |
DCHECK_LT(index, count_); |
||
230 |
DCHECK_NULL(index_); |
||
231 |
16221656 |
buffer_[index] = value; |
|
232 |
16221656 |
} |
|
233 |
|||
234 |
/** |
||
235 |
* Get value at position index |
||
236 |
*/ |
||
237 |
25985755 |
inline const NativeT GetValue(const size_t index) const { |
|
238 |
DCHECK_NULL(index_); |
||
239 |
DCHECK_LT(index, count_); |
||
240 |
25985755 |
return buffer_[index]; |
|
241 |
} |
||
242 |
|||
243 |
/** |
||
244 |
* Effectively, a synonym for GetValue/SetValue |
||
245 |
*/ |
||
246 |
32946544 |
Reference operator[](size_t index) { |
|
247 |
DCHECK_NULL(index_); |
||
248 |
32946544 |
return Reference(this, index); |
|
249 |
} |
||
250 |
|||
251 |
4333988 |
NativeT operator[](size_t index) const { |
|
252 |
4333988 |
return GetValue(index); |
|
253 |
} |
||
254 |
|||
255 |
1270619 |
size_t Length() const { |
|
256 |
1270619 |
return count_; |
|
257 |
} |
||
258 |
|||
259 |
// Should only be used to extend the array. |
||
260 |
// Should only be used on an owning array, not one created as a sub array of |
||
261 |
// an owning `AliasedBufferBase`. |
||
262 |
10 |
void reserve(size_t new_capacity) { |
|
263 |
DCHECK_NULL(index_); |
||
264 |
DCHECK_GE(new_capacity, count_); |
||
265 |
DCHECK_EQ(byte_offset_, 0); |
||
266 |
20 |
const v8::HandleScope handle_scope(isolate_); |
|
267 |
|||
268 |
10 |
const size_t old_size_in_bytes = sizeof(NativeT) * count_; |
|
269 |
const size_t new_size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT), |
||
270 |
10 |
new_capacity); |
|
271 |
|||
272 |
// allocate v8 new ArrayBuffer |
||
273 |
10 |
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( |
|
274 |
10 |
isolate_, new_size_in_bytes); |
|
275 |
|||
276 |
// allocate new native buffer |
||
277 |
10 |
NativeT* new_buffer = static_cast<NativeT*>(ab->GetBackingStore()->Data()); |
|
278 |
// copy old content |
||
279 |
10 |
memcpy(new_buffer, buffer_, old_size_in_bytes); |
|
280 |
|||
281 |
// allocate v8 TypedArray |
||
282 |
10 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity); |
|
283 |
|||
284 |
// move over old v8 TypedArray |
||
285 |
30 |
js_array_ = std::move(v8::Global<V8T>(isolate_, js_array)); |
|
286 |
|||
287 |
10 |
buffer_ = new_buffer; |
|
288 |
10 |
count_ = new_capacity; |
|
289 |
10 |
} |
|
290 |
|||
291 |
private: |
||
292 |
v8::Isolate* isolate_ = nullptr; |
||
293 |
size_t count_ = 0; |
||
294 |
size_t byte_offset_ = 0; |
||
295 |
NativeT* buffer_ = nullptr; |
||
296 |
v8::Global<V8T> js_array_; |
||
297 |
|||
298 |
// Deserialize data |
||
299 |
const AliasedBufferIndex* index_ = nullptr; |
||
300 |
}; |
||
301 |
|||
302 |
typedef AliasedBufferBase<int32_t, v8::Int32Array> AliasedInt32Array; |
||
303 |
typedef AliasedBufferBase<uint8_t, v8::Uint8Array> AliasedUint8Array; |
||
304 |
typedef AliasedBufferBase<uint32_t, v8::Uint32Array> AliasedUint32Array; |
||
305 |
typedef AliasedBufferBase<double, v8::Float64Array> AliasedFloat64Array; |
||
306 |
typedef AliasedBufferBase<uint64_t, v8::BigUint64Array> AliasedBigUint64Array; |
||
307 |
} // namespace node |
||
308 |
|||
309 |
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
310 |
|||
311 |
#endif // SRC_ALIASED_BUFFER_H_ |
Generated by: GCOVR (Version 3.4) |