coven_storage/
memory_reads.rs1use super::*;
2
3pub(super) struct HeldRead {
4 path: ObjectPath,
5 observed: tokio::sync::oneshot::Sender<()>,
6 resume: tokio::sync::oneshot::Receiver<()>,
7}
8
9pub(super) struct HeldBody {
11 path: ObjectPath,
12 at: u64,
13 observed: tokio::sync::oneshot::Sender<()>,
14 resume: tokio::sync::oneshot::Receiver<()>,
15}
16
17#[derive(Clone, Copy)]
19pub enum BodyFailure {
20 Truncated(u64),
22 Interrupted(u64),
24 ExtraByte,
26}
27
28impl MemoryStorage {
29 pub async fn hold_next_read(
31 &self,
32 path: ObjectPath,
33 observed: tokio::sync::oneshot::Sender<()>,
34 resume: tokio::sync::oneshot::Receiver<()>,
35 ) {
36 let mut state = self.provider.state.lock().await;
37 assert!(state.held_read.is_none(), "a read is already held");
38 state.held_read = Some(HeldRead { path, observed, resume });
39 }
40
41 pub async fn hold_body_at(
44 &self,
45 path: ObjectPath,
46 at: u64,
47 observed: tokio::sync::oneshot::Sender<()>,
48 resume: tokio::sync::oneshot::Receiver<()>,
49 ) {
50 let mut state = self.provider.state.lock().await;
51 assert!(state.held_body.is_none(), "a body is already held");
52 state.held_body = Some(HeldBody { path, at, observed, resume });
53 }
54
55 pub async fn received_buffers(&self) -> Vec<usize> {
57 self.provider.state.lock().await.received_buffers.clone()
58 }
59}
60
61struct BodyMoves {
66 path: ObjectPath,
67 part: Option<u64>,
68 size: u64,
69 next_part: u64,
71}
72
73impl BodyMoves {
74 fn delivered(&mut self, position: u64, length: u64) -> u64 {
76 let Some(part) = self.part else {
77 return length;
78 };
79 let last = (position + length - 1) / part;
80 let first = self.next_part.max(position / part);
81 self.next_part = self.next_part.max(last + 1);
82 (first..=last).map(|index| (self.size - index * part).min(part)).sum()
83 }
84}
85
86struct MemoryBody {
87 moves: BodyMoves,
88 bytes: Arc<[u8]>,
89 start: usize,
90 end: usize,
91 position: usize,
92 chunk_size: usize,
93 failure: Option<BodyFailure>,
94 hold: Option<HeldBody>,
95 state: Arc<Mutex<MemoryState>>,
96}
97
98#[async_trait]
99impl ObjectBody for MemoryBody {
100 #[expect(
101 clippy::map_err_ignore,
102 reason = "a send fails only when the receiver is gone, which this names"
103 )]
104 async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError> {
105 let received = self.position - self.start;
106 let end = match self.failure {
107 Some(BodyFailure::Truncated(at) | BodyFailure::Interrupted(at)) => {
108 if received as u64 >= at {
109 return match self.failure {
110 Some(BodyFailure::Truncated(_)) => Ok(0),
111 _ => Err(StorageFailure::Network.into()),
112 };
113 }
114 self.end.min(self.start.saturating_add(at as usize))
115 }
116 Some(BodyFailure::ExtraByte) if self.position == self.end => {
117 output[0] = 1;
118 self.failure = None;
119 self.state.lock().await.received_buffers.push(1);
120 return Ok(1);
121 }
122 _ => self.end,
123 };
124 let end = match &self.hold {
125 Some(hold) if (hold.at as usize) > self.position => end.min(hold.at as usize),
126 Some(_) => {
127 let hold = self.hold.take().expect("held body");
128 hold.observed
129 .send(())
130 .map_err(|_| StorageFailure::Protocol.with_source("body observer dropped"))?;
131 hold.resume
132 .await
133 .map_err(|_| StorageFailure::Protocol.with_source("body release dropped"))?;
134 end
135 }
136 None => end,
137 };
138 let length = (end - self.position).min(output.len()).min(self.chunk_size);
139 output[..length].copy_from_slice(&self.bytes[self.position..self.position + length]);
140 if length > 0 {
141 let moved = self.moves.delivered(self.position as u64, length as u64);
142 let mut state = self.state.lock().await;
143 state.received_buffers.push(length);
144 *state.moved.entry(self.moves.path.clone()).or_default() += moved;
145 }
146 self.position += length;
147 Ok(length)
148 }
149}
150
151impl MemoryProvider {
152 #[expect(
153 clippy::map_err_ignore,
154 reason = "a send fails only when the receiver is gone, which this names"
155 )]
156 pub(super) async fn stream_recorded(
157 &self,
158 path: &ObjectPath,
159 range: Option<ByteRange>,
160 ) -> Result<ObjectStream<'static>, StorageError> {
161 self.before(MemoryRequest::Read(path.clone(), range)).await?;
162 let mut state = self.state.lock().await;
163 assert!(!state.faults.read_panics.contains(path), "a read of {path:?} panicked");
164 let result = (|| {
165 if let Some(failure) = state.faults.read_failures.get(path) {
166 return Err((*failure).into());
167 }
168 let object = state.objects.get(path).ok_or(StorageFailure::NotFound)?;
169 let metadata = object.metadata(path, self.time_resolution);
170 metadata.read_length(path, range)?;
171 let start = range.map_or(0, ByteRange::start) as usize;
172 let end = range.map_or(metadata.size, ByteRange::end) as usize;
173 let mut bytes = object.bytes.clone();
174 if let Some(garble) = state.faults.garbled_reads.get_mut(path).filter(|garble| {
175 garble.reads > 0 && (start as u64..end as u64).contains(&garble.at)
176 }) {
177 let mut garbled = bytes.to_vec();
178 garbled[garble.at as usize] ^= garble.reads;
179 bytes = garbled.into();
180 garble.reads -= 1;
181 }
182 let body = MemoryBody {
183 moves: BodyMoves {
184 path: path.clone(),
185 part: self.whole_part_reads.then_some(self.part_size as u64),
186 size: metadata.size,
187 next_part: 0,
188 },
189 bytes,
190 start,
191 end,
192 position: start,
193 chunk_size: state.faults.read_chunk_size,
194 failure: state.faults.body_failures.get(path).copied(),
195 hold: None,
196 state: self.state.clone(),
197 };
198 if body.chunk_size == 0 {
199 return Err(StorageFailure::InvalidConfiguration.into());
200 }
201 let mut body = body;
202 if state.held_body.as_ref().is_some_and(|held| {
203 held.path == *path && (start as u64) < held.at && held.at < end as u64
204 }) {
205 body.hold = state.held_body.take();
206 }
207 if let Some(range) = range {
208 state.ranges.push(range);
209 }
210 state.reads.push((path.clone(), start as u64, (end - start) as u64));
211 ObjectStream::new(metadata, range, body)
212 })();
213 let held = if state.held_read.as_ref().is_some_and(|held| held.path == *path) {
214 state.held_read.take()
215 } else {
216 None
217 };
218 drop(state);
219 if let Some(held) = held {
220 held.observed
221 .send(())
222 .map_err(|_| StorageFailure::Protocol.with_source("read observer dropped"))?;
223 held.resume
224 .await
225 .map_err(|_| StorageFailure::Protocol.with_source("read release dropped"))?;
226 }
227 result
228 }
229}