PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
admission_journal_codec.cpp
Go to the documentation of this file.
4#include <string_view>
5#include <new>
6
7namespace pineforge::admission {
8inline namespace market_admission_v2 {
9namespace {
10namespace jc = detail::journal_codec;
12
13// Path depth is fixed by the schema. Decimal array subscripts have at most
14// twenty digits; neither text values nor array populations enter this buffer.
15struct Path {
16 std::array<char, 256> bytes{};
17 std::size_t length = 0;
18 Path() { append("event"); }
19 void append(std::string_view value) {
20 if (value.size() > bytes.size() - length)
21 throw coded<std::length_error>(RunFailureCode::engine_invariant, {},
22 "admission field path");
23 std::memcpy(bytes.data() + length, value.data(), value.size());
24 length += value.size();
25 }
26 Path child(const char* name) const {
27 Path out = *this;
28 out.append("."); out.append(name);
29 return out;
30 }
31 Path element(std::uint64_t index) const {
32 Path out = *this;
33 out.append("[");
34 std::array<char, 20> digits{};
35 std::size_t count = 0;
36 do { digits[count++] = char('0' + index % 10); index /= 10; } while (index);
37 while (count) out.append(std::string_view(&digits[--count], 1));
38 out.append("]");
39 return out;
40 }
41 bool is(std::string_view value) const noexcept {
42 return value == std::string_view(bytes.data(), length);
43 }
44};
45template<class T> constexpr std::uint64_t tag() {
46 if constexpr (std::is_same_v<T, std::string>) return 3;
47 else if constexpr (std::is_same_v<T, double>) return 2;
48 else if constexpr (std::is_same_v<T, bool> || std::is_unsigned_v<T>) return 0;
49 else return 1;
50}
51
52struct Save {
53 static constexpr bool loading = false;
54 static constexpr bool owning = false;
55 jc::Writer& out;
56 bool counting = false;
57 std::uint64_t rows = 0;
58 std::uint64_t* next_offset = nullptr;
59 std::uint64_t* next_row = nullptr;
60 std::array<std::uint64_t, 4>* array_elements = nullptr;
61 std::array<std::uint64_t, 4>* array_rows = nullptr;
62 std::size_t arrays = 0;
63 bool stopped = false;
64 bool good() const { return out.checked_status() == Status::ok && !stopped; }
65 template<class T> void field(const Path& path, const char* name, T& value) {
66 if (!good()) return;
67 const auto row = rows++;
68 if (counting || (next_row && row < *next_row)) return;
69 if (next_offset && out.position >= out.offset + out.destination.size) {
70 stopped = true;
71 return;
72 }
73 const auto key = path.child(name);
74 out.word(key.length); out.bytes(key.bytes.data(), key.length);
75 out.word(tag<T>());
76 if constexpr (std::is_same_v<T, std::string>) out.text(value);
77 else if constexpr (std::is_same_v<T, double>) {
78 std::uint64_t bits = 0; std::memcpy(&bits, &value, sizeof bits); out.word(bits);
79 } else out.word(static_cast<std::uint64_t>(value));
80 if (next_offset && out.checked_status() == Status::ok) {
81 if (out.position <= out.offset + out.destination.size) {
82 *next_offset = out.position;
83 *next_row = rows;
84 } else stopped = true; // Retry only this field at the next page.
85 }
86 }
87 std::uint64_t begin_array(std::size_t slot) {
88 if (!array_elements || !good()) return 0;
89 // Restore the field ordinal at the first unfinished element without
90 // traversing any completed element, including its optional fields.
91 rows = std::max(rows, (*array_rows)[slot]);
92 return (*array_elements)[slot];
93 }
94 void end_element(std::size_t slot, std::uint64_t next) {
95 if (!array_elements || !good()) return;
96 (*array_elements)[slot] = next;
97 (*array_rows)[slot] = rows;
98 }
99};
100
101class FoldSource final : public detail::ScratchByteSource {
102 const detail::ScratchByteSource& source_;
103public:
104 mutable Status failure = Status::ok;
105 mutable std::optional<detail::ReportScratchIoError> carried;
106 explicit FoldSource(const detail::ScratchByteSource& source) : source_(source) {}
107 std::uint64_t size() const noexcept override { return source_.size(); }
108 Status read(std::uint64_t offset, detail::ScratchBuffer out) const noexcept override {
109 const auto status = source_.read(offset,out);
110 if (status != Status::ok && failure == Status::ok) {
111 failure = status;
112 if (status == Status::io_error)
113 if (const auto* error = source_.carried_failure()) carried = *error;
114 }
115 return status;
116 }
117};
118struct CanonicalFold {
119 std::uint64_t digest;
120 std::uint64_t after_sequence;
121 bool active = true;
122 bool validation_skipped = false;
123 void bytes(const char* data, std::size_t size) noexcept {
124 for (std::size_t i = 0; i != size; ++i)
125 digest = (digest ^ static_cast<unsigned char>(data[i])) * 1099511628211ULL;
126 }
127 void word(std::uint64_t value) noexcept {
128 for (unsigned i = 0; i != 8; ++i)
129 digest = (digest ^ ((value >> (8 * i)) & 255)) * 1099511628211ULL;
130 }
131 template<class T> void scalar(const T& value) noexcept {
132 if constexpr (std::is_same_v<T, double>) {
133 std::uint64_t bits = 0;
134 std::memcpy(&bits, &value, sizeof bits);
135 if ((bits & 0x7fffffffffffffffULL) == 0) bits = 0;
136 else if ((bits & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL
137 && (bits & 0x000fffffffffffffULL)) bits = 0x7ff8000000000000ULL;
138 word(bits);
139 } else word(static_cast<std::uint64_t>(value));
140 }
141};
142constexpr std::size_t kJournalFoldWorkspaceLimit = 1024;
143constexpr std::size_t kJournalFoldFieldBytes = 256;
144// Fixed candidate/read-failure state, two diagnostic transport slots, two
145// summaries and eight words for scalar/pointer controls, plus the existing
146// comparison buffer reused for text. Nothing is retained between calls.
147// ReportScratchIoError copies share their prepared message/argument payload.
148// The Reader page and non-owning schema frame remain the existing workspace.
149constexpr std::size_t kJournalFoldWorkspaceBytes = sizeof(CanonicalFold) + sizeof(FoldSource)
150 + 2 * sizeof(std::optional<detail::ReportScratchIoError>) + 2 * sizeof(JournalRecordSummary)
151 + 8 * sizeof(std::uint64_t) + kJournalFoldFieldBytes;
152static_assert(kJournalFoldWorkspaceBytes <= kJournalFoldWorkspaceLimit,
153 "fixed admission fold workspace");
154static_assert(std::is_nothrow_copy_constructible_v<detail::ReportScratchIoError>
155 && std::is_nothrow_copy_assignable_v<detail::ReportScratchIoError>,
156 "diagnostic transport must be nonthrowing");
157
158template<bool Owning, bool Folding = false> struct Load {
159 static constexpr bool loading = true;
160 static constexpr bool owning = Owning;
161 jc::Reader& in;
162 std::uint64_t rows = 0, expected_rows = 0;
163 JournalRecordSummary summary;
165 int bar = -1;
166 CanonicalFold* fold = nullptr;
167 bool good() const { return in.status == Status::ok; }
168 template<class T> void field(const Path& path, const char* name, T& value) {
169 if (!good()) return;
170 if (++rows > expected_rows) { in.status = Status::corrupt; return; }
171 const auto key = path.child(name);
172 const auto length = in.word();
173 if (!good()) return;
174 if (length != key.length) { in.status = Status::corrupt; return; }
175 std::array<char, kJournalFoldFieldBytes> bytes{};
176 in.bytes(bytes.data(), key.length);
177 if (!good()) return;
178 if (std::memcmp(bytes.data(), key.bytes.data(), key.length) || in.word() != tag<T>()) {
179 in.status = Status::corrupt; return;
180 }
181 if constexpr (Folding) {
182 if (fold->active) {
183 fold->word(length); fold->bytes(bytes.data(), key.length); fold->word(tag<T>());
184 }
185 }
186 if constexpr (std::is_same_v<T, std::string>) {
187 if constexpr (!Folding) in.text(value, Owning);
188 else {
189 const auto size = in.word();
190 if (!good()) return;
191 if (size > in.remaining() || size > value.max_size()) {
192 in.status = Status::corrupt; return;
193 }
194 if (!fold->active) in.skip(size);
195 else {
196 fold->word(size);
197 const auto end = in.position + size;
198 if (end > in.cached_at + in.cached_size) fold->validation_skipped = true;
199 while (in.position != end && good()) {
200 const auto count = static_cast<std::size_t>(
201 std::min<std::uint64_t>(bytes.size(), end - in.position));
202 in.bytes(bytes.data(), count);
203 if (good()) fold->bytes(bytes.data(), count);
204 }
205 }
206 }
207 }
208 else if constexpr (std::is_same_v<T, double>) in.real(value);
209 else if constexpr (std::is_same_v<T, bool>) {
210 const auto raw = in.word();
211 if (raw > 1) in.status = Status::corrupt;
212 value = raw == 1;
213 } else if constexpr (std::is_enum_v<T>) {
214 std::underlying_type_t<T> raw{}; in.integer(raw); value = static_cast<T>(raw);
215 } else in.integer(value);
216 if constexpr (std::is_integral_v<T> || std::is_enum_v<T>) {
217 if (key.is("event.observation.command") || key.is("event.receipt.sequence")) {
218 summary.sequence = static_cast<std::uint64_t>(value);
219 if constexpr (Folding)
220 if (summary.sequence <= fold->after_sequence) fold->active = false;
221 }
222 if (key.is("event.outcome")) outcome = static_cast<Outcome>(value);
223 if (key.is("event.observation.bar")) bar = static_cast<int>(value);
224 }
225 if constexpr (Folding && !std::is_same_v<T, std::string>)
226 if (good() && fold->active) fold->scalar(value);
227 }
228};
229
230template<class A> struct Schema {
231 A& a;
232 template<class T> void field(const Path& p, const char* name, T& value) {
233 a.field(p, name, value);
234 }
235 void birth(const Path& p, OrderBirth& value) {
236 auto cause = value.cause(); auto bar = value.bar(); auto timestamp = value.timestamp();
237 auto domain = value.cursor().domain(); auto position = value.cursor().position();
238 auto index = value.cursor().index(); auto count = value.cursor().count();
239 auto price = value.cursor_price();
240 auto first = value.first_fill(); auto last = value.last_fill();
241 auto ordinal = value.evaluation_ordinal();
242 field(p,"cause",cause); field(p,"bar",bar); field(p,"timestamp",timestamp);
243 field(p,"cursor_domain",domain); field(p,"cursor_position",position);
244 field(p,"cursor_index",index); field(p,"cursor_count",count);
245 field(p,"cursor_price",price); field(p,"first_fill",first);
246 field(p,"last_fill",last); field(p,"evaluation_ordinal",ordinal);
247 if constexpr (A::loading) {
248 if constexpr (A::owning) {
249 if (!a.good()) return;
250 }
251 switch (cause) {
253 if constexpr (A::owning) value = OrderBirth{};
254 break;
256 if constexpr (A::owning) value = OrderBirth::direct_command(bar,timestamp);
257 break;
259 if constexpr (A::owning) value = OrderBirth::chart_evaluation(bar,timestamp);
260 break;
262 if constexpr (A::owning) {
263 const auto cursor = position == BirthCursorPosition::Point
264 ? BirthCursor::point(domain,index,count) : BirthCursor::segment(domain,index,count);
265 value = OrderBirth::fill_evaluation(bar,timestamp,cursor,price,first,last,ordinal);
266 }
267 break;
268 }
269 default:
270 if constexpr (A::owning)
271 throw coded<std::invalid_argument>(RunFailureCode::engine_invariant, {},
272 "invalid admission birth");
273 else a.in.status = Status::corrupt;
274 }
275 }
276 }
277 void config(const Path& p, Configuration& x) {
278#define F(name) field(p,#name,x.name)
279 F(process_on_close); F(calc_on_fills); F(magnifier); F(fill_recalculation); F(scheduler);
280 F(slippage); F(pyramiding); F(default_quantity_type); F(default_quantity_value);
281 F(long_margin); F(short_margin); F(commission_value); F(commission_type); F(pointvalue);
282 F(fx); F(quantity_step); F(mintick); F(risk_direction); F(loss_days_limit);
283 F(drawdown_limit); F(intraday_loss_limit); F(position_limit); F(fill_cap_active); F(risk_halted);
284#undef F
285 }
286 void sizing(const Path& p, SizingObservation& x) {
287 field(p,"quantity",x.quantity); field(p,"equity",x.equity); field(p,"price",x.price);
288 field(p,"mark",x.mark); field(p,"fx",x.fx);
289 }
290 void observation(const Path& p, CommandObservation& x) {
291#define F(name) field(p,#name,x.name)
292 F(command); F(kind); birth(p.child("birth"),x.birth); F(id); F(requested_quantity);
293 F(quantity_type); F(buy); field(p.child("prices"),"limit",x.prices.limit);
294 field(p.child("prices"),"stop",x.prices.stop); F(oca_name); F(oca_type);
295 config(p.child("configuration"),x.configuration); F(bar); F(placement_side);
296 F(placement_cycle); F(prior_close_quantity); F(held_quantity); F(held_entries);
297 F(realized_equity); F(placement_equity); F(signal_close); F(quantized_fixed_quantity);
298 bool present = x.original_sizing.has_value();
299 field(p,"original_sizing_present",present);
300 if constexpr (A::loading) {
301 if (present) x.original_sizing.emplace(); else x.original_sizing.reset();
302 }
303 if (present) sizing(p.child("original_sizing"),*x.original_sizing);
304 F(explicit_equity); F(explicit_price);
305#undef F
306 }
307 void receipt(const Path& p, ReviewReceipt& x) {
308 field(p,"sequence",x.sequence); field(p,"checkpoint",x.checkpoint);
309 field(p,"bar",x.bar); field(p,"target_command",x.target_command);
310 }
311 void receipt(const Path& p, SizingRevision& x) {
312 field(p,"sequence",x.sequence); field(p,"cause_fill",x.cause_fill);
313 field(p,"bar",x.bar); field(p,"target_command",x.target_command);
314 }
315 void draft(const Path& p, Draft& x) {
316 bool present = bool(x.observation());
317 field(p,"observation_present",present);
318 if (present) {
319 if constexpr (A::loading) {
320 CommandObservation value;
321 observation(p.child("observation"),value);
322 if constexpr (A::owning)
323 if (a.good()) x.bind(std::make_shared<const CommandObservation>(std::move(value)));
324 } else observation(p.child("observation"),const_cast<CommandObservation&>(*x.observation()));
325 }
326 present = x.review().has_value(); field(p,"review_present",present);
327 if (present) {
328 ReviewReceipt value;
329 if constexpr (!A::loading) value = *x.review();
330 receipt(p.child("review"),value);
331 if constexpr (A::loading && A::owning) if (a.good()) x.reviewed(value);
332 }
333 present = x.sizing_revision().has_value(); field(p,"sizing_revision_present",present);
334 if (present) {
335 SizingRevision value;
336 if constexpr (!A::loading) value = *x.sizing_revision();
337 receipt(p.child("sizing_revision"),value);
338 if constexpr (A::loading && A::owning) if (a.good()) x.sizing_revised(value);
339 }
340 }
341 void book(const Path& p, BookObservation& x) {
342#define F(name) field(p,#name,x.name)
343 F(incarnation); F(priority); F(bar); F(type); F(placement_side); F(buy);
344 F(id); F(oca_name); F(oca_type); birth(p.child("birth"),x.birth);
345 field(p.child("prices"),"limit",x.prices.limit); field(p.child("prices"),"stop",x.prices.stop);
346 field(p.child("prices"),"trail_points",x.prices.trail_points);
347 field(p.child("prices"),"trail_price",x.prices.trail_price);
348 field(p.child("prices"),"trail_offset",x.prices.trail_offset); draft(p.child("draft"),x.draft);
349#undef F
350 }
351 template<class T, class Fn> void array(const Path& p, std::vector<T>& rows, Fn emit) {
352 auto count = static_cast<std::uint64_t>(rows.size());
353 field(p,"size",count);
354 if constexpr (A::loading) {
355 if (count > a.expected_rows - std::min(a.rows,a.expected_rows)
356 || count > rows.max_size()) { a.in.status = Status::corrupt; return; }
357 if constexpr (A::owning) rows.resize(static_cast<std::size_t>(count));
358 }
359 std::uint64_t first = 0;
360 std::size_t slot = 0;
361 if constexpr (!A::loading) {
362 slot = a.arrays++;
363 first = a.begin_array(slot);
364 }
365 for (std::uint64_t i = first; i != count && a.good(); ++i) {
366 if constexpr (A::loading && !A::owning) { T row{}; emit(p.element(i),row); }
367 else emit(p.element(i),rows[static_cast<std::size_t>(i)]);
368 if constexpr (!A::loading) a.end_element(slot,i+1);
369 }
370 }
371 void event(Event& value) {
372 const Path p;
373 auto kind = static_cast<std::uint64_t>(value.index()); field(p,"kind",kind);
374 if constexpr (A::loading) {
375 switch (kind) {
376 case 0: value.template emplace<0>(); break;
377 case 1: value.template emplace<1>(); break;
378 case 2: value.template emplace<2>(); break;
379 default: a.in.status = Status::corrupt; return;
380 }
381 }
382 std::visit([&](auto& x) {
383 using T = std::decay_t<decltype(x)>;
384 if constexpr (std::is_same_v<T,CommandEvent>) {
385 if constexpr (A::loading) {
386 CommandObservation input;
387 observation(p.child("observation"),input);
388 if constexpr (A::owning)
389 if (a.good()) x.observation =
390 std::make_shared<const CommandObservation>(std::move(input));
391 } else {
392 if (!x.observation) { a.out.status = Status::programming_error; return; }
393 observation(p.child("observation"),const_cast<CommandObservation&>(*x.observation));
394 }
395 field(p,"outcome",x.outcome); field(p,"admitted_incarnation",x.admitted_incarnation);
396 array(p.child("before"),x.before,[&](const auto& q,auto& row){book(q,row);});
397 array(p.child("removed"),x.removed,[&](const auto& q,auto& id){field(q,"incarnation",id);});
398 } else if constexpr (std::is_same_v<T,ReviewEvent>) {
399 receipt(p.child("receipt"),x.receipt); config(p.child("configuration"),x.configuration);
400 field(p,"open_price",x.open_price); field(p,"position_side",x.position_side);
401 field(p,"position_cycle",x.position_cycle);
402 array(p.child("book"),x.book,[&](const auto& q,auto& row){book(q,row);});
403 array(p.child("reviewed"),x.reviewed,[&](const auto& q,auto& row){book(q,row);});
404 array(p.child("resolutions"),x.resolutions,[&](const auto& q,auto& row){
405 field(q,"incarnation",row.incarnation); field(q,"kind",row.kind);
406 field(q,"peer_incarnation",row.peer_incarnation); field(q,"own_priority",row.own_priority);
407 field(q,"peer_priority",row.peer_priority); field(q,"transaction_quantity",row.transaction_quantity);
408 });
409 array(p.child("causes"),x.causes,[&](const auto& q,auto& id){field(q,"sequence",id);});
410 } else {
411 receipt(p.child("receipt"),x.receipt); field(p,"incarnation",x.incarnation);
412 sizing(p.child("before"),x.before); sizing(p.child("after"),x.after);
413 field(p,"affordability_equity_before",x.affordability_equity_before);
414 field(p,"affordability_equity_after",x.affordability_equity_after);
415 }
416 },value);
417 }
418};
419
420template<bool Owning, bool Folding = false> Status decode(const detail::ScratchByteSource& source,
421 detail::ScratchBuffer page, Event& event, JournalRecordSummary& summary,
422 const detail::ReportScratchIoError*& failure, CanonicalFold* fold = nullptr) noexcept {
423 failure = nullptr;
424 if (!page.data || !page.size) return Status::programming_error;
425 try {
426 jc::Reader in{source,page};
427 Load<Owning,Folding> load{in};
428 load.fold = fold;
429 load.expected_rows = in.word();
430 if (load.expected_rows > in.remaining() / 24) return Status::corrupt;
431 Schema<Load<Owning,Folding>>{load}.event(event);
432 if (load.rows != load.expected_rows && in.status == Status::ok) in.status = Status::corrupt;
433 const auto status = in.finish();
434 if (status == Status::ok) {
435 summary = load.summary;
436 summary.rejected = rejected_command(load.outcome);
437 summary.rejected_bar = load.bar;
438 }
439 return status;
440 } catch (const std::bad_alloc&) {
441 if constexpr (Owning) failure = &detail::scratch_out_of_memory_failure();
442 return Status::programming_error;
443 }
444 catch (...) { return Status::corrupt; }
445}
446} // namespace
447
448JournalRecordSource::JournalRecordSource(const Event& event) noexcept : event_(&event) {
449 jc::observe_work(jc::WorkOperation::payload_source);
450 try {
451 jc::Writer counter;
452 Save count{counter,true};
453 Schema<Save>{count}.event(const_cast<Event&>(event));
454 rows_ = count.rows;
455 jc::Writer out;
456 out.word(rows_);
457 Save save{out};
458 Schema<Save>{save}.event(const_cast<Event&>(event));
459 length_ = out.position;
460 status_ = out.checked_status();
461 } catch (...) { status_ = Status::programming_error; }
462}
464 detail::ScratchBuffer buffer) const noexcept {
465 if (status_ != Status::ok) return status_;
466 if (offset > length_ || buffer.size > length_ - offset || (buffer.size && !buffer.data))
467 return Status::programming_error;
468 if (!buffer.size) return Status::ok;
469 jc::observe_work(jc::WorkOperation::payload_read, buffer.size);
470 try {
471 // Forward pages resume within the current array element. A seek before
472 // the saved field boundary (including the second pass) starts anew.
473 if (offset < next_offset_) {
474 next_offset_ = next_row_ = 0;
475 array_elements_.fill(0);
476 array_rows_.fill(0);
477 }
478 jc::Writer out{next_offset_,offset,buffer};
479 if (!next_offset_) out.word(rows_);
480 Save save{out,false,0,&next_offset_,&next_row_,&array_elements_,&array_rows_};
481 Schema<Save>{save}.event(const_cast<Event&>(*event_));
482 return out.checked_status();
483 } catch (...) { return Status::programming_error; }
484}
487 detail::ScratchBuffer page, Event& event) {
488 try {
489 Event result;
490 JournalRecordSummary summary;
491 const detail::ReportScratchIoError* failure = nullptr;
492 const auto status = decode<true>(source,page,result,summary,failure);
494 if (status == Status::ok) event = std::move(result);
495 return status;
496 } catch (const std::bad_alloc&) {
498 return Status::programming_error; // unreachable: the helper always throws a non-null failure
499 }
500}
502 detail::ScratchBuffer page, JournalRecordSummary& summary) noexcept {
503 Event scratch;
504 const detail::ReportScratchIoError* failure = nullptr;
505 return decode<false>(source,page,scratch,summary,failure);
506}
508 detail::ScratchBuffer page, JournalRecordSummary& summary, std::uint64_t& digest,
509 std::uint64_t after_sequence, std::optional<detail::ReportScratchIoError>* failure) noexcept {
510 if (failure) failure->reset();
511 Event scratch;
512 FoldSource input(source);
513 CanonicalFold fold{digest,after_sequence};
514 JournalRecordSummary candidate;
515 const detail::ReportScratchIoError* unused = nullptr;
516 const auto status = decode<false,true>(input,page,scratch,candidate,unused,&fold);
517 if (status != Status::ok && fold.validation_skipped) {
518 // A skipped text region changes the old scan's refill boundaries.
519 // On any fused failure, replay that scan to retain its validation
520 // priority: its next page can fail before the fused schema error.
521 // Keep its tag-read error handling too. Never retry the hash or turn
522 // the original failure into a successful query.
523 JournalRecordSummary checked;
524 const auto validation = scan_journal_record(source,page,checked);
525 if (validation != Status::ok) {
526 if (failure && validation == Status::io_error)
527 if (const auto* error = source.carried_failure()) *failure = *error;
528 return validation;
529 }
530 if (failure) *failure = input.carried;
531 return input.failure != Status::ok ? input.failure : status;
532 }
533 if (failure && status == Status::io_error) *failure = input.carried;
534 if (status == Status::ok) {
535 summary = candidate;
536 if (summary.sequence > after_sequence) digest = fold.digest;
537 }
538 return status;
539}
541 detail::ScratchBuffer page, std::uint64_t& digest) noexcept {
542 if (!page.data || !page.size) return Status::programming_error;
543 jc::Reader in{source,page};
544 const auto rows = in.word();
545 if (rows > in.remaining() / 24) return Status::corrupt;
546 auto h = digest;
547 const auto byte = [&](unsigned value) { h = (h ^ value) * 1099511628211ULL; };
548 const auto word = [&](std::uint64_t value) {
549 for (unsigned i = 0; i != 8; ++i) byte((value >> (8 * i)) & 255);
550 };
551 const auto text = [&] {
552 const auto length = in.word();
553 if (length > in.remaining()) { in.status = Status::corrupt; return; }
554 word(length);
555 std::array<std::byte, 97> bytes{};
556 for (std::uint64_t at = 0; at < length && in.status == Status::ok;) {
557 const auto count = static_cast<std::size_t>(std::min<std::uint64_t>(bytes.size(),length-at));
558 in.bytes(bytes.data(),count);
559 for (std::size_t i = 0; i != count; ++i) byte(std::to_integer<unsigned>(bytes[i]));
560 at += count;
561 }
562 };
563 for (std::uint64_t i = 0; i != rows && in.status == Status::ok; ++i) {
564 text();
565 const auto kind = in.word(); word(kind);
566 if (kind == 3) text();
567 else if (kind < 3) {
568 auto value = in.word();
569 if (kind == 2) {
570 if ((value & 0x7fffffffffffffffULL) == 0) value = 0;
571 else if ((value & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL
572 && (value & 0x000fffffffffffffULL)) value = 0x7ff8000000000000ULL;
573 }
574 word(value);
575 } else in.status = Status::corrupt;
576 }
577 const auto status = in.finish();
578 if (status == Status::ok) digest = h;
579 return status;
580}
581} // inline namespace market_admission_v2
582} // namespace pineforge::admission
#define F(name)
static BirthCursor point(BirthCursorDomain domain, int index, int count)
static OrderBirth direct_command(int bar, int64_t timestamp)
static OrderBirth chart_evaluation(int bar, int64_t timestamp)
static OrderBirth fill_evaluation(int bar, int64_t timestamp, BirthCursor cursor, double price, uint64_t first_fill, uint64_t last_fill, uint64_t evaluation_ordinal)
detail::ScratchStatus read(std::uint64_t, detail::ScratchBuffer) const noexcept override
JournalRecordSource encode_journal_record(const Event &) noexcept
detail::ScratchStatus scan_journal_record(const detail::ScratchByteSource &source, detail::ScratchBuffer page, JournalRecordSummary &summary) noexcept
detail::ScratchStatus decode_journal_record(const detail::ScratchByteSource &, detail::ScratchBuffer, Event &)
detail::ScratchStatus fold_journal_record(const detail::ScratchByteSource &source, detail::ScratchBuffer page, std::uint64_t &digest) noexcept
std::variant< CommandEvent, ReviewEvent, SizingEvent > Event
detail::ScratchStatus fold_validated_journal_record(const detail::ScratchByteSource &source, detail::ScratchBuffer page, JournalRecordSummary &summary, std::uint64_t &digest, std::uint64_t after_sequence, std::optional< detail::ReportScratchIoError > *failure) noexcept
void receipt(const std::string &document, char *json, std::size_t capacity, std::size_t *required)
std::uint64_t page(std::uint64_t bytes)
void rethrow_carried_scratch_failure(const ReportScratchIoError *failure)
const ReportScratchIoError & scratch_out_of_memory_failure() noexcept