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 |
182630 |
AliasedBufferBase(v8::Isolate* isolate, |
|
38 |
const size_t count, |
||
39 |
const AliasedBufferIndex* index = nullptr) |
||
40 |
✗✓ | 182630 |
: isolate_(isolate), count_(count), byte_offset_(0), index_(index) { |
41 |
✗✓ | 182630 |
CHECK_GT(count, 0); |
42 |
✓✓ | 182630 |
if (index != nullptr) { |
43 |
// Will be deserialized later. |
||
44 |
96300 |
return; |
|
45 |
} |
||
46 |
86330 |
const v8::HandleScope handle_scope(isolate_); |
|
47 |
const size_t size_in_bytes = |
||
48 |
86330 |
MultiplyWithOverflowCheck(sizeof(NativeT), count); |
|
49 |
|||
50 |
// allocate v8 ArrayBuffer |
||
51 |
86330 |
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( |
|
52 |
isolate_, size_in_bytes); |
||
53 |
86330 |
buffer_ = static_cast<NativeT*>(ab->Data()); |
|
54 |
|||
55 |
// allocate v8 TypedArray |
||
56 |
86330 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count); |
|
57 |
✓✗ | 172660 |
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 |
28088 |
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 |
✓✓✓✓ ✗✓✗✓ |
14044 |
index_(index) { |
79 |
✓✓✓✓ ✗✓✗✓ |
14044 |
if (index != nullptr) { |
80 |
// Will be deserialized later. |
||
81 |
10700 |
return; |
|
82 |
} |
||
83 |
3344 |
const v8::HandleScope handle_scope(isolate_); |
|
84 |
3344 |
v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer(); |
|
85 |
|||
86 |
// validate that the byte_offset is aligned with sizeof(NativeT) |
||
87 |
✗✓✗✓ ✗✓ |
3338 |
CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0); |
88 |
// validate this fits inside the backing buffer |
||
89 |
✗✓✗✓ ✗✓✗✓ |
6688 |
CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count), |
90 |
ab->ByteLength() - byte_offset); |
||
91 |
|||
92 |
3344 |
buffer_ = reinterpret_cast<NativeT*>( |
|
93 |
3344 |
const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset)); |
|
94 |
|||
95 |
3344 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count); |
|
96 |
✓✗✓✗ ✓✗✓✗ |
6688 |
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 |
132 |
AliasedBufferIndex Serialize(v8::Local<v8::Context> context, |
|
109 |
v8::SnapshotCreator* creator) { |
||
110 |
DCHECK_NULL(index_); |
||
111 |
264 |
return creator->AddData(context, GetJSArray()); |
|
112 |
} |
||
113 |
|||
114 |
117700 |
inline void Deserialize(v8::Local<v8::Context> context) { |
|
115 |
DCHECK_NOT_NULL(index_); |
||
116 |
117700 |
v8::Local<V8T> arr = |
|
117 |
✗✓ | 353100 |
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 |
235400 |
uint8_t* raw = static_cast<uint8_t*>(arr->Buffer()->Data()); |
|
123 |
117700 |
buffer_ = reinterpret_cast<NativeT*>(raw + byte_offset_); |
|
124 |
117700 |
js_array_.Reset(isolate_, arr); |
|
125 |
117700 |
index_ = nullptr; |
|
126 |
117700 |
} |
|
127 |
|||
128 |
AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept { |
||
129 |
DCHECK_NULL(index_); |
||
130 |
this->~AliasedBufferBase(); |
||
131 |
isolate_ = that.isolate_; |
||
132 |
count_ = that.count_; |
||
133 |
byte_offset_ = that.byte_offset_; |
||
134 |
buffer_ = that.buffer_; |
||
135 |
|||
136 |
js_array_.Reset(isolate_, that.js_array_.Get(isolate_)); |
||
137 |
|||
138 |
that.buffer_ = nullptr; |
||
139 |
that.js_array_.Reset(); |
||
140 |
return *this; |
||
141 |
} |
||
142 |
|||
143 |
/** |
||
144 |
* Helper class that is returned from operator[] to support assignment into |
||
145 |
* a specified location. |
||
146 |
*/ |
||
147 |
class Reference { |
||
148 |
public: |
||
149 |
40843286 |
Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index) |
|
150 |
40843286 |
: aliased_buffer_(aliased_buffer), index_(index) {} |
|
151 |
|||
152 |
1 |
Reference(const Reference& that) |
|
153 |
1 |
: aliased_buffer_(that.aliased_buffer_), |
|
154 |
1 |
index_(that.index_) { |
|
155 |
1 |
} |
|
156 |
|||
157 |
12128263 |
inline Reference& operator=(const NativeT& val) { |
|
158 |
12128263 |
aliased_buffer_->SetValue(index_, val); |
|
159 |
12128263 |
return *this; |
|
160 |
} |
||
161 |
|||
162 |
3263211 |
inline Reference& operator=(const Reference& val) { |
|
163 |
3263211 |
return *this = static_cast<NativeT>(val); |
|
164 |
} |
||
165 |
|||
166 |
25309331 |
operator NativeT() const { |
|
167 |
25309331 |
return aliased_buffer_->GetValue(index_); |
|
168 |
} |
||
169 |
|||
170 |
1913560 |
inline Reference& operator+=(const NativeT& val) { |
|
171 |
1913560 |
const NativeT current = aliased_buffer_->GetValue(index_); |
|
172 |
1913560 |
aliased_buffer_->SetValue(index_, current + val); |
|
173 |
1913560 |
return *this; |
|
174 |
} |
||
175 |
|||
176 |
1 |
inline Reference& operator+=(const Reference& val) { |
|
177 |
1 |
return this->operator+=(static_cast<NativeT>(val)); |
|
178 |
} |
||
179 |
|||
180 |
219565 |
inline Reference& operator-=(const NativeT& val) { |
|
181 |
219565 |
const NativeT current = aliased_buffer_->GetValue(index_); |
|
182 |
219565 |
aliased_buffer_->SetValue(index_, current - val); |
|
183 |
219565 |
return *this; |
|
184 |
} |
||
185 |
|||
186 |
private: |
||
187 |
AliasedBufferBase<NativeT, V8T>* aliased_buffer_; |
||
188 |
size_t index_; |
||
189 |
}; |
||
190 |
|||
191 |
/** |
||
192 |
* Get the underlying v8 TypedArray overlayed on top of the native buffer |
||
193 |
*/ |
||
194 |
470934 |
v8::Local<V8T> GetJSArray() const { |
|
195 |
DCHECK_NULL(index_); |
||
196 |
✗✓ | 941868 |
return js_array_.Get(isolate_); |
197 |
} |
||
198 |
|||
199 |
60 |
void Release() { |
|
200 |
DCHECK_NULL(index_); |
||
201 |
60 |
js_array_.Reset(); |
|
202 |
60 |
} |
|
203 |
|||
204 |
/** |
||
205 |
* Get the underlying v8::ArrayBuffer underlying the TypedArray and |
||
206 |
* overlaying the native buffer |
||
207 |
*/ |
||
208 |
3344 |
v8::Local<v8::ArrayBuffer> GetArrayBuffer() const { |
|
209 |
6688 |
return GetJSArray()->Buffer(); |
|
210 |
} |
||
211 |
|||
212 |
/** |
||
213 |
* Get the underlying native buffer. Note that all reads/writes should occur |
||
214 |
* through the GetValue/SetValue/operator[] methods |
||
215 |
*/ |
||
216 |
3630 |
inline const NativeT* GetNativeBuffer() const { |
|
217 |
DCHECK_NULL(index_); |
||
218 |
3630 |
return buffer_; |
|
219 |
} |
||
220 |
|||
221 |
/** |
||
222 |
* Synonym for GetBuffer() |
||
223 |
*/ |
||
224 |
96 |
inline const NativeT* operator * () const { |
|
225 |
96 |
return GetNativeBuffer(); |
|
226 |
} |
||
227 |
|||
228 |
/** |
||
229 |
* Set position index to given value. |
||
230 |
*/ |
||
231 |
22319824 |
inline void SetValue(const size_t index, NativeT value) { |
|
232 |
DCHECK_LT(index, count_); |
||
233 |
DCHECK_NULL(index_); |
||
234 |
22319824 |
buffer_[index] = value; |
|
235 |
22319824 |
} |
|
236 |
|||
237 |
/** |
||
238 |
* Get value at position index |
||
239 |
*/ |
||
240 |
33010776 |
inline const NativeT GetValue(const size_t index) const { |
|
241 |
DCHECK_NULL(index_); |
||
242 |
DCHECK_LT(index, count_); |
||
243 |
33010776 |
return buffer_[index]; |
|
244 |
} |
||
245 |
|||
246 |
/** |
||
247 |
* Effectively, a synonym for GetValue/SetValue |
||
248 |
*/ |
||
249 |
40837042 |
Reference operator[](size_t index) { |
|
250 |
DCHECK_NULL(index_); |
||
251 |
40837042 |
return Reference(this, index); |
|
252 |
} |
||
253 |
|||
254 |
2564849 |
NativeT operator[](size_t index) const { |
|
255 |
2564849 |
return GetValue(index); |
|
256 |
} |
||
257 |
|||
258 |
821955 |
size_t Length() const { |
|
259 |
821955 |
return count_; |
|
260 |
} |
||
261 |
|||
262 |
// Should only be used to extend the array. |
||
263 |
// Should only be used on an owning array, not one created as a sub array of |
||
264 |
// an owning `AliasedBufferBase`. |
||
265 |
4 |
void reserve(size_t new_capacity) { |
|
266 |
DCHECK_NULL(index_); |
||
267 |
DCHECK_GE(new_capacity, count_); |
||
268 |
DCHECK_EQ(byte_offset_, 0); |
||
269 |
4 |
const v8::HandleScope handle_scope(isolate_); |
|
270 |
|||
271 |
4 |
const size_t old_size_in_bytes = sizeof(NativeT) * count_; |
|
272 |
4 |
const size_t new_size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT), |
|
273 |
new_capacity); |
||
274 |
|||
275 |
// allocate v8 new ArrayBuffer |
||
276 |
4 |
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( |
|
277 |
isolate_, new_size_in_bytes); |
||
278 |
|||
279 |
// allocate new native buffer |
||
280 |
4 |
NativeT* new_buffer = static_cast<NativeT*>(ab->Data()); |
|
281 |
// copy old content |
||
282 |
4 |
memcpy(new_buffer, buffer_, old_size_in_bytes); |
|
283 |
|||
284 |
// allocate v8 TypedArray |
||
285 |
4 |
v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity); |
|
286 |
|||
287 |
// move over old v8 TypedArray |
||
288 |
✓✗ | 8 |
js_array_ = std::move(v8::Global<V8T>(isolate_, js_array)); |
289 |
|||
290 |
4 |
buffer_ = new_buffer; |
|
291 |
4 |
count_ = new_capacity; |
|
292 |
4 |
} |
|
293 |
|||
294 |
private: |
||
295 |
v8::Isolate* isolate_ = nullptr; |
||
296 |
size_t count_ = 0; |
||
297 |
size_t byte_offset_ = 0; |
||
298 |
NativeT* buffer_ = nullptr; |
||
299 |
v8::Global<V8T> js_array_; |
||
300 |
|||
301 |
// Deserialize data |
||
302 |
const AliasedBufferIndex* index_ = nullptr; |
||
303 |
}; |
||
304 |
|||
305 |
typedef AliasedBufferBase<int32_t, v8::Int32Array> AliasedInt32Array; |
||
306 |
typedef AliasedBufferBase<uint8_t, v8::Uint8Array> AliasedUint8Array; |
||
307 |
typedef AliasedBufferBase<uint32_t, v8::Uint32Array> AliasedUint32Array; |
||
308 |
typedef AliasedBufferBase<double, v8::Float64Array> AliasedFloat64Array; |
||
309 |
typedef AliasedBufferBase<int64_t, v8::BigInt64Array> AliasedBigInt64Array; |
||
310 |
} // namespace node |
||
311 |
|||
312 |
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
313 |
|||
314 |
#endif // SRC_ALIASED_BUFFER_H_ |
Generated by: GCOVR (Version 4.2) |