@auto_js/utility 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CMakeLists.txt +45 -0
  2. package/LICENSE +13 -0
  3. package/_module.cc +14 -0
  4. package/assertions.cc +32 -0
  5. package/container/mutable_priority_queue.cc +35 -0
  6. package/container/sealed_map.cc +121 -0
  7. package/container/segmented_priority_queue.cc +105 -0
  8. package/container/tinker_queue.cc +73 -0
  9. package/functional/bind.cc +151 -0
  10. package/functional/elide.cc +52 -0
  11. package/functional/flat_tuple.cc +90 -0
  12. package/functional/function_constant.cc +58 -0
  13. package/functional/function_ref.cc +68 -0
  14. package/functional/functional.cc +107 -0
  15. package/functional/regular_return.cc +55 -0
  16. package/include/auto_js/chunk_view.h +363 -0
  17. package/include/auto_js/no_unique_address.h +8 -0
  18. package/memory/autorelease_pool.cc +281 -0
  19. package/memory/comparator.cc +53 -0
  20. package/memory/memory.cc +64 -0
  21. package/memory/noinit_allocator.cc +64 -0
  22. package/meta/algorithm.cc +74 -0
  23. package/package.json +11 -0
  24. package/platform/lockable.cc +261 -0
  25. package/platform/shim/darwin.cc +8 -0
  26. package/platform/stop_token.cc +107 -0
  27. package/platform/timer.cc +175 -0
  28. package/platform/timer.h.cc +36 -0
  29. package/type_traits/function_traits.cc +173 -0
  30. package/type_traits/transform.cc +60 -0
  31. package/type_traits/type_of.cc +72 -0
  32. package/type_traits/type_pack.cc +116 -0
  33. package/type_traits/type_traits.cc +5 -0
  34. package/utility/constant_wrapper.cc +57 -0
  35. package/utility/covariant_value.cc +33 -0
  36. package/utility/facade.cc +126 -0
  37. package/utility/hash.cc +37 -0
  38. package/utility/ranges.cc +55 -0
  39. package/utility/string.cc +328 -0
  40. package/utility/utility.cc +150 -0
  41. package/utility/variant.cc +26 -0
