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-04-27 04:12:29 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 <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
147829
class AliasedBufferBase {
37
 public:
38
70525
  AliasedBufferBase(v8::Isolate* isolate,
39
                    const size_t count,
40
                    const AliasedBufferIndex* index = nullptr)
41
141050
      : isolate_(isolate), count_(count), byte_offset_(0), index_(index) {
42




70525
    CHECK_GT(count, 0);
43




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


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

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


4916
    CHECK_LE(MultiplyWithOverflowCheck(sizeof(NativeT), count),
91
             ab->ByteLength() - byte_offset);
92
93
2458
    buffer_ = reinterpret_cast<NativeT*>(
94
2458
        const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset));
95
96
2458
    v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count);
97
4916
    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
47540
  inline void Deserialize(v8::Local<v8::Context> context) {
116
    DCHECK_NOT_NULL(index_);
117
    v8::Local<V8T> arr =
118
190160
        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
95080
        static_cast<uint8_t*>(arr->Buffer()->GetBackingStore()->Data());
125
47540
    buffer_ = reinterpret_cast<NativeT*>(raw + byte_offset_);
126
47540
    js_array_.Reset(isolate_, arr);
127
47540
    index_ = nullptr;
128
47540
  }
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
33715251
    Reference(AliasedBufferBase<NativeT, V8T>* aliased_buffer, size_t index)
152
33715251
        : 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
11554160
    inline Reference& operator=(const NativeT& val) {
160
11554160
      aliased_buffer_->SetValue(index_, val);
161
11554155
      return *this;
162
    }
163
164
5194481
    inline Reference& operator=(const Reference& val) {
165
5194481
      return *this = static_cast<NativeT>(val);
166
    }
167
168
20104615
    operator NativeT() const {
169
20104615
      return aliased_buffer_->GetValue(index_);
170
    }
171
172
1553586
    inline Reference& operator+=(const NativeT& val) {
173
1553586
      const NativeT current = aliased_buffer_->GetValue(index_);
174
1553586
      aliased_buffer_->SetValue(index_, current + val);
175
1553586
      return *this;
176
    }
177
178
1
    inline Reference& operator+=(const Reference& val) {
179
1
      return this->operator+=(static_cast<NativeT>(val));
180
    }
181
182
509141
    inline Reference& operator-=(const NativeT& val) {
183
509141
      const NativeT current = aliased_buffer_->GetValue(index_);
184
509142
      aliased_buffer_->SetValue(index_, current - val);
185
509143
      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
214480
  v8::Local<V8T> GetJSArray() const {
197
    DCHECK_NULL(index_);
198
428959
    return js_array_.Get(isolate_);
199
  }
200
201
40
  void Release() {
202
    DCHECK_NULL(index_);
203
40
    js_array_.Reset();
204
40
  }
205
206
  /**
207
  *  Get the underlying v8::ArrayBuffer underlying the TypedArray and
208
  *  overlaying the native buffer
209
  */
210
2457
  v8::Local<v8::ArrayBuffer> GetArrayBuffer() const {
211
4915
    return GetJSArray()->Buffer();
212
  }
213
214
  /**
215
   *  Get the underlying native buffer. Note that all reads/writes should occur
216
   *  through the GetValue/SetValue/operator[] methods
217
   */
218
2554
  inline const NativeT* GetNativeBuffer() const {
219
    DCHECK_NULL(index_);
220
2554
    return buffer_;
221
  }
222
223
  /**
224
   *  Synonym for GetBuffer()
225
   */
226
48
  inline const NativeT* operator * () const {
227
48
    return GetNativeBuffer();
228
  }
229
230
  /**
231
   *  Set position index to given value.
232
   */
233
16654013
  inline void SetValue(const size_t index, NativeT value) {
234
    DCHECK_LT(index, count_);
235
    DCHECK_NULL(index_);
236
16654013
    buffer_[index] = value;
237
16654013
  }
238
239
  /**
240
   *  Get value at position index
241
   */
242
26631046
  inline const NativeT GetValue(const size_t index) const {
243
    DCHECK_NULL(index_);
244
    DCHECK_LT(index, count_);
245
26631046
    return buffer_[index];
246
  }
247
248
  /**
249
   *  Effectively, a synonym for GetValue/SetValue
250
   */
251
33715088
  Reference operator[](size_t index) {
252
    DCHECK_NULL(index_);
253
33715088
    return Reference(this, index);
254
  }
255
256
4464435
  NativeT operator[](size_t index) const {
257
4464435
    return GetValue(index);
258
  }
259
260
1302818
  size_t Length() const {
261
1302818
    return count_;
262
  }
263
264
  // Should only be used to extend the array.
265
  // Should only be used on an owning array, not one created as a sub array of
266
  // an owning `AliasedBufferBase`.
267
10
  void reserve(size_t new_capacity) {
268
    DCHECK_NULL(index_);
269
    DCHECK_GE(new_capacity, count_);
270
    DCHECK_EQ(byte_offset_, 0);
271
20
    const v8::HandleScope handle_scope(isolate_);
272
273
10
    const size_t old_size_in_bytes = sizeof(NativeT) * count_;
274
    const size_t new_size_in_bytes = MultiplyWithOverflowCheck(sizeof(NativeT),
275
10
                                                              new_capacity);
276
277
    // allocate v8 new ArrayBuffer
278
10
    v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
279
10
        isolate_, new_size_in_bytes);
280
281
    // allocate new native buffer
282
10
    NativeT* new_buffer = static_cast<NativeT*>(ab->GetBackingStore()->Data());
283
    // copy old content
284
10
    memcpy(new_buffer, buffer_, old_size_in_bytes);
285
286
    // allocate v8 TypedArray
287
10
    v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity);
288
289
    // move over old v8 TypedArray
290
30
    js_array_ = std::move(v8::Global<V8T>(isolate_, js_array));
291
292
10
    buffer_ = new_buffer;
293
10
    count_ = new_capacity;
294
10
  }
295
296
 private:
297
  v8::Isolate* isolate_ = nullptr;
298
  size_t count_ = 0;
299
  size_t byte_offset_ = 0;
300
  NativeT* buffer_ = nullptr;
301
  v8::Global<V8T> js_array_;
302
303
  // Deserialize data
304
  const AliasedBufferIndex* index_ = nullptr;
305
};
306
307
typedef AliasedBufferBase<int32_t, v8::Int32Array> AliasedInt32Array;
308
typedef AliasedBufferBase<uint8_t, v8::Uint8Array> AliasedUint8Array;
309
typedef AliasedBufferBase<uint32_t, v8::Uint32Array> AliasedUint32Array;
310
typedef AliasedBufferBase<double, v8::Float64Array> AliasedFloat64Array;
311
typedef AliasedBufferBase<uint64_t, v8::BigUint64Array> AliasedBigUint64Array;
312
}  // namespace node
313
314
#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
315
316
#endif  // SRC_ALIASED_BUFFER_H_