Skip to main content

coven_storage/
memory_reads.rs

1use super::*;
2
3pub(super) struct HeldRead {
4    path: ObjectPath,
5    observed: tokio::sync::oneshot::Sender<()>,
6    resume: tokio::sync::oneshot::Receiver<()>,
7}
8
9/// A response body that stops at an object offset until released.
10pub(super) struct HeldBody {
11    path: ObjectPath,
12    at: u64,
13    observed: tokio::sync::oneshot::Sender<()>,
14    resume: tokio::sync::oneshot::Receiver<()>,
15}
16
17/// Failures after response metadata has arrived.
18#[derive(Clone, Copy)]
19pub enum BodyFailure {
20    /// EOF before the declared response length.
21    Truncated(u64),
22    /// A transport failure after this many response bytes, including at EOF.
23    Interrupted(u64),
24    /// A byte beyond the declared response length.
25    ExtraByte,
26}
27
28impl MemoryStorage {
29    /// Hold an exact read after fixing its response, including a missing object.
30    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    /// Stall the next response body for `path` that reaches object offset `at`,
42    /// after delivering every byte before it, until `resume` is released.
43    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    /// Buffers delivered by response bodies, without counting metadata as bytes.
56    pub async fn received_buffers(&self) -> Vec<usize> {
57        self.provider.state.lock().await.received_buffers.clone()
58    }
59}
60
61/// What one response body has moved, counted as it delivers bytes: each
62/// byte, or on a provider whose reads move whole parts, each part whole as
63/// the body delivers its first byte, as CloudKit fetches a part only once
64/// its stream reaches it.
65struct BodyMoves {
66    path: ObjectPath,
67    part: Option<u64>,
68    size: u64,
69    /// The first part this body has not moved.
70    next_part: u64,
71}
72
73impl BodyMoves {
74    /// The bytes delivering `length` bytes from `position` moves.
75    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}