1use alloc::vec::Vec;
4
5use crate::StdSourceError;
6use zencodec::source::{AccessPattern, ReadAt, SourceError};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum BufferPolicy {
12 Window {
17 window_bytes: usize,
19 },
20 Spool {
25 max_bytes: usize,
27 },
28}
29
30impl BufferPolicy {
31 pub const fn for_pattern(pattern: AccessPattern, max_bytes: usize) -> Self {
34 match pattern {
35 AccessPattern::Sequential => Self::Window {
36 window_bytes: 64 * 1024,
37 },
38 AccessPattern::BoundedBacktrack { window_bytes } => Self::Window {
39 window_bytes: if window_bytes > max_bytes as u64 {
40 max_bytes
41 } else {
42 window_bytes as usize
43 },
44 },
45 _ => Self::Spool { max_bytes },
46 }
47 }
48
49 pub const fn try_for_pattern(
55 pattern: AccessPattern,
56 max_bytes: usize,
57 ) -> Result<Self, SourceError> {
58 match pattern {
59 AccessPattern::Sequential => Ok(Self::Window {
60 window_bytes: if max_bytes < 64 * 1024 {
61 max_bytes
62 } else {
63 64 * 1024
64 },
65 }),
66 AccessPattern::BoundedBacktrack { window_bytes } => {
67 if window_bytes > max_bytes as u64 {
68 Err(SourceError::BeyondLimit {
69 needed: window_bytes,
70 limit: max_bytes as u64,
71 })
72 } else {
73 Ok(Self::Window {
74 window_bytes: window_bytes as usize,
75 })
76 }
77 }
78 _ => Ok(Self::Spool { max_bytes }),
79 }
80 }
81
82 const fn cap(&self) -> usize {
83 match self {
84 Self::Window { window_bytes } => *window_bytes,
85 Self::Spool { max_bytes } => *max_bytes,
86 }
87 }
88}
89
90pub struct BufferedReadAt<R: std::io::Read> {
97 reader: R,
98 policy: BufferPolicy,
99 buf: Vec<u8>,
101 buf_start: u64,
102 watermark: u64,
104 eof: bool,
105 exceeded: Option<(u64, u64)>,
107}
108
109impl<R: std::io::Read> BufferedReadAt<R> {
110 pub fn new(reader: R, policy: BufferPolicy) -> Self {
112 Self {
113 reader,
114 policy,
115 buf: Vec::new(),
116 buf_start: 0,
117 watermark: 0,
118 eof: false,
119 exceeded: None,
120 }
121 }
122
123 pub fn watermark(&self) -> u64 {
125 self.watermark
126 }
127
128 pub fn retained_bytes(&self) -> usize {
131 self.buf.len()
132 }
133
134 fn check_live(&self) -> Result<(), StdSourceError> {
135 if let Some((needed, limit)) = self.exceeded {
136 return Err(SourceError::BeyondLimit { needed, limit }.into());
137 }
138 Ok(())
139 }
140
141 fn reserve_retained(&mut self, incoming: usize) -> Result<(), StdSourceError> {
142 let needed = self
143 .buf
144 .len()
145 .saturating_add(incoming)
146 .min(self.policy.cap());
147 if needed > self.buf.capacity() {
148 self.buf
149 .try_reserve_exact(needed - self.buf.len())
150 .map_err(|source| StdSourceError::Allocation {
151 requested: needed,
152 source,
153 })?;
154 }
155 Ok(())
156 }
157
158 fn remember(&mut self, bytes: &[u8]) {
159 match self.policy {
160 BufferPolicy::Window { window_bytes } => {
161 if bytes.len() >= window_bytes {
162 self.buf.clear();
163 self.buf
164 .extend_from_slice(&bytes[bytes.len() - window_bytes..]);
165 } else {
166 let drop = self
167 .buf
168 .len()
169 .saturating_add(bytes.len())
170 .saturating_sub(window_bytes);
171 self.buf.drain(..drop);
172 self.buf.extend_from_slice(bytes);
173 }
174 self.buf_start = self.watermark - self.buf.len() as u64;
175 }
176 BufferPolicy::Spool { .. } => self.buf.extend_from_slice(bytes),
177 }
178 }
179
180 fn read_more(&mut self, out: &mut [u8]) -> Result<usize, StdSourceError> {
183 self.check_live()?;
184 if self.eof || out.is_empty() {
185 return Ok(0);
186 }
187 let mut want = out.len();
188 if let BufferPolicy::Spool { max_bytes } = self.policy {
189 want = want.min(max_bytes - self.buf.len());
190 if want == 0 {
191 let mut probe = [0];
194 let n = loop {
195 match self.reader.read(&mut probe) {
196 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
197 result => break result?,
198 }
199 };
200 if n == 0 {
201 self.eof = true;
202 return Ok(0);
203 }
204 self.watermark += 1;
205 let limit = max_bytes as u64;
206 self.exceeded = Some((self.watermark, limit));
207 return Err(SourceError::BeyondLimit {
208 needed: self.watermark,
209 limit,
210 }
211 .into());
212 }
213 }
214 self.reserve_retained(want)?;
215 let n = loop {
216 match self.reader.read(&mut out[..want]) {
217 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
218 result => break result?,
219 }
220 };
221 if n == 0 {
222 self.eof = true;
223 } else {
224 self.watermark += n as u64;
225 self.remember(&out[..n]);
226 }
227 Ok(n)
228 }
229
230 fn advance_to(&mut self, offset: u64) -> Result<(), StdSourceError> {
231 let mut scratch = [0; 16 * 1024];
232 while self.watermark < offset && !self.eof {
233 let want = (offset - self.watermark).min(scratch.len() as u64) as usize;
234 self.read_more(&mut scratch[..want])?;
235 }
236 Ok(())
237 }
238}
239
240impl<R: std::io::Read> ReadAt for BufferedReadAt<R> {
241 type Error = StdSourceError;
242
243 fn len(&mut self) -> Result<u64, Self::Error> {
250 self.check_live()?;
251 let mut scratch = [0; 16 * 1024];
252 while self.read_more(&mut scratch)? != 0 {}
253 Ok(self.watermark)
254 }
255
256 fn read_exact_at(&mut self, offset: u64, out: &mut [u8]) -> Result<(), Self::Error> {
257 if out.is_empty() {
258 return Ok(());
259 }
260 self.check_live()?;
261 let end = offset
262 .checked_add(out.len() as u64)
263 .ok_or(SourceError::UnexpectedEof {
264 offset,
265 len: out.len() as u64,
266 total: None,
267 })?;
268 if let BufferPolicy::Spool { max_bytes } = self.policy
269 && end > max_bytes as u64
270 {
271 return Err(SourceError::BeyondLimit {
272 needed: end,
273 limit: max_bytes as u64,
274 }
275 .into());
276 }
277 if self.read_at(offset, out)? != out.len() {
278 return Err(SourceError::UnexpectedEof {
279 offset,
280 len: out.len() as u64,
281 total: self.eof.then_some(self.watermark),
282 }
283 .into());
284 }
285 Ok(())
286 }
287
288 fn read_at(&mut self, offset: u64, out: &mut [u8]) -> Result<usize, Self::Error> {
292 if out.is_empty() {
293 return Ok(0);
294 }
295 self.check_live()?;
296 offset
297 .checked_add(out.len() as u64)
298 .ok_or(SourceError::UnexpectedEof {
299 offset,
300 len: out.len() as u64,
301 total: self.len_hint(),
302 })?;
303 if offset < self.buf_start {
304 return Err(SourceError::BeyondWindow {
305 offset,
306 oldest_retained: self.buf_start,
307 }
308 .into());
309 }
310 self.advance_to(offset)?;
311 if offset > self.watermark {
312 return Ok(0);
313 }
314 let mut filled = ((self.watermark - offset) as usize).min(out.len());
315 if filled != 0 {
316 let start = (offset - self.buf_start) as usize;
317 out[..filled].copy_from_slice(&self.buf[start..start + filled]);
318 }
319 while filled < out.len() {
320 let n = self.read_more(&mut out[filled..])?;
321 if n == 0 {
322 break;
323 }
324 filled += n;
325 }
326 Ok(filled)
327 }
328
329 fn len_hint(&self) -> Option<u64> {
330 self.eof.then_some(self.watermark)
331 }
332
333 fn read_many_at(&mut self, reqs: &mut [(u64, &mut [u8])]) -> Result<(), Self::Error> {
334 reqs.sort_unstable_by_key(|(offset, _)| *offset);
336 for (offset, out) in reqs.iter_mut() {
337 self.read_exact_at(*offset, out)?;
338 }
339 Ok(())
340 }
341}
342
343impl BufferPolicy {
344 pub const fn max_bytes(&self) -> usize {
346 self.cap()
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use std::io::{self, Read};
354
355 #[test]
356 fn checked_policy_never_clamps_required_history() {
357 for cap in [0, 1, 16, 65_536] {
358 assert!(
359 BufferPolicy::try_for_pattern(AccessPattern::Sequential, cap)
360 .unwrap()
361 .cap()
362 <= cap
363 );
364 for required in [0, 1, 16, 65_536, u64::MAX] {
365 let result = BufferPolicy::try_for_pattern(
366 AccessPattern::BoundedBacktrack {
367 window_bytes: required,
368 },
369 cap,
370 );
371 if required > cap as u64 {
372 assert!(
373 matches!(result, Err(SourceError::BeyondLimit { needed, limit })
374 if needed == required && limit == cap as u64)
375 );
376 } else {
377 assert_eq!(
378 result.unwrap(),
379 BufferPolicy::Window {
380 window_bytes: required as usize
381 }
382 );
383 }
384 }
385 }
386 }
387
388 #[test]
389 fn forward_reads_retain_only_the_window_even_when_output_is_larger() {
390 let input: Vec<u8> = (0..101).collect();
391 for window in [0, 1, 7, 16, 128] {
392 for chunk in [1, 7, 17, 64, 128] {
393 let mut source = BufferedReadAt::new(
394 io::Cursor::new(&input),
395 BufferPolicy::Window {
396 window_bytes: window,
397 },
398 );
399 let mut output = Vec::new();
400 let mut buf = alloc::vec![0; chunk];
401 loop {
402 let n = source.read_at(output.len() as u64, &mut buf).unwrap();
403 output.extend_from_slice(&buf[..n]);
404 assert!(source.buf.len() <= window);
405 if n == 0 {
406 break;
407 }
408 }
409 assert_eq!(output, input);
410 let retained = window.min(input.len());
411 let offset = input.len() - retained;
412 let mut back = alloc::vec![0; retained];
413 source.read_exact_at(offset as u64, &mut back).unwrap();
414 assert_eq!(back, input[offset..]);
415 if offset != 0 {
416 assert!(matches!(
417 source.read_at(offset as u64 - 1, &mut [0; 1]),
418 Err(StdSourceError::Source(SourceError::BeyondWindow { .. }))
419 ));
420 }
421 }
422 }
423 }
424
425 #[test]
426 fn allocation_refusal_precedes_input_consumption() {
427 let mut source = BufferedReadAt::new(
428 io::repeat(0),
429 BufferPolicy::Spool {
430 max_bytes: usize::MAX,
431 },
432 );
433 assert!(matches!(
434 source.reserve_retained(usize::MAX),
435 Err(StdSourceError::Allocation { .. })
436 ));
437 assert_eq!(source.watermark(), 0);
438 assert!(source.buf.is_empty());
439 }
440
441 struct InterruptedOnce(bool);
442 impl Read for InterruptedOnce {
443 fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
444 if !self.0 {
445 self.0 = true;
446 return Err(io::ErrorKind::Interrupted.into());
447 }
448 out.fill(7);
449 Ok(out.len())
450 }
451 }
452
453 #[test]
454 fn interrupted_reads_do_not_lose_data_or_progress() {
455 let mut source = BufferedReadAt::new(
456 InterruptedOnce(false),
457 BufferPolicy::Window { window_bytes: 2 },
458 );
459 let mut out = [0; 8];
460 source.read_exact_at(0, &mut out).unwrap();
461 assert_eq!(out, [7; 8]);
462 assert_eq!(source.watermark(), 8);
463 assert_eq!(source.retained_bytes(), 2);
464 }
465}