/src/node/src/aliased_buffer.h
Line | Count | 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 "memory_tracker.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, class V8T> |
32 | | class AliasedBufferBase : public MemoryRetainer { |
33 | | public: |
34 | | static_assert(std::is_scalar<NativeT>::value); |
35 | | |
36 | | AliasedBufferBase(v8::Isolate* isolate, |
37 | | const size_t count, |
38 | | const AliasedBufferIndex* index = nullptr); |
39 | | |
40 | | /** |
41 | | * Create an AliasedBufferBase over a sub-region of another aliased buffer. |
42 | | * The two will share a v8::ArrayBuffer instance & |
43 | | * a native buffer, but will each read/write to different sections of the |
44 | | * native buffer. |
45 | | * |
46 | | * Note that byte_offset must by aligned by sizeof(NativeT). |
47 | | */ |
48 | | // TODO(refack): refactor into a non-owning `AliasedBufferBaseView` |
49 | | AliasedBufferBase( |
50 | | v8::Isolate* isolate, |
51 | | const size_t byte_offset, |
52 | | const size_t count, |
53 | | const AliasedBufferBase<uint8_t, v8::Uint8Array>& backing_buffer, |
54 | | const AliasedBufferIndex* index = nullptr); |
55 | | |
56 | | AliasedBufferBase(const AliasedBufferBase& that); |
57 | | |
58 | | AliasedBufferIndex Serialize(v8::Local<v8::Context> context, |
59 | | v8::SnapshotCreator* creator); |
60 | | |
61 | | inline void Deserialize(v8::Local<v8::Context> context); |
62 | | |
63 | | AliasedBufferBase& operator=(AliasedBufferBase&& that) noexcept; |
64 | | |
65 | | /** |
66 | | * Helper class that is returned from operator[] to support assignment into |
67 | | * a specified location. |
68 | | */ |
69 | | class Reference { |
70 | | public: |
71 | | Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index) |
72 | 8.44M | : aliased_buffer_(aliased_buffer), index_(index) {} node::AliasedBufferBase<unsigned int, v8::Uint32Array>::Reference::Reference(node::AliasedBufferBase<unsigned int, v8::Uint32Array>*, unsigned long) Line | Count | Source | 72 | 2.51M | : aliased_buffer_(aliased_buffer), index_(index) {} |
node::AliasedBufferBase<int, v8::Int32Array>::Reference::Reference(node::AliasedBufferBase<int, v8::Int32Array>*, unsigned long) Line | Count | Source | 72 | 137k | : aliased_buffer_(aliased_buffer), index_(index) {} |
node::AliasedBufferBase<double, v8::Float64Array>::Reference::Reference(node::AliasedBufferBase<double, v8::Float64Array>*, unsigned long) Line | Count | Source | 72 | 5.65M | : aliased_buffer_(aliased_buffer), index_(index) {} |
node::AliasedBufferBase<unsigned char, v8::Uint8Array>::Reference::Reference(node::AliasedBufferBase<unsigned char, v8::Uint8Array>*, unsigned long) Line | Count | Source | 72 | 134k | : aliased_buffer_(aliased_buffer), index_(index) {} |
|
73 | | |
74 | | Reference(const Reference& that) |
75 | | : aliased_buffer_(that.aliased_buffer_), |
76 | | index_(that.index_) { |
77 | | } |
78 | | |
79 | 6.48M | inline Reference& operator=(const NativeT& val) { |
80 | 6.48M | aliased_buffer_->SetValue(index_, val); |
81 | 6.48M | return *this; |
82 | 6.48M | } node::AliasedBufferBase<int, v8::Int32Array>::Reference::operator=(int const&) Line | Count | Source | 79 | 137k | inline Reference& operator=(const NativeT& val) { | 80 | 137k | aliased_buffer_->SetValue(index_, val); | 81 | 137k | return *this; | 82 | 137k | } |
node::AliasedBufferBase<double, v8::Float64Array>::Reference::operator=(double const&) Line | Count | Source | 79 | 4.81M | inline Reference& operator=(const NativeT& val) { | 80 | 4.81M | aliased_buffer_->SetValue(index_, val); | 81 | 4.81M | return *this; | 82 | 4.81M | } |
node::AliasedBufferBase<unsigned int, v8::Uint32Array>::Reference::operator=(unsigned int const&) Line | Count | Source | 79 | 1.39M | inline Reference& operator=(const NativeT& val) { | 80 | 1.39M | aliased_buffer_->SetValue(index_, val); | 81 | 1.39M | return *this; | 82 | 1.39M | } |
node::AliasedBufferBase<unsigned char, v8::Uint8Array>::Reference::operator=(unsigned char const&) Line | Count | Source | 79 | 134k | inline Reference& operator=(const NativeT& val) { | 80 | 134k | aliased_buffer_->SetValue(index_, val); | 81 | 134k | return *this; | 82 | 134k | } |
|
83 | | |
84 | 361k | inline Reference& operator=(const Reference& val) { |
85 | 361k | return *this = static_cast<NativeT>(val); |
86 | 361k | } |
87 | | |
88 | 1.44M | operator NativeT() const { |
89 | 1.44M | return aliased_buffer_->GetValue(index_); |
90 | 1.44M | } node::AliasedBufferBase<double, v8::Float64Array>::Reference::operator double() const Line | Count | Source | 88 | 728k | operator NativeT() const { | 89 | 728k | return aliased_buffer_->GetValue(index_); | 90 | 728k | } |
node::AliasedBufferBase<unsigned int, v8::Uint32Array>::Reference::operator unsigned int() const Line | Count | Source | 88 | 716k | operator NativeT() const { | 89 | 716k | return aliased_buffer_->GetValue(index_); | 90 | 716k | } |
Unexecuted instantiation: node::AliasedBufferBase<int, v8::Int32Array>::Reference::operator int() const |
91 | | |
92 | 241k | inline Reference& operator+=(const NativeT& val) { |
93 | 241k | const NativeT current = aliased_buffer_->GetValue(index_); |
94 | 241k | aliased_buffer_->SetValue(index_, current + val); |
95 | 241k | return *this; |
96 | 241k | } node::AliasedBufferBase<unsigned int, v8::Uint32Array>::Reference::operator+=(unsigned int const&) Line | Count | Source | 92 | 134k | inline Reference& operator+=(const NativeT& val) { | 93 | 134k | const NativeT current = aliased_buffer_->GetValue(index_); | 94 | 134k | aliased_buffer_->SetValue(index_, current + val); | 95 | 134k | return *this; | 96 | 134k | } |
node::AliasedBufferBase<double, v8::Float64Array>::Reference::operator+=(double const&) Line | Count | Source | 92 | 106k | inline Reference& operator+=(const NativeT& val) { | 93 | 106k | const NativeT current = aliased_buffer_->GetValue(index_); | 94 | 106k | aliased_buffer_->SetValue(index_, current + val); | 95 | 106k | return *this; | 96 | 106k | } |
|
97 | | |
98 | | inline Reference& operator+=(const Reference& val) { |
99 | | return this->operator+=(static_cast<NativeT>(val)); |
100 | | } |
101 | | |
102 | 269k | inline Reference& operator-=(const NativeT& val) { |
103 | 269k | const NativeT current = aliased_buffer_->GetValue(index_); |
104 | 269k | aliased_buffer_->SetValue(index_, current - val); |
105 | 269k | return *this; |
106 | 269k | } |
107 | | |
108 | | private: |
109 | | AliasedBufferBase<NativeT, V8T>* aliased_buffer_; |
110 | | size_t index_; |
111 | | }; |
112 | | |
113 | | /** |
114 | | * Get the underlying v8 TypedArray overlayed on top of the native buffer |
115 | | */ |
116 | | v8::Local<V8T> GetJSArray() const; |
117 | | |
118 | | void Release(); |
119 | | |
120 | | /** |
121 | | * Make the global reference to the typed array weak. The caller must make |
122 | | * sure that no operation can be done on the AliasedBuffer when the typed |
123 | | * array becomes unreachable. Usually this means the caller must maintain |
124 | | * a JS reference to the typed array from JS object. |
125 | | */ |
126 | | inline void MakeWeak(); |
127 | | |
128 | | /** |
129 | | * Get the underlying v8::ArrayBuffer underlying the TypedArray and |
130 | | * overlaying the native buffer |
131 | | */ |
132 | | v8::Local<v8::ArrayBuffer> GetArrayBuffer() const; |
133 | | |
134 | | /** |
135 | | * Get the underlying native buffer. Note that all reads/writes should occur |
136 | | * through the GetValue/SetValue/operator[] methods |
137 | | */ |
138 | | inline const NativeT* GetNativeBuffer() const; |
139 | | |
140 | | /** |
141 | | * Synonym for GetBuffer() |
142 | | */ |
143 | | inline const NativeT* operator*() const; |
144 | | |
145 | | /** |
146 | | * Set position index to given value. |
147 | | */ |
148 | | inline void SetValue(const size_t index, NativeT value); |
149 | | |
150 | | /** |
151 | | * Get value at position index |
152 | | */ |
153 | | inline const NativeT GetValue(const size_t index) const; |
154 | | |
155 | | /** |
156 | | * Effectively, a synonym for GetValue/SetValue |
157 | | */ |
158 | | Reference operator[](size_t index); |
159 | | |
160 | | NativeT operator[](size_t index) const; |
161 | | |
162 | | size_t Length() const; |
163 | | |
164 | | // Should only be used to extend the array. |
165 | | // Should only be used on an owning array, not one created as a sub array of |
166 | | // an owning `AliasedBufferBase`. |
167 | | void reserve(size_t new_capacity); |
168 | | |
169 | | inline size_t SelfSize() const override; |
170 | | |
171 | | inline const char* MemoryInfoName() const override; |
172 | | inline void MemoryInfo(node::MemoryTracker* tracker) const override; |
173 | | |
174 | | private: |
175 | | inline bool is_valid() const; |
176 | | v8::Isolate* isolate_ = nullptr; |
177 | | size_t count_ = 0; |
178 | | size_t byte_offset_ = 0; |
179 | | NativeT* buffer_ = nullptr; |
180 | | v8::Global<V8T> js_array_; |
181 | | |
182 | | // Deserialize data |
183 | | const AliasedBufferIndex* index_ = nullptr; |
184 | | }; |
185 | | |
186 | | #define ALIASED_BUFFER_LIST(V) \ |
187 | | V(int8_t, Int8Array) \ |
188 | | V(uint8_t, Uint8Array) \ |
189 | | V(int16_t, Int16Array) \ |
190 | | V(uint16_t, Uint16Array) \ |
191 | | V(int32_t, Int32Array) \ |
192 | | V(uint32_t, Uint32Array) \ |
193 | | V(float, Float32Array) \ |
194 | | V(double, Float64Array) \ |
195 | | V(int64_t, BigInt64Array) |
196 | | |
197 | | #define V(NativeT, V8T) \ |
198 | | typedef AliasedBufferBase<NativeT, v8::V8T> Aliased##V8T; |
199 | | ALIASED_BUFFER_LIST(V) |
200 | | #undef V |
201 | | |
202 | | } // namespace node |
203 | | |
204 | | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
205 | | |
206 | | #endif // SRC_ALIASED_BUFFER_H_ |