1use ozzy_proto::{Envelope, Opcode};
4use ozzy_proto::{LinkSessionId, NodeId, RequestId};
5
6use super::{
7 COMMON_BYTES, ControlEncoding, PrepareBatch, Reader, WireError, WireLimits, Writer,
8 validate_prefix, validate_scope,
9};
10use crate::flow::{Channel, ReceiveEpoch, Report};
11use crate::{OpNumber, Scope};
12
13pub use crate::flow::Probe as FlowProbe;
14
15const PROBE_BYTES: usize = COMMON_BYTES + 40 + 8;
16const STATE_BYTES: usize = COMMON_BYTES + 16 + 8 + 40 + 40 + 8 + 8 + 4;
17
18fn validate_probe(probe: FlowProbe) -> Result<(), WireError> {
19 validate_scope(probe.scope)?;
20 validate_prefix(probe.tail)?;
21 if probe.available < probe.tail.op || probe.available.0 == u64::MAX {
22 return Err(WireError::Prefix);
23 }
24 if probe.request_id.as_bytes() == &[0; 16] {
25 return Err(WireError::Correlation);
26 }
27 Ok(())
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct FlowState {
33 pub handle: u32,
35 pub request_id: Option<RequestId>,
37 pub report: Report,
39 pub repair_limit: Option<OpNumber>,
42}
43
44impl FlowState {
45 fn validate(self) -> Result<(), WireError> {
46 self.report.validate_shape()?;
47 if self.handle == 0 {
48 return Err(WireError::Correlation);
49 }
50 if self.repair_limit == Some(OpNumber(u64::MAX)) {
51 return Err(WireError::Prefix);
52 }
53 validate_prefix(self.report.base)?;
54 validate_prefix(self.report.received)?;
55 if self.request_id.is_some_and(|id| id.as_bytes() == &[0; 16]) {
56 return Err(WireError::Correlation);
57 }
58 Ok(())
59 }
60
61 pub fn validate_response(self, probe: FlowProbe) -> Result<(), WireError> {
67 self.validate()?;
68 validate_probe(probe)?;
69 if self.request_id != Some(probe.request_id) || self.report.channel.scope != probe.scope {
70 return Err(WireError::Correlation);
71 }
72 Ok(())
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum FlowMessage<'a> {
79 Probe(FlowProbe),
81 State(FlowState),
83 Prepare {
85 epoch: ReceiveEpoch,
87 batch: PrepareBatch<'a>,
89 },
90}
91
92pub fn encode_flow_probe(
94 sender: NodeId,
95 session: LinkSessionId,
96 probe: FlowProbe,
97 output: &mut [u8],
98 limits: WireLimits,
99) -> Result<ControlEncoding, WireError> {
100 validate_probe(probe)?;
101 let header = Envelope {
102 opcode: Opcode::ReplicaOpen,
103 response: false,
104 request_id: Some(probe.request_id),
105 sender,
106 session: Some(session),
107 }
108 .encode_header(PROBE_BYTES, 0, limits.envelope)?;
109 if output.len() < PROBE_BYTES {
110 return Err(WireError::Capacity);
111 }
112 let mut writer = Writer::new(&mut output[..PROBE_BYTES]);
113 writer.scope(probe.scope, sender);
114 writer.prefix(probe.tail);
115 writer.u64(probe.available.0);
116 Ok(ControlEncoding {
117 header,
118 metadata_bytes: PROBE_BYTES,
119 })
120}
121
122pub fn encode_flow_state(
125 sender: NodeId,
126 session: LinkSessionId,
127 state: FlowState,
128 output: &mut [u8],
129 limits: WireLimits,
130) -> Result<ControlEncoding, WireError> {
131 state.validate()?;
132 let header = Envelope {
133 opcode: Opcode::ReplicaState,
134 response: state.request_id.is_some(),
135 request_id: state.request_id,
136 sender,
137 session: Some(session),
138 }
139 .encode_header(STATE_BYTES, 0, limits.envelope)?;
140 if output.len() < STATE_BYTES {
141 return Err(WireError::Capacity);
142 }
143 let mut writer = Writer::new(&mut output[..STATE_BYTES]);
144 let report = state.report;
145 writer.scope(report.channel.scope, sender);
146 writer.bytes(&report.channel.epoch.get().to_be_bytes());
147 writer.u64(report.revision);
148 writer.prefix(report.base);
149 writer.prefix(report.received);
150 writer.u64(report.received_bytes);
151 writer.u64(state.repair_limit.map_or(u64::MAX, |op| op.0));
152 writer.bytes(&state.handle.to_be_bytes());
153 Ok(ControlEncoding {
154 header,
155 metadata_bytes: STATE_BYTES,
156 })
157}
158
159pub(super) fn decode_probe(
160 scope: Scope,
161 envelope: Envelope,
162 mut reader: Reader<'_>,
163 payload: &[u8],
164) -> Result<FlowProbe, WireError> {
165 if !payload.is_empty() {
166 return Err(WireError::Payload);
167 }
168 if envelope.response {
169 return Err(WireError::Correlation);
170 }
171 let probe = FlowProbe {
172 scope,
173 request_id: envelope.request_id.ok_or(WireError::Correlation)?,
174 tail: reader.prefix()?,
175 available: OpNumber(reader.u64()?),
176 };
177 reader.finish()?;
178 validate_probe(probe)?;
179 Ok(probe)
180}
181
182pub(super) fn decode_state(
183 scope: Scope,
184 envelope: Envelope,
185 mut reader: Reader<'_>,
186 payload: &[u8],
187) -> Result<FlowState, WireError> {
188 if !payload.is_empty() {
189 return Err(WireError::Payload);
190 }
191 if envelope.response != envelope.request_id.is_some() {
192 return Err(WireError::Correlation);
193 }
194 let epoch = ReceiveEpoch::new(u128::from_be_bytes(reader.bytes()?))?;
195 let state = FlowState {
196 handle: 1,
197 request_id: envelope.request_id,
198 report: Report {
199 channel: Channel { scope, epoch },
200 revision: reader.u64()?,
201 base: reader.prefix()?,
202 received: reader.prefix()?,
203 received_bytes: reader.u64()?,
204 },
205 repair_limit: match reader.u64()? {
206 u64::MAX => None,
207 value => Some(OpNumber(value)),
208 },
209 };
210 let state = FlowState {
211 handle: reader.u32()?,
212 ..state
213 };
214 reader.finish()?;
215 state.validate()?;
216 Ok(state)
217}
218
219pub fn flow_state_route(
222 packet: ozzy_proto::Packet<'_>,
223 limits: ozzy_proto::EnvelopeLimits,
224) -> Result<FlowState, WireError> {
225 packet
226 .envelope
227 .validate_frames(packet.metadata.len(), packet.payload.len(), limits)?;
228 if packet.envelope.opcode != Opcode::ReplicaState {
229 return Err(WireError::UnsupportedCommand);
230 }
231 let mut reader = Reader::new(packet.metadata);
232 let scope = reader.routing_scope(packet.envelope.sender)?;
233 decode_state(scope, packet.envelope, reader, packet.payload)
234}