26using pineforge::detail::ScratchBuffer;
27using pineforge::detail::ScratchByteSource;
31using pineforge::detail::ScratchKeyOrder;
32using pineforge::detail::ScratchReadback;
33using pineforge::detail::ScratchRecordInfo;
34using pineforge::detail::ScratchSpanSource;
36using pineforge::detail::CommandScratchStore;
48 bool pending() const noexcept {
return status_ != ScratchStatus::ok; }
50 if (
pending() || status == ScratchStatus::ok)
return;
52 if (status == ScratchStatus::io_error)
53 if (
const auto* failure = store.carried_failure()) failure_ = *failure;
58 std::optional<pineforge::detail::ReportScratchIoError> failure_;
60static_assert(std::is_nothrow_copy_constructible_v<pineforge::detail::ReportScratchIoError>
61 && std::is_nothrow_copy_assignable_v<pineforge::detail::ReportScratchIoError>,
62 "teardown must retain the typed backing failure without throwing");
85 std::int64_t cycle, std::uint64_t count,
void*,
OpeningReader) = 0;
99 void (*
observe_rows)(
void*, std::uint64_t scope, std::size_t rows)
noexcept =
nullptr;
125 if (status == ScratchStatus::io_error)
151 const auto status = scratch.
lots(host, mark, &visit,
153 return (*
static_cast<Visit*
>(context))(lot);
161 const auto status = scratch.
working(host, &visit,
163 return (*
static_cast<Visit*
>(context))(request);
173 ScratchField field = ScratchField::value, std::uint64_t offset = 0)
174 : reader_(&reader), handle_(handle), field_(field), offset_(offset) {
177 const auto bytes = field == ScratchField::key ? info.key_bytes : info.value_bytes;
179 size_ = bytes - offset;
181 std::uint64_t
size() const noexcept
override {
return size_; }
183 return reader_->carried_failure();
185 bool empty() const noexcept {
return size_ == 0; }
187 if (offset > size_ || destination.size > size_ - offset)
188 return ScratchStatus::programming_error;
189 return reader_->read(handle_, field_, offset_ + offset, destination);
195 std::uint64_t offset_;
196 std::uint64_t size_ = 0;
200 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(text.data()), text.size()});
207 : parts_{{{
reinterpret_cast<const std::byte*
>(left.data()), left.size()},
208 {
reinterpret_cast<const std::byte*
>(
"\x1f"), 1},
209 {
reinterpret_cast<const std::byte*
>(right.data()), right.size()}}} {}
210 std::uint64_t
size() const noexcept
override {
211 return parts_[0].size + 1 + parts_[2].size;
214 if (offset >
size() || destination.size >
size() - offset
215 || (destination.size && !destination.data))
216 return ScratchStatus::programming_error;
217 for (
const auto& part : parts_) {
218 if (offset >= part.size) { offset -= part.size;
continue; }
219 const auto count = std::min<std::size_t>(destination.size, part.size - offset);
220 if (count) std::memcpy(destination.data, part.data + offset, count);
221 if (count) destination.data += count;
222 destination.size -= count;
224 if (!destination.size)
break;
226 return ScratchStatus::ok;
229 std::array<pineforge::detail::ScratchBytes, 3> parts_;
234 std::uint64_t right_offset,
int& result)
noexcept {
235 if (left_offset > left.size() || right_offset > right.size())
236 return ScratchStatus::programming_error;
237 const auto left_size = left.size() - left_offset;
238 const auto right_size = right.size() - right_offset;
239 const auto common = std::min(left_size, right_size);
240 std::array<std::byte, 128> a{}, b{};
241 for (std::uint64_t offset = 0; offset < common;) {
242 const auto count =
static_cast<std::size_t
>(std::min<std::uint64_t>(
243 a.size(), common - offset));
244 auto status = left.read(left_offset + offset, {a.data(), count});
245 if (status != ScratchStatus::ok)
return status;
246 status = right.read(right_offset + offset, {b.data(), count});
247 if (status != ScratchStatus::ok)
return status;
248 const int order = std::memcmp(a.data(), b.data(), count);
249 if (order != 0) { result = order;
return ScratchStatus::ok; }
252 result = left_size < right_size ? -1 : (right_size < left_size ? 1 : 0);
253 return ScratchStatus::ok;
257 if (left.
size() != right.
size())
return false;
260 if (status == ScratchStatus::io_error) {
271 return right == left;
274 return !(left == right);
277 return !(right == left);
288 std::uint64_t major = 0, std::uint64_t ordinal = 0,
290 : words_{scope, kind, major, ordinal}, text_(text) {}
291 std::uint64_t
size() const noexcept
override {
295 if (offset >
size() || destination.size >
size() - offset
296 || (destination.size && !destination.data))
297 return ScratchStatus::programming_error;
299 const auto count = std::min<std::size_t>(destination.size,
301 std::memcpy(destination.data,
302 reinterpret_cast<const std::byte*
>(words_.data()) + offset, count);
303 destination.data += count;
304 destination.size -= count;
307 return destination.size
317 return {0x5832355832370002ULL, stats,
319 int& result)
noexcept {
322 if (left.size() <
sizeof(a) || right.size() <
sizeof(b))
323 return ScratchStatus::programming_error;
324 auto status = left.read(0, {
reinterpret_cast<std::byte*
>(a.data()),
sizeof(a)});
325 if (status != ScratchStatus::ok)
return status;
326 status = right.read(0, {
reinterpret_cast<std::byte*
>(b.data()),
sizeof(b)});
327 if (status != ScratchStatus::ok)
return status;
328 for (std::size_t i = 0; i < 3; ++i) {
329 if (a[i] == b[i])
continue;
330 result = a[i] < b[i] ? -1 : 1;
331 return ScratchStatus::ok;
334 if (status != ScratchStatus::ok || result != 0)
return status;
335 result = a[3] < b[3] ? -1 : (b[3] < a[3] ? 1 : 0);
336 return ScratchStatus::ok;
345 {reinterpret_cast<std::byte*>(words.data()), sizeof(words)}), context.
store);
353 if (status == ScratchStatus::end)
return 0;
361 std::optional<pineforge::detail::ReportScratchIoError> failure;
362 if (status == ScratchStatus::io_error)
372 auto status = operation();
373 if (status == ScratchStatus::ok)
374 status = context.
store.
commit(ScratchDurability::buffered);
383 std::uint64_t scope, std::uint64_t first_kind, std::uint64_t last_kind,
385 if (failure.pending())
return;
388 auto status = store.lower_bound(key, handle);
389 if (status != ScratchStatus::ok)
return status;
391 status = store.read(handle, ScratchField::key, 0,
392 {
reinterpret_cast<std::byte*
>(words.data()),
sizeof(words)});
393 if (status != ScratchStatus::ok)
return status;
394 return words[0] != scope || words[1] > last_kind
395 ? ScratchStatus::end : ScratchStatus::ok;
399 auto status = next(handle);
400 if (status == ScratchStatus::end)
return;
401 if (status != ScratchStatus::ok) {
402 failure.remember(status, store);
405 status = store.begin();
406 if (status != ScratchStatus::ok) {
407 failure.remember(status, store);
411 status = store.erase(handle);
413 status = next(handle);
414 if (status == ScratchStatus::end) {
415 status = ScratchStatus::ok;
418 if (status != ScratchStatus::ok)
break;
420 if (status == ScratchStatus::ok)
421 status = store.commit(ScratchDurability::buffered);
422 if (status != ScratchStatus::ok) {
423 failure.remember(status, store);
424 const auto rollback_status = store.rollback();
425 failure.remember(rollback_status, store);
435 if (
context.next_scope == std::numeric_limits<std::uint64_t>::max())
442 : context_(std::exchange(other.context_,
nullptr)), scope_(other.scope_) {}
444 if (!context_)
return;
445 if (!context_->teardown_failure.pending()
446 && !(context_->versions && context_->versions->teardown_failed())) {
447 if (context_->versions) context_->versions->release_scope(scope_);
448 if (!(context_->versions && context_->versions->teardown_failed()))
449 exit_scratch_erase_scope_noexcept(context_->store, scope_, 0,
450 std::numeric_limits<std::uint64_t>::max(), context_->teardown_failure);
457 std::uint64_t
scope() const noexcept {
return scope_; }
462 std::numeric_limits<std::uint64_t>::max(), context_->teardown_failure);
467 std::uint64_t scope_ = 0;
476 void put(std::uint64_t major, std::uint64_t value, std::uint64_t ordinal = 0) {
480 void put(
const std::string& text, std::uint64_t value) {
488 static constexpr std::uint64_t
absent = std::numeric_limits<std::uint64_t>::max();
489 std::uint64_t
find(std::uint64_t major)
const {
491 return find_key(key);
493 std::uint64_t
find(
const std::string& text)
const {
499 return find_key(key);
501 template<
class Visit>
void each(Visit visit)
const { walk(0,
false, visit); }
502 template<
class Visit>
void each(std::uint64_t major, Visit visit)
const {
503 walk(major,
true, visit);
507 const ScratchSpanSource payload({
reinterpret_cast<const std::byte*
>(&value),
sizeof(value)});
508 auto& context = scope_.
context();
511 return context.store.put(key, payload, handle);
515 auto& context = scope_.
context();
517 if (context.observe_lookup)
518 context.observe_lookup(context.observation, handle ? 1 : 0);
519 if (!handle)
return absent;
520 const ExitScratchText found(context.store, handle, ScratchField::key);
522 std::uint64_t value = 0;
524 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.store);
527 template<
class Visit>
void walk(std::uint64_t major,
bool restricted, Visit visit)
const {
528 auto& context = scope_.context();
529 const ExitScratchKey
key(scope_.scope(), 0, major);
533 if (words[0] != scope_.scope() || (restricted && words[2] != major))
break;
534 std::uint64_t
value = 0;
536 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.store);
537 if (!visit(value))
break;
538 const auto status = context.store.next(handle, handle);
539 if (status == ScratchStatus::end)
break;
543 ExitScratchScope scope_;
547 static_assert(std::is_trivially_copyable_v<T>,
548 "external payloads require a version, never an object/pointer spill");
550 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(&value),
sizeof(value)});
555 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.
store);
564 return {context.
store, handle};
572 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(&value.incarnation),
573 sizeof(value.incarnation)});
577 std::uint64_t incarnation = 0;
579 {reinterpret_cast<std::byte*>(&incarnation), sizeof(incarnation)}), context.
store);
580 return {context.
run, incarnation};
594template<
class T, ExitScratchRowMode Mode = ExitScratchRowMode::per_row>
603 : scope_(std::move(other.scope_)), count_(other.count_),
604 batch_open_(std::exchange(other.batch_open_,
false)) {}
606 std::uint64_t
scope() const noexcept {
return scope_.scope(); }
608 std::size_t
size() const noexcept {
return static_cast<std::size_t
>(count_); }
609 bool empty() const noexcept {
return count_ == 0; }
612 if constexpr (std::is_same_v<T, native_order::RequestHandle>) {
613 if (value.run !=
context().run) {
615 if (batch_open_ &&
context().store.transaction_active())
616 fail_batch(ScratchStatus::programming_error);
621 if (batch_enabled()) {
623 || !
context().store.transaction_active())
625 put_batch(count_, value);
646 if (!batch_open_)
return;
647 if (!
context().store.transaction_active()) {
654 rollback_batch_noexcept();
658 if (status != ScratchStatus::ok)
663 void set(std::size_t index,
const T& value) {
664 static_assert(!std::is_same_v<T, std::string>,
"text selections are immutable");
671 auto front()
const {
return (*
this)[0]; }
678 template<
class Key,
class Visit>
void ordered(Key key, Visit visit)
const {
680 for (std::size_t i = 0; i <
size(); ++i) index.
put(key((*
this)[i]), i, i);
681 index.
each([&](std::uint64_t ordinal) {
682 return visit((*
this)[ordinal],
static_cast<std::size_t
>(ordinal));
686 static_assert(std::is_same_v<T, std::string>,
"text index only");
692 if (!handle)
return false;
694 return words[0] ==
scope() && words[1] == 1
698 template<
class Visit>
void ordered_text(Visit visit,
bool unique =
false)
const {
699 static_assert(std::is_same_v<T, std::string>,
"text index only");
705 if (words[0] !=
scope() || words[1] != 1)
break;
706 const auto row = find(words[3]);
710 if (!visit(text))
break;
714 if (status == ScratchStatus::end)
break;
722 std::declval<const ExitScratchContext&>(),
ScratchHandle{}));
736 const_iterator
begin()
const {
return {
this, 0}; }
737 const_iterator
end()
const {
return {
this,
size()}; }
740 bool batch_enabled() const noexcept {
762 void rollback_batch_noexcept() noexcept {
764 if (!batch_open_)
return;
766 if (!
context().store.transaction_active())
return;
771 ScratchStatus put_value(std::uint64_t index,
const T& value) {
772 const ExitScratchKey
key(
scope(), 0, 0, index);
776 if constexpr (std::is_same_v<T, std::string>) {
777 if (status != ScratchStatus::ok)
return status;
778 const ExitScratchKey
ordered(
scope(), 1, 0, index, &payload);
779 const ScratchSpanSource
empty({
nullptr, 0});
784 void put_batch(std::uint64_t index,
const T& value) {
785 const auto status = put_value(index, value);
786 if (status != ScratchStatus::ok)
789 void observe() const noexcept {
794 const ExitScratchKey
key(
scope(), 0, 0, index);
801 void put(std::uint64_t index,
const T& value) {
803 return put_value(index, value);
806 ExitScratchScope scope_;
807 std::uint64_t count_ = 0;
809 bool batch_open_ =
false;
coded(RunFailureCode code, const RunFailureArgs &args, const std::string &what)
ScratchStatus begin() noexcept
const ReportScratchIoError * carried_failure() const noexcept override
ScratchStatus read(ScratchHandle, ScratchField, std::uint64_t offset, ScratchBuffer destination) const noexcept override
ScratchStatus rollback() noexcept
ScratchStatus commit(ScratchDurability durability) noexcept
ScratchStatus next(ScratchHandle current, ScratchHandle &result) const noexcept
ScratchStatus lower_bound(const ScratchByteSource &probe, ScratchHandle &result) const noexcept
ScratchStatus put(const ScratchByteSource &key, const ScratchByteSource &value, ScratchHandle &result) noexcept
bool transaction_active() const noexcept
bool has_io_observer() const noexcept
virtual const ReportScratchIoError * carried_failure() const noexcept
virtual std::uint64_t size() const noexcept=0
virtual ScratchStatus point(ScratchHandle, ScratchRecordInfo &info) const noexcept=0
virtual const ReportScratchIoError * carried_failure() const noexcept
The public native host: an abstract subclass of BacktestEngine with no PineScript on it.
ScratchStatus status() const noexcept
ExitScratchError(ScratchStatus status)
void put(const std::string &text, std::uint64_t value)
void put(std::uint64_t major, std::uint64_t value, std::uint64_t ordinal=0)
std::uint64_t find(std::uint64_t major) const
ExitScratchIndex(const PineExecutionAdapter &owner)
static constexpr std::uint64_t absent
std::uint64_t find(const std::string &text) const
ExitScratchIndex(ExitScratchContext &context)
void each(std::uint64_t major, Visit visit) const
std::uint64_t find(const ScratchByteSource &bytes) const
void put(const ScratchByteSource &bytes, std::uint64_t value)
void each(Visit visit) const
ExitScratchKey(std::uint64_t scope, std::uint64_t kind=0, std::uint64_t major=0, std::uint64_t ordinal=0, const ScratchByteSource *text=nullptr) noexcept
ScratchStatus read(std::uint64_t offset, ScratchBuffer destination) const noexcept override
std::uint64_t size() const noexcept override
std::uint64_t size() const noexcept override
ScratchStatus read(std::uint64_t offset, ScratchBuffer destination) const noexcept override
ExitScratchPairText(const std::string &left, const std::string &right) noexcept
void ordered(Key key, Visit visit) const
std::uint64_t scope() const noexcept
const_iterator begin() const
ExitScratchRows & operator=(const ExitScratchRows &)=delete
void ordered_text(Visit visit, bool unique=false) const
bool contains(const std::string &text) const
const_iterator end() const
void push_back(const T &value)
bool empty() const noexcept
auto operator[](std::size_t index) const
ExitScratchRows(const PineExecutionAdapter &owner)
ExitScratchRows(ExitScratchRows &&other) noexcept
std::size_t size() const noexcept
ExitScratchContext & context() const noexcept
void set(std::size_t index, const T &value)
~ExitScratchRows() noexcept(false)
ExitScratchRows(ExitScratchContext &context)
ExitScratchRows(const ExitScratchRows &)=delete
ExitScratchScope(const ExitScratchScope &)=delete
ExitScratchScope(ExitScratchScope &&other) noexcept
~ExitScratchScope() noexcept(false)
ExitScratchContext & context() const noexcept
ExitScratchScope(ExitScratchContext &context)
std::uint64_t scope() const noexcept
ExitScratchScope & operator=(const ExitScratchScope &)=delete
bool pending() const noexcept
void remember(ScratchStatus status, const ScratchReadback &store) noexcept
const pineforge::detail::ReportScratchIoError * carried_failure() const noexcept override
ScratchStatus read(std::uint64_t offset, ScratchBuffer destination) const noexcept override
ExitScratchText(const ScratchReadback &reader, ScratchHandle handle, ScratchField field=ScratchField::value, std::uint64_t offset=0)
bool empty() const noexcept
std::uint64_t size() const noexcept override
virtual std::uint64_t placement(std::uint64_t scope, const PlacementSnapshot &)=0
virtual std::uint64_t request(std::uint64_t scope, const native_order::Request &)=0
virtual std::uint64_t pending(std::uint64_t scope, const native_order::Request &, const PlacementSnapshot &, const std::string &key)=0
virtual ~ExitScratchVersions()=default
bool teardown_failed() const noexcept
virtual void pending(std::uint64_t version, native_order::Request &, PlacementSnapshot &, std::string &key) const =0
virtual void release_scope(std::uint64_t scope) noexcept=0
ExitScratchTeardownFailure teardown_failure_
virtual native_order::Request request(std::uint64_t version) const =0
virtual native_order::Owner freeze_openings(const native_order::RunIdentity &, std::int64_t cycle, std::uint64_t count, void *, OpeningReader)=0
virtual PlacementSnapshot placement(std::uint64_t version) const =0
ScratchStatus(*)(void *, std::uint64_t, native_order::RequestHandle &) OpeningReader
void check_teardown_failure() const
std::uint64_t ScratchHandle
void rethrow_carried_scratch_failure(const ReportScratchIoError *failure)
std::variant< Independent, WaitForApplied, BindOpening, BindOpenings, BindCohort > Owner
ScratchStatus exit_scratch_compare_text(const ScratchByteSource &left, std::uint64_t left_offset, const ScratchByteSource &right, std::uint64_t right_offset, int &result) noexcept
void exit_scratch_lots(ExitScratchContext &scratch, const NativeStrategyHost &host, double mark, Visit visit)
std::array< std::uint64_t, 4 > ExitScratchKeyWords
ExitScratchVersions & exit_scratch_versions(ExitScratchContext &context)
constexpr std::size_t kExitScopeEraseBatch
bool operator==(const ExitScratchText &left, const std::string &right)
ExitScratchContext & exit_scratch(const PineExecutionAdapter &)
void exit_scratch_check(ScratchStatus status)
bool exit_scratch_equal(const ScratchByteSource &left, const ScratchByteSource &right)
ScratchKeyOrder exit_scratch_key_order(ExitScratchOrderStats *stats=nullptr) noexcept
ExitScratchRows< T, ExitScratchRowMode::bounded_batch > ExitScratchBatchRows
void exit_scratch_edit_failure(ExitScratchContext &context, ScratchStatus status)
void exit_scratch_working(ExitScratchContext &scratch, const NativeStrategyHost &host, Visit visit)
ScratchSpanSource exit_scratch_text(const std::string &text) noexcept
void exit_scratch_erase_scope_noexcept(CommandScratchStore &store, std::uint64_t scope, std::uint64_t first_kind, std::uint64_t last_kind, ExitScratchTeardownFailure &failure) noexcept
constexpr std::size_t exit_scratch_key_bytes
ExitScratchKeyWords exit_scratch_words(ExitScratchContext &context, ScratchHandle handle)
ScratchHandle exit_scratch_seek(ExitScratchContext &context, const ScratchByteSource &key)
bool operator!=(const ExitScratchText &left, const std::string &right)
void exit_scratch_failure(ScratchStatus)
void exit_scratch_edit(ExitScratchContext &context, Edit operation)
void exit_scratch_check_teardown(const ExitScratchContext &context)
Owning value row for one open physical lot, copied at query time by native_open_lots(mark) — the lot-...
Owning value row for one live request, copied at query time.
Aggregate field order keeps market construction: Request{Transact{1.0}, "buy", "comment"}...
ScratchStatus(* lots)(const NativeStrategyHost &, double, void *, bool(*)(void *, const NativeOpenLot &))
CommandScratchStore & store
void(* observe_lookup)(void *, std::size_t candidates) noexcept
void(* observe_rows)(void *, std::uint64_t scope, std::size_t rows) noexcept
native_order::RunIdentity run
ExitScratchTeardownFailure teardown_failure
ScratchStatus(* working)(const NativeStrategyHost &, void *, bool(*)(void *, const NativeWorkingRequest &))
ExitScratchVersions * versions
std::uint64_t comparisons
decltype(ExitScratchValue< T >::read( std::declval< const ExitScratchContext & >(), ScratchHandle{})) value_type
std::input_iterator_tag iterator_category
value_type operator*() const
const ExitScratchRows * rows
const_iterator & operator++()
std::ptrdiff_t difference_type
bool operator==(const const_iterator &other) const
bool operator!=(const const_iterator &other) const
const_iterator operator++(int)
static ScratchSpanSource source(const native_order::RequestHandle &value) noexcept
static native_order::RequestHandle read(const ExitScratchContext &context, ScratchHandle handle)
static ScratchSpanSource source(const std::string &value) noexcept
static ExitScratchText read(const ExitScratchContext &context, ScratchHandle handle)
static ScratchSpanSource source(const T &value) noexcept
static T read(const ExitScratchContext &context, ScratchHandle handle)