3#include "../read_through_window.hpp"
18static_assert(
sizeof(double) == 8 && std::numeric_limits<double>::is_iec559,
19 "position-tail scratch records preserve binary64 quantities");
21[[noreturn]]
void invalid_record() {
23 "invalid Pine position-tail spill record");
28struct PositionTailSpill::State {
29 PositionTailBinding::File file;
30 std::uint64_t end = 0;
31 std::uint64_t rows = 0;
32 mutable ::pineforge::detail::ReadThroughWindow cache;
33 mutable std::array<unsigned char, kPageBytes> transfer{};
35 State(
const std::string& directory, std::size_t cache_bytes)
38 cache_bytes, this, read_backing) {}
39 State(
const std::string& directory, CacheCapacity capacity)
40 : file(directory), cache(
kPageBytes, capacity, this, read_backing) {}
42 static void read_backing(
void* context, std::uint64_t offset,
43 void* destination, std::size_t size) {
44 static_cast<State*
>(context)->file.read(offset, destination, size);
47 void write(std::uint64_t offset,
const void* data, std::size_t size) {
48 const auto* bytes =
static_cast<const unsigned char*
>(
data);
49 for (std::size_t done = 0; done < size;) {
51 const std::size_t count = std::min(size - done,
kWindowBytes);
52 file.write(offset + done, bytes + done, count);
57 void read(std::uint64_t offset,
void* data, std::size_t size)
const {
58 if (offset > end || size > end - offset) invalid_record();
59 auto* bytes =
static_cast<unsigned char*
>(
data);
60 const auto page_size = cache.page_size();
63 std::size_t left = size;
65 const std::uint64_t last = offset + left - 1;
66 const auto page = last / page_size;
67 const auto begin =
page * page_size;
68 const auto prefix =
static_cast<std::size_t
>(
69 std::min<std::uint64_t>(page_size, end - begin));
70 cache.get(page, transfer.data(), prefix);
71 const std::size_t count =
static_cast<std::size_t
>(
72 std::min<std::uint64_t>(left, last - begin + 1));
73 std::memcpy(bytes + left - count, transfer.data() + last - begin + 1 - count, count);
80 std::size_t cache_bytes)
81 : state_(
std::make_unique<State>(scratch_directory, cache_bytes)) {}
85 : state_(
std::make_unique<State>(scratch_directory, capacity)) {}
90 const auto length = trade.
entry_id.size();
91 const auto maximum =
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max());
92 if (length > std::numeric_limits<std::uint32_t>::max()
95 || state_->rows == std::numeric_limits<std::uint64_t>::max()) {
97 "Pine position-tail spill offset exhausted");
99 std::array<unsigned char, kFooterBytes> footer{};
100 const auto id_size =
static_cast<std::uint32_t
>(length);
101 std::memcpy(footer.data(), &trade.
exit_time, 8);
102 std::memcpy(footer.data() + 8, &trade.
qty, 8);
104 footer[24] = trade.
is_long ? 1 : 0;
106 std::memcpy(footer.data() + 26, &id_size, 4);
107 state_->cache.invalidate();
108 state_->write(state_->end, trade.
entry_id.data(), length);
109 state_->write(state_->end + length, footer.data(), footer.size());
112 state_->end +=
static_cast<std::uint64_t
>(length) +
kFooterBytes;
117 state_->cache.invalidate();
118 state_->file.clear();
126 return state_->cache.payload_bytes();
129 return state_->file.read_count();
132 return state_->file.read_bytes();
135 return state_->cache.stats();
142 if (cursor > state_->end) invalid_record();
143 if (cursor == 0)
return false;
145 std::array<unsigned char, kFooterBytes> footer{};
146 state_->read(cursor -
kFooterBytes, footer.data(), footer.size());
148 std::memcpy(&next.
exit_time, footer.data(), 8);
149 std::memcpy(&next.
qty, footer.data() + 8, 8);
151 if (footer[24] > 1 || footer[25] > 1) invalid_record();
152 next.
is_long = footer[24] != 0;
167 std::array<unsigned char, 256> bytes{};
168 std::size_t left = entry_id.size();
170 const std::size_t count = std::min(left, bytes.size());
171 const std::size_t begin = left - count;
173 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).
std::uint64_t backing_read_count() const noexcept
std::size_t cache_control_bytes() const noexcept
::pineforge::detail::ReadThroughCacheStats cache_stats() const noexcept
static constexpr std::size_t kFooterBytes
std::size_t cache_capacity_bytes() const noexcept
PositionTailSpill(const std::string &scratch_directory, std::size_t cache_bytes=kWindowBytes)
bool previous(std::uint64_t &cursor, Row &row) const
std::uint64_t row_count() const noexcept
std::uint64_t size_bytes() const noexcept
std::uint64_t backing_read_bytes() const noexcept
static constexpr std::size_t kPageBytes
void append(const Trade &trade)
bool entry_id_equals(const Row &row, std::string_view entry_id) const
static constexpr std::size_t kWindowBytes
std::uint64_t page(std::uint64_t bytes)
constexpr char kMarginCallLabel[]
uint64_t entry_incarnation
std::uint64_t entry_id_offset
std::uint32_t entry_id_size
std::uint64_t entry_incarnation