串联的 &[u8] 切片如何在不进行额外复制的情况下实现 Read 特性?
How can concatenated &[u8] slices implement the Read trait without additional copying?
Read
trait 是为 &[u8]
实施的。我怎样才能在几个串联的 u8
切片上获得 Read
特征而不首先实际进行任何串联?
如果我先连接,将有两个副本——将多个数组复制到一个数组中,然后通过 Read
特性从单个数组复制到目的地。我想避免第一次复制。
我想要 Read
优于 &[&[u8]]
的特性,将多个切片视为单个连续切片。
fn foo<R: std::io::Read + Send>(data: R) {
// ...
}
let a: &[u8] = &[1, 2, 3, 4, 5];
let b: &[u8] = &[1, 2];
let c: &[&[u8]] = &[a, b];
foo(c); // <- this won't compile because `c` is not a slice of bytes.
围绕切片创建包装器类型并为其实现 Read
。与 相比,我将切片委托给 Read
的实现:
use std::{io::Read, mem};
struct Wrapper<'a, 'b>(&'a mut [&'b [u8]]);
impl<'a, 'b> Read for Wrapper<'a, 'b> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let slices = mem::take(&mut self.0);
match slices {
[head, ..] => {
let n_bytes = head.read(buf)?;
if head.is_empty() {
// Advance the child slice
self.0 = &mut slices[1..];
} else {
// More to read, put back all the child slices
self.0 = slices;
}
Ok(n_bytes)
}
_ => Ok(0),
}
}
}
fn main() {
let parts: &mut [&[u8]] = &mut [b"hello ", b"world"];
let mut w = Wrapper(parts);
let mut buf = Vec::new();
w.read_to_end(&mut buf).unwrap();
assert_eq!(b"hello world", &*buf);
}
更有效的实现将实现 Read
中的更多方法,例如 read_to_end
or read_vectored
。
另请参阅:
- How do I implement a trait I don't own for a type I don't own?
您可以使用 multi_reader
crate,它可以连接任意数量的实现 Read
:
的值
let a: &[u8] = &[1, 2, 3, 4, 5];
let b: &[u8] = &[1, 2];
let c: &[&[u8]] = &[a, b];
foo(multi_reader::MultiReader::new(c.iter().copied()));
如果你不想依赖外部 crate,你可以将切片包装在你自己的结构中并为其实现 Read
:
struct MultiRead<'a> {
sources: &'a [&'a [u8]],
pos_in_current: usize,
}
impl<'a> MultiRead<'a> {
fn new(sources: &'a [&'a [u8]]) -> MultiRead<'a> {
MultiRead {
sources,
pos_in_current: 0,
}
}
}
impl Read for MultiRead<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let current = loop {
if self.sources.is_empty() {
return Ok(0); // EOF
}
let current = self.sources[0];
if self.pos_in_current < current.len() {
break current;
}
self.pos_in_current = 0;
self.sources = &self.sources[1..];
};
let read_size = buf.len().min(current.len() - self.pos_in_current);
buf[..read_size].copy_from_slice(¤t[self.pos_in_current..][..read_size]);
self.pos_in_current += read_size;
Ok(read_size)
}
}
Read
trait 是为 &[u8]
实施的。我怎样才能在几个串联的 u8
切片上获得 Read
特征而不首先实际进行任何串联?
如果我先连接,将有两个副本——将多个数组复制到一个数组中,然后通过 Read
特性从单个数组复制到目的地。我想避免第一次复制。
我想要 Read
优于 &[&[u8]]
的特性,将多个切片视为单个连续切片。
fn foo<R: std::io::Read + Send>(data: R) {
// ...
}
let a: &[u8] = &[1, 2, 3, 4, 5];
let b: &[u8] = &[1, 2];
let c: &[&[u8]] = &[a, b];
foo(c); // <- this won't compile because `c` is not a slice of bytes.
围绕切片创建包装器类型并为其实现 Read
。与 Read
的实现:
use std::{io::Read, mem};
struct Wrapper<'a, 'b>(&'a mut [&'b [u8]]);
impl<'a, 'b> Read for Wrapper<'a, 'b> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let slices = mem::take(&mut self.0);
match slices {
[head, ..] => {
let n_bytes = head.read(buf)?;
if head.is_empty() {
// Advance the child slice
self.0 = &mut slices[1..];
} else {
// More to read, put back all the child slices
self.0 = slices;
}
Ok(n_bytes)
}
_ => Ok(0),
}
}
}
fn main() {
let parts: &mut [&[u8]] = &mut [b"hello ", b"world"];
let mut w = Wrapper(parts);
let mut buf = Vec::new();
w.read_to_end(&mut buf).unwrap();
assert_eq!(b"hello world", &*buf);
}
更有效的实现将实现 Read
中的更多方法,例如 read_to_end
or read_vectored
。
另请参阅:
- How do I implement a trait I don't own for a type I don't own?
您可以使用 multi_reader
crate,它可以连接任意数量的实现 Read
:
let a: &[u8] = &[1, 2, 3, 4, 5];
let b: &[u8] = &[1, 2];
let c: &[&[u8]] = &[a, b];
foo(multi_reader::MultiReader::new(c.iter().copied()));
如果你不想依赖外部 crate,你可以将切片包装在你自己的结构中并为其实现 Read
:
struct MultiRead<'a> {
sources: &'a [&'a [u8]],
pos_in_current: usize,
}
impl<'a> MultiRead<'a> {
fn new(sources: &'a [&'a [u8]]) -> MultiRead<'a> {
MultiRead {
sources,
pos_in_current: 0,
}
}
}
impl Read for MultiRead<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let current = loop {
if self.sources.is_empty() {
return Ok(0); // EOF
}
let current = self.sources[0];
if self.pos_in_current < current.len() {
break current;
}
self.pos_in_current = 0;
self.sources = &self.sources[1..];
};
let read_size = buf.len().min(current.len() - self.pos_in_current);
buf[..read_size].copy_from_slice(¤t[self.pos_in_current..][..read_size]);
self.pos_in_current += read_size;
Ok(read_size)
}
}