File size: 4,086 Bytes
f86fb3b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | #pragma once
#include <cstdint>
#include <span>
#include <string_view>
#include <cstring>
#include <optional>
#include <tuple>
#include "../core/types.hpp"
namespace quant::protocol {
constexpr uint16_t PROTOCOL_MAGIC = 0x5154; // 'Q' 'T' (Quant Trading)
enum class MessageType : uint8_t {
MARKET_DATA_TICK = 0x01,
NEW_ORDER_SINGLE = 0x02,
ORDER_CANCEL_REQ = 0x03,
EXECUTION_REPORT = 0x04,
HEARTBEAT = 0x05,
GAP_RECOVERY_REQ = 0x06,
GAP_RECOVERY_RES = 0x07
};
#pragma pack(push, 1)
// Standard 19-byte protocol framing header
struct FrameHeader {
uint16_t magic{PROTOCOL_MAGIC};
uint8_t msg_type{0};
uint16_t body_len{0};
uint64_t seq_num{0};
uint64_t timestamp_ns{0};
};
struct MarketDataTickPayload {
core::Symbol symbol;
int64_t bid_price_raw{0};
int64_t ask_price_raw{0};
uint32_t bid_qty{0};
uint32_t ask_qty{0};
int64_t last_price_raw{0};
uint32_t last_qty{0};
};
struct NewOrderSinglePayload {
core::ClientId client_id{0};
core::ClientOrderId client_order_id{0};
core::Symbol symbol;
uint8_t side{0}; // core::Side
uint8_t order_type{0}; // core::OrderType
int64_t price_raw{0};
uint32_t qty{0};
};
struct OrderCancelReqPayload {
core::ClientId client_id{0};
core::ClientOrderId client_order_id{0};
core::ClientOrderId orig_client_order_id{0};
core::Symbol symbol;
};
struct ExecutionReportPayload {
core::ClientId client_id{0};
core::ClientOrderId client_order_id{0};
core::OrderId engine_order_id{0};
uint64_t exec_id{0};
uint8_t exec_type{0}; // core::ExecType
int64_t fill_price_raw{0};
uint32_t fill_qty{0};
uint32_t leaves_qty{0};
uint8_t reject_reason{0}; // core::RejectReason
};
struct HeartbeatPayload {
uint64_t ping_timestamp_ns{0};
};
struct GapRecoveryReqPayload {
core::ClientId client_id{0};
uint64_t from_seq{0};
uint64_t to_seq{0};
};
#pragma pack(pop)
// Maximum single message frame size (for network buffers)
constexpr size_t MAX_FRAME_SIZE = 128;
class BinaryProtocol {
public:
/**
* Parse header from raw buffer using C++20 std::span.
* Returns std::nullopt if buffer is smaller than FrameHeader or magic mismatch.
*/
static std::optional<FrameHeader> parse_header(std::span<const uint8_t> buffer) noexcept;
/**
* Serialize a complete message (Header + Payload) into destination buffer.
* Returns total bytes written, or 0 if buffer too small.
*/
template <typename PayloadType>
static size_t serialize_message(
std::span<uint8_t> dest,
MessageType msg_type,
uint64_t seq_num,
uint64_t timestamp_ns,
const PayloadType& payload
) noexcept {
constexpr size_t total_size = sizeof(FrameHeader) + sizeof(PayloadType);
if (dest.size() < total_size) return 0;
FrameHeader hdr;
hdr.magic = PROTOCOL_MAGIC;
hdr.msg_type = static_cast<uint8_t>(msg_type);
hdr.body_len = static_cast<uint16_t>(sizeof(PayloadType));
hdr.seq_num = seq_num;
hdr.timestamp_ns = timestamp_ns;
std::memcpy(dest.data(), &hdr, sizeof(FrameHeader));
std::memcpy(dest.data() + sizeof(FrameHeader), &payload, sizeof(PayloadType));
return total_size;
}
/**
* Zero-copy cast payload from span buffer.
* Returns pointer to payload inside the span, or nullptr if length check fails.
*/
template <typename PayloadType>
static const PayloadType* parse_payload(std::span<const uint8_t> buffer) noexcept {
constexpr size_t total_size = sizeof(FrameHeader) + sizeof(PayloadType);
if (buffer.size() < total_size) return nullptr;
const auto* hdr = reinterpret_cast<const FrameHeader*>(buffer.data());
if (hdr->magic != PROTOCOL_MAGIC || hdr->body_len != sizeof(PayloadType)) {
return nullptr;
}
return reinterpret_cast<const PayloadType*>(buffer.data() + sizeof(FrameHeader));
}
};
} // namespace quant::protocol
|