@@ -0,0 +1,33 @@
1
+ module;
2
+ #include <utility>
3
+ #include <variant>
4
+ export module util:utility.covariant_value;
5
+ import :utility.facade;
6
+ import :type_traits;
7
+
8
+ namespace util {
9
+
10
+ // Allows passing around virtual classes by value, by enumerating all derived types.
11
+ export template <class Type, class... Types>
12
+ // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
13
+ class covariant_value : public pointer_facade {
14
+ public:
15
+ template <class Derived>
16
+ requires(... || (type<Derived> == type<Types>))
17
+ constexpr explicit covariant_value(Derived value) :
18
+ value_{std::move(value)} {}
19
+
20
+ constexpr auto operator*(this auto& self) -> auto& {
21
+ using reference_type = util::apply_cvref_t<decltype(self), Type>;
22
+ if (self.value_.index() == std::variant_npos) {
23
+ std::unreachable();
24
+ } else {
25
+ return std::visit([](auto& value) -> reference_type { return value; }, self.value_);
26
+ }
27
+ }
28
+
29
+ private:
30
+ std::variant<Types...> value_;
31
+ };
32
+
33
+ } // namespace util
@@ -0,0 +1,126 @@
1
+ module;
2
+ #include <memory>
3
+ #include <type_traits>
4
+ #include <utility>
5
+ export module util:utility.facade;
6
+
7
+ namespace util {
8
+
9
+ // Internal addition facade
10
+ template <class difference_type_>
11
+ class addition_facade {
12
+ public:
13
+ using difference_type = difference_type_;
14
+
15
+ // ++Type
16
+ auto operator++(this auto& self) -> auto& { return self += 1; }
17
+
18
+ // Type++
19
+ auto operator++(this auto& self, int) {
20
+ auto result = self;
21
+ ++self;
22
+ return result;
23
+ }
24
+
25
+ // Type + difference_type
26
+ auto operator+(this const auto& self, difference_type offset) {
27
+ auto result = self;
28
+ return result += offset;
29
+ }
30
+
31
+ // difference_type + Type
32
+ friend auto operator+(difference_type left, const auto& right) {
33
+ return right + left;
34
+ }
35
+ };
36
+
37
+ // Internal subtraction facade
38
+ template <class difference_type_, class wide_size_type = difference_type_>
39
+ class subtraction_facade {
40
+ public:
41
+ auto operator--() = delete;
42
+ auto operator-() = delete;
43
+ auto operator-=(auto) = delete;
44
+ };
45
+
46
+ template <class difference_type_, class wide_size_type>
47
+ requires std::is_signed_v<difference_type_>
48
+ class subtraction_facade<difference_type_, wide_size_type> {
49
+ public:
50
+ using difference_type = difference_type_;
51
+
52
+ // Type -= difference_type
53
+ auto operator-=(this auto& self, difference_type offset) -> auto& { return self += -offset; }
54
+
55
+ // --Type
56
+ auto operator--(this auto& self) -> auto& { return self -= 1; }
57
+
58
+ // Type--
59
+ auto operator--(this auto& self, int) {
60
+ auto result = self;
61
+ --self;
62
+ return result;
63
+ }
64
+
65
+ // Type - difference_type
66
+ auto operator-(this const auto& self, difference_type offset) {
67
+ auto result = self;
68
+ return result -= offset;
69
+ }
70
+
71
+ // Type - Type
72
+ auto operator-(this const auto& self, decltype(self) right) -> difference_type {
73
+ return static_cast<difference_type>(wide_size_type{+self} - wide_size_type{+right});
74
+ }
75
+ };
76
+
77
+ /**
78
+ * Facade class for numeric arithmetic operations. You have to implement
79
+ * `operator+=(difference_type)` and unary `operator+()` (for subtraction) and the rest is handled
80
+ * automatically.
81
+ */
82
+ export template <class difference_type_, class wide_size_type = difference_type_>
83
+ class arithmetic_facade
84
+ : public addition_facade<difference_type_>,
85
+ public subtraction_facade<difference_type_, wide_size_type> {
86
+ public:
87
+ using difference_type = difference_type_;
88
+
89
+ using addition_facade<difference_type>::operator+;
90
+ using addition_facade<difference_type>::operator++;
91
+
92
+ using subtraction_facade<difference_type, wide_size_type>::operator-;
93
+ using subtraction_facade<difference_type, wide_size_type>::operator--;
94
+ using subtraction_facade<difference_type, wide_size_type>::operator-=;
95
+ };
96
+
97
+ /**
98
+ * Implements `operator[]` in the context of `operator+()` and `operator*()`.
99
+ */
100
+ export template <class difference_type>
101
+ class array_facade {
102
+ public:
103
+ auto operator[](this auto&& self, difference_type offset) -> decltype(auto) {
104
+ return *(std::forward<decltype(self)>(self) + offset);
105
+ }
106
+ };
107
+
108
+ /**
109
+ * Implements `operator->()` in the context of `operator*()`.
110
+ */
111
+ export class pointer_facade {
112
+ public:
113
+ constexpr auto operator->(this auto&& self) -> auto* {
114
+ return std::addressof(*std::forward<decltype(self)>(self));
115
+ }
116
+ };
117
+
118
+ /**
119
+ * Implements the requirements of `std::random_access_iterator`.
120
+ */
121
+ export template <class difference_type, class wide_size_type = difference_type>
122
+ class random_access_iterator_facade
123
+ : public arithmetic_facade<difference_type, wide_size_type>,
124
+ public array_facade<difference_type> {};
125
+
126
+ } // namespace util
@@ -0,0 +1,37 @@
1
+ module;
2
+ #include <array>
3
+ #include <bit>
4
+ #include <cstdint>
5
+ #include <source_location>
6
+ #include <string_view>
7
+ export module util:utility.hash;
8
+
9
+ namespace util {
10
+
11
+ // `constexpr` hash for property lookup
12
+ export template <class Char>
13
+ constexpr auto fnv1a_hash(std::basic_string_view<Char> view) -> uint32_t {
14
+ constexpr uint32_t prime = 0x100'0193;
15
+ uint32_t hash = 0x811c'9dc5;
16
+ for (auto character : view) {
17
+ auto bytes = std::bit_cast<std::array<uint8_t, sizeof(Char)>>(character);
18
+ for (auto byte : bytes) {
19
+ hash = hash ^ byte;
20
+ hash *= prime;
21
+ }
22
+ }
23
+ return hash;
24
+ }
25
+
26
+ // constexpr `typeid(Type).hash_code()` replacement
27
+ template <class Type>
28
+ consteval auto make_type_hash() -> uint32_t {
29
+ // "uint32_t make_type_hash() [Type = int]"
30
+ constexpr auto name = std::source_location::current().function_name();
31
+ return fnv1a_hash(std::string_view{name});
32
+ }
33
+
34
+ export template <class Type>
35
+ constexpr auto type_hash = make_type_hash<Type>();
36
+
37
+ } // namespace util
@@ -0,0 +1,55 @@
1
+ module;
2
+ #include <ranges>
3
+ #include <utility>
4
+ export module util:utility.ranges;
5
+
6
+ #if _LIBCPP_VERSION
7
+ // clang 22.1.0 w/ -stdlib=libc++
8
+ // /workspace/packages/utility/utility/ranges.cc:25:10: error: invalid operands to binary expression [...]
9
+ // 25 | range | std::views::transform([...]);
10
+ export {
11
+ using std::ranges::operator|;
12
+ }
13
+ #elif _MSVC_STL_UPDATE
14
+ // Haha, also with MS STL wow
15
+ export {
16
+ using std::ranges::_Pipe::operator|;
17
+ }
18
+ #endif
19
+
20
+ namespace util {
21
+
22
+ // Forward value category of a range to the iterated elements.
23
+ // Don't give it a temporary or it will dangle!
24
+ export constexpr auto forward_range(auto& range) -> auto& {
25
+ return range;
26
+ }
27
+
28
+ export constexpr auto forward_range(auto&& range) {
29
+ using value_type = decltype(*std::begin(std::forward<decltype(range)>(range)));
30
+ if constexpr (std::is_reference_v<value_type>) {
31
+ auto forward =
32
+ range | std::views::transform([](auto& value) -> auto&& {
33
+ return std::forward_like<decltype(range)>(value);
34
+ });
35
+ return forward;
36
+ } else {
37
+ return std::forward<decltype(range)>(range);
38
+ }
39
+ }
40
+
41
+ // Some good thoughts here. It's strange that there isn't an easier way to transform an underlying
42
+ // range.
43
+ // https://brevzin.github.io/c++/2024/05/18/range-customization/
44
+ template <class Type>
45
+ concept meta_range = requires(Type range) { range.into_range(); };
46
+
47
+ export constexpr auto into_range(std::ranges::range auto&& range) -> auto&& {
48
+ return std::forward<decltype(range)>(range);
49
+ }
50
+
51
+ export constexpr auto into_range(meta_range auto&& range) {
52
+ return std::forward<decltype(range)>(range).into_range();
53
+ }
54
+
55
+ } // namespace util
@@ -0,0 +1,328 @@
1
+ module;
2
+ #include <algorithm>
3
+ #include <array>
4
+ #include <bit>
5
+ #include <cstdint>
6
+ #include <limits>
7
+ #include <stdexcept>
8
+ #include <string>
9
+ #include <utility>
10
+ export module util:utility.string;
11
+ import :utility.constant_wrapper;
12
+
13
+ namespace util {
14
+
15
+ // Helper to make `std::string_view` from a constant_wrapper or string literal
16
+ export template <class Char, std::size_t Size>
17
+ struct consteval_string_view : public std::basic_string_view<Char> {
18
+ constexpr explicit consteval_string_view(const Char (&string)[ Size + 1 ]) noexcept : std::basic_string_view<Char>{string, Size} {}
19
+ };
20
+
21
+ template <class Char, std::size_t Size>
22
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
23
+ consteval_string_view(const Char (&string)[ Size ]) -> consteval_string_view<Char, Size - 1>;
24
+
25
+ template <class Char, std::size_t Extent, fixed_value<Char[ Extent ]> Value>
26
+ consteval_string_view(util::constant_wrapper<Value>) -> consteval_string_view<Char, Extent - 1>;
27
+
28
+ // Helper for `codepoint_char_sequence` which stores an array of the most characters it will take to
29
+ // represent a codepoint in a given character type.
30
+ template <class Char, std::size_t Extent>
31
+ class codepoint_char_container {
32
+ public:
33
+ using value_type = Char;
34
+ using container_type = std::array<value_type, Extent>;
35
+ using iterator = container_type::const_iterator;
36
+
37
+ // NOLINTNEXTLINE(modernize-use-equals-default)
38
+ constexpr codepoint_char_container() {};
39
+
40
+ [[nodiscard]] constexpr auto begin() const -> iterator { return chars_.begin(); }
41
+ [[nodiscard]] constexpr auto data() -> container_type& { return chars_; }
42
+ [[nodiscard]] constexpr auto end() const -> iterator { return std::ranges::find(chars_, 0); }
43
+ [[nodiscard]] constexpr auto size() const -> std::size_t { return end() - begin(); }
44
+
45
+ private:
46
+ container_type chars_;
47
+ };
48
+
49
+ // Range of characters encoding a single Unicode codepoint
50
+ template <class Char>
51
+ class codepoint_char_range;
52
+
53
+ // Latin-1, or "one-byte".
54
+ template <>
55
+ class codepoint_char_range<char> : public codepoint_char_container<char, 1> {
56
+ public:
57
+ explicit constexpr codepoint_char_range(char32_t codepoint) {
58
+ if (codepoint > std::numeric_limits<char>::max()) {
59
+ throw std::range_error{"Codepoint out of range for ASCII, 'char'"};
60
+ }
61
+ data()[ 0 ] = static_cast<char>(codepoint);
62
+ }
63
+ };
64
+
65
+ // UTF-8, up to 4 bytes (both char and char8_t)
66
+ template <class Char>
67
+ class codepoint_utf8_range : public codepoint_char_container<Char, 4> {
68
+ public:
69
+ using codepoint_char_container<Char, 4>::data;
70
+ constexpr static char32_t max = std::numeric_limits<char32_t>::max();
71
+
72
+ explicit constexpr codepoint_utf8_range(char32_t codepoint) {
73
+ constexpr auto to = [](unsigned segment) -> Char {
74
+ return std::bit_cast<Char>(static_cast<Char>(segment));
75
+ };
76
+ if (codepoint < 0x80) {
77
+ data()[ 0 ] = to(codepoint);
78
+ data()[ 1 ] = 0;
79
+ } else if (codepoint < 0x800) {
80
+ data()[ 0 ] = to((codepoint >> 6) + 0xc0);
81
+ data()[ 1 ] = to((codepoint & 0x3f) + 0x80);
82
+ data()[ 2 ] = 0;
83
+ } else if (codepoint < 0x1'0000) {
84
+ data()[ 0 ] = to((codepoint >> 12) + 0xe0);
85
+ data()[ 1 ] = to(((codepoint >> 6) & 0x3f) + 0x80);
86
+ data()[ 2 ] = to((codepoint & 0x3f) + 0x80);
87
+ data()[ 3 ] = 0;
88
+ } else if (codepoint < 0x11'0000) {
89
+ data()[ 0 ] = to((codepoint >> 18) + 0xf0);
90
+ data()[ 1 ] = to(((codepoint >> 12) & 0x3f) + 0x80);
91
+ data()[ 2 ] = to(((codepoint >> 6) & 0x3f) + 0x80);
92
+ data()[ 3 ] = to((codepoint & 0x3f) + 0x80);
93
+ } else {
94
+ std::unreachable();
95
+ }
96
+ }
97
+ };
98
+
99
+ template <>
100
+ class codepoint_char_range<char8_t> : public codepoint_utf8_range<char8_t> {
101
+ using codepoint_utf8_range<char8_t>::codepoint_utf8_range;
102
+ };
103
+
104
+ // UTF-16, up to 2 `char16_t`'s
105
+ template <>
106
+ class codepoint_char_range<char16_t> : public codepoint_char_container<char16_t, 2> {
107
+ public:
108
+ constexpr static char32_t max = std::numeric_limits<char32_t>::max();
109
+
110
+ explicit constexpr codepoint_char_range(char32_t codepoint) {
111
+ if (codepoint < 0x1'0000) {
112
+ data()[ 0 ] = static_cast<char16_t>(codepoint);
113
+ data()[ 1 ] = 0;
114
+ } else if (codepoint < 0x11'0000) {
115
+ auto twenty_bits = codepoint - 0x1'0000;
116
+ // /usr/include/c++/v1/__type_traits/promote.h:38:1: error: redefinition of '__promote_t' as different kind of symbol
117
+ // 38 | using __promote_t _LIBCPP_NODEBUG =
118
+ // | ^
119
+ // /usr/include/c++/v1/__type_traits/promote.h:38:1: note: previous definition is here
120
+ // 38 | using __promote_t _LIBCPP_NODEBUG =
121
+ // nb: `twenty_bits >> 10` == `twenty_bits / 0x400`
122
+ data()[ 0 ] = static_cast<char16_t>((twenty_bits >> 10) + 0xd800);
123
+ // nb: `twenty_bits & 0x3ff` == `twenty_bits % 0x400`
124
+ data()[ 1 ] = static_cast<char16_t>((twenty_bits & 0x3ff) + 0xdc00);
125
+ } else {
126
+ std::unreachable();
127
+ }
128
+ }
129
+ };
130
+
131
+ // UTF-32, easy.
132
+ template <>
133
+ class codepoint_char_range<char32_t> : public codepoint_char_container<char32_t, 1> {
134
+ public:
135
+ explicit constexpr codepoint_char_range(char32_t codepoint) { data()[ 0 ] = codepoint; }
136
+ };
137
+
138
+ // Codepoint interpolation forward reader, converting a range of characters into a range of
139
+ // `char32_t`.
140
+ template <class Char>
141
+ class codepoint_forward_view;
142
+
143
+ template <class Char>
144
+ codepoint_forward_view(std::basic_string_view<Char>) -> codepoint_forward_view<Char>;
145
+
146
+ // Latin-1 and UTF-32 can just read the string one character at a time.
147
+ template <class Char>
148
+ class codepoint_char_forward_view {
149
+ private:
150
+ using iterator_type = std::basic_string_view<Char>::const_iterator;
151
+
152
+ public:
153
+ explicit constexpr codepoint_char_forward_view(std::basic_string_view<Char> view) :
154
+ pos_{view.begin()},
155
+ end_{view.end()} {}
156
+
157
+ [[nodiscard]] constexpr auto eof() const -> bool { return pos_ == end_; }
158
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
159
+ constexpr auto read() -> char32_t { return static_cast<char32_t>(*pos_++); }
160
+
161
+ private:
162
+ iterator_type pos_;
163
+ iterator_type end_;
164
+ };
165
+
166
+ template <>
167
+ class codepoint_forward_view<char> : public codepoint_char_forward_view<char> {
168
+ using codepoint_char_forward_view<char>::codepoint_char_forward_view;
169
+ };
170
+
171
+ template <>
172
+ class codepoint_forward_view<char32_t> : public codepoint_char_forward_view<char32_t> {
173
+ using codepoint_char_forward_view<char32_t>::codepoint_char_forward_view;
174
+ };
175
+
176
+ // UTF-8 forward iterator reader
177
+ template <class Char>
178
+ class codepoint_utf8_forward_view {
179
+ private:
180
+ using iterator_type = std::basic_string_view<Char>::const_iterator;
181
+
182
+ public:
183
+ explicit constexpr codepoint_utf8_forward_view(std::basic_string_view<Char> view) :
184
+ pos_{view.begin()},
185
+ end_{view.end()} {}
186
+
187
+ [[nodiscard]] constexpr auto eof() const -> bool { return pos_ == end_; }
188
+ constexpr auto read() -> char32_t {
189
+ // Check the expected length of the byte sequence from the leading byte's bit pattern
190
+ auto sequence_length = [ & ]() -> unsigned {
191
+ auto byte0 = std::bit_cast<uint8_t>(*pos_);
192
+ if (byte0 < 0x80) {
193
+ return 1;
194
+ } else if (byte0 < 0xe0) {
195
+ return 2;
196
+ } else if (byte0 < 0xf0) {
197
+ return 3;
198
+ } else if (byte0 < 0xf8) {
199
+ return 4;
200
+ } else {
201
+ std::unreachable();
202
+ }
203
+ }();
204
+
205
+ // Replacement character for mojibake
206
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
207
+ if (pos_ + sequence_length > end_) {
208
+ pos_ = end_;
209
+ return 0xfffd;
210
+ }
211
+
212
+ // Dump bytes
213
+ std::array<Char, 4> characters{};
214
+ std::ranges::copy_n(pos_, sequence_length, characters.begin());
215
+ pos_ += sequence_length;
216
+ auto [ byte0, byte1, byte2, byte3 ] = std::bit_cast<std::array<uint8_t, 4>>(characters);
217
+
218
+ // Convert byte sequence to codepoint
219
+ switch (sequence_length) {
220
+ case 1: return byte0;
221
+ case 2: return ((byte0 & 0x1f) << 6) | (byte1 & 0x3f);
222
+ case 3: return ((byte0 & 0x0f) << 12) | ((byte1 & 0x3f) << 6) | (byte2 & 0x3f);
223
+ default: return ((byte0 & 0x07) << 18) | ((byte1 & 0x3f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
224
+ }
225
+ }
226
+
227
+ private:
228
+ iterator_type pos_;
229
+ iterator_type end_;
230
+ };
231
+
232
+ template <>
233
+ class codepoint_forward_view<char8_t> : public codepoint_utf8_forward_view<char8_t> {
234
+ using codepoint_utf8_forward_view<char8_t>::codepoint_utf8_forward_view;
235
+ };
236
+
237
+ // UTF-16 forward reader
238
+ template <>
239
+ class codepoint_forward_view<char16_t> {
240
+ private:
241
+ using iterator_type = std::basic_string_view<char16_t>::const_iterator;
242
+
243
+ public:
244
+ explicit constexpr codepoint_forward_view(std::basic_string_view<char16_t> view) :
245
+ pos_{view.begin()},
246
+ end_{view.end()} {}
247
+
248
+ [[nodiscard]] constexpr auto eof() const -> bool { return pos_ == end_; }
249
+
250
+ constexpr auto read() -> char32_t {
251
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
252
+ auto char0 = *pos_++;
253
+ if (char0 >= 0xd800 && char0 < 0xdc00) {
254
+ if (eof()) {
255
+ // nb: Unpaired surrogate
256
+ return char0;
257
+ }
258
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
259
+ auto char1 = *pos_++;
260
+ if (char1 >= 0xdc00 && char1 < 0xe000) {
261
+ return ((char0 - 0xd800) * 0x400) + (char1 - 0xdc00) + 0x1'0000;
262
+ } else {
263
+ // nb: Unpaired surrogate
264
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
265
+ --pos_;
266
+ return char0;
267
+ }
268
+ } else {
269
+ return char0;
270
+ }
271
+ }
272
+
273
+ private:
274
+ iterator_type pos_;
275
+ iterator_type end_;
276
+ };
277
+
278
+ // Interpolate the given character range to a `std::basic_string<Char>`. If the requested character
279
+ // type is too small to represent a codepoint, a `std::range_error` is thrown.
280
+ export template <class To, class From>
281
+ constexpr auto interpolate_string(std::basic_string_view<From> from) -> std::basic_string<To> {
282
+ auto reader = codepoint_forward_view{from};
283
+ std::basic_string<To> result;
284
+
285
+ // Calculate destination length. In some cases you can calculate it in constant time but you're
286
+ // not allowed to throw from `resize_and_overwrite` anyway. So you'd still have to check the range
287
+ // before.
288
+ // TODO: It may be worth looking at the generated code for common cases and seeing if it needs
289
+ // optimization.
290
+ auto size = [ & ]() -> std::size_t {
291
+ auto size_reader = reader;
292
+ std::size_t size = 0;
293
+ while (!size_reader.eof()) {
294
+ size += codepoint_char_range<To>{size_reader.read()}.size();
295
+ }
296
+ return size;
297
+ }();
298
+
299
+ // Write string
300
+ result.resize_and_overwrite(size, [ & ](To* result, size_t /*length*/) -> size_t {
301
+ auto first = result;
302
+ while (!reader.eof()) {
303
+ auto chunk = codepoint_char_range<To>{reader.read()};
304
+ std::ranges::copy(chunk, result);
305
+ result += chunk.size();
306
+ }
307
+ return result - first;
308
+ });
309
+ return result;
310
+ };
311
+
312
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
313
+ export template <class To, class From, std::size_t Extent, fixed_value<From[ Extent ]> Value>
314
+ constexpr auto interpolate_string(util::constant_wrapper<Value> /*cw*/) {
315
+ constexpr auto make = []() { return interpolate_string<To>(std::basic_string_view{Value.value, Extent - 1}); };
316
+ constexpr auto chars = [ = ]() {
317
+ std::array<To, make().size()> result{};
318
+ std::ranges::copy(make(), result.data());
319
+ return result;
320
+ }();
321
+ return [ = ]<std::size_t... Indices>(std::index_sequence<Indices...>) {
322
+ // NOLINTNEXTLINE(modernize-avoid-c-arrays)
323
+ constexpr To string[ chars.size() + 1 ] = {chars[ Indices ]..., 0};
324
+ return util::cw<string>;
325
+ }(std::make_index_sequence<chars.size()>());
326
+ };
327
+
328
+ } // namespace util