conmonrs/streaming_server/
budget.rs1use bytes::{Bytes, BytesMut};
7use std::{ops::Deref, sync::Arc};
8use tokio::sync::{OwnedSemaphorePermit, Semaphore};
9
10#[derive(Clone, Debug)]
12pub(super) struct Budget {
13 bytes: Arc<Semaphore>,
14 max: u32,
15}
16
17impl Budget {
18 pub(super) fn new(max: u32) -> Self {
20 Self {
21 bytes: Arc::new(Semaphore::new(max as usize)),
22 max,
23 }
24 }
25
26 pub(super) async fn hold(&self, data: Bytes) -> Held {
30 let len = data.len();
31 self.hold_as(data, len).await
32 }
33
34 pub(super) async fn hold_as(&self, data: Bytes, len: usize) -> Held {
36 let len = u32::try_from(len).map_or(self.max, |len| len.min(self.max));
37 let permit = self.bytes.clone().acquire_many_owned(len).await.ok();
39 Held { data, permit }
40 }
41
42 #[cfg(test)]
44 pub(super) fn available(&self) -> usize {
45 self.bytes.available_permits()
46 }
47}
48
49#[derive(Debug)]
51pub(super) struct Held {
52 data: Bytes,
53 permit: Option<OwnedSemaphorePermit>,
54}
55
56impl Held {
57 pub(super) fn into_bytes(self) -> Bytes {
59 self.data
60 }
61}
62
63impl Deref for Held {
64 type Target = Bytes;
65
66 fn deref(&self) -> &Bytes {
67 &self.data
68 }
69}
70
71#[derive(Debug, Default)]
76pub(super) struct HeldBuf {
77 data: BytesMut,
78 permit: Option<OwnedSemaphorePermit>,
79}
80
81impl HeldBuf {
82 pub(super) fn cost(&self, len: usize) -> usize {
85 self.capacity_for(len) - self.data.capacity()
86 }
87
88 pub(super) fn push(&mut self, held: Held) {
91 let capacity = self.capacity_for(held.len());
92 if capacity > self.data.capacity() {
93 let mut data = BytesMut::with_capacity(capacity);
94 data.extend_from_slice(&self.data);
95 self.data = data;
96 }
97 self.data.extend_from_slice(&held.data);
98 match (&mut self.permit, held.permit) {
99 (Some(permit), Some(other)) => permit.merge(other),
100 (permit, other) => *permit = permit.take().or(other),
101 }
102 }
103
104 fn capacity_for(&self, len: usize) -> usize {
107 let (len, capacity) = (self.data.len() + len, self.data.capacity());
108 if len <= capacity {
109 capacity
110 } else {
111 len.max(2 * capacity)
112 }
113 }
114}
115
116impl Deref for HeldBuf {
117 type Target = [u8];
118
119 fn deref(&self) -> &[u8] {
120 &self.data
121 }
122}
123
124#[cfg(test)]
126impl From<Bytes> for Held {
127 fn from(data: Bytes) -> Self {
128 Self { data, permit: None }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use tokio::time::{self, Duration};
136
137 #[tokio::test(start_paused = true)]
138 async fn data_waits_for_room() {
139 let budget = Budget::new(10);
140 let first = budget.hold(Bytes::from_static(b"0123456")).await;
141 assert_eq!(budget.available(), 3);
142 assert!(
143 time::timeout(
144 Duration::from_secs(1),
145 budget.hold(Bytes::from_static(b"0123"))
146 )
147 .await
148 .is_err()
149 );
150 assert_eq!(first.into_bytes(), Bytes::from_static(b"0123456"));
151 let second = budget.hold(Bytes::from_static(b"0123")).await;
152 assert_eq!(&second[..], b"0123");
153 assert_eq!(budget.available(), 6);
154 drop(second);
155 assert_eq!(budget.available(), 10);
156 }
157
158 async fn push(budget: &Budget, buf: &mut HeldBuf, data: &'static [u8]) {
159 let cost = buf.cost(data.len());
160 buf.push(budget.hold_as(Bytes::from_static(data), cost).await);
161 }
162
163 #[tokio::test]
164 async fn held_data_gets_collected() {
165 let budget = Budget::new(10);
166 let mut buf = HeldBuf::default();
167 assert!(buf.is_empty());
168 for data in [&b"012"[..], b"", b"3456"] {
169 push(&budget, &mut buf, data).await;
170 }
171 assert_eq!(&buf[..], b"0123456");
172 assert_eq!(budget.available(), 10 - buf.data.capacity());
173 drop(buf);
174 assert_eq!(budget.available(), 10);
175 }
176
177 #[tokio::test]
178 async fn held_buffers_count_their_capacity() {
179 let budget = Budget::new(1 << 20);
180 let mut buf = HeldBuf::default();
181 for _ in 0..1000 {
182 push(&budget, &mut buf, b"0123456789").await;
183 assert_eq!(budget.available(), (1 << 20) - buf.data.capacity());
184 }
185 assert_eq!(buf.len(), 10_000);
186 assert!(buf.data.capacity() < 20_000, "{}", buf.data.capacity());
188 drop(buf);
189 assert_eq!(budget.available(), 1 << 20);
190 }
191
192 #[tokio::test]
193 async fn large_data_takes_the_whole_budget() {
194 let budget = Budget::new(4);
195 let held = budget.hold(Bytes::from_static(b"0123456789")).await;
196 assert_eq!(budget.available(), 0);
197 drop(held);
198 assert_eq!(budget.available(), 4);
199 }
200}