1use bytes::Bytes;
2
3pub const INLINE_PAYLOAD_LEN: usize = 32;
4pub const INLINE_PAYLOAD_SLOT: u32 = 0xFFFF_FFFF;
5
6#[repr(C, align(64))]
10#[derive(Debug, Clone, Copy)]
11pub struct MsgDesc {
12 pub msg_type: u8,
14 pub flags: u8,
15 pub _reserved: [u8; 2],
16 pub id: u32,
17 pub method_id: u64,
18
19 pub payload_slot: u32,
21 pub payload_generation: u32,
22 pub payload_offset: u32,
23 pub payload_len: u32,
24
25 pub inline_payload: [u8; INLINE_PAYLOAD_LEN],
27}
28
29impl MsgDesc {
30 #[inline]
31 pub fn new(msg_type: u8, id: u32, method_id: u64) -> Self {
32 Self {
33 msg_type,
34 flags: 0,
35 _reserved: [0; 2],
36 id,
37 method_id,
38 payload_slot: INLINE_PAYLOAD_SLOT,
39 payload_generation: 0,
40 payload_offset: 0,
41 payload_len: 0,
42 inline_payload: [0; INLINE_PAYLOAD_LEN],
43 }
44 }
45
46 #[inline]
47 pub fn inline_payload_bytes(&self) -> &[u8] {
48 let len = (self.payload_len as usize).min(INLINE_PAYLOAD_LEN);
49 &self.inline_payload[..len]
50 }
51}
52
53#[derive(Debug, Clone)]
55pub enum Payload {
56 Inline,
58 Owned(Vec<u8>),
60 Bytes(Bytes),
62}
63
64impl Payload {
65 pub fn as_slice<'a>(&'a self, desc: &'a MsgDesc) -> &'a [u8] {
66 match self {
67 Self::Inline => desc.inline_payload_bytes(),
68 Self::Owned(buf) => buf.as_slice(),
69 Self::Bytes(buf) => buf.as_ref(),
70 }
71 }
72
73 pub fn external_slice(&self) -> Option<&[u8]> {
74 match self {
75 Self::Inline => None,
76 Self::Owned(buf) => Some(buf.as_slice()),
77 Self::Bytes(buf) => Some(buf.as_ref()),
78 }
79 }
80
81 pub fn len(&self, desc: &MsgDesc) -> usize {
82 self.as_slice(desc).len()
83 }
84
85 pub fn is_inline(&self) -> bool {
86 matches!(self, Self::Inline)
87 }
88}
89
90#[derive(Debug, Clone)]
92pub struct Frame {
93 pub desc: MsgDesc,
94 pub payload: Payload,
95}
96
97impl Frame {
98 #[inline]
99 pub fn new(desc: MsgDesc) -> Self {
100 Self {
101 desc,
102 payload: Payload::Inline,
103 }
104 }
105
106 #[inline]
107 pub fn with_inline_payload(mut desc: MsgDesc, payload: &[u8]) -> Option<Self> {
108 if payload.len() > INLINE_PAYLOAD_LEN {
109 return None;
110 }
111 desc.payload_slot = INLINE_PAYLOAD_SLOT;
112 desc.payload_generation = 0;
113 desc.payload_offset = 0;
114 desc.payload_len = payload.len() as u32;
115 desc.inline_payload[..payload.len()].copy_from_slice(payload);
116 Some(Self {
117 desc,
118 payload: Payload::Inline,
119 })
120 }
121
122 #[inline]
123 pub fn with_owned_payload(mut desc: MsgDesc, payload: Vec<u8>) -> Self {
124 desc.payload_slot = 0;
125 desc.payload_generation = 0;
126 desc.payload_offset = 0;
127 desc.payload_len = payload.len() as u32;
128 Self {
129 desc,
130 payload: Payload::Owned(payload),
131 }
132 }
133
134 #[inline]
135 pub fn with_bytes_payload(mut desc: MsgDesc, payload: Bytes) -> Self {
136 desc.payload_slot = 0;
137 desc.payload_generation = 0;
138 desc.payload_offset = 0;
139 desc.payload_len = payload.len() as u32;
140 Self {
141 desc,
142 payload: Payload::Bytes(payload),
143 }
144 }
145
146 #[inline]
147 pub fn payload_bytes(&self) -> &[u8] {
148 self.payload.as_slice(&self.desc)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn msg_desc_is_one_cache_line() {
158 static_assertions::const_assert!(std::mem::size_of::<MsgDesc>() == 64);
159 static_assertions::const_assert!(std::mem::align_of::<MsgDesc>() == 64);
160 }
161
162 #[test]
163 fn inline_payload_roundtrips() {
164 let mut desc = MsgDesc::new(1, 7, 0);
165 let payload = b"hello";
166 let frame = Frame::with_inline_payload(desc, payload).expect("inline payload");
167 assert!(frame.payload.is_inline());
168 assert_eq!(frame.payload_bytes(), payload);
169 desc.payload_len = 999;
170 assert_eq!(desc.inline_payload_bytes().len(), INLINE_PAYLOAD_LEN);
171 }
172}