@lucent-lang/lucent 0.0.4 → 0.1.0

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 (152) hide show
  1. package/README.md +97 -0
  2. package/app.plugin.js +46 -10
  3. package/dist/{bench-CR3E7e2e.js → bench-B555m0wU.js} +10 -6
  4. package/dist/{build-CAIrRSYA.js → build-UZ5Zm5m6.js} +1 -1
  5. package/dist/{build-check-QozuNJm5.js → build-check-Dmf8_yiw.js} +28 -13
  6. package/dist/{check-1SCmYlqA.js → check-D-H9WLG4.js} +1 -1
  7. package/dist/cli.js +62 -13
  8. package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
  9. package/dist/compiler.js +4 -4
  10. package/dist/{dashboard-B-NzjjaN.js → dashboard-C8iF5-6T.js} +2 -2
  11. package/dist/{dev-BYWMP7oC.js → dev-B3lSx5CF.js} +62 -23
  12. package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
  13. package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
  14. package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
  15. package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
  16. package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
  17. package/dist/pipeline-BBY0df_2.js +700 -0
  18. package/dist/project-BLhWwnns.js +342 -0
  19. package/dist/sdk-coverage-DFGiDOzR.js +113 -0
  20. package/dist/sdk-diff-DpPYWzrl.js +109 -0
  21. package/dist/sdk-lock-QOxEFD1o.js +38 -0
  22. package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-DX6ztlPN.js} +2 -2
  23. package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-BShBL1LL.js} +2 -2
  24. package/dist/{sdk-show-CkrdlqlP.js → sdk-show-B3bI-0M7.js} +5 -4
  25. package/dist/sdk-usage-ljX5bTuZ.js +95 -0
  26. package/dist/src-DlICbK91.js +39682 -0
  27. package/dist/trace-Bi4hkmPx.js +139 -0
  28. package/lib/core.cjs +368 -1
  29. package/lib/sdk/android.d.ts +54 -1
  30. package/lib/sdk/compose.d.ts +94 -0
  31. package/lib/sdk/compose.schemas.json.gz +0 -0
  32. package/lib/sdk/core.d.ts +201 -0
  33. package/lib/sdk/ios.d.ts +75 -2
  34. package/lib/sdk/ui.d.ts +129 -0
  35. package/metro/transformer.cjs +11 -2
  36. package/package.json +2 -1
  37. package/runtime/cpp/lucent/abort.cpp +59 -11
  38. package/runtime/cpp/lucent/abort.h +22 -6
  39. package/runtime/cpp/lucent/array.h +12 -2
  40. package/runtime/cpp/lucent/async.h +101 -21
  41. package/runtime/cpp/lucent/bigint.cpp +898 -0
  42. package/runtime/cpp/lucent/bigint.h +302 -0
  43. package/runtime/cpp/lucent/buffer.cpp +228 -0
  44. package/runtime/cpp/lucent/buffer.h +328 -0
  45. package/runtime/cpp/lucent/builtins.cpp +4 -1
  46. package/runtime/cpp/lucent/bytes.h +21 -2
  47. package/runtime/cpp/lucent/callback.cpp +29 -0
  48. package/runtime/cpp/lucent/callback.h +230 -0
  49. package/runtime/cpp/lucent/compute.cpp +370 -0
  50. package/runtime/cpp/lucent/compute.h +455 -0
  51. package/runtime/cpp/lucent/conversions.h +111 -0
  52. package/runtime/cpp/lucent/core.h +16 -5
  53. package/runtime/cpp/lucent/equality.h +107 -28
  54. package/runtime/cpp/lucent/execution.cpp +416 -0
  55. package/runtime/cpp/lucent/execution.h +270 -0
  56. package/runtime/cpp/lucent/extension.cpp +97 -0
  57. package/runtime/cpp/lucent/extension.h +210 -0
  58. package/runtime/cpp/lucent/helpers.h +12 -5
  59. package/runtime/cpp/lucent/items.h +52 -0
  60. package/runtime/cpp/lucent/jserror.cpp +9 -0
  61. package/runtime/cpp/lucent/jserror.h +11 -0
  62. package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
  63. package/runtime/cpp/lucent/jsi/convert.h +128 -59
  64. package/runtime/cpp/lucent/jsi/host.cpp +213 -35
  65. package/runtime/cpp/lucent/jsi/host.h +116 -16
  66. package/runtime/cpp/lucent/json.h +19 -1
  67. package/runtime/cpp/lucent/lifecycle.cpp +260 -0
  68. package/runtime/cpp/lucent/lifecycle.h +124 -0
  69. package/runtime/cpp/lucent/lucent.h +7 -0
  70. package/runtime/cpp/lucent/map.h +41 -7
  71. package/runtime/cpp/lucent/native.cpp +35 -47
  72. package/runtime/cpp/lucent/native.h +116 -26
  73. package/runtime/cpp/lucent/number.cpp +38 -38
  74. package/runtime/cpp/lucent/number.h +33 -0
  75. package/runtime/cpp/lucent/operation.h +65 -0
  76. package/runtime/cpp/lucent/ops.h +14 -6
  77. package/runtime/cpp/lucent/platform/android.cpp +223 -10
  78. package/runtime/cpp/lucent/platform/android.h +227 -4
  79. package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
  80. package/runtime/cpp/lucent/platform/android_requests.h +140 -0
  81. package/runtime/cpp/lucent/platform/compose.h +256 -0
  82. package/runtime/cpp/lucent/platform/ios.h +325 -25
  83. package/runtime/cpp/lucent/platform/ios.mm +1 -14
  84. package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
  85. package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
  86. package/runtime/cpp/lucent/platform/swiftui.h +190 -0
  87. package/runtime/cpp/lucent/presentation.cpp +11 -0
  88. package/runtime/cpp/lucent/presentation.h +109 -0
  89. package/runtime/cpp/lucent/reactive.cpp +406 -0
  90. package/runtime/cpp/lucent/reactive.h +566 -0
  91. package/runtime/cpp/lucent/report.cpp +40 -0
  92. package/runtime/cpp/lucent/report.h +16 -0
  93. package/runtime/cpp/lucent/resource.cpp +83 -0
  94. package/runtime/cpp/lucent/resource.h +75 -0
  95. package/runtime/cpp/lucent/scheduler.cpp +38 -124
  96. package/runtime/cpp/lucent/scheduler.h +102 -83
  97. package/runtime/cpp/lucent/scope.cpp +308 -0
  98. package/runtime/cpp/lucent/scope.h +452 -0
  99. package/runtime/cpp/lucent/sizing.h +263 -0
  100. package/runtime/cpp/lucent/slots.h +231 -0
  101. package/runtime/cpp/lucent/trace.cpp +539 -0
  102. package/runtime/cpp/lucent/trace.h +251 -0
  103. package/runtime/cpp/lucent/transport.cpp +9 -0
  104. package/runtime/cpp/lucent/transport.h +341 -0
  105. package/runtime/cpp/lucent/view.cpp +123 -0
  106. package/runtime/cpp/lucent/view.h +157 -0
  107. package/runtime/cpp/rn/LucentComponentView.h +121 -0
  108. package/runtime/cpp/rn/LucentComponentView.mm +479 -0
  109. package/runtime/cpp/rn/LucentModule.cpp +37 -7
  110. package/runtime/cpp/rn/LucentModule.h +3 -0
  111. package/runtime/cpp/rn/LucentViewChildren.h +80 -0
  112. package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
  113. package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
  114. package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
  115. package/runtime/cpp/rn/LucentViewRequests.h +104 -0
  116. package/runtime/cpp/rn/LucentViewSizing.h +345 -0
  117. package/runtime/cpp/rn/LucentViewSlots.h +242 -0
  118. package/runtime/cpp/rn/LucentViewValues.h +125 -0
  119. package/runtime/cpp/rn/LucentViews.h +440 -0
  120. package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
  121. package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
  122. package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
  123. package/runtime/cpp/third_party/dragonbox/README.md +16 -0
  124. package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
  125. package/runtime/js/index.d.ts +11 -1
  126. package/runtime/js/index.js +84 -8
  127. package/runtime/js/views.d.ts +38 -0
  128. package/runtime/js/views.js +226 -0
  129. package/runtime/native/android/CMakeLists.txt +4 -1
  130. package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
  131. package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
  132. package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
  133. package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
  134. package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
  135. package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
  136. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
  137. package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
  138. package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
  139. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
  140. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
  141. package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
  142. package/runtime/test/jsi/harness.cpp +9 -0
  143. package/schemas/build.schema.json +96 -1
  144. package/schemas/check.schema.json +56 -1
  145. package/schemas/lucent.schema.json +495 -9
  146. package/schemas/sdk-diff.schema.json +70 -0
  147. package/schemas/sdk-lock.schema.json +67 -0
  148. package/ts-plugin/index.js +77 -4
  149. package/dist/pipeline-CH7-2cxu.js +0 -232
  150. package/dist/project-D8tEet0k.js +0 -150
  151. package/dist/sdk-coverage-BoW_3Efd.js +0 -64
  152. package/dist/src-BD7f1_aZ.js +0 -11493
