1mod probe;
10mod receiver;
11mod sender;
12mod transmit;
13
14pub use probe::{Probe, ProbeError, ProbeScheduler, ProbeTiming};
15pub use receiver::Receiver;
16pub use sender::Sender;
17pub use transmit::{OpenRequest, Repair, StatusOutcome, TransmitError, Transmitter};
18
19use std::collections::VecDeque;
20
21use crate::{Digest, PipelineLimits, Prefix, Scope};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct ReceiveEpoch(u128);
29
30impl ReceiveEpoch {
31 pub const fn new(value: u128) -> Result<Self, FlowError> {
33 if value == 0 {
34 Err(FlowError::Invalid)
35 } else {
36 Ok(Self(value))
37 }
38 }
39
40 pub const fn get(self) -> u128 {
42 self.0
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Channel {
49 pub scope: Scope,
51 pub epoch: ReceiveEpoch,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct Operation {
60 pub prefix: Prefix,
62 pub previous_digest: Digest,
64 pub body_bytes: u64,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct Report {
74 pub channel: Channel,
76 pub revision: u64,
78 pub base: Prefix,
80 pub received: Prefix,
82 pub received_bytes: u64,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
88pub enum FlowError {
89 #[error("invalid replica flow metadata")]
91 Invalid,
92 #[error("replica flow channel mismatch")]
94 Channel,
95 #[error("replica flow capacity exhausted")]
97 Capacity,
98 #[error("replica flow history mismatch")]
100 History,
101 #[error("replica flow report is inconsistent")]
103 Report,
104 #[error("replica flow counter exhausted")]
106 Exhausted,
107 #[error("replica flow metadata allocation failed")]
109 Allocation,
110}
111
112fn capacities(limits: PipelineLimits) -> Result<(u64, u64), FlowError> {
113 if limits.max_operations == 0 || limits.max_body_bytes == 0 {
114 return Err(FlowError::Invalid);
115 }
116 Ok((
117 u64::try_from(limits.max_operations).map_err(|_| FlowError::Invalid)?,
118 u64::try_from(limits.max_body_bytes).map_err(|_| FlowError::Invalid)?,
119 ))
120}
121
122fn ledger(limits: PipelineLimits) -> Result<VecDeque<Operation>, FlowError> {
123 capacities(limits)?;
124 let mut entries = VecDeque::new();
125 entries
126 .try_reserve_exact(limits.max_operations)
127 .map_err(|_| FlowError::Allocation)?;
128 Ok(entries)
129}
130
131fn prefix_valid(prefix: Prefix) -> bool {
132 (prefix.op.0 == 0) == (prefix.digest == Digest::ZERO)
133}
134
135fn batch(operations: &[Operation], previous: Prefix) -> Result<(Prefix, u64), FlowError> {
136 if operations.is_empty() {
137 return Err(FlowError::Invalid);
138 }
139 let mut end = previous;
140 let mut bytes = 0u64;
141 for operation in operations {
142 if operation.body_bytes == 0 || !prefix_valid(operation.prefix) {
143 return Err(FlowError::Invalid);
144 }
145 if operation.prefix.op.0 != end.op.0.checked_add(1).ok_or(FlowError::Exhausted)?
146 || operation.previous_digest != end.digest
147 {
148 return Err(FlowError::History);
149 }
150 bytes = bytes
151 .checked_add(operation.body_bytes)
152 .ok_or(FlowError::Exhausted)?;
153 end = operation.prefix;
154 }
155 Ok((end, bytes))
156}
157
158impl Report {
159 pub fn validate(self, limits: PipelineLimits) -> Result<(), FlowError> {
162 capacities(limits)?;
163 self.validate_shape()
164 }
165
166 pub fn validate_shape(self) -> Result<(), FlowError> {
172 if self.channel.scope.group_id.as_bytes() == &[0; 16]
173 || self.channel.scope.configuration_digest == Digest::ZERO
174 || self.revision == 0
175 || !prefix_valid(self.base)
176 || !prefix_valid(self.received)
177 {
178 return Err(FlowError::Invalid);
179 }
180 let received = self
181 .received
182 .op
183 .0
184 .checked_sub(self.base.op.0)
185 .ok_or(FlowError::History)?;
186 if (received == 0 && self.received != self.base)
187 || (received == 0) != (self.received_bytes == 0)
188 || self.received_bytes < received
189 {
190 return Err(FlowError::Report);
191 }
192 Ok(())
193 }
194}