photon_ring/pod.rs
1// Copyright 2026 Photon Ring Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! The [`Pod`] marker trait for seqlock-safe payload types.
5
6/// Marker trait for types safe to use with seqlock-stamped ring buffers.
7///
8/// A type is `Pod` ("Plain Old Data") if **every possible bit pattern**
9/// of `size_of::<T>()` bytes represents a valid value of `T`. This is
10/// stricter than [`Copy`] — it excludes types where certain bit patterns
11/// are undefined behavior, such as `bool` (only 0/1 valid), `char`
12/// (must be a valid Unicode scalar), `NonZero*` (must be nonzero), and
13/// references (must point to valid memory).
14///
15/// # Why this matters
16///
17/// The seqlock read protocol performs an optimistic non-atomic read that
18/// may observe a partially-written ("torn") value. If the torn bit pattern
19/// violates a type's validity invariant, this is undefined behavior even
20/// though the value is detected and discarded by the stamp check. `Pod`
21/// guarantees that no bit pattern is invalid, making torn reads harmless.
22///
23/// # Safety
24///
25/// Implementors must ensure:
26/// 1. `T` is `Copy` (no destructor, no move semantics).
27/// 2. `T` is `Send` (safe to transfer across threads).
28/// 3. Every possible bit pattern of `size_of::<T>()` bytes is a valid `T`.
29/// 4. `T` has **no padding bytes**. Padding is uninitialized memory, and the
30/// `atomic-slots` feature reads the payload as atomic words — reading an
31/// uninitialized byte as part of an integer is undefined behaviour even
32/// though every *initialized* bit pattern is valid. Add explicit padding
33/// fields (`_pad: [u8; 3]`) so the whole value is initialized rather than
34/// letting the compiler insert implicit padding.
35///
36/// This is why multi-field tuples are **not** `Pod`: `(u8, u64)` has
37/// `repr(Rust)` layout with 7 implicit padding bytes. Use a `#[repr(C)]`
38/// struct with explicit padding fields for multi-field payloads.
39///
40/// # What types are NOT `Pod`?
41///
42/// | Type | Why | What to use instead |
43/// |---|---|---|
44/// | `bool` | Only 0 and 1 are valid | `u8` (0 = false, 1 = true) |
45/// | `char` | Must be valid Unicode scalar | `u32` |
46/// | `NonZero<u32>` | Zero is invalid | `u32` |
47/// | `Option<T>` | Discriminant has invalid patterns | `u8` sentinel (e.g., 255 = None) |
48/// | `enum` (Rust) | Only declared variants are valid | `u8` or `u32` with constants |
49/// | `&T`, `&str` | Pointer must be valid | Not supported — use value types |
50/// | `String`, `Vec` | Heap-allocated, has `Drop` | Fixed `[u8; N]` buffer |
51///
52/// # Converting real-world types
53///
54/// A common pattern: your domain model uses enums and `Option`, but the
55/// Photon Ring message struct uses plain integers:
56///
57/// ```rust
58/// // Domain type (NOT Pod — has Option and enum)
59/// // enum Side { Buy, Sell }
60/// // struct Order { price: f64, qty: u32, side: Side, tag: Option<u32> }
61///
62/// // Photon Ring message (Pod — all fields are plain numerics)
63/// #[repr(C)]
64/// #[derive(Clone, Copy)]
65/// struct OrderMsg {
66/// price: f64,
67/// qty: u32,
68/// side: u8, // 0 = Buy, 1 = Sell
69/// tag: u32, // 0 = None, nonzero = Some(value)
70/// _pad: [u8; 3], // explicit padding for alignment
71/// }
72/// unsafe impl photon_ring::Pod for OrderMsg {}
73///
74/// // Convert at the boundary:
75/// // let msg = OrderMsg { price: 100.0, qty: 10, side: 0, tag: 0, _pad: [0;3] };
76/// // publisher.publish(msg);
77/// ```
78///
79/// # Pre-implemented types
80///
81/// `Pod` is implemented for all primitive numeric types, arrays of `Pod`
82/// types, and the zero- and one-element tuples. Larger tuples are excluded
83/// because their layout may include padding.
84///
85/// For user-defined structs, use `unsafe impl`:
86/// ```
87/// #[repr(C)]
88/// #[derive(Clone, Copy)]
89/// struct Quote {
90/// price: f64,
91/// volume: u32,
92/// _pad: u32,
93/// }
94///
95/// // SAFETY: Quote is #[repr(C)], all fields are plain numerics,
96/// // and every bit pattern is a valid Quote.
97/// unsafe impl photon_ring::Pod for Quote {}
98/// ```
99pub unsafe trait Pod: Copy + Send + 'static {}
100
101// Primitive numeric types — every bit pattern is valid
102unsafe impl Pod for u8 {}
103unsafe impl Pod for u16 {}
104unsafe impl Pod for u32 {}
105unsafe impl Pod for u64 {}
106unsafe impl Pod for u128 {}
107unsafe impl Pod for i8 {}
108unsafe impl Pod for i16 {}
109unsafe impl Pod for i32 {}
110unsafe impl Pod for i64 {}
111unsafe impl Pod for i128 {}
112unsafe impl Pod for f32 {}
113unsafe impl Pod for f64 {}
114unsafe impl Pod for usize {}
115unsafe impl Pod for isize {}
116
117// Arrays of Pod types
118unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
119
120// Tuples of `Pod` types.
121//
122// Only the zero- and one-element tuples are covered. A multi-field tuple has
123// `repr(Rust)` layout, so the compiler may insert padding between fields —
124// `(u8, u64)` carries 7 padding bytes. Padding is uninitialized memory, which
125// violates requirement 4 and is undefined to read as part of an atomic word
126// under the `atomic-slots` feature. Use a `#[repr(C)]` struct with explicit
127// padding fields instead.
128unsafe impl Pod for () {}
129unsafe impl<A: Pod> Pod for (A,) {}