Coverage Report

Created: 2025-07-04 09:33

/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_