FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
codec.hpp
Go to the documentation of this file.
1#pragma once
2// Protocol codec concepts (plan 7). Only the shapes are defined here (M3 fills in FIX / ITCH
3// / OUCH / MDP3 under codecs/<proto>/). A binary-protocol venue plugs into the same net
4// stack as the JSON venues:
5//
6// Connection<Stream, Pipeline<Framer, SessionLayer, Decoder>>
7//
8// where WsClient is "just" a Framer whose frames are text, and the venue JSON decoders are
9// Decoders. Nothing in this header allocates or is virtual.
14
15#include <concepts>
16#include <cstddef>
17#include <cstdint>
18#include <cstring>
19#include <span>
20#include <string_view>
21#include <type_traits>
22
23namespace fastmm::codecs {
24
25[[nodiscard]] std::string_view library_name() noexcept;
26
27// One complete application frame carved out of a byte stream. `consumed` is how many input
28// bytes the framer used (frame + framing overhead); zero means "need more bytes".
29struct FrameView {
30 std::span<const std::byte> payload;
31 std::size_t consumed = 0;
32 std::uint8_t kind = 0; // protocol-specific tag (WS opcode, SoupBin packet type, ...)
33 [[nodiscard]] bool complete() const noexcept { return consumed != 0; }
34};
36// Splits a byte stream into frames without copying. Must be re-entrant on partial input.
37template <class F>
38concept Framer = requires(F& f, std::span<const std::byte> in) {
39 { f.next(in) } noexcept -> std::same_as<FrameView>;
40};
41
42// Turns one frame into zero or more normalised engine events written to the sink.
43template <class D>
44concept Decoder =
45 requires(D& d, const FrameView& frame, std::int64_t rx_ts, fastmm::venues::EventSink& sink) {
46 { d.decode(frame, rx_ts, sink) } noexcept -> std::same_as<fastmm::venues::ParseStatus>;
47 };
48
49// Serialises one outbound command into wire bytes. Returns bytes written; 0 on failure.
50template <class E>
51concept Encoder =
52 requires(E& e, const fastmm::venues::OrderCommand& cmd, std::span<std::byte> out) {
53 { e.encode(cmd, out) } noexcept -> std::same_as<std::size_t>;
54 };
55
56enum class SessionState : std::uint8_t { Down = 0, LoggingOn = 1, Up = 2, Recovering = 3 };
57
58// Session-level protocol machinery (FIX Logon/Heartbeat/Resend, SoupBinTCP login, MoldUDP
59// gap requests). on_frame() consumes session frames and returns false for application
60// frames the Decoder should see; on_timer() emits heartbeats / test requests.
61template <class S>
62concept SessionLayer = requires(S& s, const FrameView& frame, std::int64_t now_ns) {
63 { s.on_frame(frame) } noexcept -> std::same_as<bool>;
64 { s.on_timer(now_ns) } noexcept;
65 { s.state() } noexcept -> std::same_as<SessionState>;
66};
67
68// Big/little-endian wire fields for packed structs (M3). Each holds raw bytes (alignment 1), and
69// get()/set() go through memcpy plus a byte swap, so a field at any offset of a packed layout is
70// read and written without undefined behaviour (a uint32_t member at an odd offset is misaligned,
71// and calling members on it is what UBSan reports). Never take the address of the value as an
72// integer; use get()/set().
73namespace detail {
74template <class T, bool big_endian>
75struct EndianField {
76 unsigned char raw[sizeof(T)];
77 [[nodiscard]] FASTMM_FORCE_INLINE T get() const noexcept {
78 T v;
79 std::memcpy(&v, raw, sizeof(T));
80 return big_endian ? swap(v) : v;
81 }
82 FASTMM_FORCE_INLINE void set(T v) noexcept {
83 const T w = big_endian ? swap(v) : v;
84 std::memcpy(raw, &w, sizeof(T));
85 }
86
87 private:
88 [[nodiscard]] static T swap(T v) noexcept {
89 switch (sizeof(T)) {
90 case 2:
91 return static_cast<T>(__builtin_bswap16(static_cast<std::uint16_t>(v)));
92 case 4:
93 return static_cast<T>(__builtin_bswap32(static_cast<std::uint32_t>(v)));
94 default:
95 return static_cast<T>(__builtin_bswap64(static_cast<std::uint64_t>(v)));
96 }
97 }
98};
99} // namespace detail
100
101using be16_t = detail::EndianField<std::uint16_t, true>;
102using be32_t = detail::EndianField<std::uint32_t, true>;
103using be64_t = detail::EndianField<std::uint64_t, true>;
104using le16_t = detail::EndianField<std::uint16_t, false>;
105using le32_t = detail::EndianField<std::uint32_t, false>;
106using le64_t = detail::EndianField<std::uint64_t, false>;
107static_assert(sizeof(be16_t) == 2 && sizeof(be32_t) == 4 && sizeof(be64_t) == 8);
108static_assert(sizeof(le16_t) == 2 && sizeof(le32_t) == 4 && sizeof(le64_t) == 8);
109static_assert(alignof(be32_t) == 1 && alignof(be64_t) == 1 && alignof(le64_t) == 1);
110static_assert(std::is_trivially_copyable_v<be64_t> && std::is_standard_layout_v<be64_t>);
111
112} // namespace fastmm::codecs
Definition event_sink.hpp:34
Turns one frame into zero or more normalised engine events written to the sink.
Definition codec.hpp:46
Splits a byte stream into frames without copying. Must be re-entrant on partial input.
Definition codec.hpp:40
Session-level protocol machinery (FIX Logon/Heartbeat/Resend, SoupBinTCP login, MoldUDP gap requests)...
Definition codec.hpp:64
EventSink (5.15): the net -> engine seam.
Hot-path connector concepts (6.2).
Event bus message structs (5.5).
Definition codec.hpp:25
detail::EndianField< std::uint64_t, false > le64_t
Definition codec.hpp:108
SessionState
Definition codec.hpp:58
std::string_view library_name() noexcept
detail::EndianField< std::uint16_t, true > be16_t
Definition codec.hpp:103
detail::EndianField< std::uint64_t, true > be64_t
Definition codec.hpp:105
detail::EndianField< std::uint16_t, false > le16_t
Definition codec.hpp:106
detail::EndianField< std::uint32_t, false > le32_t
Definition codec.hpp:107
detail::EndianField< std::uint32_t, true > be32_t
Definition codec.hpp:104
OrderCommand: a uniform, non-owning view over the three outbound engine messages (OutNewOrderMsg / Ou...
One complete application frame carved out of a byte stream.
Definition codec.hpp:31
Definition order_commands.hpp:28