Coverage Report

Created: 2025-10-31 09:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/deps/v8/include/v8-memory-span.h
Line
Count
Source
1
// Copyright 2021 the V8 project authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#ifndef INCLUDE_V8_MEMORY_SPAN_H_
6
#define INCLUDE_V8_MEMORY_SPAN_H_
7
8
#include <stddef.h>
9
10
#include <array>
11
#include <cstddef>
12
#include <iterator>
13
#include <type_traits>
14
15
#include "v8config.h"  // NOLINT(build/include_directory)
16
17
// TODO(pkasting): Use <compare>/spaceship unconditionally after dropping
18
// support for old libstdc++ versions.
19
#if __has_include(<version>)
20
#include <version>
21
#endif
22
#if defined(__cpp_lib_three_way_comparison) && \
23
    __cpp_lib_three_way_comparison >= 201711L
24
#define V8_HAVE_SPACESHIP_OPERATOR 1
25
#else
26
#define V8_HAVE_SPACESHIP_OPERATOR 0
27
#endif
28
29
// TODO(pkasting): Make this block unconditional after dropping support for old
30
// libstdc++ versions.
31
#if __has_include(<ranges>)
32
#include <ranges>
33
34
namespace v8 {
35
36
template <typename T>
37
class V8_EXPORT MemorySpan;
38
39
}  // namespace v8
40
41
// Mark `MemorySpan` as satisfying the `view` and `borrowed_range` concepts.
42
// This should be done before the definition of `MemorySpan`, so that any
43
// inlined calls to range functionality use the correct specializations.
44
template <typename T>
45
inline constexpr bool std::ranges::enable_view<v8::MemorySpan<T>> = true;
46
template <typename T>
47
inline constexpr bool std::ranges::enable_borrowed_range<v8::MemorySpan<T>> =
48
    true;
