hopr_protocol_session/utils/
mod.rs1pub mod skip_queue;
2
3#[cfg(test)]
4pub mod test;
5
6use std::{
7 cmp::Ordering,
8 sync::Arc,
9 time::{Duration, Instant},
10};
11
12use ringbuffer::{AllocRingBuffer, RingBuffer};
13
14use crate::{
15 errors::SessionError,
16 protocol::{FrameId, Segment, SeqIndicator, SeqNum},
17};
18
19#[derive(Clone)]
20pub(crate) struct RingBufferProducer<T>(Arc<parking_lot::FairMutex<AllocRingBuffer<T>>>);
21
22impl<T> std::fmt::Debug for RingBufferProducer<T> {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_tuple("RingBufferProducer").finish()
25 }
26}
27
28impl<T> RingBufferProducer<T> {
29 pub fn push(&mut self, item: T) -> bool {
32 let mut rb = self.0.lock();
33 let had_capacity = !rb.is_full();
34 rb.enqueue(item);
35 had_capacity
36 }
37}
38
39#[derive(Debug)]
40pub(crate) struct RingBufferView<T>(Arc<parking_lot::FairMutex<AllocRingBuffer<T>>>);
41
42impl<T> Clone for RingBufferView<T> {
43 fn clone(&self) -> Self {
44 Self(self.0.clone())
45 }
46}
47
48impl<T: Clone> RingBufferView<T> {
49 pub fn find<F: FnMut(&T) -> bool>(&self, mut predicate: F) -> Vec<T> {
50 self.0.lock().iter().filter(|item| predicate(item)).cloned().collect()
51 }
52}
53
54pub(crate) fn searchable_ringbuffer<T: Send + 'static>(capacity: usize) -> (RingBufferProducer<T>, RingBufferView<T>) {
55 let rb = Arc::new(parking_lot::FairMutex::new(AllocRingBuffer::new(capacity)));
56 (RingBufferProducer(rb.clone()), RingBufferView(rb))
57}
58
59const MAX_BACKOFF: Duration = Duration::from_secs(300);
60
61pub(crate) fn next_deadline_with_backoff(n: usize, base: f64, duration: Duration) -> Instant {
62 let backoff = duration.mul_f64(base.powi(n.min((i32::MAX / 2) as usize) as i32 + 1));
63 Instant::now() + backoff.min(MAX_BACKOFF)
64}
65
66#[derive(Debug, Copy, Clone, Eq)]
67pub(crate) struct RetriedFrameId {
68 pub frame_id: FrameId,
69 pub retry_count: usize,
70 max_retries: usize,
71 first_sent: std::time::Instant,
74}
75
76impl RetriedFrameId {
77 pub fn no_retries(frame_id: FrameId) -> Self {
78 Self {
79 frame_id,
80 retry_count: 1,
81 max_retries: 1,
82 first_sent: std::time::Instant::now(),
83 }
84 }
85
86 pub fn with_retries(frame_id: FrameId, max_retries: usize) -> Self {
87 Self {
88 frame_id,
89 retry_count: 1,
90 max_retries,
91 first_sent: std::time::Instant::now(),
92 }
93 }
94
95 pub fn next(self) -> Option<Self> {
96 if self.retry_count < self.max_retries {
97 Some(Self {
98 frame_id: self.frame_id,
99 retry_count: self.retry_count + 1,
100 max_retries: self.max_retries,
101 first_sent: self.first_sent,
102 })
103 } else {
104 None
105 }
106 }
107
108 pub fn age(&self) -> std::time::Duration {
110 self.first_sent.elapsed()
111 }
112}
113
114impl PartialEq<Self> for RetriedFrameId {
115 fn eq(&self, other: &Self) -> bool {
116 self.frame_id.eq(&other.frame_id)
117 }
118}
119
120impl PartialOrd<Self> for RetriedFrameId {
121 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
122 Some(self.cmp(other))
123 }
124}
125
126impl Ord for RetriedFrameId {
127 fn cmp(&self, other: &Self) -> Ordering {
128 self.frame_id.cmp(&other.frame_id)
129 }
130}
131
132pub fn segment_into<T: AsRef<[u8]>, E: Extend<Segment>>(
135 data: T,
136 max_segment_size: usize,
137 frame_id: FrameId,
138 segments: &mut E,
139) -> crate::errors::Result<()> {
140 if frame_id == 0 {
141 return Err(SessionError::InvalidFrameId);
142 }
143
144 if max_segment_size == 0 {
145 return Err(SessionError::IncorrectMessageLength);
146 }
147
148 let data = data.as_ref();
149
150 let num_chunks = data.len().div_ceil(max_segment_size);
151 if num_chunks > SeqNum::MAX as usize {
152 return Err(SessionError::DataTooLong);
153 }
154
155 let chunks = data.chunks(max_segment_size);
156
157 let seq_len = SeqIndicator::try_from(chunks.len() as SeqNum)?;
158 segments.extend(chunks.enumerate().map(|(idx, data)| Segment {
159 frame_id,
160 seq_flags: seq_len,
161 seq_idx: idx as u8,
162 data: data.into(),
163 }));
164
165 Ok(())
166}
167
168#[allow(unused)]
170pub fn segment<T: AsRef<[u8]>>(data: T, max_segment_size: usize, frame_id: u32) -> crate::errors::Result<Vec<Segment>> {
171 let mut out = Vec::with_capacity(data.as_ref().len().div_ceil(max_segment_size));
172 segment_into(data, max_segment_size, frame_id, &mut out)?;
173 Ok(out)
174}
175
176#[cfg(test)]
177mod tests {
178 use hex_literal::hex;
179
180 use super::*;
181
182 #[test]
183 fn ring_buffer_producer_push_returns_false_when_overwriting() {
184 let (mut tx, _rx) = searchable_ringbuffer::<u32>(2);
185 assert!(tx.push(1));
186 assert!(tx.push(2));
187 assert!(!tx.push(3)); }
189
190 #[test]
191 fn segment_should_split_data_correctly() -> anyhow::Result<()> {
192 let data = hex!("deadbeefcafebabe");
193
194 let segments = segment(data, 3, 1)?;
195 assert_eq!(3, segments.len());
196
197 assert_eq!(hex!("deadbe"), segments[0].data.as_ref());
198 assert_eq!(0, segments[0].seq_idx);
199 assert_eq!(3, segments[0].seq_flags.seq_len());
200 assert_eq!(1, segments[0].frame_id);
201
202 assert_eq!(hex!("efcafe"), segments[1].data.as_ref());
203 assert_eq!(1, segments[1].seq_idx);
204 assert_eq!(3, segments[1].seq_flags.seq_len());
205 assert_eq!(1, segments[1].frame_id);
206
207 assert_eq!(hex!("babe"), segments[2].data.as_ref());
208 assert_eq!(2, segments[2].seq_idx);
209 assert_eq!(3, segments[2].seq_flags.seq_len());
210 assert_eq!(1, segments[2].frame_id);
211
212 Ok(())
213 }
214}