17static_assert(
sizeof(double) == 8 && std::numeric_limits<double>::is_iec559,
18 "position-tail scratch records preserve binary64 quantities");
20[[noreturn]]
void invalid_record() {
22 "invalid Pine position-tail spill record");
27struct PositionTailSpill::State {
28 PositionTailBinding::File file;
29 std::uint64_t end = 0;
30 std::uint64_t rows = 0;
32 mutable std::array<unsigned char, kWindowBytes> window{};
33 mutable std::uint64_t window_begin = 0;
34 mutable std::size_t window_size = 0;
36 explicit State(
const std::string& directory) : file(directory) {}
38 void write(std::uint64_t offset,
const void* data, std::size_t size) {
39 const auto* bytes =
static_cast<const unsigned char*
>(
data);
40 for (std::size_t done = 0; done < size;) {
42 const std::size_t count = std::min(size - done,
kWindowBytes);
43 file.write(offset + done, bytes + done, count);
48 void refill(std::uint64_t offset)
const {
51 const std::uint64_t limit = offset + 1;
53 const auto size =
static_cast<std::size_t
>(limit - begin);
55 file.read(begin, window.data(), size);
60 void read(std::uint64_t offset,
void* data, std::size_t size)
const {
61 if (offset > end || size > end - offset) invalid_record();
62 auto* bytes =
static_cast<unsigned char*
>(
data);
65 std::size_t left = size;
67 const std::uint64_t last = offset + left - 1;
68 if (last < window_begin || last - window_begin >= window_size) refill(last);
69 const std::size_t count =
static_cast<std::size_t
>(
70 std::min<std::uint64_t>(left, last - window_begin + 1));
71 std::memcpy(bytes + left - count,
72 window.data() +
static_cast<std::size_t
>(last - window_begin + 1) - count,
80 : state_(
std::make_unique<State>(scratch_directory)) {}
85 const auto length = trade.
entry_id.size();
86 const auto maximum =
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max());
87 if (length > std::numeric_limits<std::uint32_t>::max()
90 || state_->rows == std::numeric_limits<std::uint64_t>::max()) {
92 "Pine position-tail spill offset exhausted");
94 std::array<unsigned char, kFooterBytes> footer{};
95 const auto id_size =
static_cast<std::uint32_t
>(length);
96 std::memcpy(footer.data(), &trade.
exit_time, 8);
97 std::memcpy(footer.data() + 8, &trade.
qty, 8);
99 footer[24] = trade.
is_long ? 1 : 0;
101 std::memcpy(footer.data() + 26, &id_size, 4);
102 state_->write(state_->end, trade.
entry_id.data(), length);
103 state_->write(state_->end + length, footer.data(), footer.size());
106 state_->end +=
static_cast<std::uint64_t
>(length) +
kFooterBytes;
108 state_->window_size = 0;
112 state_->file.clear();
115 state_->window_size = 0;
122 if (cursor > state_->end) invalid_record();
123 if (cursor == 0)
return false;
125 std::array<unsigned char, kFooterBytes> footer{};
126 state_->read(cursor -
kFooterBytes, footer.data(), footer.size());
128 std::memcpy(&next.
exit_time, footer.data(), 8);
129 std::memcpy(&next.
qty, footer.data() + 8, 8);
131 if (footer[24] > 1 || footer[25] > 1) invalid_record();
132 next.
is_long = footer[24] != 0;
147 std::array<unsigned char, 256> bytes{};
148 std::size_t left = entry_id.size();
150 const std::size_t count = std::min(left, bytes.size());
151 const std::size_t begin = left - count;
153 if (std::memcmp(bytes.data(), entry_id.data() + begin, count) != 0)
return false;
Base with a code: throw coded<std::invalid_argument>(code, {args}, text).
PositionTailSpill(const std::string &scratch_directory)
static constexpr std::size_t kFooterBytes
bool previous(std::uint64_t &cursor, Row &row) const
std::uint64_t row_count() const noexcept
std::uint64_t size_bytes() const noexcept
void append(const Trade &trade)
bool entry_id_equals(const Row &row, std::string_view entry_id) const
static constexpr std::size_t kWindowBytes
constexpr char kMarginCallLabel[]
uint64_t entry_incarnation
std::uint64_t entry_id_offset
std::uint32_t entry_id_size
std::uint64_t entry_incarnation