PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
journal_codec.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <algorithm>
5#include <array>
6#include <cstring>
7#include <limits>
8#include <string>
9#include <type_traits>
10
12
15 void* context;
16 void (*observe)(void*, WorkOperation, std::uint64_t) noexcept;
17};
18
19// Borrowed registration on the calling thread. A nonnull observer must have a
20// valid callback and outlive its registration; restore the returned pointer.
21// payload_source/read cover stored and admission-encoded sources alike.
22// payload_read reports requested bytes; the other operations report zero.
23inline thread_local const WorkObserver* work_observer = nullptr;
24inline const WorkObserver* set_work_observer(const WorkObserver* observer) noexcept {
25 const auto previous = work_observer;
26 work_observer = observer;
27 return previous;
28}
29inline void observe_work(WorkOperation operation, std::uint64_t bytes = 0) noexcept {
30 if (work_observer)
31 work_observer->observe(work_observer->context, operation, bytes);
32}
33
34// A counting/slicing sink. Each read spells intersecting bytes directly into
35// the caller's destination, without constructing a contiguous record.
36struct Writer {
37 std::uint64_t position = 0;
38 std::uint64_t offset = 0;
41
42 // Explicit caller status reads, excluding the internal bytes() guard.
47 void bytes(const void* data, std::uint64_t length) noexcept {
48 if (status != ScratchStatus::ok) return;
49 if (length > std::numeric_limits<std::uint64_t>::max() - position) {
51 return;
52 }
53 const auto end = position + length;
54 const auto begin = std::max(position, offset);
55 const auto finish = std::min(end, offset + destination.size);
56 if (begin < finish)
57 std::memcpy(destination.data + static_cast<std::size_t>(begin - offset),
58 static_cast<const std::byte*>(data)
59 + static_cast<std::size_t>(begin - position),
60 static_cast<std::size_t>(finish - begin));
61 position = end;
62 }
63 void word(std::uint64_t value) noexcept {
65 std::array<std::byte, 8> bytes{};
66 for (unsigned i = 0; i != 8; ++i)
67 bytes[i] = static_cast<std::byte>(value >> (8 * i));
68 this->bytes(bytes.data(), bytes.size());
69 }
70 void text(const std::string& value) noexcept {
71 word(value.size());
72 bytes(value.data(), value.size());
73 }
74};
75
76struct Reader {
79 std::uint64_t position = 0;
81 std::uint64_t cached_at = 0;
82 std::size_t cached_size = 0;
83
84 std::uint64_t remaining() const noexcept {
85 return position <= source.size() ? source.size() - position : 0;
86 }
87 void bytes(void* destination, std::size_t length) noexcept {
88 if (status != ScratchStatus::ok) return;
89 if (!page.data || !page.size) {
91 return;
92 }
93 if (length > remaining()) { status = ScratchStatus::corrupt; return; }
94 auto* out = static_cast<std::byte*>(destination);
95 while (length) {
99 cached_size = static_cast<std::size_t>(
100 std::min<std::uint64_t>(page.size, remaining()));
101 status = source.read(position, {page.data, cached_size});
102 if (status != ScratchStatus::ok) return;
103 }
104 const auto at = static_cast<std::size_t>(position - cached_at);
105 const auto count = std::min(length, cached_size - at);
106 std::memcpy(out, page.data + at, count);
107 out += count;
108 position += count;
109 length -= count;
110 }
111 }
112 void skip(std::uint64_t length) noexcept {
113 if (status != ScratchStatus::ok) return;
114 if (length > remaining()) { status = ScratchStatus::corrupt; return; }
115 position += length;
116 }
117 std::uint64_t word() noexcept {
118 std::array<std::byte, 8> bytes{};
119 this->bytes(bytes.data(), bytes.size());
120 std::uint64_t value = 0;
121 for (unsigned i = 0; i != 8; ++i)
122 value |= std::uint64_t(std::to_integer<unsigned>(bytes[i])) << (8 * i);
123 return value;
124 }
125 bool presence() noexcept {
126 std::byte value{};
127 bytes(&value, 1);
128 if (value != std::byte{0} && value != std::byte{1})
130 return value == std::byte{1};
131 }
132 template<class T> void integer(T& out) noexcept {
133 const auto value = word();
134 if constexpr (std::is_signed_v<T>) {
135 const auto maximum = static_cast<std::uint64_t>(std::numeric_limits<T>::max());
136 const auto minimum = static_cast<std::uint64_t>(std::numeric_limits<T>::min());
137 if (value > maximum && value < minimum) {
139 return;
140 }
141 std::int64_t signed_value = 0;
142 std::memcpy(&signed_value, &value, sizeof value);
143 out = static_cast<T>(signed_value);
144 } else {
145 if (value > std::numeric_limits<T>::max()) {
147 return;
148 }
149 out = static_cast<T>(value);
150 }
151 }
152 void real(double& out) noexcept {
153 const auto value = word();
154 std::memcpy(&out, &value, sizeof value);
155 }
156 void text(std::string& out, bool owning) {
157 const auto length = word();
158 if (status != ScratchStatus::ok) return;
159 if (length > remaining() || length > out.max_size()) {
161 return;
162 }
163 if (!owning) { skip(length); return; }
164 out.resize(static_cast<std::size_t>(length));
165 bytes(out.data(), out.size());
166 }
167 ScratchStatus finish() const noexcept {
168 if (status != ScratchStatus::ok) return status;
170 }
171};
172
173class WordSource final : public ScratchByteSource {
174public:
175 explicit WordSource(std::uint64_t value) noexcept {
176 for (unsigned i = 0; i != 8; ++i)
177 bytes_[i] = static_cast<std::byte>(value >> (8 * i));
178 }
179 std::uint64_t size() const noexcept override { return bytes_.size(); }
180 ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override {
181 return ScratchSpanSource({bytes_.data(), bytes_.size()}).read(offset, out);
182 }
183private:
184 std::array<std::byte, 8> bytes_{};
185};
186
187class StoreSource final : public ScratchByteSource {
188public:
190 std::uint64_t length) noexcept : store_(store), handle_(handle), length_(length) {
192 }
193 std::uint64_t size() const noexcept override { return length_; }
194 const ReportScratchIoError* carried_failure() const noexcept override {
195 return store_.carried_failure();
196 }
197 ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override {
199 return store_.read(handle_, ScratchField::value, offset, out);
200 }
201private:
202 const ScratchReadback& store_;
203 ScratchHandle handle_;
204 std::uint64_t length_;
205};
206
207inline ScratchStatus find(CommandScratchStore& store, std::uint64_t key,
208 ScratchRecordInfo& out) noexcept {
209 const WordSource probe(key);
211 auto status = store.lower_bound(probe, handle);
213 if (status != ScratchStatus::ok) return status;
214 std::array<std::byte, 8> bytes{}, expected{};
215 status = store.read(handle, ScratchField::key, 0, {bytes.data(), bytes.size()});
216 if (status != ScratchStatus::ok) return status;
217 probe.read(0, {expected.data(), expected.size()});
218 if (bytes != expected) return ScratchStatus::not_found;
219 return store.point(handle, out);
220}
221
224 status = store.commit(ScratchDurability::buffered);
225 if (status != ScratchStatus::ok && store.transaction_active()) {
226 const auto rollback = store.rollback();
227 if (rollback != ScratchStatus::ok) return rollback;
228 }
229 return status;
230}
231inline ScratchStatus put(CommandScratchStore& store, std::uint64_t key,
232 const ScratchByteSource& value) noexcept {
233 auto status = store.begin();
234 if (status != ScratchStatus::ok) return status;
236 const WordSource probe(key);
237 return finish(store, store.put(probe, value, handle));
238}
239inline ScratchStatus clear(CommandScratchStore& store) noexcept {
240 auto status = store.begin();
241 if (status != ScratchStatus::ok) return status;
243 while ((status = store.first(handle)) == ScratchStatus::ok) {
244 status = store.erase(handle);
245 if (status != ScratchStatus::ok) break;
246 }
248 return finish(store, status);
249}
250
251} // namespace pineforge::detail::journal_codec
ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override
StoreSource(const ScratchReadback &store, ScratchHandle handle, std::uint64_t length) noexcept
std::uint64_t size() const noexcept override
const ReportScratchIoError * carried_failure() const noexcept override
ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override
std::uint64_t size() const noexcept override
const WorkObserver * set_work_observer(const WorkObserver *observer) noexcept
ScratchStatus find(CommandScratchStore &store, std::uint64_t key, ScratchRecordInfo &out) noexcept
thread_local const WorkObserver * work_observer
ScratchStatus finish(CommandScratchStore &store, ScratchStatus status) noexcept
ScratchStatus clear(CommandScratchStore &store) noexcept
void observe_work(WorkOperation operation, std::uint64_t bytes=0) noexcept
ScratchStatus put(CommandScratchStore &store, std::uint64_t key, const ScratchByteSource &value) noexcept
std::uint64_t ScratchHandle
constexpr ScratchHandle scratch_no_handle
void bytes(void *destination, std::size_t length) noexcept
void text(std::string &out, bool owning)
ScratchStatus finish() const noexcept
std::uint64_t remaining() const noexcept
void skip(std::uint64_t length) noexcept
void(* observe)(void *, WorkOperation, std::uint64_t) noexcept
void bytes(const void *data, std::uint64_t length) noexcept
void word(std::uint64_t value) noexcept
ScratchStatus checked_status() const noexcept
void text(const std::string &value) noexcept