49
#endif
50
51
namespace v8 {
52
53
/**
54
 * Points to an unowned contiguous buffer holding a known number of elements.
55
 *
56
 * This is similar to std::span (under consideration for C++20), but does not
57
 * require advanced C++ support. In the (far) future, this may be replaced with
58
 * or aliased to std::span.
59
 *
60
 * To facilitate future migration, this class exposes a subset of the interface
61
 * implemented by std::span.
62
 */
63
template <typename T>
64
class V8_EXPORT MemorySpan {
65
 private:
66
  /** Some C++ machinery, brought from the future. */
67
  template <typename From, typename To>
68
  using is_array_convertible = std::is_convertible<From (*)[], To (*)[]>;
69
  template <typename From, typename To>
70
  static constexpr bool is_array_convertible_v =
71
      is_array_convertible<From, To>::value;
72
73
  template <typename It>
74
  using iter_reference_t = decltype(*std::declval<It&>());
75
76
  template <typename It, typename = void>
77
  struct is_compatible_iterator : std::false_type {};
78
  template <typename It>
79
  struct is_compatible_iterator<
80
      It,
81
      std::void_t<
82
          std::is_base_of<std::random_access_iterator_tag,
83
                          typename std::iterator_traits<It>::iterator_category>,
84
          is_array_convertible<std::remove_reference_t<iter_reference_t<It>>,
85
                               T>>> : std::true_type {};
86
  template <typename It>
87
  static constexpr bool is_compatible_iterator_v =
88
      is_compatible_iterator<It>::value;
89
90
  // SFINAE-compatible wrapper for `std::to_address()`.
91
  // Adapted from "base/types/to_address.h" in chromium.
92
  template <typename U>
93
    requires(!std::is_function_v<U>)
94
35
  [[nodiscard]] static constexpr U* to_address(U* p) noexcept {
95
35
    return p;
96
35
  }
97
98
  template <typename It>
99
    requires(
100
        requires(const It& it) { std::pointer_traits<It>::to_address(it); } ||
101
        requires(const It& it) { it.operator->(); })
102
0
  [[nodiscard]] static constexpr auto to_address(const It& it) noexcept {
103
0
    return std::to_address(it);
104
0
  }
105
106
 public:
107
  /** The default constructor creates an empty span. */
108
  constexpr MemorySpan() = default;
109
110
  /** Constructor from nullptr and count, for backwards compatibility.
111
   * This is not compatible with C++20 std::span.
112
   */
113
  constexpr MemorySpan(std::nullptr_t, size_t) {}
114
115
  /** Constructor from "iterator" and count. */
116
  template <typename Iterator,
117
            std::enable_if_t<is_compatible_iterator_v<Iterator>, bool> = true>
118
  constexpr MemorySpan(Iterator first,
119
                       size_t count)  // NOLINT(runtime/explicit)
120
35
      : data_(to_address(first)), size_(count) {}
Unexecuted instantiation: _ZN2v810MemorySpanIKNS_5LocalINS_6StringEEEEC2INS_8internal15WrappedIteratorINSt3__111__wrap_iterIPNS7_14LocalUncheckedIS2_EEEES3_EETnNS9_9enable_ifIX24is_compatible_iterator_vIT_EEbE4typeELb1EEESH_m
_ZN2v810MemorySpanIKNS_9CFunctionEEC2IPS1_TnNSt3__19enable_ifIX24is_compatible_iterator_vIT_EEbE4typeELb1EEES8_m
Line
Count
Source
120
35
      : data_(to_address(first)), size_(count) {}
121
122
  /** Constructor from two "iterators". */
123
  template <typename Iterator,
124
            std::enable_if_t<is_compatible_iterator_v<Iterator> &&
125
                                 !std::is_convertible_v<Iterator, size_t>,
126
                             bool> = true>
127
  constexpr MemorySpan(Iterator first,
128
                       Iterator last)  // NOLINT(runtime/explicit)
129
      : data_(to_address(first)), size_(last - first) {}
130
131
  /** Implicit conversion from C-style array. */
132
  template <size_t N>
133
  constexpr MemorySpan(T (&a)[N]) noexcept  // NOLINT(runtime/explicit)
134
70
      : data_(a), size_(N) {}
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<6ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [6ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<6ul>(v8::MaybeLocal<v8::Value> (&) [6ul])
v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<5ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [5ul])
Line
Count
Source
134
35
      : data_(a), size_(N) {}
v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<5ul>(v8::MaybeLocal<v8::Value> (&) [5ul])
Line
Count
Source
134
35
      : data_(a), size_(N) {}
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<3ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [3ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<2ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [2ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<2ul>(v8::MaybeLocal<v8::Value> (&) [2ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<3ul>(v8::MaybeLocal<v8::Value> (&) [3ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<9ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [9ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<9ul>(v8::MaybeLocal<v8::Value> (&) [9ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<4ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [4ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<4ul>(v8::MaybeLocal<v8::Value> (&) [4ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<14ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [14ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<14ul>(v8::MaybeLocal<v8::Value> (&) [14ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<19ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [19ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<19ul>(v8::MaybeLocal<v8::Value> (&) [19ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<7ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [7ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<7ul>(v8::MaybeLocal<v8::Value> (&) [7ul])
Unexecuted instantiation: v8::MemorySpan<std::__1::basic_string_view<char, std::__1::char_traits<char> > const>::MemorySpan<8ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> > const (&) [8ul])
Unexecuted instantiation: v8::MemorySpan<v8::MaybeLocal<v8::Value> >::MemorySpan<8ul>(v8::MaybeLocal<v8::Value> (&) [8ul])
135
136
  /** Implicit conversion from std::array. */
137
  template <typename U, size_t N,
138
            std::enable_if_t<is_array_convertible_v<U, T>, bool> = true>
139
  constexpr MemorySpan(
140
      std::array<U, N>& a) noexcept  // NOLINT(runtime/explicit)
141
      : data_(a.data()), size_{N} {}
142
143
  /** Implicit conversion from const std::array. */
144
  template <typename U, size_t N,
145
            std::enable_if_t<is_array_convertible_v<const U, T>, bool> = true>
146
  constexpr MemorySpan(
147
      const std::array<U, N>& a) noexcept  // NOLINT(runtime/explicit)
148
      : data_(a.data()), size_{N} {}
149
150
  /** Returns a pointer to the beginning of the buffer. */
151
  [[nodiscard]] constexpr T* data() const { return data_; }
152
  /** Returns the number of elements that the buffer holds. */
153
  [[nodiscard]] constexpr size_t size() const { return size_; }
154
155
  [[nodiscard]] constexpr T& operator[](size_t i) const { return data_[i]; }
156
157
  /** Returns true if the buffer is empty. */
158
  [[nodiscard]] constexpr bool empty() const { return size() == 0; }
159
160
  class Iterator {
161
   public:
162
    using difference_type = std::ptrdiff_t;
163
    using value_type = T;
164
    using pointer = value_type*;
165
    using reference = value_type&;
166
    using iterator_category = std::random_access_iterator_tag;
167
    // There seems to be no feature-test macro covering this, so use the
168
    // presence of `<ranges>` as a crude proxy, since it was added to the
169
    // standard as part of the Ranges papers.
170
    // TODO(pkasting): Add this unconditionally after dropping support for old
171
    // libstdc++ versions.
172
#if __has_include(<ranges>)
173
    using iterator_concept = std::contiguous_iterator_tag;
174
#endif
175
176
    // Required to satisfy `std::semiregular<>`.
177
    constexpr Iterator() = default;
178
179
    [[nodiscard]] friend constexpr bool operator==(const Iterator& a,
180
210
                                                   const Iterator& b) {
181
      // TODO(pkasting): Replace this body with `= default` after dropping
182
      // support for old gcc versions.
183
210
      return a.ptr_ == b.ptr_;
184
210
    }
185
#if V8_HAVE_SPACESHIP_OPERATOR
186
    [[nodiscard]] friend constexpr auto operator<=>(const Iterator&,
187
                                                    const Iterator&) = default;
188
#else
189
    // Assume that if spaceship isn't present, operator rewriting might not be
190
    // either.
191
    [[nodiscard]] friend constexpr bool operator!=(const Iterator& a,
192
                                                   const Iterator& b) {
193
      return a.ptr_ != b.ptr_;
194
    }
195
196
    [[nodiscard]] friend constexpr bool operator<(const Iterator& a,
197
                                                  const Iterator& b) {
198
      return a.ptr_ < b.ptr_;
199
    }
200
    [[nodiscard]] friend constexpr bool operator<=(const Iterator& a,
201
                                                   const Iterator& b) {
202
      return a.ptr_ <= b.ptr_;
203
    }
204
    [[nodiscard]] friend constexpr bool operator>(const Iterator& a,
205
                                                  const Iterator& b) {
206
      return a.ptr_ > b.ptr_;
207
    }
208
    [[nodiscard]] friend constexpr bool operator>=(const Iterator& a,
209
                                                   const Iterator& b) {
210
      return a.ptr_ >= b.ptr_;
211
    }
212
#endif
213
214
175
    constexpr Iterator& operator++() {
215
175
      ++ptr_;
216
175
      return *this;
217
175
    }
218
219
    constexpr Iterator operator++(int) {
220
      Iterator temp = *this;
221
      ++*this;
222
      return temp;
223
    }
224
225
    constexpr Iterator& operator--() {
226
      --ptr_;
227
      return *this;
228
    }
229
230
    constexpr Iterator operator--(int) {
231
      Iterator temp = *this;
232
      --*this;
233
      return temp;
234
    }
235
236
    constexpr Iterator& operator+=(difference_type rhs) {
237
      ptr_ += rhs;
238
      return *this;
239
    }
240
241
    [[nodiscard]] friend constexpr Iterator operator+(Iterator lhs,
242
                                                      difference_type rhs) {
243
      lhs += rhs;
244
      return lhs;
245
    }
246
247
    [[nodiscard]] friend constexpr Iterator operator+(difference_type lhs,
248
                                                      const Iterator& rhs) {
249
      return rhs + lhs;
250
    }
251
252
    constexpr Iterator& operator-=(difference_type rhs) {
253
      ptr_ -= rhs;
254
      return *this;
255
    }
256
257
    [[nodiscard]] friend constexpr Iterator operator-(Iterator lhs,
258
                                                      difference_type rhs) {
259
      lhs -= rhs;
260
      return lhs;
261
    }
262
263
    [[nodiscard]] friend constexpr difference_type operator-(
264
        const Iterator& lhs, const Iterator& rhs) {
265
      return lhs.ptr_ - rhs.ptr_;
266
    }
267
268
175
    [[nodiscard]] constexpr reference operator*() const { return *ptr_; }
269
    [[nodiscard]] constexpr pointer operator->() const { return ptr_; }
270
    [[nodiscard]] constexpr reference operator[](size_t offset) const {
271
      return ptr_[offset];
272
    }
273
274
   private:
275
    friend class MemorySpan<T>;
276
277
70
    constexpr explicit Iterator(T* ptr) : ptr_(ptr) {}
278
279
    T* ptr_ = nullptr;
280
  };
281
282
35
  [[nodiscard]] Iterator begin() const { return Iterator(data_); }
283
35
  [[nodiscard]] Iterator end() const { return Iterator(data_ + size_); }
284
285
 private:
286
  T* data_ = nullptr;
287
  size_t size_ = 0;
288
};
289
290
/**
291
 * Helper function template to create an array of fixed length, initialized by
292
 * the provided initializer list, without explicitly specifying the array size,
293
 * e.g.
294
 *
295
 *   auto arr = v8::to_array<Local<String>>({v8_str("one"), v8_str("two")});
296
 *
297
 * In the future, this may be replaced with or aliased to std::to_array (under
298
 * consideration for C++20).
299
 */
300
301
namespace detail {
302
template <class T, std::size_t N, std::size_t... I>
303
[[nodiscard]] constexpr std::array<std::remove_cv_t<T>, N> to_array_lvalue_impl(
304
    T (&a)[N], std::index_sequence<I...>) {
305
  return {{a[I]...}};
306
}
307
308
template <class T, std::size_t N, std::size_t... I>
309
[[nodiscard]] constexpr std::array<std::remove_cv_t<T>, N> to_array_rvalue_impl(
310
    T (&&a)[N], std::index_sequence<I...>) {
311
  return {{std::move(a[I])...}};
312
}
313
}  // namespace detail
314
315
template <class T, std::size_t N>
316
[[nodiscard]] constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) {
317
  return detail::to_array_lvalue_impl(a, std::make_index_sequence<N>{});
318
}
319
320
template <class T, std::size_t N>
321
[[nodiscard]] constexpr std::array<std::remove_cv_t<T>, N> to_array(
322
    T (&&a)[N]) {
323
  return detail::to_array_rvalue_impl(std::move(a),
324
                                      std::make_index_sequence<N>{});
325
}
326
327
}  // namespace v8
328
#endif  // INCLUDE_V8_MEMORY_SPAN_H_