GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/aliased_buffer.h Lines: 99 99 100.0 %
Date: 2021-06-04 04:12:13 Branches: 33 54 61.1 %

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
151050
class AliasedBufferBase {
36
 public:
37
70794
  AliasedBufferBase(v8::Isolate* isolate,
38
                    const size_t count,
39
                    const AliasedBufferIndex* index = nullptr)
40
141588
      : isolate_(isolate), count_(count), byte_offset_(0), index_(index) {
41




70794
    CHECK_GT(count, 0);
42




70794
    if (index != nullptr) {
43
      // Will be deserialized later.
44
38160
      return;
45
    }
46
65268
    const v8::HandleScope handle_scope(isolate_);
47
    const size_t size_in_bytes =
48
32634
        MultiplyWithOverflowCheck(sizeof(NativeT), count);
49
50
    // allocate v8 ArrayBuffer
51
32634
    v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
52
32634
        isolate_, size_in_bytes);
53
32634
    buffer_ = static_cast<NativeT*>(ab->GetBackingStore()->Data());
54
55
    // allocate v8 TypedArray
56
32634
    v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
57
65268
    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
12004
  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
24008
        index_(index) {
79


12004
    if (index != nullptr) {
80
      // Will be deserialized later.
81
9540
      return;
82
    }
83
4928
    const v8::HandleScope handle_scope(isolate_);
84
2464
    v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer();
85
86
    // validate that the byte_offset is aligned with sizeof(NativeT)
87

2458
    CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0);
88
    // validate this fits inside the backing buffer
89


4928
    CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count),
90
             ab->ByteLength() - byte_offset);
91
92
2464
    buffer_ = reinterpret_cast<NativeT*>(
93
2464
        const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));
94
95
2464
    v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count);
96
4928
    js_array_ = v8::Global<V8T>(isolate, js_array);
97
  }
98
99
8
  AliasedBufferBase(const AliasedBufferBase& that)
100
8
      : isolate_(that.isolate_),
101
8
        count_(that.count_),
102
8
        byte_offset_(that.byte_offset_),
103
32
        buffer_(that.buffer_) {
104
    DCHECK_NULL(index_);
105
24
    js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray());
106
8
  }
107
108
80
  AliasedBufferIndex Serialize(v8::Local<v8::Context> context,
109
                              v8::SnapshotCreator* creator) {
110
    DCHECK_NULL(index_);
111
160
    return creator->AddData(context, GetJSArray());
112
  }
113
114
47700
  inline void Deserialize(v8::Local<v8::Context> context) {
115
    DCHECK_NOT_NULL(index_);
116
    v8::Local<V8T> arr =
117
190800
        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
95400
        static_cast<uint8_t*>(arr->Buffer()->GetBackingStore()->Data());
124
47700
    buffer_ = reinterpret_cast<NativeT*>(raw + byte_offset_);
125
47700
    js_array_.Reset(isolate_, arr);
126
47700
    index_ = nullptr;
127
47700
  }
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
21136568
    Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index)
151
21136568
        : 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
6697929
    inline Reference& operator=(const NativeT& val) {
159
6697929
      aliased_buffer_->SetValue(index_, val);
160
6697817
      return *this;
161
    }
162
163
3270463
    inline Reference& operator=(const Reference& val) {
164
3270463
      return *this = static_cast<NativeT>(val);
165
    }
166
167
13156771
    operator NativeT() const {
168
13156771
      return aliased_buffer_->GetValue(index_);
169
    }
170
171
1071899
    inline Reference& operator+=(const NativeT& val) {
172
1071899
      const NativeT current = aliased_buffer_->GetValue(index_);
173
1071920
      aliased_buffer_->SetValue(index_, current + val);
174
1071923
      return *this;
175
    }
176
177
1
    inline Reference& operator+=(const Reference& val) {
178
1
      return this->operator+=(static_cast<NativeT>(val));
179
    }
180
181
217336
    inline Reference& operator-=(const NativeT& val) {
182
217336
      const NativeT current = aliased_buffer_->GetValue(index_);
183
217343
      aliased_buffer_->SetValue(index_, current - val);
184
217344
      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
211246
  v8::Local<V8T> GetJSArray() const {
196
    DCHECK_NULL(index_);
197
422492
    return js_array_.Get(isolate_);
198
  }
199
200
40
  void Release() {
201
    DCHECK_NULL(index_);
202
40
    js_array_.Reset();
203
40
  }
204
205
  /**
206
  *  Get the underlying v8::ArrayBuffer underlying the TypedArray and
207
  *  overlaying the native buffer
208
  */
209
2464
  v8::Local<v8::ArrayBuffer> GetArrayBuffer() const {
210
4928
    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
2560
  inline const NativeT* GetNativeBuffer() const {
218
    DCHECK_NULL(index_);
219
2560
    return buffer_;
220
  }
221
222
  /**
223
   *  Synonym for GetBuffer()
224
   */
225
48
  inline const NativeT* operator * () const {
226
48
    return GetNativeBuffer();
227
  }
228
229
  /**
230
   *  Set position index to given value.
231
   */
232
10963286
  inline void SetValue(const size_t index, NativeT value) {
233
    DCHECK_LT(index, count_);
234
    DCHECK_NULL(index_);
235
10963286
    buffer_[index] = value;
236
10963286
  }
237
238
  /**
239
   *  Get value at position index
240
   */
241
17024307
  inline const NativeT GetValue(const size_t index) const {
242
    DCHECK_NULL(index_);
243
    DCHECK_LT(index, count_);
244
17024307
    return buffer_[index];
245
  }
246
247
  /**
248
   *  Effectively, a synonym for GetValue/SetValue
249
   */
250
21136464
  Reference operator[](size_t index) {
251
    DCHECK_NULL(index_);
252
21136464
    return Reference(this, index);
253
  }
254
255
2578878
  NativeT operator[](size_t index) const {
256
2578878
    return GetValue(index);
257
  }
258
259
821955
  size_t Length() const {
260
821955
    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
10
  void reserve(size_t new_capacity) {
267
    DCHECK_NULL(index_);
268
    DCHECK_GE(new_capacity, count_);
269
    DCHECK_EQ(byte_offset_, 0);
270
20
    const v8::HandleScope handle_scope(isolate_);
271
272
10
    const size_t old_size_in_bytes = sizeof(NativeT) * count_;
273
    const size_t new_size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT),
274
10
                                                              new_capacity);
275
276
    // allocate v8 new ArrayBuffer
277
10
    v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
278
10
        isolate_, new_size_in_bytes);
279
280
    // allocate new native buffer
281
10
    NativeT* new_buffer = static_cast<NativeT*>(ab->GetBackingStore()->Data());
282
    // copy old content
283
10
    memcpy(new_buffer, buffer_, old_size_in_bytes);
284
285
    // allocate v8 TypedArray
286
10
    v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity);
287
288
    // move over old v8 TypedArray
289
30
    js_array_ = std::move(v8::Global<V8T>(isolate_, js_array));
290
291
10
    buffer_ = new_buffer;
292
10
    count_ = new_capacity;
293
10
  }
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_