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