@@ -0,0 +1,302 @@
1
+ // Lucent runtime — BigInt.
2
+ //
3
+ // `bigint` with JavaScript's semantics at any precision: an immutable value,
4
+ // cheap to copy. A value that fits in 64 bits (every native ID and count,
5
+ // Long.MAX_VALUE, NSNotFound) is held inline and never allocates; a larger
6
+ // one lives in shared, immutable 32-bit limbs, so copying it is a
7
+ // reference-count increment. The form is canonical (a value that fits in
8
+ // int64 is always inline), so equality and hashing are cheap and exact.
9
+ //
10
+ // Every operation is JavaScript's: division truncates toward zero and the
11
+ // remainder takes the dividend's sign; bitwise operators and shifts act on
12
+ // an infinitely wide two's complement; `>>>` throws a TypeError; division
13
+ // by zero, a negative exponent, and a result beyond 2^30 bits (V8's limit)
14
+ // throw a RangeError. Conversions to native 64-bit integers are exact
15
+ // (RangeError when out of range) or wrapping (BigInt.asIntN(64) and
16
+ // asUintN(64)).
17
+ #pragma once
18
+
19
+ #include <compare>
20
+ #include <concepts>
21
+ #include <cstddef>
22
+ #include <cstdint>
23
+ #include <functional>
24
+ #include <limits>
25
+ #include <memory>
26
+ #include <optional>
27
+ #include <string_view>
28
+ #include <type_traits>
29
+
30
+ #include "core.h"
31
+ #include "jsstring.h"
32
+
33
+ namespace lucent {
34
+
35
+ class BigInt {
36
+ public:
37
+ /// 0n.
38
+ constexpr BigInt() noexcept = default;
39
+
40
+ /// Any C++ integer, exactly: what a native 64-bit value becomes.
41
+ template <std::integral I>
42
+ requires(!std::same_as<I, bool>)
43
+ explicit BigInt(I value) {
44
+ if constexpr (std::is_signed_v<I>) {
45
+ small_ = static_cast<int64_t>(value);
46
+ } else {
47
+ *this = fromUint64(static_cast<uint64_t>(value));
48
+ }
49
+ }
50
+
51
+ static BigInt fromInt64(int64_t value) noexcept {
52
+ BigInt b;
53
+ b.small_ = value;
54
+ return b;
55
+ }
56
+
57
+ static BigInt fromUint64(uint64_t value) {
58
+ if (value <= static_cast<uint64_t>(INT64_MAX)) return fromInt64(static_cast<int64_t>(value));
59
+
60
+ return fromUint64Beyond(value);
61
+ }
62
+
63
+ /// `BigInt(number)`: RangeError unless `value` is an integer.
64
+ static BigInt fromDouble(double value);
65
+
66
+ /// `BigInt(string)`: whitespace around a decimal integer with an optional
67
+ /// sign, or a 0x/0o/0b literal without one; empty is 0n. Anything else
68
+ /// throws a SyntaxError.
69
+ static BigInt parse(const String& text);
70
+ static BigInt parse(std::string_view text);
71
+
72
+ /// Digits in `radix` (2 to 36) after an optional "-", as
73
+ /// BigInt.prototype.toString writes them. SyntaxError otherwise.
74
+ static BigInt fromDigits(std::string_view digits, int radix);
75
+
76
+ // --- JavaScript operators ------------------------------------------------------
77
+
78
+ friend BigInt operator+(const BigInt& a, const BigInt& b) {
79
+ int64_t r;
80
+ if (!a.big_ && !b.big_ && !__builtin_add_overflow(a.small_, b.small_, &r)) return fromInt64(r);
81
+
82
+ return add(a, b, false);
83
+ }
84
+
85
+ friend BigInt operator-(const BigInt& a, const BigInt& b) {
86
+ int64_t r;
87
+ if (!a.big_ && !b.big_ && !__builtin_sub_overflow(a.small_, b.small_, &r)) return fromInt64(r);
88
+
89
+ return add(a, b, true);
90
+ }
91
+
92
+ friend BigInt operator*(const BigInt& a, const BigInt& b) {
93
+ int64_t r;
94
+ if (!a.big_ && !b.big_ && !__builtin_mul_overflow(a.small_, b.small_, &r)) return fromInt64(r);
95
+
96
+ return multiply(a, b);
97
+ }
98
+
99
+ /// Truncates toward zero. RangeError when `b` is 0n.
100
+ friend BigInt operator/(const BigInt& a, const BigInt& b) {
101
+ if (!a.big_ && !b.big_ && b.small_ != 0 && !(a.small_ == INT64_MIN && b.small_ == -1)) return fromInt64(a.small_ / b.small_);
102
+
103
+ return divide(a, b, false);
104
+ }
105
+
106
+ /// Takes the dividend's sign. RangeError when `b` is 0n.
107
+ friend BigInt operator%(const BigInt& a, const BigInt& b) {
108
+ if (!a.big_ && !b.big_ && b.small_ != 0) return fromInt64(b.small_ == -1 ? 0 : a.small_ % b.small_);
109
+
110
+ return divide(a, b, true);
111
+ }
112
+
113
+ /// `base ** exponent`. RangeError for a negative exponent.
114
+ static BigInt pow(const BigInt& base, const BigInt& exponent);
115
+
116
+ BigInt operator-() const {
117
+ if (!big_ && small_ != INT64_MIN) return fromInt64(-small_);
118
+
119
+ return negateBeyond();
120
+ }
121
+
122
+ BigInt operator~() const {
123
+ if (!big_) return fromInt64(~small_);
124
+
125
+ return -(*this) - fromInt64(1);
126
+ }
127
+
128
+ friend BigInt operator&(const BigInt& a, const BigInt& b) {
129
+ if (!a.big_ && !b.big_) return fromInt64(a.small_ & b.small_);
130
+
131
+ return bitwise(a, b, '&');
132
+ }
133
+
134
+ friend BigInt operator|(const BigInt& a, const BigInt& b) {
135
+ if (!a.big_ && !b.big_) return fromInt64(a.small_ | b.small_);
136
+
137
+ return bitwise(a, b, '|');
138
+ }
139
+
140
+ friend BigInt operator^(const BigInt& a, const BigInt& b) {
141
+ if (!a.big_ && !b.big_) return fromInt64(a.small_ ^ b.small_);
142
+
143
+ return bitwise(a, b, '^');
144
+ }
145
+
146
+ /// A negative count shifts the other way.
147
+ friend BigInt operator<<(const BigInt& a, const BigInt& count) { return shift(a, count, true); }
148
+
149
+ /// Rounds toward negative infinity, as JavaScript's `>>` does.
150
+ friend BigInt operator>>(const BigInt& a, const BigInt& count) { return shift(a, count, false); }
151
+
152
+ /// `>>>`: always a TypeError, as in JavaScript.
153
+ [[noreturn]] static BigInt unsignedShiftRight(const BigInt& a, const BigInt& count);
154
+
155
+ BigInt& operator+=(const BigInt& b) { return *this = *this + b; }
156
+ BigInt& operator-=(const BigInt& b) { return *this = *this - b; }
157
+ BigInt& operator*=(const BigInt& b) { return *this = *this * b; }
158
+ BigInt& operator/=(const BigInt& b) { return *this = *this / b; }
159
+ BigInt& operator%=(const BigInt& b) { return *this = *this % b; }
160
+ BigInt& operator&=(const BigInt& b) { return *this = *this & b; }
161
+ BigInt& operator|=(const BigInt& b) { return *this = *this | b; }
162
+ BigInt& operator^=(const BigInt& b) { return *this = *this ^ b; }
163
+ BigInt& operator<<=(const BigInt& b) { return *this = *this << b; }
164
+ BigInt& operator>>=(const BigInt& b) { return *this = *this >> b; }
165
+
166
+ // --- comparisons ---------------------------------------------------------------
167
+
168
+ friend bool operator==(const BigInt& a, const BigInt& b) noexcept {
169
+ if (!a.big_ && !b.big_) return a.small_ == b.small_;
170
+
171
+ return equalBeyond(a, b);
172
+ }
173
+
174
+ friend std::strong_ordering operator<=>(const BigInt& a, const BigInt& b) noexcept {
175
+ if (!a.big_ && !b.big_) return a.small_ <=> b.small_;
176
+
177
+ return compareBeyond(a, b);
178
+ }
179
+
180
+ /// `a < b`, `a == b` (loose) and the rest against a number, exactly:
181
+ /// unordered when `b` is NaN.
182
+ friend std::partial_ordering compare(const BigInt& a, double b) noexcept;
183
+ friend std::partial_ordering operator<=>(const BigInt& a, double b) noexcept { return compare(a, b); }
184
+ friend bool operator==(const BigInt& a, double b) noexcept { return compare(a, b) == 0; }
185
+
186
+ bool isZero() const noexcept { return !big_ && small_ == 0; }
187
+
188
+ /// -1, 0 or 1.
189
+ int sign() const noexcept;
190
+
191
+ // --- BigInt.* and conversions -------------------------------------------------------
192
+
193
+ /// BigInt.asIntN / BigInt.asUintN. `bits` is converted as ToIndex:
194
+ /// RangeError if negative or beyond 2^53 - 1.
195
+ static BigInt asIntN(double bits, const BigInt& value);
196
+ static BigInt asUintN(double bits, const BigInt& value);
197
+
198
+ /// String(x): decimal, without the `n`.
199
+ String toString() const;
200
+
201
+ /// x.toString(radix). RangeError unless 2 <= radix <= 36.
202
+ String toString(double radix) const;
203
+
204
+ /// Number(x): the nearest double, ties to even (±Infinity beyond range).
205
+ double toDouble() const noexcept;
206
+
207
+ /// For a native 64-bit parameter: RangeError when out of range.
208
+ int64_t toInt64() const {
209
+ if (!big_) return small_;
210
+
211
+ throwOutOfRange(true);
212
+ }
213
+
214
+ uint64_t toUint64() const {
215
+ if (!big_ && small_ >= 0) return static_cast<uint64_t>(small_);
216
+
217
+ return toUint64Beyond();
218
+ }
219
+
220
+ /// The low 64 bits (BigInt.asIntN(64, x), asUintN(64, x)).
221
+ int64_t wrapToInt64() const noexcept { return static_cast<int64_t>(wrapToUint64()); }
222
+
223
+ uint64_t wrapToUint64() const noexcept {
224
+ if (!big_) return static_cast<uint64_t>(small_);
225
+
226
+ return wrapBeyond();
227
+ }
228
+
229
+ std::optional<int64_t> tryInt64() const noexcept {
230
+ if (!big_) return small_;
231
+
232
+ return std::nullopt;
233
+ }
234
+
235
+ std::optional<uint64_t> tryUint64() const noexcept;
236
+
237
+ /// Equal values hash equal (Map and Set keys).
238
+ size_t hash() const noexcept;
239
+
240
+ private:
241
+ /// Beyond int64: a sign and a magnitude in 32-bit limbs, least
242
+ /// significant first, without leading zeros.
243
+ struct Big;
244
+
245
+ static BigInt fromUint64Beyond(uint64_t value);
246
+ [[noreturn]] void throwOutOfRange(bool isSigned) const;
247
+ uint64_t toUint64Beyond() const;
248
+ uint64_t wrapBeyond() const noexcept;
249
+ static BigInt add(const BigInt& a, const BigInt& b, bool subtract);
250
+ static BigInt multiply(const BigInt& a, const BigInt& b);
251
+ static BigInt divide(const BigInt& a, const BigInt& b, bool remainder);
252
+ static BigInt bitwise(const BigInt& a, const BigInt& b, char op);
253
+ static BigInt shift(const BigInt& a, const BigInt& count, bool left);
254
+ static bool equalBeyond(const BigInt& a, const BigInt& b) noexcept;
255
+ static std::strong_ordering compareBeyond(const BigInt& a, const BigInt& b) noexcept;
256
+ BigInt negateBeyond() const;
257
+
258
+ friend struct BigIntAccess;
259
+
260
+ int64_t small_ = 0;
261
+ std::shared_ptr<const Big> big_;
262
+ };
263
+
264
+ /// `===` (and SameValueZero, which is the same for bigints).
265
+ inline bool strictEquals(const BigInt& a, const BigInt& b) noexcept { return a == b; }
266
+
267
+ /// String(x) and template literals.
268
+ String toJsString(const BigInt& value);
269
+
270
+ namespace detail {
271
+ [[noreturn]] void throwOutOfNativeRange(const BigInt& value, const char* what, bool isSigned, size_t bits);
272
+ }
273
+
274
+ /// A bigint as the native integer `I` an API takes (the SDK glue: NSInteger,
275
+ /// NSUInteger, int64_t, jlong, a struct field's own C type): exactly, or
276
+ /// RangeError naming `what`, the parameter or field it is for. The other
277
+ /// way needs no helper: BigInt(v) holds every native integer exactly.
278
+ template <std::integral I>
279
+ requires(!std::same_as<I, bool>)
280
+ I toNativeInteger(const BigInt& value, const char* what) {
281
+ if constexpr (std::is_signed_v<I>) {
282
+ auto v = value.tryInt64();
283
+ if (v && *v >= std::numeric_limits<I>::min() && *v <= std::numeric_limits<I>::max()) return static_cast<I>(*v);
284
+ } else {
285
+ auto v = value.tryUint64();
286
+ if (v && *v <= std::numeric_limits<I>::max()) return static_cast<I>(*v);
287
+ }
288
+
289
+ detail::throwOutOfNativeRange(value, what, std::is_signed_v<I>, sizeof(I) * 8);
290
+ }
291
+
292
+ } // namespace lucent
293
+
294
+ template <>
295
+ struct std::hash<lucent::BigInt> {
296
+ size_t operator()(const lucent::BigInt& v) const noexcept { return v.hash(); }
297
+ };
298
+
299
+ /// A bigint literal beyond int64, parsed once per call site (the compiler
300
+ /// emits BigInt::fromInt64 for the others).
301
+ #define LUCENT_BIGINT(literal) \
302
+ ([]() -> const ::lucent::BigInt& { static const ::lucent::BigInt v = ::lucent::BigInt::parse(std::string_view(literal)); return v; }())
@@ -0,0 +1,228 @@
1
+ #include "buffer.h"
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+ #include <cstring>
6
+
7
+ #include "report.h"
8
+ #include "trace.h"
9
+
10
+ namespace lucent {
11
+
12
+ namespace {
13
+
14
+ struct Counters {
15
+ std::atomic<uint64_t> allocated{0};
16
+ std::atomic<uint64_t> adopted{0};
17
+ std::atomic<uint64_t> released{0};
18
+ std::atomic<uint64_t> transfers{0};
19
+ std::atomic<uint64_t> copies{0};
20
+ std::atomic<uint64_t> bytesCopied{0};
21
+ };
22
+
23
+ // Constant-initialized: usable during static initialization.
24
+ Counters counters;
25
+
26
+ void countCopy(size_t bytes) {
27
+ counters.copies.fetch_add(1, std::memory_order_relaxed);
28
+ counters.bytesCopied.fetch_add(bytes, std::memory_order_relaxed);
29
+
30
+ trace::instant(trace::Category::Copy, "buffer.copy", {.parent = trace::currentId(), .value = static_cast<int64_t>(bytes)});
31
+ }
32
+
33
+ void freeAllocated(uint8_t* data, size_t) { delete[] data; }
34
+
35
+ } // namespace
36
+
37
+ NativeBuffer NativeBufferObject::allocate(double size, String origin) {
38
+ if (!(size >= 0) || std::trunc(size) != size || size > 9007199254740991.0) throwRangeError("Invalid buffer size");
39
+
40
+ auto count = static_cast<size_t>(size);
41
+ auto* data = new uint8_t[count]();
42
+
43
+ counters.allocated.fetch_add(1, std::memory_order_relaxed);
44
+ trace::instant(trace::Category::Alloc, "buffer.allocate", {.parent = trace::currentId(), .value = static_cast<int64_t>(count)});
45
+ return NativeBuffer(new NativeBufferObject(data, count, freeAllocated, nullptr, std::move(origin), 0));
46
+ }
47
+
48
+ NativeBuffer NativeBufferObject::adopt(uint8_t* data, size_t size, Destroy destroy, ExecutionContext* destroyOn,
49
+ String origin) {
50
+ counters.adopted.fetch_add(1, std::memory_order_relaxed);
51
+ trace::instant(trace::Category::Alloc, "buffer.adopt", {.parent = trace::currentId(), .value = static_cast<int64_t>(size)});
52
+ return NativeBuffer(new NativeBufferObject(data, size, std::move(destroy), destroyOn, std::move(origin), 0));
53
+ }
54
+
55
+ NativeBuffer NativeBufferObject::fromBytes(const Bytes& bytes, String origin) {
56
+ NativeBuffer buffer = allocate(static_cast<double>(bytes.size()), std::move(origin));
57
+
58
+ if (bytes.size() > 0) std::memcpy(buffer->data_, bytes.data(), bytes.size());
59
+ countCopy(bytes.size());
60
+
61
+ return buffer;
62
+ }
63
+
64
+ NativeBufferObject::NativeBufferObject(uint8_t* data, size_t size, Destroy destroy, ExecutionContext* destroyOn,
65
+ String origin, uint32_t generation)
66
+ : data_(data),
67
+ size_(size),
68
+ destroy_(std::move(destroy)),
69
+ destroyOn_(destroyOn),
70
+ origin_(std::move(origin)),
71
+ generation_(generation) {}
72
+
73
+ NativeBufferObject::~NativeBufferObject() {
74
+ // Nothing refers to it any more: nothing borrows it or changes its state.
75
+ if (state() != State::Open) return;
76
+
77
+ try {
78
+ release(data_, size_.load(std::memory_order_relaxed), std::move(destroy_), destroyOn_);
79
+ } catch (...) {
80
+ reportUncaught(std::current_exception(), "native buffer");
81
+ }
82
+ }
83
+
84
+ NativeBufferStats NativeBufferObject::stats() {
85
+ NativeBufferStats s;
86
+ s.allocated = counters.allocated.load(std::memory_order_relaxed);
87
+ s.adopted = counters.adopted.load(std::memory_order_relaxed);
88
+ s.released = counters.released.load(std::memory_order_relaxed);
89
+ s.transfers = counters.transfers.load(std::memory_order_relaxed);
90
+ s.copies = counters.copies.load(std::memory_order_relaxed);
91
+ s.bytesCopied = counters.bytesCopied.load(std::memory_order_relaxed);
92
+ return s;
93
+ }
94
+
95
+ Bytes NativeBufferObject::toBytes() {
96
+ return withRead([](std::span<const uint8_t> bytes) { return copyOut(bytes); });
97
+ }
98
+
99
+ Bytes NativeBufferObject::copyOut(std::span<const uint8_t> bytes) {
100
+ countCopy(bytes.size());
101
+ return Bytes::copy(bytes.data(), bytes.size());
102
+ }
103
+
104
+ void NativeBufferObject::copyIn(std::span<uint8_t> to, const Bytes& from) {
105
+ size_t count = std::min(to.size(), from.size());
106
+
107
+ if (count > 0) std::memcpy(to.data(), from.data(), count);
108
+ countCopy(count);
109
+ }
110
+
111
+ bool NativeBufferObject::close() {
112
+ uint32_t expected = 0;
113
+
114
+ if (!word_.compare_exchange_strong(expected, static_cast<uint32_t>(State::Closed), std::memory_order_acq_rel)) {
115
+ State now = stateOf(expected);
116
+ if (now == State::Closed || now == State::Transferred) return false;
117
+
118
+ refuse(expected);
119
+ }
120
+
121
+ uint8_t* data = data_;
122
+ size_t size = size_.exchange(0, std::memory_order_acq_rel);
123
+ Destroy destroy = std::move(destroy_);
124
+ data_ = nullptr;
125
+ destroy_ = nullptr;
126
+
127
+ release(data, size, std::move(destroy), destroyOn_);
128
+ return true;
129
+ }
130
+
131
+ NativeBuffer NativeBufferObject::transfer() {
132
+ NativeBuffer moved = beginTransfer();
133
+ commitTransfer();
134
+ return moved;
135
+ }
136
+
137
+ NativeBuffer NativeBufferObject::beginTransfer() {
138
+ seize(State::Transferring);
139
+
140
+ NativeBuffer moved(new NativeBufferObject(data_, size_.exchange(0, std::memory_order_acq_rel), std::move(destroy_),
141
+ destroyOn_, origin_, generation_ + 1));
142
+ data_ = nullptr;
143
+ destroy_ = nullptr;
144
+
145
+ return moved;
146
+ }
147
+
148
+ void NativeBufferObject::commitTransfer() {
149
+ word_.store(static_cast<uint32_t>(State::Transferred), std::memory_order_release);
150
+ counters.transfers.fetch_add(1, std::memory_order_relaxed);
151
+ }
152
+
153
+ void NativeBufferObject::abortTransfer(const NativeBuffer& successor) {
154
+ // The successor never left the failed copy: nothing borrows it.
155
+ data_ = successor->data_;
156
+ size_.store(successor->size_.exchange(0, std::memory_order_acq_rel), std::memory_order_relaxed);
157
+ destroy_ = std::move(successor->destroy_);
158
+
159
+ successor->data_ = nullptr;
160
+ successor->destroy_ = nullptr;
161
+ successor->word_.store(static_cast<uint32_t>(State::Transferred), std::memory_order_relaxed);
162
+
163
+ word_.store(0, std::memory_order_release);
164
+ }
165
+
166
+ void NativeBufferObject::release(uint8_t* data, size_t size, Destroy destroy, ExecutionContext* on) {
167
+ counters.released.fetch_add(1, std::memory_order_relaxed);
168
+
169
+ if (!destroy) return;
170
+
171
+ detail::runOn(on, [data, size, destroy = std::move(destroy)] { destroy(data, size); });
172
+ }
173
+
174
+ void NativeBufferObject::seize(State to) {
175
+ uint32_t expected = 0;
176
+ if (!word_.compare_exchange_strong(expected, static_cast<uint32_t>(to), std::memory_order_acq_rel)) refuse(expected);
177
+ }
178
+
179
+ void NativeBufferObject::refuse(uint32_t word) const {
180
+ const char* why = "is borrowed";
181
+
182
+ switch (stateOf(word)) {
183
+ case State::Open:
184
+ break;
185
+ case State::Transferring:
186
+ why = "is being transferred";
187
+ break;
188
+ case State::Transferred:
189
+ why = "was transferred";
190
+ break;
191
+ case State::Closed:
192
+ why = "is closed";
193
+ break;
194
+ }
195
+
196
+ String name = origin_.empty() ? String::fromLatin1("NativeBuffer")
197
+ : String::fromLatin1("NativeBuffer (") + origin_ + String::fromLatin1(")");
198
+ throwError(String::fromLatin1("InvalidStateError"), name + String::fromLatin1(" ") + String::fromLatin1(why));
199
+ }
200
+
201
+ // --- Borrow ---------------------------------------------------------------------
202
+
203
+ NativeBufferObject::Borrow::Borrow(NativeBufferObject& buffer, bool write)
204
+ : keep_(buffer.shared_from_this()), buffer_(buffer), write_(write) {
205
+ std::atomic<uint32_t>& word = buffer.word_;
206
+
207
+ if (write) {
208
+ uint32_t expected = 0;
209
+ if (!word.compare_exchange_strong(expected, kWriter, std::memory_order_acquire)) buffer.refuse(expected);
210
+
211
+ return;
212
+ }
213
+
214
+ uint32_t now = word.load(std::memory_order_relaxed);
215
+ do {
216
+ if (stateOf(now) != State::Open || (now & kWriter)) buffer.refuse(now);
217
+ } while (!word.compare_exchange_weak(now, now + kReader, std::memory_order_acquire, std::memory_order_relaxed));
218
+ }
219
+
220
+ NativeBufferObject::Borrow::~Borrow() {
221
+ if (write_) {
222
+ buffer_.word_.fetch_and(~kWriter, std::memory_order_release);
223
+ } else {
224
+ buffer_.word_.fetch_sub(kReader, std::memory_order_release);
225
+ }
226
+ }
227
+
228
+ } // namespace lucent