Leka
A low-latency C++20 price-time-priority limit order book and matching engine
▶ Replay viewer
Loading...
Searching...
No Matches
order_index.hpp
1//include/lob/index/order_index.hpp
2
3#ifndef ORDER_INDEX_HPP
4#define ORDER_INDEX_HPP
5#include "lob/types/order_id.hpp"
6#include "lob/order/order.hpp"
7#include <cstddef>
8#include <vector>
9
10namespace lob {
11
12/**
13 * @brief Open-addressed OrderId -> Order* index with average O(1) lookup.
14 *
15 * OrderId::isValid() reserves zero, which lets an empty Slot's default
16 * OrderId double as the "unoccupied" sentinel: no separate occupancy bitmap
17 * or tombstone state is needed. Slots live in one contiguous std::vector, so
18 * every element of the whole probe sequence for a lookup is one flat array,
19 * not the one-heap-allocation-per-entry, pointer-chasing structure a chained
20 * hash table (such as std::unordered_map) builds.
21 *
22 * This is called on every add, cancel, reduce, and fill, so it is the
23 * hottest lookup in the engine. Unlike PriceLevel's PriceLadder, there is no
24 * natural fixed bound on how many orders can be live at once, so this grows
25 * by amortized doubling like std::vector rather than being pre-sized once.
26 *
27 * Removal uses backward-shift deletion rather than tombstones: on removal,
28 * every following entry in the same probe run is shifted back into the
29 * vacated slot if doing so keeps it reachable, and the search for the next
30 * vacated slot continues until a genuinely empty one is found. This keeps
31 * lookups a single clean scan for empty forever, with no growing tombstone
32 * debt from a long session with many cancels, at the cost of removal being
33 * more than a single slot write.
34 */
36 public:
37 /**
38 * @brief Adds an order and rejects duplicate IDs.
39 *
40 * @details Only checks this index's own invariants: that @p order is
41 * non-null and that its ID is not already present. It does not
42 * re-check @p order's overall field validity (side, type, price,
43 * quantity, timestamp) — that is OrderBook::addOrderWithQuantities's
44 * job, and it always runs before an order reaches any book structure,
45 * this index included. PriceLevel::addOrder follows the same
46 * division: each class enforces only the invariant it alone owns,
47 * rather than every class re-verifying the whole Order on every
48 * insert.
49 */
50 void addOrder(Order* order);
51
52 /** Removes an order after verifying pointer identity. */
53 void removeOrder(Order* order);
54
55 /** Finds an order by ID, or returns nullptr when absent. */
56 Order* findOrder(const OrderId& orderId) const;
57
58 /** Returns the number of indexed orders. */
59 std::size_t size() const { return count; }
60
61 /**
62 * @brief Grows the table, if needed, so @p orderCount entries fit
63 * under MaxLoadFactor without a later doubling.
64 *
65 * Unlike PriceLadder, this table has no fixed bound to pre-size to
66 * exactly, so it still grows on demand for anyone who does not call
67 * this first (see the class comment). But a table that doubles while
68 * holding many live entries pays for an O(n) rehash of everything
69 * still indexed at the moment it happens, not the O(1) amortized cost
70 * the growth policy implies on average — a single one of those, late
71 * in a run with a large book, is a real tail-latency event (see
72 * ARCH_DECISIONS.md ADR-008). Calling this once, for a known or
73 * comfortably over-estimated order count, moves that rehash out of
74 * the hot path the same way OrderPool::reserve() moves page creation
75 * out of it.
76 */
77 void reserve(std::size_t orderCount);
78
79 private:
80 /** One slot; an OrderId{} key (0, reserved invalid) means empty. */
81 struct Slot {
82 OrderId key;
83 Order* value{nullptr};
84 };
85
86 static constexpr std::size_t InitialCapacity = 16; // must stay a power of two
87 static constexpr double MaxLoadFactor = 0.5;
88
89 std::vector<Slot> slots;
90 std::size_t count{0};
91
92 std::size_t indexFor(const OrderId& id, std::size_t capacity) const;
93 std::size_t findSlot(const OrderId& id) const;
94 void growIfNeeded();
95};
96
97} // namespace lob
98#endif // ORDER_INDEX_HPP
Type-safe identifier for an order; zero is reserved as invalid.
Definition order_id.hpp:10
Open-addressed OrderId -> Order* index with average O(1) lookup.
std::size_t size() const
Returns the number of indexed orders.
Order * findOrder(const OrderId &orderId) const
Finds an order by ID, or returns nullptr when absent.
void reserve(std::size_t orderCount)
Grows the table, if needed, so orderCount entries fit under MaxLoadFactor without a later doubling.
void removeOrder(Order *order)
Removes an order after verifying pointer identity.
void addOrder(Order *order)
Adds an order and rejects duplicate IDs.
Represents a resting or incoming order and its lifecycle state.
Definition order.hpp:17