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) {
30 self.0.lock().enqueue(item);
31 }
32}
33
34#[derive(Debug)]
35pub(crate) struct RingBufferView<T>(Arc<parking_lot::FairMutex<AllocRingBuffer<T>>>);
36
37impl<T> Clone for RingBufferView<T> {
38 fn clone(&self) -> Self {
39 Self(self.0.clone())
40 }
41}
42
43impl<T: Clone> RingBufferView<T> {
44 pub fn find<F: FnMut(&T) -> bool>(&self, mut predicate: F) -> Vec<T> {
45 self.0.lock().iter().filter(|item| predicate(item)).cloned().collect()
46 }
47}
48
49pub(crate) fn searchable_ringbuffer<T: Send + 'static>(capacity: usize) -> (RingBufferProducer<T>, RingBufferView<T>) {
50 let rb = Arc::new(parking_lot::FairMutex::new(AllocRingBuffer::new(capacity)));
51 (RingBufferProducer(rb.clone()), RingBufferView(rb))
52}
53
54const MAX_BACKOFF: Duration = Duration::from_secs(300);
55
56pub(crate) fn next_deadline_with_backoff(n: usize, base: f64, duration: Duration) -> Instant {
57 let backoff = duration.mul_f64(base.powi(n.min((i32::MAX / 2) as usize) as i32 + 1));
58 Instant::now() + backoff.min(MAX_BACKOFF)
59}
60
61#[derive(Debug, Copy, Clone, Eq)]
62pub(crate) struct RetriedFrameId {
63 pub frame_id: FrameId,
64 pub retry_count: usize,
65 max_retries: usize,
66}
67
68impl RetriedFrameId {
69 pub fn no_retries(frame_id: FrameId) -> Self {
70 Self {
71 frame_id,
72 retry_count: 1,
73 max_retries: 1,
74 }
75 }
76
77 pub fn with_retries(frame_id: FrameId, max_retries: usize) -> Self {
78 Self {
79 frame_id,
80 retry_count: 1,
81 max_retries,
82 }
83 }
84
85 pub fn next(self) -> Option<Self> {
86 if self.retry_count < self.max_retries {
87 Some(Self {
88 frame_id: self.frame_id,
89 retry_count: self.retry_count + 1,
90 max_retries: self.max_retries,
91 })
92 } else {
93 None
94 }
95 }
96}
97
98impl PartialEq<Self> for RetriedFrameId {
99 fn eq(&self, other: &Self) -> bool {
100 self.frame_id.eq(&other.frame_id)
101 }
102}
103
104impl PartialOrd<Self> for RetriedFrameId {
105 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
106 Some(self.cmp(other))
107 }
108}
109
110impl Ord for RetriedFrameId {
111 fn cmp(&self, other: &Self) -> Ordering {
112 self.frame_id.cmp(&other.frame_id)
113 }
114}
115
116pub fn segment_into<T: AsRef<[u8]>, E: Extend<Segment>>(
119 data: T,
120 max_segment_size: usize,
121 frame_id: FrameId,
122 segments: &mut E,
123) -> crate::errors::Result<()> {
124 if frame_id == 0 {
125 return Err(SessionError::InvalidFrameId);
126 }
127
128 if max_segment_size == 0 {
129 return Err(SessionError::IncorrectMessageLength);
130 }
131
132 let data = data.as_ref();
133
134 let num_chunks = data.len().div_ceil(max_segment_size);
135 if num_chunks > SeqNum::MAX as usize {
136 return Err(SessionError::DataTooLong);
137 }
138
139 let chunks = data.chunks(max_segment_size);
140
141 let seq_len = SeqIndicator::try_from(chunks.len() as SeqNum)?;
142 segments.extend(chunks.enumerate().map(|(idx, data)| Segment {
143 frame_id,
144 seq_flags: seq_len,
145 seq_idx: idx as u8,
146 data: data.into(),
147 }));
148
149 Ok(())
150}
151
152#[allow(unused)]
154pub fn segment<T: AsRef<[u8]>>(data: T, max_segment_size: usize, frame_id: u32) -> crate::errors::Result<Vec<Segment>> {
155 let mut out = Vec::with_capacity(data.as_ref().len().div_ceil(max_segment_size));
156 segment_into(data, max_segment_size, frame_id, &mut out)?;
157 Ok(out)
158}
159
160#[cfg(test)]
161mod tests {
162 use hex_literal::hex;
163
164 use super::*;
165
166 #[test]
167 fn segment_should_split_data_correctly() -> anyhow::Result<()> {
168 let data = hex!("deadbeefcafebabe");
169
170 let segments = segment(data, 3, 1)?;
171 assert_eq!(3, segments.len());
172
173 assert_eq!(hex!("deadbe"), segments[0].data.as_ref());
174 assert_eq!(0, segments[0].seq_idx);
175 assert_eq!(3, segments[0].seq_flags.seq_len());
176 assert_eq!(1, segments[0].frame_id);
177
178 assert_eq!(hex!("efcafe"), segments[1].data.as_ref());
179 assert_eq!(1, segments[1].seq_idx);
180 assert_eq!(3, segments[1].seq_flags.seq_len());
181 assert_eq!(1, segments[1].frame_id);
182
183 assert_eq!(hex!("babe"), segments[2].data.as_ref());
184 assert_eq!(2, segments[2].seq_idx);
185 assert_eq!(3, segments[2].seq_flags.seq_len());
186 assert_eq!(1, segments[2].frame_id);
187
188 Ok(())
189 }
190}