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;
47 bool pending() const noexcept {
return status_ != ScratchStatus::ok; }
49 if (
pending() || status == ScratchStatus::ok)
return;
51 if (status == ScratchStatus::io_error)
52 if (
const auto* failure = store.carried_failure()) failure_ = *failure;
57 std::optional<pineforge::detail::ReportScratchIoError> failure_;
59static_assert(std::is_nothrow_copy_constructible_v<pineforge::detail::ReportScratchIoError>
60 && std::is_nothrow_copy_assignable_v<pineforge::detail::ReportScratchIoError>,
61 "teardown must retain the typed backing failure without throwing");
84 std::int64_t cycle, std::uint64_t count,
void*,
OpeningReader) = 0;
98 void (*
observe_rows)(
void*, std::uint64_t scope, std::size_t rows)
noexcept =
nullptr;
124 if (status == ScratchStatus::io_error)
150 const auto status = scratch.
lots(host, mark, &visit,
152 return (*
static_cast<Visit*
>(context))(lot);
160 const auto status = scratch.
working(host, &visit,
162 return (*
static_cast<Visit*
>(context))(request);
172 ScratchField field = ScratchField::value, std::uint64_t offset = 0)
173 : reader_(&reader), handle_(handle), field_(field), offset_(offset) {
176 const auto bytes = field == ScratchField::key ? info.key_bytes : info.value_bytes;
178 size_ = bytes - offset;
180 std::uint64_t
size() const noexcept
override {
return size_; }
182 return reader_->carried_failure();
184 bool empty() const noexcept {
return size_ == 0; }
186 if (offset > size_ || destination.size > size_ - offset)
187 return ScratchStatus::programming_error;
188 return reader_->read(handle_, field_, offset_ + offset, destination);
194 std::uint64_t offset_;
195 std::uint64_t size_ = 0;
199 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(text.data()), text.size()});
206 : parts_{{{
reinterpret_cast<const std::byte*
>(left.data()), left.size()},
207 {
reinterpret_cast<const std::byte*
>(
"\x1f"), 1},
208 {
reinterpret_cast<const std::byte*
>(right.data()), right.size()}}} {}
209 std::uint64_t
size() const noexcept
override {
210 return parts_[0].size + 1 + parts_[2].size;
213 if (offset >
size() || destination.size >
size() - offset
214 || (destination.size && !destination.data))
215 return ScratchStatus::programming_error;
216 for (
const auto& part : parts_) {
217 if (offset >= part.size) { offset -= part.size;
continue; }
218 const auto count = std::min<std::size_t>(destination.size, part.size - offset);
219 if (count) std::memcpy(destination.data, part.data + offset, count);
220 if (count) destination.data += count;
221 destination.size -= count;
223 if (!destination.size)
break;
225 return ScratchStatus::ok;
228 std::array<pineforge::detail::ScratchBytes, 3> parts_;
233 std::uint64_t right_offset,
int& result)
noexcept {
234 if (left_offset > left.size() || right_offset > right.size())
235 return ScratchStatus::programming_error;
236 const auto left_size = left.size() - left_offset;
237 const auto right_size = right.size() - right_offset;
238 const auto common = std::min(left_size, right_size);
239 std::array<std::byte, 128> a{}, b{};
240 for (std::uint64_t offset = 0; offset < common;) {
241 const auto count =
static_cast<std::size_t
>(std::min<std::uint64_t>(
242 a.size(), common - offset));
243 auto status = left.read(left_offset + offset, {a.data(), count});
244 if (status != ScratchStatus::ok)
return status;
245 status = right.read(right_offset + offset, {b.data(), count});
246 if (status != ScratchStatus::ok)
return status;
247 const int order = std::memcmp(a.data(), b.data(), count);
248 if (order != 0) { result = order;
return ScratchStatus::ok; }
251 result = left_size < right_size ? -1 : (right_size < left_size ? 1 : 0);
252 return ScratchStatus::ok;
256 if (left.
size() != right.
size())
return false;
259 if (status == ScratchStatus::io_error) {
270 return right == left;
273 return !(left == right);
276 return !(right == left);
287 std::uint64_t major = 0, std::uint64_t ordinal = 0,
289 : words_{scope, kind, major, ordinal}, text_(text) {}
290 std::uint64_t
size() const noexcept
override {
294 if (offset >
size() || destination.size >
size() - offset
295 || (destination.size && !destination.data))
296 return ScratchStatus::programming_error;
298 const auto count = std::min<std::size_t>(destination.size,
300 std::memcpy(destination.data,
301 reinterpret_cast<const std::byte*
>(words_.data()) + offset, count);
302 destination.data += count;
303 destination.size -= count;
306 return destination.size
316 return {0x5832355832370002ULL, stats,
318 int& result)
noexcept {
321 if (left.size() <
sizeof(a) || right.size() <
sizeof(b))
322 return ScratchStatus::programming_error;
323 auto status = left.read(0, {
reinterpret_cast<std::byte*
>(a.data()),
sizeof(a)});
324 if (status != ScratchStatus::ok)
return status;
325 status = right.read(0, {
reinterpret_cast<std::byte*
>(b.data()),
sizeof(b)});
326 if (status != ScratchStatus::ok)
return status;
327 for (std::size_t i = 0; i < 3; ++i) {
328 if (a[i] == b[i])
continue;
329 result = a[i] < b[i] ? -1 : 1;
330 return ScratchStatus::ok;
333 if (status != ScratchStatus::ok || result != 0)
return status;
334 result = a[3] < b[3] ? -1 : (b[3] < a[3] ? 1 : 0);
335 return ScratchStatus::ok;
344 {reinterpret_cast<std::byte*>(words.data()), sizeof(words)}), context.
store);
352 if (status == ScratchStatus::end)
return 0;
359 auto status = operation();
360 if (status == ScratchStatus::ok)
361 status = context.
store.
commit(ScratchDurability::buffered);
362 if (status != ScratchStatus::ok) {
365 std::optional<pineforge::detail::ReportScratchIoError> failure;
366 if (status == ScratchStatus::io_error)
380 std::uint64_t scope, std::uint64_t first_kind, std::uint64_t last_kind,
382 if (failure.pending())
return;
385 auto status = store.lower_bound(key, handle);
386 if (status != ScratchStatus::ok)
return status;
388 status = store.read(handle, ScratchField::key, 0,
389 {
reinterpret_cast<std::byte*
>(words.data()),
sizeof(words)});
390 if (status != ScratchStatus::ok)
return status;
391 return words[0] != scope || words[1] > last_kind
392 ? ScratchStatus::end : ScratchStatus::ok;
396 auto status = next(handle);
397 if (status == ScratchStatus::end)
return;
398 if (status != ScratchStatus::ok) {
399 failure.remember(status, store);
402 status = store.begin();
403 if (status != ScratchStatus::ok) {
404 failure.remember(status, store);
408 status = store.erase(handle);
410 status = next(handle);
411 if (status == ScratchStatus::end) {
412 status = ScratchStatus::ok;
415 if (status != ScratchStatus::ok)
break;
417 if (status == ScratchStatus::ok)
418 status = store.commit(ScratchDurability::buffered);
419 if (status != ScratchStatus::ok) {
420 failure.remember(status, store);
421 const auto rollback_status = store.rollback();
422 failure.remember(rollback_status, store);
432 if (
context.next_scope == std::numeric_limits<std::uint64_t>::max())
439 : context_(std::exchange(other.context_,
nullptr)), scope_(other.scope_) {}
441 if (!context_)
return;
442 if (!context_->teardown_failure.pending()
443 && !(context_->versions && context_->versions->teardown_failed())) {
444 if (context_->versions) context_->versions->release_scope(scope_);
445 if (!(context_->versions && context_->versions->teardown_failed()))
446 exit_scratch_erase_scope_noexcept(context_->store, scope_, 0,
447 std::numeric_limits<std::uint64_t>::max(), context_->teardown_failure);
454 std::uint64_t
scope() const noexcept {
return scope_; }
459 std::numeric_limits<std::uint64_t>::max(), context_->teardown_failure);
464 std::uint64_t scope_ = 0;
473 void put(std::uint64_t major, std::uint64_t value, std::uint64_t ordinal = 0) {
477 void put(
const std::string& text, std::uint64_t value) {
485 static constexpr std::uint64_t
absent = std::numeric_limits<std::uint64_t>::max();
486 std::uint64_t
find(std::uint64_t major)
const {
488 return find_key(key);
490 std::uint64_t
find(
const std::string& text)
const {
496 return find_key(key);
498 template<
class Visit>
void each(Visit visit)
const { walk(0,
false, visit); }
499 template<
class Visit>
void each(std::uint64_t major, Visit visit)
const {
500 walk(major,
true, visit);
504 const ScratchSpanSource payload({
reinterpret_cast<const std::byte*
>(&value),
sizeof(value)});
505 auto& context = scope_.
context();
508 return context.store.put(key, payload, handle);
512 auto& context = scope_.
context();
514 if (context.observe_lookup)
515 context.observe_lookup(context.observation, handle ? 1 : 0);
516 if (!handle)
return absent;
517 const ExitScratchText found(context.store, handle, ScratchField::key);
519 std::uint64_t value = 0;
521 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.store);
524 template<
class Visit>
void walk(std::uint64_t major,
bool restricted, Visit visit)
const {
525 auto& context = scope_.context();
526 const ExitScratchKey
key(scope_.scope(), 0, major);
530 if (words[0] != scope_.scope() || (restricted && words[2] != major))
break;
531 std::uint64_t
value = 0;
533 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.store);
534 if (!visit(value))
break;
535 const auto status = context.store.next(handle, handle);
536 if (status == ScratchStatus::end)
break;
540 ExitScratchScope scope_;
544 static_assert(std::is_trivially_copyable_v<T>,
545 "external payloads require a version, never an object/pointer spill");
547 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(&value),
sizeof(value)});
552 {reinterpret_cast<std::byte*>(&value), sizeof(value)}), context.
store);
561 return {context.
store, handle};
569 return ScratchSpanSource({
reinterpret_cast<const std::byte*
>(&value.incarnation),
570 sizeof(value.incarnation)});
574 std::uint64_t incarnation = 0;
576 {reinterpret_cast<std::byte*>(&incarnation), sizeof(incarnation)}), context.
store);
577 return {context.
run, incarnation};
592 : scope_(std::move(other.scope_)), count_(other.count_) {}
593 std::uint64_t
scope() const noexcept {
return scope_.scope(); }
595 std::size_t
size() const noexcept {
return static_cast<std::size_t
>(count_); }
596 bool empty() const noexcept {
return count_ == 0; }
599 if constexpr (std::is_same_v<T, native_order::RequestHandle>) {
600 if (value.run !=
context().run)
607 void set(std::size_t index,
const T& value) {
608 static_assert(!std::is_same_v<T, std::string>,
"text selections are immutable");
614 auto front()
const {
return (*
this)[0]; }
620 template<
class Key,
class Visit>
void ordered(Key key, Visit visit)
const {
622 for (std::size_t i = 0; i <
size(); ++i) index.
put(key((*
this)[i]), i, i);
623 index.
each([&](std::uint64_t ordinal) {
624 return visit((*
this)[ordinal],
static_cast<std::size_t
>(ordinal));
628 static_assert(std::is_same_v<T, std::string>,
"text index only");
634 if (!handle)
return false;
636 return words[0] ==
scope() && words[1] == 1
640 template<
class Visit>
void ordered_text(Visit visit,
bool unique =
false)
const {
641 static_assert(std::is_same_v<T, std::string>,
"text index only");
647 if (words[0] !=
scope() || words[1] != 1)
break;
648 const auto row = find(words[3]);
652 if (!visit(text))
break;
656 if (status == ScratchStatus::end)
break;
664 std::declval<const ExitScratchContext&>(),
ScratchHandle{}));
682 void observe() const noexcept {
687 const ExitScratchKey key(
scope(), 0, 0, index);
694 void put(std::uint64_t index,
const T& value) {
695 const ExitScratchKey key(
scope(), 0, 0, index);
700 if constexpr (std::is_same_v<T, std::string>) {
701 if (status != ScratchStatus::ok)
return status;
702 const ExitScratchKey
ordered(
scope(), 1, 0, index, &payload);
709 ExitScratchScope scope_;
710 std::uint64_t count_ = 0;
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
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
ExitScratchRows(const PineExecutionAdapter &owner)
void ordered(Key key, Visit visit) const
bool empty() const noexcept
void push_back(const T &value)
std::size_t size() const noexcept
void ordered_text(Visit visit, bool unique=false) const
ExitScratchRows(const ExitScratchRows &)=delete
bool contains(const std::string &text) const
ExitScratchRows(ExitScratchContext &context)
std::uint64_t scope() const noexcept
ExitScratchRows(ExitScratchRows &&other) noexcept
const_iterator begin() const
ExitScratchContext & context() const noexcept
const_iterator end() const
auto operator[](std::size_t index) const
ExitScratchRows & operator=(const ExitScratchRows &)=delete
void set(std::size_t index, const T &value)
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
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
const ExitScratchRows * rows
std::ptrdiff_t difference_type
bool operator!=(const const_iterator &other) const
const_iterator & operator++()
std::input_iterator_tag iterator_category
const_iterator operator++(int)
bool operator==(const const_iterator &other) const
value_type operator*() const
decltype(ExitScratchValue< T >::read( std::declval< const ExitScratchContext & >(), ScratchHandle{})) value_type
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)