1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt::Debug;
/// Like [`std::io::Read`], but only supporting [`read_exact`], and with an
/// arbitrary error type.
///
/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
/// [`read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact
pub trait Load {
type Error;
fn load(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>;
}
#[cfg(feature = "std")]
impl<T> Load for T
where
T: std::io::Read,
{
type Error = std::io::Error;
fn load(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
self.read_exact(buf)
}
}
/// A buffering wrapper around a [`Load`].
///
/// This fills some of the simpler niches of a [`std::io::BufReader`] for no_std
/// use-cases, and allowing a static buffer.
///
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`Load`]: trait.Load.html
#[derive(Clone, Debug)]
pub struct BufferedLoader<Buffer, Loader>
where
Buffer: AsRef<[u8]> + AsMut<[u8]> + Debug,
Loader: Load,
{
loader: Loader,
buffer: Buffer,
/// The amount of buffer that has been read already.
offset: usize,
}
impl<const N: usize, Loader> BufferedLoader<[u8; N], Loader>
where
Loader: Load,
{
pub fn new_static(loader: Loader) -> Self {
Self {
loader,
buffer: [0u8; N],
offset: N,
}
}
}
#[cfg(feature = "alloc")]
impl<Loader> BufferedLoader<Vec<u8>, Loader>
where
Loader: Load,
{
pub fn new_dynamic(loader: Loader, buffer_size: usize) -> Self {
let mut buffer = Vec::with_capacity(buffer_size);
buffer.resize(buffer_size, 0);
Self {
loader,
buffer,
offset: buffer_size,
}
}
pub fn resize(&mut self, new_size: usize) {
let invalid = new_size > self.buffer.len();
self.buffer.resize(new_size, 0);
if invalid {
self.offset = new_size;
}
}
}
impl<Buffer, Loader> BufferedLoader<Buffer, Loader>
where
Buffer: AsRef<[u8]> + AsMut<[u8]> + Debug,
Loader: Load,
{
/// Drop what's remaining in the buffer and give a mutable reference to the
/// inner loader, so it can be seeked or otherwise manipulated.
pub fn loader(&mut self) -> &mut Loader {
self.offset = self.buffer.as_ref().len();
&mut self.loader
}
}
impl<Buffer, Loader> Load for BufferedLoader<Buffer, Loader>
where
Buffer: AsRef<[u8]> + AsMut<[u8]> + Debug,
Loader: Load,
{
type Error = Loader::Error;
/// Efficiently fill the destination buffer, calling the underlying
/// [`Load::load`] as few times as possible.
///
/// [`Load::Load`]: trait.Load.html#method.load
fn load(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
if buf.is_empty() {
return Ok(());
}
let buffer = self.buffer.as_mut();
let buffer_size = buffer.len();
let buffer_remaining = buffer_size - self.offset;
if buf.len() <= buffer_remaining {
// There are enough bytes left in the buffer to consume without
// reading.
let end = self.offset + buf.len();
buf.copy_from_slice(&buffer[self.offset..end]);
self.offset = end;
} else {
// First empty the buffer.
if buffer_remaining > 0 {
buf[..buffer_remaining].copy_from_slice(&buffer[self.offset..buffer_size]);
let copied = buffer_size - self.offset;
buf = &mut buf[copied..];
}
let buffers = buf.len() / buffer_size;
let remainder = buf.len() % buffer_size;
// Load full-sized chunks directly to the destination, bypassing
// the buffer.
if buffers > 0 {
let buffers_bytes = buffers * buffer_size;
self.loader.load(&mut buf[..buffers_bytes])?;
buf = &mut buf[buffers_bytes..];
}
// Fill the buffer for the remainder, if there is any.
if remainder > 0 {
self.loader.load(buffer)?;
buf.copy_from_slice(&buffer[..remainder]);
self.offset = remainder;
} else {
// We have emptied the remaining buffer, so mark this empty.
self.offset = buffer_size;
}
}
Ok(())
}
}