PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
array.hpp
Go to the documentation of this file.
1// Script-owned arrays with bounded resident storage.
2//
3// A generated constructor binds a root before bar zero and chooses
4// preallocated(store, proved_count) for fixed-width, non-owning cells, or
5// ScriptArray(store) for general growth. Logical size, capacity and payload
6// live in Store objects: prepared scalar/handle cells need no array allocation
7// on a bar, and general growth adds backing pages without a resident directory.
8// Owning codecs (strings/nested arrays) also allocate their payload objects;
9// reserving their parent slots alone is not a proof of fixed payload bytes.
10// Exhausting a proof's reserve follows general growth, never drops or refuses.
11//
12// Copies are values, as in the generated vector path. alias() is an explicit
13// borrowed reference to the same root (the owner and Store must outlive it).
14// Assignment to a bound array preserves its root, including inside a Tx.
15// Nested values own child ids; drawing values retain their four-byte handles,
16// including handles to deleted records. The drawing arena owns those records.
17//
18// get/set implement the tolerant negative-index -> na/no-write access rule.
19// The compiler's checked v6 calls use get_i/set_i/remove_i/insert (end-relative
20// negative indices); get_raw/set_raw/remove_raw/insert_raw are checked v5
21// calls. Do not substitute the tolerant accessor for either checked path.
22// Selecting these calls and wiring o1::Tx into generated calc-on-fill
23// snapshots belong to the compiler integration, not to the kernel scheduler.
24#ifndef PINEFORGE_ARRAY_HPP
25#define PINEFORGE_ARRAY_HPP
26
28#include <pineforge/na.hpp>
29
30#include <algorithm>
31#include <cmath>
32#include <cstddef>
33#include <cstdint>
34#include <exception>
35#include <initializer_list>
36#include <limits>
37#include <memory>
38#include <string>
39#include <type_traits>
40#include <utility>
41
42namespace pineforge {
43
44template<class T> class ScriptArray;
45
47
49 std::uint64_t magic;
50 std::uint64_t size;
51 std::uint64_t capacity;
52 std::uint64_t element_bytes;
53};
54static_assert(sizeof(StoreHeader) == 32, "array object header layout");
55inline constexpr std::uint64_t kHeaderMagic = UINT64_C(0x5046415252303032);
56inline constexpr std::uint64_t kPayloadPageBytes = 4096;
57
58[[noreturn]] void index_error(const char*, std::int64_t, std::uint64_t, bool missing = false);
59[[noreturn]] void index_error_double(const char*, double, std::uint64_t);
60[[noreturn]] void empty_array_error(const char*);
61[[noreturn]] void size_error();
62[[noreturn]] void invariant(const char*);
63[[noreturn]] void inverted_range_error(const char*);
64std::int64_t checked_index_double(double, std::uint64_t, const char*);
66o1::Id store_string(o1::Store&, const std::string&);
67std::string load_string(const o1::Store&, o1::Id);
68
69inline void read_exact(const o1::Store& store, o1::Id id, std::uint64_t offset,
70 void* destination, std::size_t bytes) {
71 if (store.read(id, offset, destination, bytes) != bytes)
72 invariant("array store short read");
73}
74
75inline void write_exact(o1::Store& store, o1::Id id, std::uint64_t offset,
76 const void* source, std::size_t bytes) {
77 if (store.write(id, offset, source, bytes) != bytes)
78 invariant("array store short write");
79}
80
81inline std::uint64_t checked(std::int64_t raw, std::uint64_t size,
82 const char* method, bool relative, bool end = false) {
83 if (raw == std::numeric_limits<std::int64_t>::min()) index_error(method, raw, size, true);
84 // size is restricted to INT64_MAX at construction/reservation. Adding a
85 // nonnegative size to a negative int64 cannot overflow.
86 const auto index = relative && raw < 0 ? raw + static_cast<std::int64_t>(size) : raw;
87 if (index < 0 || (end ? static_cast<std::uint64_t>(index) > size
88 : static_cast<std::uint64_t>(index) >= size))
89 index_error(method, raw, size);
90 return static_cast<std::uint64_t>(index);
91}
92
93template<class Index>
94std::int64_t index_integer(Index value, std::uint64_t size, const char* method) {
95 static_assert(std::is_arithmetic_v<Index>, "array index must be numeric");
96 if constexpr (std::is_floating_point_v<Index>) {
97 return checked_index_double(static_cast<double>(value), size, method);
98 } else if constexpr (std::is_unsigned_v<Index>) {
99 if (value > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()))
100 index_error_double(method, static_cast<double>(value), size);
101 } else if constexpr (!std::is_same_v<Index, bool>) {
102 if (is_na(value)) index_error(method, std::numeric_limits<std::int64_t>::min(), size, true);
103 }
104 return static_cast<std::int64_t>(value);
105}
106
107template<class T, class = void> struct HasId : std::false_type {};
108template<class T>
109struct HasId<T, std::void_t<decltype(std::declval<T>().id)>> : std::true_type {};
110
111// An element codec owns a fixed-size, trivially copyable Cell in the parent.
112// Extensions for other state families supply encode/decode/clone/destroy/hash.
113template<class T, class Enable = void>
115 static_assert(std::is_trivially_copyable_v<T>,
116 "array element requires a fixed cell codec");
117 using Cell = T;
118 static constexpr bool owns_objects = false;
119 static Cell encode(o1::Store&, const T& value) { return value; }
120 static T decode(const o1::Store&, const Cell& value) { return value; }
121 static Cell clone(const o1::Store&, const Cell& value, o1::Store&) { return value; }
122 static void destroy(o1::Store&, const Cell&) {}
123 template<class Sink>
124 static void hash(const o1::Store&, const Cell& value, Sink& sink) {
125 if constexpr (std::is_same_v<T, bool>) sink.b(value);
126 else if constexpr (std::is_floating_point_v<T>) sink.d(value);
127 else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) sink.i(value);
128 else if constexpr (std::is_integral_v<T>) sink.u(value);
129 else if constexpr (HasId<T>::value) sink.i(value.id);
130 else {
131 static_assert(std::is_arithmetic_v<T>,
132 "structured array element needs a canonical hash codec");
133 }
134 }
135};
136
137template<>
138struct ElementCodec<std::string, void> {
139 using Cell = o1::Id;
140 static constexpr bool owns_objects = true;
141 static Cell encode(o1::Store& store, const std::string& value) {
142 return store_string(store, value);
143 }
144 static std::string decode(const o1::Store& store, Cell value) {
145 return load_string(store, value);
146 }
147 static Cell clone(const o1::Store& source, Cell value, o1::Store& target) {
148 return value == o1::kNullId ? value : clone_bytes(source, value, target);
149 }
150 static void destroy(o1::Store& store, Cell value) {
151 if (value != o1::kNullId && store.alive(value)) store.release(value);
152 }
153 template<class Sink>
154 static void hash(const o1::Store& store, Cell value, Sink& sink) {
155 const auto bytes = value == o1::kNullId ? 0 : store.size(value);
156 sink.u(bytes);
157 unsigned char buffer[kPayloadPageBytes];
158 for (std::uint64_t offset = 0; offset < bytes;) {
159 const auto take = static_cast<std::size_t>(
160 std::min<std::uint64_t>(sizeof buffer, bytes - offset));
161 read_exact(store, value, offset, buffer, take);
162 sink.bytes(buffer, take);
163 offset += take;
164 }
165 }
166};
167
168template<class U> struct ElementCodec<ScriptArray<U>, void>;
169
170} // namespace detail::script_array
171
172template<class T>
173class ScriptArray {
175 using Cell = typename Codec::Cell;
177 static_assert(std::is_trivially_copyable_v<Cell>, "array cells must be byte-addressable");
178
179 detail::o1::Store* store_ = nullptr;
181 bool owner_ = false;
182 struct Borrow {};
183 template<class, class> friend struct detail::script_array::ElementCodec;
184
186 : store_(&store), id_(id) {}
187
188 static constexpr std::uint64_t max_elements() {
189 constexpr auto byte_limit =
190 (std::numeric_limits<std::uint64_t>::max() - sizeof(Header)) / sizeof(Cell);
191 constexpr auto index_limit =
192 static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max());
193 return byte_limit < index_limit ? byte_limit : index_limit;
194 }
195
196 static std::uint64_t rounded_capacity(std::uint64_t count) {
197 if (count > max_elements()) detail::script_array::size_error();
198 constexpr auto page = detail::script_array::kPayloadPageBytes;
199 const auto bytes = count * sizeof(Cell);
200 const auto limit = std::numeric_limits<std::uint64_t>::max();
201 if (bytes > limit - (page - 1)) return count;
202 const auto capacity = ((bytes + page - 1) / page * page) / sizeof(Cell);
203 return std::min(capacity, max_elements());
204 }
205
206 static std::uint64_t offset(std::uint64_t index) {
207 return sizeof(Header) + index * sizeof(Cell);
208 }
209
210 Header header() const {
211 if (store_ == nullptr || id_ == detail::o1::kNullId)
212 detail::script_array::invariant("array has no bound store object");
213 Header value{};
214 detail::script_array::read_exact(*store_, id_, 0, &value, sizeof value);
216 value.element_bytes != sizeof(Cell) || value.size > value.capacity ||
217 value.capacity > max_elements())
218 detail::script_array::invariant("array object header is corrupt");
219 return value;
220 }
221
222 void write_header(const Header& value) {
223 detail::script_array::write_exact(*store_, id_, 0, &value, sizeof value);
224 }
225
226 Cell read_cell(std::uint64_t index) const {
227 Cell value{};
228 detail::script_array::read_exact(*store_, id_, offset(index), &value, sizeof value);
229 return value;
230 }
231
232 void write_cell(std::uint64_t index, const Cell& value) {
233 detail::script_array::write_exact(*store_, id_, offset(index), &value, sizeof value);
234 }
235
236 T value_at(std::uint64_t index) const { return Codec::decode(*store_, read_cell(index)); }
237
238 void set_at(std::uint64_t index, const T& value) {
239 const Cell next = Codec::encode(*store_, value);
240 const Cell previous = read_cell(index);
241 write_cell(index, next);
242 Codec::destroy(*store_, previous);
243 }
244
245 void grow(std::uint64_t needed) {
246 const auto before = header();
247 if (needed <= before.capacity) return;
248 if (needed > max_elements()) detail::script_array::size_error();
249 // Geometric reservation amortizes backing growth; this is never a
250 // resident-page or element-count cap. Main's Store keeps ids stable.
251 const auto doubled = before.capacity <= max_elements() / 2
252 ? before.capacity * 2 : max_elements();
253 reserve(std::max(needed, doubled));
254 }
255
256 void push_cell(const Cell& cell) {
257 auto h = header();
258 if (h.size == max_elements()) detail::script_array::size_error();
259 grow(h.size + 1);
260 h = header();
261 write_cell(h.size, cell);
262 ++h.size;
263 write_header(h);
264 }
265
266 T remove_at(std::uint64_t index) {
267 auto h = header();
268 const Cell removed = read_cell(index);
269 T value = Codec::decode(*store_, removed);
270 // Move fixed cells, not decoded values. Each nested subtree keeps
271 // precisely one owning parent slot, even across a page boundary.
272 for (std::uint64_t i = index + 1; i < h.size; ++i)
273 write_cell(i - 1, read_cell(i));
274 write_cell(h.size - 1, Cell{});
275 --h.size;
276 write_header(h);
277 Codec::destroy(*store_, removed);
278 return value;
279 }
280
281 void insert_at(std::uint64_t index, const T& value) {
282 auto h = header();
283 if (h.size == max_elements()) detail::script_array::size_error();
284 grow(h.size + 1);
285 h = header();
286 const Cell next = Codec::encode(*store_, value);
287 for (auto i = h.size; i > index; --i) write_cell(i, read_cell(i - 1));
288 write_cell(index, next);
289 ++h.size;
290 write_header(h);
291 }
292
293 detail::o1::Id clone_into(detail::o1::Store& destination) const {
294 ScriptArray result(destination);
295 result.reserve(size());
296 const auto n = size();
297 for (std::uint64_t i = 0; i < n; ++i)
298 result.push_cell(Codec::clone(*store_, read_cell(i), destination));
299 const auto id = result.id_;
300 result.owner_ = false;
301 return id;
302 }
303
304 static void destroy_tree(detail::o1::Store& store, detail::o1::Id id) {
305 if (id == detail::o1::kNullId || !store.alive(id)) return;
306 ScriptArray borrowed(store, id, Borrow{});
307 borrowed.clear();
308 store.release(id);
309 }
310
311 void dispose() noexcept {
312 if (!owner_ || store_ == nullptr || id_ == detail::o1::kNullId) return;
313 try {
314 destroy_tree(*store_, id_);
315 } catch (...) {
316 // A backing failure during destruction cannot produce a usable
317 // partially released value.
318 std::terminate();
319 }
320 }
321
322 void assign(const ScriptArray& other) {
323 if (this == &other || (store_ == other.store_ && id_ == other.id_)) return;
324 if (store_ == nullptr) {
325 if (other.store_ == nullptr) return;
326 store_ = other.store_;
327 id_ = other.clone_into(*store_);
328 owner_ = true;
329 return;
330 }
331 // The root NEVER changes here. A Tx can therefore restore assignment
332 // through every existing borrowed alias by restoring only store bytes.
333 clear();
334 reserve(other.size());
335 for (std::uint64_t i = 0, n = other.size(); i < n; ++i)
336 push_cell(Codec::clone(*other.store_, other.read_cell(i), *store_));
337 }
338
339public:
340 using value_type = T;
341 static constexpr std::size_t element_bytes = sizeof(Cell);
342 static constexpr std::size_t header_bytes = sizeof(Header);
344
345 // Default is the unbound/na value (also used by na<ScriptArray<T>>).
346 // A created, mutable empty array is ScriptArray(store).
347 ScriptArray() = default;
348
349 explicit ScriptArray(detail::o1::Store& store) : store_(&store), owner_(true) {
350 id_ = store.create(sizeof(Header), alignof(Header));
351 write_header({detail::script_array::kHeaderMagic, 0, 0, sizeof(Cell)});
352 }
353
354 ScriptArray(detail::o1::Store& store, std::uint64_t count, const T& initial = na<T>())
355 : ScriptArray(store) {
356 reserve(count);
357 for (std::uint64_t i = 0; i < count; ++i) push_back(initial);
358 }
359
360 static ScriptArray preallocated(detail::o1::Store& store, std::uint64_t capacity) {
361 ScriptArray result(store);
362 result.reserve(capacity);
363 return result;
364 }
365
366 static ScriptArray from(detail::o1::Store& store, std::initializer_list<T> values) {
367 auto result = preallocated(store, values.size());
368 for (const auto& value : values) result.push_back(value);
369 return result;
370 }
371
372 ScriptArray(const ScriptArray& other) {
373 if (other.store_ != nullptr) {
374 store_ = other.store_;
375 id_ = other.clone_into(*store_);
376 owner_ = true;
377 }
378 }
379 ScriptArray& operator=(const ScriptArray& other) { assign(other); return *this; }
380
381 ScriptArray(ScriptArray&& other) noexcept
382 : store_(other.store_), id_(other.id_), owner_(other.owner_) {
383 other.store_ = nullptr;
384 other.id_ = detail::o1::kNullId;
385 other.owner_ = false;
386 }
387
388 ScriptArray& operator=(ScriptArray&& other) {
389 if (this == &other) return *this;
390 if (store_ == nullptr) {
391 store_ = other.store_;
392 id_ = other.id_;
393 owner_ = other.owner_;
394 other.store_ = nullptr;
395 other.id_ = detail::o1::kNullId;
396 other.owner_ = false;
397 } else {
398 const bool same_root = store_ == other.store_ && id_ == other.id_;
399 assign(other);
400 // Only an owner is consumed by a move; a borrowed alias keeps its root.
401 if (!same_root && other.owner_) other.clear();
402 }
403 return *this;
404 }
405
406 ~ScriptArray() { dispose(); }
407
408 bool store_backed() const noexcept { return store_ != nullptr; }
409 detail::o1::Id id() const noexcept { return id_; }
411 if (store_ == nullptr) detail::script_array::invariant("array has no bound store");
412 return *store_;
413 }
414 bool alive() const { return store_ != nullptr && store_->alive(id_); }
415 std::uint64_t size() const { return store_ == nullptr ? 0 : header().size; }
416 std::uint64_t capacity() const { return store_ == nullptr ? 0 : header().capacity; }
417 bool empty() const { return size() == 0; }
418 ScriptArray alias() const { return ScriptArray(store(), id_, Borrow{}); }
419 ScriptArray copy() const { return ScriptArray(*this); }
420
421 template<class Index>
422 T get(Index index) const {
423 if constexpr (std::is_floating_point_v<Index>)
424 if (std::isnan(index) || index < 0) return na<T>();
425 if constexpr (std::is_signed_v<Index>)
426 if (index < 0) return na<T>();
427 return get_raw(detail::script_array::index_integer(index, size(), "get"));
428 }
429
430 template<class Index>
431 void set(Index index, const T& value) {
432 if constexpr (std::is_floating_point_v<Index>)
433 if (std::isnan(index) || index < 0) return;
434 if constexpr (std::is_signed_v<Index>)
435 if (index < 0) return;
436 set_raw(detail::script_array::index_integer(index, size(), "set"), value);
437 }
438
439 T get_i(std::int64_t index) const {
440 return value_at(detail::script_array::checked(index, size(), "get", true));
441 }
442 void set_i(std::int64_t index, const T& value) {
443 set_at(detail::script_array::checked(index, size(), "set", true), value);
444 }
445 T remove_i(std::int64_t index) {
446 return remove_at(detail::script_array::checked(index, size(), "remove", true));
447 }
448 template<class Index>
449 T get_raw(Index index) const {
450 const auto n = size();
451 return value_at(detail::script_array::checked(
452 detail::script_array::index_integer(index, n, "get"), n, "get", false));
453 }
454 template<class Index>
455 void set_raw(Index index, const T& value) {
456 const auto n = size();
458 detail::script_array::index_integer(index, n, "set"), n, "set", false), value);
459 }
460 template<class Index>
461 T remove_raw(Index index) {
462 const auto n = size();
463 return remove_at(detail::script_array::checked(
464 detail::script_array::index_integer(index, n, "remove"), n, "remove", false));
465 }
466 template<class Index>
467 T get_v6(Index index) const {
468 return get_i(detail::script_array::index_integer(index, size(), "get"));
469 }
470 template<class Index>
471 void set_v6(Index index, const T& value) {
472 set_i(detail::script_array::index_integer(index, size(), "set"), value);
473 }
474 template<class Index>
475 T remove(Index index) {
476 return remove_i(detail::script_array::index_integer(index, size(), "remove"));
477 }
478
479 template<class Function>
480 void mutate(std::int64_t index, Function&& function) {
481 const auto at = detail::script_array::checked(index, size(), "get", true);
482 T value = value_at(at);
483 std::forward<Function>(function)(value);
484 set_at(at, value);
485 }
486
487 void reserve(std::uint64_t count) {
488 auto h = header();
489 if (count <= h.capacity) return;
490 const auto next = rounded_capacity(count);
491 if (!store_->resize(id_, offset(next)))
492 detail::script_array::invariant("array store resize failed");
493 h.capacity = next;
494 write_header(h);
495 }
496
497 void push_back(const T& value) {
498 const auto h = header();
499 if (h.size == max_elements()) detail::script_array::size_error();
500 grow(h.size + 1);
501 push_cell(Codec::encode(*store_, value));
502 }
503 void push(const T& value) { push_back(value); }
504
505 template<class Index>
506 void insert(Index index, const T& value) {
507 const auto n = size();
508 const auto at = detail::script_array::checked(
509 detail::script_array::index_integer(index, n, "insert"), n, "insert", true, true);
510 insert_at(at, value);
511 }
512 template<class Index>
513 void insert_raw(Index index, const T& value) {
514 const auto n = size();
515 const auto at = detail::script_array::checked(
516 detail::script_array::index_integer(index, n, "insert"), n, "insert", false, true);
517 insert_at(at, value);
518 }
519 void unshift(const T& value) { insert(0, value); }
520
521 T first() const {
523 return value_at(0);
524 }
525 T last() const {
527 return value_at(size() - 1);
528 }
529 T pop() {
531 return remove_at(size() - 1);
532 }
533 T shift() {
535 return remove_at(0);
536 }
537
538 void clear() {
539 if (store_ == nullptr) return;
540 auto h = header();
541 if constexpr (Codec::owns_objects)
542 for (std::uint64_t i = 0; i < h.size; ++i) Codec::destroy(*store_, read_cell(i));
543 h.size = 0;
544 write_header(h);
545 }
546
547 void append(const ScriptArray& other) {
548 const auto n = other.size(); // Self-concat observes the original size.
549 const auto old = size();
550 if (n > max_elements() - old) detail::script_array::size_error();
551 grow(old + n);
552 for (std::uint64_t i = 0; i < n; ++i)
553 push_cell(Codec::clone(*other.store_, other.read_cell(i), *store_));
554 }
555 void concat(const ScriptArray& other) { append(other); }
556
557 void fill(const T& value) { fill(value, 0, static_cast<std::int64_t>(size())); }
558 template<class From>
559 void fill(const T& value, From from) {
560 fill(value, from, static_cast<std::int64_t>(size()));
561 }
562 template<class From, class To>
563 void fill(const T& value, From from, To to) {
564 const auto n = size();
566 detail::script_array::index_integer(from, n, "fill"), n, "fill", false, true);
568 detail::script_array::index_integer(to, n, "fill"), n, "fill", false, true);
569 for (auto i = first; i < last; ++i) set_at(i, value);
570 }
571
572 template<class From, class To>
573 ScriptArray slice(From from, To to) const {
574 const auto n = size();
576 detail::script_array::index_integer(from, n, "slice"), n, "slice", false, true);
578 detail::script_array::index_integer(to, n, "slice"), n, "slice", false, true);
580 auto result = preallocated(store(), last - first);
581 for (auto i = first; i < last; ++i)
582 result.push_cell(Codec::clone(*store_, read_cell(i), *store_));
583 return result;
584 }
585
586 void reverse() {
587 const auto n = size();
588 for (std::uint64_t i = 0; i < n / 2; ++i) {
589 const Cell saved = read_cell(i);
590 write_cell(i, read_cell(n - i - 1));
591 write_cell(n - i - 1, saved);
592 }
593 }
594
595 // Fold logical values in order, not ids or spare capacity. A caller's sink
596 // uses the normal canonical numeric/string/handle representation. Nested
597 // hashing borrows child roots; it allocates no clones or new object ids.
598 template<class Sink>
599 void hash_into(Sink& sink) const {
601 const auto n = size();
602 sink.u(n);
603 for (std::uint64_t i = 0; i < n; ++i) Codec::hash(*store_, read_cell(i), sink);
604 }
605};
606
607namespace detail::script_array {
608
609template<class U>
610struct ElementCodec<ScriptArray<U>, void> {
611 using Cell = o1::Id;
612 static constexpr bool owns_objects = true;
613 static Cell encode(o1::Store& store, const ScriptArray<U>& value) {
614 return value.clone_into(store);
615 }
616 static ScriptArray<U> decode(const o1::Store& store, Cell child) {
617 if (child == o1::kNullId) return {};
618 ScriptArray<U> borrowed(const_cast<o1::Store&>(store), child,
619 typename ScriptArray<U>::Borrow{});
620 return borrowed.copy();
621 }
622 static Cell clone(const o1::Store& source, Cell child, o1::Store& target) {
623 if (child == o1::kNullId) return child;
624 ScriptArray<U> borrowed(const_cast<o1::Store&>(source), child,
625 typename ScriptArray<U>::Borrow{});
626 return borrowed.clone_into(target);
627 }
628 static void destroy(o1::Store& store, Cell child) {
629 ScriptArray<U>::destroy_tree(store, child);
630 }
631 template<class Sink>
632 static void hash(const o1::Store& store, Cell child, Sink& sink) {
633 if (child == o1::kNullId) {
635 } else {
636 ScriptArray<U> borrowed(const_cast<o1::Store&>(store), child,
637 typename ScriptArray<U>::Borrow{});
638 borrowed.hash_into(sink);
639 }
640 }
641};
642
643} // namespace detail::script_array
644
645// Main's o1::Tx scopes are flattened, not savepoints: a joined rollback only
646// marks the outer scope rollback-only. Generated fill hooks must own the outer
647// scope. Destroy each finished handle before another outer begin(), including
648// when commit() rolls back a rollback-only scope and then throws.
650 std::unique_ptr<detail::o1::Tx> tx_;
651public:
652 explicit ScriptArrayTransaction(detail::o1::Store& store) : tx_(store.begin()) {}
655 void commit() {
656 auto scope = std::move(tx_);
657 if (scope) scope->commit();
658 }
659 void rollback() {
660 auto scope = std::move(tx_);
661 if (scope) scope->rollback();
662 }
663 bool active() const noexcept { return static_cast<bool>(tx_); }
664};
665
666} // namespace pineforge
667#endif // PINEFORGE_ARRAY_HPP
ScriptArrayTransaction(const ScriptArrayTransaction &)=delete
ScriptArrayTransaction(detail::o1::Store &store)
Definition array.hpp:652
ScriptArrayTransaction & operator=(const ScriptArrayTransaction &)=delete
bool active() const noexcept
Definition array.hpp:663
ScriptArray(const ScriptArray &other)
Definition array.hpp:372
void set_i(std::int64_t index, const T &value)
Definition array.hpp:442
T get(Index index) const
Definition array.hpp:422
void fill(const T &value, From from, To to)
Definition array.hpp:563
bool empty() const
Definition array.hpp:417
void push(const T &value)
Definition array.hpp:503
T remove_raw(Index index)
Definition array.hpp:461
detail::o1::Store & store() const
Definition array.hpp:410
static ScriptArray from(detail::o1::Store &store, std::initializer_list< T > values)
Definition array.hpp:366
ScriptArray slice(From from, To to) const
Definition array.hpp:573
detail::o1::Id id() const noexcept
Definition array.hpp:409
T get_v6(Index index) const
Definition array.hpp:467
void mutate(std::int64_t index, Function &&function)
Definition array.hpp:480
static constexpr std::size_t element_bytes
Definition array.hpp:341
void unshift(const T &value)
Definition array.hpp:519
T remove_i(std::int64_t index)
Definition array.hpp:445
void append(const ScriptArray &other)
Definition array.hpp:547
static ScriptArray preallocated(detail::o1::Store &store, std::uint64_t capacity)
Definition array.hpp:360
std::uint64_t size() const
Definition array.hpp:415
ScriptArray & operator=(const ScriptArray &other)
Definition array.hpp:379
void concat(const ScriptArray &other)
Definition array.hpp:555
bool store_backed() const noexcept
Definition array.hpp:408
void set_v6(Index index, const T &value)
Definition array.hpp:471
ScriptArray copy() const
Definition array.hpp:419
ScriptArray & operator=(ScriptArray &&other)
Definition array.hpp:388
ScriptArray(detail::o1::Store &store)
Definition array.hpp:349
T remove(Index index)
Definition array.hpp:475
void reserve(std::uint64_t count)
Definition array.hpp:487
T get_i(std::int64_t index) const
Definition array.hpp:439
ScriptArray alias() const
Definition array.hpp:418
T get_raw(Index index) const
Definition array.hpp:449
ScriptArray(detail::o1::Store &store, std::uint64_t count, const T &initial=na< T >())
Definition array.hpp:354
void fill(const T &value, From from)
Definition array.hpp:559
void set(Index index, const T &value)
Definition array.hpp:431
void hash_into(Sink &sink) const
Definition array.hpp:599
std::uint64_t capacity() const
Definition array.hpp:416
bool alive() const
Definition array.hpp:414
void set_raw(Index index, const T &value)
Definition array.hpp:455
static constexpr std::size_t payload_page_bytes
Definition array.hpp:343
ScriptArray(ScriptArray &&other) noexcept
Definition array.hpp:381
void insert_raw(Index index, const T &value)
Definition array.hpp:513
static constexpr std::size_t header_bytes
Definition array.hpp:342
void fill(const T &value)
Definition array.hpp:557
void push_back(const T &value)
Definition array.hpp:497
void insert(Index index, const T &value)
Definition array.hpp:506
virtual std::size_t write(Id, std::uint64_t off, const void *src, std::size_t n)=0
virtual std::uint64_t size(Id) const =0
virtual std::size_t read(Id, std::uint64_t off, void *dst, std::size_t n) const =0
virtual bool alive(Id) const =0
virtual void release(Id)=0
std::uint64_t Id
Definition o1_store.hpp:46
constexpr Id kNullId
Definition o1_store.hpp:47
void empty_array_error(const char *)
o1::Id store_string(o1::Store &, const std::string &)
std::int64_t checked_index_double(double, std::uint64_t, const char *)
o1::Id clone_bytes(const o1::Store &, o1::Id, o1::Store &)
std::int64_t index_integer(Index value, std::uint64_t size, const char *method)
Definition array.hpp:94
void write_exact(o1::Store &store, o1::Id id, std::uint64_t offset, const void *source, std::size_t bytes)
Definition array.hpp:75
void invariant(const char *)
void index_error_double(const char *, double, std::uint64_t)
void inverted_range_error(const char *)
void index_error(const char *, std::int64_t, std::uint64_t, bool missing=false)
std::string load_string(const o1::Store &, o1::Id)
constexpr std::uint64_t kPayloadPageBytes
Definition array.hpp:56
std::uint64_t checked(std::int64_t raw, std::uint64_t size, const char *method, bool relative, bool end=false)
Definition array.hpp:81
constexpr std::uint64_t kHeaderMagic
Definition array.hpp:55
void read_exact(const o1::Store &store, o1::Id id, std::uint64_t offset, void *destination, std::size_t bytes)
Definition array.hpp:69
bool is_na(const Line &h)
Definition drawing.hpp:58
T na()
Definition na.hpp:12
static Cell clone(const o1::Store &source, Cell child, o1::Store &target)
Definition array.hpp:622
static void hash(const o1::Store &store, Cell child, Sink &sink)
Definition array.hpp:632
static ScriptArray< U > decode(const o1::Store &store, Cell child)
Definition array.hpp:616
static Cell encode(o1::Store &store, const ScriptArray< U > &value)
Definition array.hpp:613
static std::string decode(const o1::Store &store, Cell value)
Definition array.hpp:144
static Cell encode(o1::Store &store, const std::string &value)
Definition array.hpp:141
static void destroy(o1::Store &store, Cell value)
Definition array.hpp:150
static void hash(const o1::Store &store, Cell value, Sink &sink)
Definition array.hpp:154
static Cell clone(const o1::Store &source, Cell value, o1::Store &target)
Definition array.hpp:147
static T decode(const o1::Store &, const Cell &value)
Definition array.hpp:120
static Cell encode(o1::Store &, const T &value)
Definition array.hpp:119
static void hash(const o1::Store &, const Cell &value, Sink &sink)
Definition array.hpp:124
static void destroy(o1::Store &, const Cell &)
Definition array.hpp:122
static Cell clone(const o1::Store &, const Cell &value, o1::Store &)
Definition array.hpp:121