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_read reports requested bytes; the other operations report zero.
22inline thread_local const WorkObserver* work_observer = nullptr;
23inline const WorkObserver* set_work_observer(const WorkObserver* observer) noexcept {
24 const auto previous = work_observer;
25 work_observer = observer;
26 return previous;
27}
28inline void observe_work(WorkOperation operation, std::uint64_t bytes = 0) noexcept {
29 if (work_observer)
30 work_observer->observe(work_observer->context, operation, bytes);
31}
32
33// A counting/slicing sink. Each read spells intersecting bytes directly into
34// the caller's destination, without constructing a contiguous record.
35struct Writer {
36 std::uint64_t position = 0;
37 std::uint64_t offset = 0;
40
41 // Explicit caller status reads, excluding the internal bytes() guard.
46 void bytes(const void* data, std::uint64_t length) noexcept {
47 if (status != ScratchStatus::ok) return;
48 if (length > std::numeric_limits<std::uint64_t>::max() - position) {
50 return;
51 }
52 const auto end = position + length;
53 const auto begin = std::max(position, offset);
54 const auto finish = std::min(end, offset + destination.size);
55 if (begin < finish)
56 std::memcpy(destination.data + static_cast<std::size_t>(begin - offset),
57 static_cast<const std::byte*>(data)
58 + static_cast<std::size_t>(begin - position),
59 static_cast<std::size_t>(finish - begin));
60 position = end;
61 }
62 void word(std::uint64_t value) noexcept {
64 std::array<std::byte, 8> bytes{};
65 for (unsigned i = 0; i != 8; ++i)
66 bytes[i] = static_cast<std::byte>(value >> (8 * i));
67 this->bytes(bytes.data(), bytes.size());
68 }
69 void text(const std::string& value) noexcept {
70 word(value.size());
71 bytes(value.data(), value.size());
72 }
73};
74
75struct Reader {
78 std::uint64_t position = 0;
80 std::uint64_t cached_at = 0;
81 std::size_t cached_size = 0;
82
83 std::uint64_t remaining() const noexcept {
84 return position <= source.size() ? source.size() - position : 0;
85 }
86 void bytes(void* destination, std::size_t length) noexcept {
87 if (status != ScratchStatus::ok) return;
88 if (!page.data || !page.size) {
90 return;
91 }
92 if (length > remaining()) { status = ScratchStatus::corrupt; return; }
93 auto* out = static_cast<std::byte*>(destination);
94 while (length) {
98 cached_size = static_cast<std::size_t>(
99 std::min<std::uint64_t>(page.size, remaining()));
100 status = source.read(position, {page.data, cached_size});
101 if (status != ScratchStatus::ok) return;
102 }
103 const auto at = static_cast<std::size_t>(position - cached_at);
104 const auto count = std::min(length, cached_size - at);
105 std::memcpy(out, page.data + at, count);
106 out += count;
107 position += count;
108 length -= count;
109 }
110 }
111 void skip(std::uint64_t length) noexcept {
112 if (status != ScratchStatus::ok) return;
113 if (length > remaining()) { status = ScratchStatus::corrupt; return; }
114 position += length;
115 }
116 std::uint64_t word() noexcept {
117 std::array<std::byte, 8> bytes{};
118 this->bytes(bytes.data(), bytes.size());
119 std::uint64_t value = 0;
120 for (unsigned i = 0; i != 8; ++i)
121 value |= std::uint64_t(std::to_integer<unsigned>(bytes[i])) << (8 * i);
122 return value;
123 }
124 bool presence() noexcept {
125 std::byte value{};
126 bytes(&value, 1);
127 if (value != std::byte{0} && value != std::byte{1})
129 return value == std::byte{1};
130 }
131 template<class T> void integer(T& out) noexcept {
132 const auto value = word();
133 if constexpr (std::is_signed_v<T>) {
134 const auto maximum = static_cast<std::uint64_t>(std::numeric_limits<T>::max());
135 const auto minimum = static_cast<std::uint64_t>(std::numeric_limits<T>::min());
136 if (value > maximum && value < minimum) {
138 return;
139 }
140 std::int64_t signed_value = 0;
141 std::memcpy(&signed_value, &value, sizeof value);
142 out = static_cast<T>(signed_value);
143 } else {
144 if (value > std::numeric_limits<T>::max()) {
146 return;
147 }
148 out = static_cast<T>(value);
149 }
150 }
151 void real(double& out) noexcept {
152 const auto value = word();
153 std::memcpy(&out, &value, sizeof value);
154 }
155 void text(std::string& out, bool owning) {
156 const auto length = word();
157 if (status != ScratchStatus::ok) return;
158 if (length > remaining() || length > out.max_size()) {
160 return;
161 }
162 if (!owning) { skip(length); return; }
163 out.resize(static_cast<std::size_t>(length));
164 bytes(out.data(), out.size());
165 }
166 ScratchStatus finish() const noexcept {
167 if (status != ScratchStatus::ok) return status;
169 }
170};
171
172class WordSource final : public ScratchByteSource {
173public:
174 explicit WordSource(std::uint64_t value) noexcept {
175 for (unsigned i = 0; i != 8; ++i)
176 bytes_[i] = static_cast<std::byte>(value >> (8 * i));
177 }
178 std::uint64_t size() const noexcept override { return bytes_.size(); }
179 ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override {
180 return ScratchSpanSource({bytes_.data(), bytes_.size()}).read(offset, out);
181 }
182private:
183 std::array<std::byte, 8> bytes_{};
184};
185
186class StoreSource final : public ScratchByteSource {
187public:
189 std::uint64_t length) noexcept : store_(store), handle_(handle), length_(length) {
191 }
192 std::uint64_t size() const noexcept override { return length_; }
193 const ReportScratchIoError* carried_failure() const noexcept override {
194 return store_.carried_failure();
195 }
196 ScratchStatus read(std::uint64_t offset, ScratchBuffer out) const noexcept override {
198 return store_.read(handle_, ScratchField::value, offset, out);
199 }
200private:
201 const ScratchReadback& store_;
202 ScratchHandle handle_;
203 std::uint64_t length_;
204};
205
206inline ScratchStatus find(CommandScratchStore& store, std::uint64_t key,
207 ScratchRecordInfo& out) noexcept {
208 const WordSource probe(key);
210 auto status = store.lower_bound(probe, handle);
211 if (status == ScratchStatus::end) return ScratchStatus::not_found;
212 if (status != ScratchStatus::ok) return status;
213 std::array<std::byte, 8> bytes{}, expected{};
214 status = store.read(handle, ScratchField::key, 0, {bytes.data(), bytes.size()});
215 if (status != ScratchStatus::ok) return status;
216 probe.read(0, {expected.data(), expected.size()});
217 if (bytes != expected) return ScratchStatus::not_found;
218 return store.point(handle, out);
219}
220
221inline ScratchStatus finish(CommandScratchStore& store, ScratchStatus status) noexcept {
222 if (status == ScratchStatus::ok)
223 status = store.commit(ScratchDurability::buffered);
224 if (status != ScratchStatus::ok && store.transaction_active()) {
225 const auto rollback = store.rollback();
226 if (rollback != ScratchStatus::ok) return rollback;
227 }
228 return status;
229}
230inline ScratchStatus put(CommandScratchStore& store, std::uint64_t key,
231 const ScratchByteSource& value) noexcept {
232 auto status = store.begin();
233 if (status != ScratchStatus::ok) return status;
235 const WordSource probe(key);
236 return finish(store, store.put(probe, value, handle));
237}
238inline ScratchStatus clear(CommandScratchStore& store) noexcept {
239 auto status = store.begin();
240 if (status != ScratchStatus::ok) return status;
242 while ((status = store.first(handle)) == ScratchStatus::ok) {
243 status = store.erase(handle);
244 if (status != ScratchStatus::ok) break;
245 }
246 if (status == ScratchStatus::end) status = ScratchStatus::ok;
247 return finish(store, status);
248}
249
250} // 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