1#![no_std]
45#![forbid(unsafe_code)]
46#![warn(missing_docs)]
47
48mod jitter;
49
50pub use jitter::{decorrelated_jitter, equal_jitter, full_jitter};
51
52use core::time::Duration;
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56enum Kind {
57 Constant,
59 Linear { step: Duration },
61 Exponential { factor: u32 },
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub struct Backoff {
76 base: Duration,
77 kind: Kind,
78 max_delay: Duration,
79 max_retries: Option<u32>,
80}
81
82impl Backoff {
83 pub const fn constant(base: Duration) -> Self {
85 Self {
86 base,
87 kind: Kind::Constant,
88 max_delay: Duration::MAX,
89 max_retries: None,
90 }
91 }
92
93 pub const fn linear(base: Duration, step: Duration) -> Self {
95 Self {
96 base,
97 kind: Kind::Linear { step },
98 max_delay: Duration::MAX,
99 max_retries: None,
100 }
101 }
102
103 pub const fn exponential(base: Duration, factor: u32) -> Self {
108 Self {
109 base,
110 kind: Kind::Exponential { factor },
111 max_delay: Duration::MAX,
112 max_retries: None,
113 }
114 }
115
116 pub const fn with_max_delay(mut self, max_delay: Duration) -> Self {
118 self.max_delay = max_delay;
119 self
120 }
121
122 pub const fn with_max_retries(mut self, max_retries: u32) -> Self {
127 self.max_retries = Some(max_retries);
128 self
129 }
130
131 pub const fn base(&self) -> Duration {
133 self.base
134 }
135
136 pub const fn max_delay(&self) -> Duration {
138 self.max_delay
139 }
140
141 pub const fn max_retries(&self) -> Option<u32> {
143 self.max_retries
144 }
145
146 pub fn delay(&self, attempt: u32) -> Option<Duration> {
153 if let Some(max) = self.max_retries {
154 if attempt >= max {
155 return None;
156 }
157 }
158
159 let raw = match self.kind {
160 Kind::Constant => self.base,
161 Kind::Linear { step } => self.base.saturating_add(step.saturating_mul(attempt)),
162 Kind::Exponential { factor } => {
163 if factor <= 1 {
164 self.base
165 } else {
166 let mut delay = self.base;
167 let mut i = 0;
168 while i < attempt {
169 if delay >= self.max_delay {
170 delay = self.max_delay;
171 break;
172 }
173 let next = delay.saturating_mul(factor);
174 if next == delay {
175 break;
179 }
180 delay = next;
181 i += 1;
182 }
183 delay
184 }
185 }
186 };
187
188 Some(if raw > self.max_delay {
189 self.max_delay
190 } else {
191 raw
192 })
193 }
194
195 pub const fn delays(&self) -> Delays {
200 Delays {
201 backoff: *self,
202 attempt: 0,
203 }
204 }
205}
206
207#[derive(Debug, Clone)]
211pub struct Delays {
212 backoff: Backoff,
213 attempt: u32,
214}
215
216impl Iterator for Delays {
217 type Item = Duration;
218
219 fn next(&mut self) -> Option<Self::Item> {
220 let delay = self.backoff.delay(self.attempt)?;
221 self.attempt = self.attempt.saturating_add(1);
222 Some(delay)
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 const MS: fn(u64) -> Duration = Duration::from_millis;
231
232 #[test]
233 fn constant_is_flat() {
234 let b = Backoff::constant(MS(50));
235 assert_eq!(b.delay(0), Some(MS(50)));
236 assert_eq!(b.delay(1000), Some(MS(50)));
237 }
238
239 #[test]
240 fn linear_grows_by_step() {
241 let b = Backoff::linear(MS(100), MS(25));
242 assert_eq!(b.delay(0), Some(MS(100)));
243 assert_eq!(b.delay(1), Some(MS(125)));
244 assert_eq!(b.delay(4), Some(MS(200)));
245 }
246
247 #[test]
248 fn exponential_doubles() {
249 let b = Backoff::exponential(MS(10), 2);
250 assert_eq!(b.delay(0), Some(MS(10)));
251 assert_eq!(b.delay(1), Some(MS(20)));
252 assert_eq!(b.delay(2), Some(MS(40)));
253 assert_eq!(b.delay(3), Some(MS(80)));
254 }
255
256 #[test]
257 fn exponential_factor_three() {
258 let b = Backoff::exponential(MS(1), 3);
259 assert_eq!(b.delay(0), Some(MS(1)));
260 assert_eq!(b.delay(1), Some(MS(3)));
261 assert_eq!(b.delay(2), Some(MS(9)));
262 }
263
264 #[test]
265 fn max_delay_caps() {
266 let b = Backoff::exponential(MS(100), 2).with_max_delay(MS(500));
267 assert_eq!(b.delay(0), Some(MS(100)));
268 assert_eq!(b.delay(2), Some(MS(400)));
269 assert_eq!(b.delay(3), Some(MS(500))); assert_eq!(b.delay(50), Some(MS(500)));
271 }
272
273 #[test]
274 fn max_retries_stops() {
275 let b = Backoff::constant(MS(10)).with_max_retries(3);
276 assert_eq!(b.delay(0), Some(MS(10)));
277 assert_eq!(b.delay(2), Some(MS(10)));
278 assert_eq!(b.delay(3), None);
279 assert_eq!(b.delay(100), None);
280 }
281
282 #[test]
283 fn exponential_factor_below_two_is_constant() {
284 let b = Backoff::exponential(MS(7), 1);
285 assert_eq!(b.delay(0), Some(MS(7)));
286 assert_eq!(b.delay(9), Some(MS(7)));
287 }
288
289 #[test]
290 fn huge_attempt_saturates_without_hanging() {
291 let b = Backoff::exponential(Duration::from_secs(1), 2);
293 assert_eq!(b.delay(u32::MAX), Some(Duration::MAX));
294 }
295
296 #[test]
297 fn zero_base_exponential_does_not_hang() {
298 let b = Backoff::exponential(Duration::ZERO, 2);
301 assert_eq!(b.delay(u32::MAX), Some(Duration::ZERO));
302 assert_eq!(b.delay(0), Some(Duration::ZERO));
303
304 let capped = Backoff::exponential(Duration::ZERO, 4).with_max_delay(MS(500));
306 assert_eq!(capped.delay(u32::MAX), Some(Duration::ZERO));
307 }
308
309 #[test]
310 fn linear_saturates() {
311 let b = Backoff::linear(Duration::MAX, Duration::from_secs(1));
312 assert_eq!(b.delay(10), Some(Duration::MAX));
313 }
314
315 #[test]
316 fn delays_iterator_respects_limit() {
317 let b = Backoff::exponential(MS(10), 2).with_max_retries(4);
318 let collected: heapless_vec::Vec = b.delays().collect();
319 assert_eq!(collected.as_slice(), &[MS(10), MS(20), MS(40), MS(80)]);
320 }
321
322 #[test]
323 fn getters() {
324 let b = Backoff::exponential(MS(5), 2)
325 .with_max_delay(MS(100))
326 .with_max_retries(7);
327 assert_eq!(b.base(), MS(5));
328 assert_eq!(b.max_delay(), MS(100));
329 assert_eq!(b.max_retries(), Some(7));
330 }
331
332 mod heapless_vec {
334 use core::time::Duration;
335
336 pub struct Vec {
337 data: [Duration; 16],
338 len: usize,
339 }
340
341 impl Vec {
342 pub fn as_slice(&self) -> &[Duration] {
343 &self.data[..self.len]
344 }
345 }
346
347 impl FromIterator<Duration> for Vec {
348 fn from_iter<I: IntoIterator<Item = Duration>>(iter: I) -> Self {
349 let mut data = [Duration::ZERO; 16];
350 let mut len = 0;
351 for d in iter {
352 data[len] = d;
353 len += 1;
354 }
355 Self { data, len }
356 }
357 }
358 }
359}