1use crate::fallible::len_u64;
4use crate::{Error, Limits, Result};
5use std::io::Write;
6
7pub trait RangedSource {
16 fn size(&self) -> u64;
17 fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize>;
18}
19
20pub struct CountingSource<'a, S> {
31 source: &'a mut S,
32 bytes_read: &'a mut u64,
33 overread: Option<&'static str>,
34}
35
36impl<'a, S> CountingSource<'a, S> {
37 pub fn new(source: &'a mut S, bytes_read: &'a mut u64) -> Self {
39 Self {
40 source,
41 bytes_read,
42 overread: None,
43 }
44 }
45
46 pub fn rejecting_overread(self, reason: &'static str) -> Self {
49 Self {
50 overread: Some(reason),
51 ..self
52 }
53 }
54}
55
56impl<S: RangedSource> RangedSource for CountingSource<'_, S> {
57 fn size(&self) -> u64 {
58 self.source.size()
59 }
60
61 fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize> {
62 let read = self.source.read_at(offset, destination)?;
63 if read <= destination.len() {
64 *self.bytes_read = self.bytes_read.saturating_add(len_u64(read));
65 } else if let Some(reason) = self.overread {
66 return Err(Error::invalid(reason));
67 }
68 Ok(read)
69 }
70}
71
72impl<S: RangedSource + ?Sized> RangedSource for Box<S> {
74 fn size(&self) -> u64 {
75 (**self).size()
76 }
77
78 fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize> {
79 (**self).read_at(offset, destination)
80 }
81}
82
83impl RangedSource for &[u8] {
85 fn size(&self) -> u64 {
86 len_u64(self.len())
87 }
88
89 fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize> {
90 let start = usize::try_from(offset)
91 .ok()
92 .filter(|&start| start <= self.len())
93 .ok_or(Error::invalid("read starts beyond source size"))?;
94 let count = destination.len().min(self.len() - start);
95 destination[..count].copy_from_slice(&self[start..start + count]);
96 Ok(count)
97 }
98}
99
100#[derive(Clone, Copy, Debug, Default)]
107pub struct Payload<'a> {
108 offset: u64,
109 bytes: &'a [u8],
110}
111
112impl<'a> Payload<'a> {
113 pub fn new(offset: u64, bytes: &'a [u8]) -> Result<Self> {
115 offset
116 .checked_add(len_u64(bytes.len()))
117 .ok_or(Error::invalid("payload end overflows 64-bit offset"))?;
118 Ok(Self { offset, bytes })
119 }
120
121 pub fn offset(&self) -> u64 {
123 self.offset
124 }
125
126 pub fn bytes(&self) -> &'a [u8] {
127 self.bytes
128 }
129
130 pub fn end(&self) -> u64 {
132 self.offset + len_u64(self.bytes.len())
134 }
135
136 pub fn get(&self, start: u64, length: u64) -> Option<&'a [u8]> {
138 let first = usize::try_from(start.checked_sub(self.offset)?).ok()?;
139 let last = first.checked_add(usize::try_from(length).ok()?)?;
140 self.bytes.get(first..last)
141 }
142}
143
144impl<'a> From<&'a [u8]> for Payload<'a> {
146 fn from(bytes: &'a [u8]) -> Self {
147 Self { offset: 0, bytes }
148 }
149}
150
151impl RangedSource for Payload<'_> {
152 fn size(&self) -> u64 {
153 self.end()
154 }
155
156 fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize> {
157 let relative = offset
158 .checked_sub(self.offset)
159 .ok_or(Error::invalid("read starts before the payload"))?;
160 self.bytes.read_at(relative, destination)
161 }
162}
163
164pub fn read_payload<'a, S: RangedSource, C: Cancellation>(
168 source: &mut S,
169 offset: u64,
170 length: u64,
171 buffer: &'a mut Vec<u8>,
172 limits: &Limits,
173 cancellation: &C,
174) -> Result<Payload<'a>> {
175 limits.check_allocation(length)?;
176 let size =
177 usize::try_from(length).map_err(|_| limits.allocation_refused("payload bytes", length))?;
178 buffer.clear();
179 buffer
180 .try_reserve_exact(size)
181 .map_err(|_| limits.allocation_refused("payload bytes", length))?;
182 buffer.resize(size, 0);
183 let chunk_bytes = limits.io_chunk_bytes.max(1);
186 for (index, chunk) in buffer.chunks_mut(chunk_bytes).enumerate() {
187 let at = offset
189 .checked_add(len_u64(index * chunk_bytes))
190 .ok_or(Error::invalid("payload offset overflows 64-bit offset"))?;
191 read_exact_at(source, at, chunk, limits, cancellation)?;
192 }
193 Payload::new(offset, buffer)
194}
195
196pub trait Cancellation {
199 fn is_cancelled(&self) -> bool;
200}
201
202#[derive(Clone, Copy, Debug, Default)]
204pub struct NeverCancel;
205
206impl Cancellation for NeverCancel {
207 fn is_cancelled(&self) -> bool {
208 false
209 }
210}
211
212fn check_cancelled<C: Cancellation>(cancellation: &C) -> Result<()> {
213 if cancellation.is_cancelled() {
214 Err(crate::ErrorKind::Cancelled.into())
215 } else {
216 Ok(())
217 }
218}
219
220fn check_range(source_size: u64, offset: u64, length: u64) -> Result<()> {
221 let end = offset
222 .checked_add(length)
223 .ok_or(Error::invalid("range end overflows 64-bit offset"))?;
224 if offset > source_size {
225 return Err(Error::invalid("range starts beyond source size"));
226 }
227 if end > source_size {
228 return Err(Error::truncated(offset, length, source_size - offset));
229 }
230 Ok(())
231}
232
233pub fn read_exact_at<S: RangedSource, C: Cancellation>(
240 source: &mut S,
241 offset: u64,
242 destination: &mut [u8],
243 limits: &Limits,
244 cancellation: &C,
245) -> Result<()> {
246 let length = len_u64(destination.len());
247 limits.check_input_size(length)?;
248 if destination.len() > limits.io_chunk_bytes {
249 return Err(Error::limit(
250 "I/O request bytes",
251 len_u64(limits.io_chunk_bytes),
252 length,
253 ));
254 }
255 check_range(source.size(), offset, length)?;
256 check_cancelled(cancellation)?;
257
258 let mut done = 0;
259 while done < destination.len() {
260 let current = offset
261 .checked_add(len_u64(done))
262 .ok_or(Error::invalid("read offset overflows 64-bit offset"))?;
263 let remaining = &mut destination[done..];
264 let read = source.read_at(current, remaining)?;
265 if read > remaining.len() {
266 return Err(Error::invalid("source reported more bytes than requested"));
267 }
268 done += read;
269 check_cancelled(cancellation)?;
270 if read == 0 {
271 return Err(Error::truncated(offset, length, len_u64(done)));
272 }
273 }
274 Ok(())
275}
276
277pub(crate) fn write_counted<W: Write + ?Sized, C: Cancellation>(
283 sink: &mut W,
284 bytes: &[u8],
285 output_bytes_written: &mut u64,
286 limits: &Limits,
287 cancellation: &C,
288) -> Result<()> {
289 let attempted = output_bytes_written
290 .checked_add(len_u64(bytes.len()))
291 .ok_or(Error::invalid("output byte count overflows 64 bits"))?;
292 if attempted > limits.max_output_bytes {
293 return Err(Error::limit(
294 "output bytes",
295 limits.max_output_bytes,
296 attempted,
297 ));
298 }
299 check_cancelled(cancellation)?;
300 for chunk in bytes.chunks(limits.io_chunk_bytes.max(1)) {
302 sink.write_all(chunk)?;
303 *output_bytes_written += len_u64(chunk.len());
305 check_cancelled(cancellation)?;
306 }
307 Ok(())
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use crate::ErrorKind;
314 use crate::test_support::CancelAfter;
315 use std::io;
316
317 struct Bounded {
319 bytes: Vec<u8>,
320 limit: usize,
321 }
322
323 impl Write for Bounded {
324 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
325 let count = bytes.len().min(self.limit - self.bytes.len());
326 if count == 0 {
327 return Err(io::Error::other("sink full"));
328 }
329 self.bytes.extend_from_slice(&bytes[..count]);
330 Ok(count)
331 }
332
333 fn flush(&mut self) -> io::Result<()> {
334 Ok(())
335 }
336 }
337
338 fn chunked(chunk: usize) -> Limits {
339 Limits {
340 io_chunk_bytes: chunk,
341 ..Limits::default()
342 }
343 }
344
345 #[test]
346 fn counted_writes_check_the_output_limit_before_the_first_byte() {
347 let mut sink = Vec::new();
348 let limits = Limits {
349 max_output_bytes: 5,
350 ..Limits::default()
351 };
352 let mut count = 4;
353 assert!(matches!(
354 write_counted(&mut sink, b"xy", &mut count, &limits, &NeverCancel),
355 Err(Error {
356 kind: ErrorKind::LimitExceeded { attempted: 6, .. },
357 ..
358 })
359 ));
360 assert_eq!((count, sink.len()), (4, 0));
361 count = u64::MAX;
362 assert!(matches!(
363 write_counted(
364 &mut sink,
365 b"x",
366 &mut count,
367 &Limits::default(),
368 &NeverCancel
369 ),
370 Err(Error {
371 kind: ErrorKind::Malformed,
372 ..
373 })
374 ));
375 }
376
377 #[test]
378 fn counted_writes_count_completed_chunks_before_a_failure() {
379 let mut sink = Bounded {
380 bytes: Vec::new(),
381 limit: 3,
382 };
383 let mut count = 0;
384 let error =
385 write_counted(&mut sink, b"abcde", &mut count, &chunked(2), &NeverCancel).unwrap_err();
386 assert!(matches!(
387 error,
388 Error {
389 kind: ErrorKind::Io(_),
390 ..
391 }
392 ));
393 assert_eq!((count, sink.bytes.as_slice()), (2, &b"abc"[..]));
394 }
395
396 #[test]
397 fn counted_writes_observe_cancellation_between_chunks() {
398 let mut sink = Vec::new();
399 let mut count = 0;
400 let cancellation = CancelAfter::new(1);
402 assert!(matches!(
403 write_counted(&mut sink, b"abc", &mut count, &chunked(1), &cancellation),
404 Err(Error {
405 kind: ErrorKind::Cancelled,
406 ..
407 })
408 ));
409 assert_eq!((count, sink.as_slice()), (1, &b"a"[..]));
410 }
411
412 #[test]
413 fn core_errors_cross_an_io_write_adapter_unchanged() {
414 let carried = io::Error::from(Error::cancelled());
415 assert!(matches!(
416 Error::from(carried),
417 Error {
418 kind: ErrorKind::Cancelled,
419 ..
420 }
421 ));
422 let plain = io::Error::other("disk failed");
423 assert!(matches!(
424 Error::from(plain),
425 Error {
426 kind: ErrorKind::Io(_),
427 ..
428 }
429 ));
430 let io = io::Error::from(Error::from(ErrorKind::Io(io::Error::new(
431 io::ErrorKind::BrokenPipe,
432 "x",
433 ))));
434 assert_eq!(io.kind(), io::ErrorKind::BrokenPipe);
435 }
436}