Leka
A low-latency C++20 price-time-priority limit order book and matching engine
▶ Replay viewer
Toggle main menu visibility
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
10
namespace
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
*/
35
class
OrderIndex
{
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
lob::OrderId
Type-safe identifier for an order; zero is reserved as invalid.
Definition
order_id.hpp:10
lob::OrderIndex
Open-addressed OrderId -> Order* index with average O(1) lookup.
Definition
order_index.hpp:35
lob::OrderIndex::size
std::size_t size() const
Returns the number of indexed orders.
Definition
order_index.hpp:59
lob::OrderIndex::findOrder
Order * findOrder(const OrderId &orderId) const
Finds an order by ID, or returns nullptr when absent.
Definition
order_index.cpp:170
lob::OrderIndex::reserve
void reserve(std::size_t orderCount)
Grows the table, if needed, so orderCount entries fit under MaxLoadFactor without a later doubling.
Definition
order_index.cpp:63
lob::OrderIndex::removeOrder
void removeOrder(Order *order)
Removes an order after verifying pointer identity.
Definition
order_index.cpp:137
lob::OrderIndex::addOrder
void addOrder(Order *order)
Adds an order and rejects duplicate IDs.
Definition
order_index.cpp:115
lob::Order
Represents a resting or incoming order and its lifecycle state.
Definition
order.hpp:17
include
lob
index
order_index.hpp
Generated by
1.18.0