适配器
主线位置:两条主线的外围。
Chain、Take、Reader、IntoIter包在Buf外面,读的通常是共享主线上的Bytes(hyper 写 body 时把用户的Bytes包成Take和Chain);Limit、Writer和Chain的写方向包在BufMut外面,写的是构建主线上的BytesMut或Vec<u8>。它们都不拥有缓冲,只改游标的算法。
flowchart TB buf["Buf"] --> chain["chain:Chain"] buf --> take["take:Take"] buf --> reader["reader:Reader"] buf --> iter["IntoIter"] bm["BufMut"] --> chainm["chain_mut:Chain"] bm --> limit["limit:Limit"] bm --> writer["writer:Writer"] reader --> ioread["std::io::Read 与 BufRead"] writer --> iowrite["std::io::Write"]
六个适配器都是「一个内层值加一两个字段」的小结构体,buf/ 下每个一个文件,合起来不到 800 行,大半是文档。它们的共同点:只改写自己语义要求改的那几个方法,其他方法(get_u16、put_u32 这些)全部继承 Buf / BufMut 的默认实现,而默认实现只调三个必需方法。所以读一个适配器,只要看它的三个必需方法加上它额外覆盖的一两个。
| 适配器 | 怎么得到 | 实现 | 改写了什么 |
|---|---|---|---|
Chain<T, U> | Buf::chain、BufMut::chain_mut | Buf、BufMut、IntoIterator | 三个必需方法;Buf 方向另有 chunks_vectored、copy_to_bytes |
Take<T> | Buf::take | Buf | 三个必需方法、copy_to_bytes、chunks_vectored |
Limit<T> | BufMut::limit | BufMut | 只有三个必需方法 |
Reader<B> | Buf::reader(std) | io::Read、io::BufRead | — |
Writer<B> | BufMut::writer(std) | io::Write | — |
IntoIter<T> | Bytes、BytesMut、Chain 的 into_iter(),或 IntoIter::new | Iterator、ExactSizeIterator | — |
Reader、Writer 两个文件在 buf/mod.rs 里用 #[cfg(feature = "std")] 挡着:它们实现的是 std::io 的 trait,no_std 下没有。
Chain:两个 Buf 首尾相接
Chain 就是两个字段 a、b,先读完 a 再读 b。Buf 实现的三个必需方法加 chunks_vectored:
fn remaining(&self) -> usize {
self.a.remaining().saturating_add(self.b.remaining())
}
fn chunk(&self) -> &[u8] {
if self.a.has_remaining() {
self.a.chunk()
} else {
self.b.chunk()
}
}
fn advance(&mut self, mut cnt: usize) {
let a_rem = self.a.remaining();
if a_rem != 0 {
if a_rem >= cnt {
self.a.advance(cnt);
return;
}
// Consume what is left of a
self.a.advance(a_rem);
cnt -= a_rem;
}
self.b.advance(cnt);
}
#[cfg(feature = "std")]
fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
let mut n = self.a.chunks_vectored(dst);
n += self.b.chunks_vectored(&mut dst[n..]);
n
}
Chain 不拷贝也不分配,两段各自保持原样。chunk 只能返回一个 &[u8],而两段在内存里不相邻,所以它只能给其中一段:a 还有数据就给 a.chunk(),否则给 b.chunk()。如果 a 本身也由几段组成(比如它又是一个 Chain),给的也只是 a 的第一段。advance 跨过边界时先把 a 读完,剩下的记到 b 上。chunks_vectored 是唯一能一次拿到两段的方法:先让 a 填,b 接着填剩下的位置。
hyper 写 chunked body 时,每个数据帧被包成 Chain<Chain<ChunkSize, B>, StaticBuf>(块头、数据、\r\n),再交给 writev,用户的 Bytes 不被拷贝,见 hyper 卷的 body 的解码与编码。
chunk 只给一段:一个跨段就坏的解析器
Buf::chunk 的约定(见 Buf)是「当前位置开始的一段」,可以比 remaining() 短。对 Bytes、&[u8] 这种连续的 Buf,chunk() 恰好就是全部剩余数据,写解析器时很容易把这当成规律。换成 Chain,数据被切在哪里,解析就在哪里出错。依赖:
bytes = "=1.12.1"
use std::io::IoSlice;
use std::panic;
use bytes::{Buf, Bytes};
// 错误写法:以为 chunk() 就是全部数据
fn find_crlf_buggy(buf: &impl Buf) -> Option<usize> {
buf.chunk().windows(2).position(|w| w == b"\r\n")
}
// 错误写法:直接从 chunk() 里切 4 个字节当长度前缀
fn frame_len_buggy(buf: &impl Buf) -> u32 {
u32::from_be_bytes(buf.chunk()[..4].try_into().unwrap())
}
fn main() {
panic::set_hook(Box::new(|info| println!("panicked: {}", info.payload_as_str().unwrap_or("?"))));
// 一行请求在两次 read 之间被切开
let buf = Bytes::from_static(b"GET / HT").chain(Bytes::from_static(b"TP/1.1\r\nHost: a\r\n"));
println!("remaining = {}, chunk = {:?}", buf.remaining(), Bytes::copy_from_slice(buf.chunk()));
println!("find_crlf_buggy = {:?}", find_crlf_buggy(&buf));
let mut slices = [IoSlice::new(&[]); 4];
let n = buf.chunks_vectored(&mut slices);
println!("chunks_vectored filled {n}: {:?}", slices[..n].iter().map(|s| s.len()).collect::<Vec<_>>());
// 长度前缀被切在 2 + 2
let mut frame = Bytes::from_static(&[0, 0]).chain(Bytes::from_static(&[0, 5, b'h', b'e', b'l', b'l', b'o']));
let r = panic::catch_unwind(|| frame_len_buggy(&frame));
println!("frame_len_buggy ok = {}", r.is_ok());
println!("get_u32 = {}", frame.get_u32());
println!("rest = {:?}", frame.copy_to_bytes(5));
}
M2 Max、rustc 1.99.0,--release 跑 3 次,输出相同:
remaining = 25, chunk = b"GET / HT"
find_crlf_buggy = None
chunks_vectored filled 2: [8, 17]
panicked: range end index 4 out of range for slice of length 2
frame_len_buggy ok = false
get_u32 = 5
rest = b"hello"
remaining()是 25,完整的一行GET / HTTP/1.1\r\n就在里面,但chunk()只有前 8 个字节GET / HT,find_crlf_buggy返回None。在一个「找不到行尾就等更多数据」的循环里,这意味着永远等下去:数据早就到了,只是不在第一段里。frame_len_buggy从 2 字节的第一段里切 4 个字节,标准库切片越界 panic:range end index 4 out of range for slice of length 2。- 同一个
frame用get_u32读就是对的:get_*先看当前chunk够不够,不够就退回逐段拷贝(快慢两条路径见 Buf)。 chunks_vectored一次填了 2 个IoSlice,长度 8 和 17,两段都在。
所以解析不定长的东西(找分隔符)时,要么像 hyper 那样先把数据读进同一块 BytesMut、保证连续,要么遍历 chunks_vectored 给出的每一段并处理跨段的匹配;读定长字段就用 get_* / try_get_* / copy_to_slice,它们本来就按段处理。VecDeque<u8> 的 Buf 实现也有同样的性质:环形缓冲绕回开头时 chunk 只给前一段。
copy_to_bytes:不跨边界时不拷贝
Chain::copy_to_bytes 分三种情况:
fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes {
let a_rem = self.a.remaining();
if a_rem >= len {
self.a.copy_to_bytes(len)
} else if a_rem == 0 {
self.b.copy_to_bytes(len)
} else {
assert!(
len - a_rem <= self.b.remaining(),
"`len` greater than remaining"
);
let mut ret = crate::BytesMut::with_capacity(len);
ret.put(&mut self.a);
ret.put((&mut self.b).take(len - a_rem));
ret.freeze()
}
}
要的字节全在一段里,就交给那一段自己的 copy_to_bytes;两段都是 Bytes 时这是 split_to,不拷贝(切片)。只有跨过边界时才分配一个 BytesMut,把两边各拷一部分进去,这时结果必须是连续的,没有别的办法。这个覆盖来自 PR #481(「Specialize copy_to_bytes for Chain and Take」,1.1.0),说明写得很直白:「Chain it works if the requested bytes does not cross boundary between Chain members.」用指针相等看一遍:
use bytes::{Buf, Bytes};
fn main() {
let a = Bytes::from(b"hello ".to_vec());
let b = Bytes::from(b"world".to_vec());
let (pa, pb) = (a.as_ptr(), b.as_ptr());
let mut c = a.chain(b);
let x = c.copy_to_bytes(3); // 全在 a 里
println!("within a: {:?} shares a: {}", x, x.as_ptr() == pa);
let y = c.copy_to_bytes(5); // 跨过 a、b 的边界
println!("across: {:?} shares a: {} shares b: {}", y, y.as_ptr() == pa.wrapping_add(3), y.as_ptr() == pb);
let z = c.copy_to_bytes(3); // a 已读完,全在 b 里
println!("within b: {:?} shares b: {}", z, z.as_ptr() == pb.wrapping_add(2));
}
跑 3 次,输出相同:
within a: b"hel" shares a: true
across: b"lo wo" shares a: false shares b: false
within b: b"rld" shares b: true
第一次和第三次的结果直接指在 a、b 原来的缓冲里(第三次是 b 的第 2 个字节,前两个被第二次读走了);第二次跨边界,是一块新分配。
BufMut 方向,以及两个 remaining 的加法
Chain 的 BufMut 实现和 Buf 方向逐行对称:chunk_mut 在 a.has_remaining_mut() 时给 a.chunk_mut(),否则给 b 的;advance_mut 先填满 a 再记到 b 上。典型用法是「先写进一块固定的数组,写满了溢出到一个 Vec」。两个方向的 remaining / remaining_mut 都用 saturating_add,而不是普通的加法。
Chain 的 remaining_mut 从「溢出就 panic」改成了饱和加法最早是 self.a.remaining_mut() + self.b.remaining_mut()。PR #482(「Panic on integer overflow in Chain::remaining」,1.1.0,说明只有一句「Make it safer.」)把两个方向都改成 checked_add(..).unwrap()。
问题是会增长的缓冲把 remaining_mut 报得很大:Vec<u8> 是 isize::MAX - len,当时的 BytesMut 是 usize::MAX - len(BufMut 与 UninitSlice)。把一个定长数组和一个 Vec 接起来、再接一个,加法就溢出,checked_add(..).unwrap() 直接 panic。PR #488(「Fix chain remaining_mut(), allowing to chain growing buffer」,1.2.0)的作者就是这样踩到的:「Stumbled upon panic when was trying to chain fixed buffer with growing buffer.」它把 remaining_mut 改成 saturating_add,并给 trait 文档加了一条注:「remaining_mut may return value smaller than actual available space.」作者也写明了代价:饱和之后,写入前后 remaining_mut 可能都是 usize::MAX,看不出变化。新加的测试 chain_growing_buffer 把一个 10 字节数组、一个 Vec 和一个空 Vec 接起来写 15 个字节,注释是「Required for potential overflow because remaining_mut for Vec is isize::MAX - vec.len(), but for chain_mut is usize::MAX」。Buf 方向的 remaining 后来在 1.6.0 的一次清理(#635)里也从 checked_add(..).unwrap() 换成了 saturating_add。
Take:最多读 limit 个字节
Take 是 inner 加一个 limit,Buf 实现把每个方法都截到 limit 以内:
fn remaining(&self) -> usize {
cmp::min(self.inner.remaining(), self.limit)
}
fn chunk(&self) -> &[u8] {
let bytes = self.inner.chunk();
&bytes[..cmp::min(bytes.len(), self.limit)]
}
fn advance(&mut self, cnt: usize) {
assert!(cnt <= self.limit);
self.inner.advance(cnt);
self.limit -= cnt;
}
fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes {
assert!(len <= self.remaining(), "`len` greater than remaining");
let r = self.inner.copy_to_bytes(len);
self.limit -= len;
r
}
advance 超过 limit 是一个不带信息的 assert!,panic 信息就是 assertion failed: cnt <= self.limit。copy_to_bytes 直接转给内层(同样来自 #481),所以 Bytes 外面套一层 Take 依然不拷贝。Take 读多少,内层就真的前进多少:into_inner 拿回内层时,被读掉的字节已经不在了,limit 之外的还在。hyper 用它表示「body 只写 Content-Length 那么多」(BufKind::Limited(Take<B>)),见 body 的解码与编码。
chunks_vectored 比较长:
#[cfg(feature = "std")]
fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
if self.limit == 0 {
return 0;
}
const LEN: usize = 16;
let mut slices: [IoSlice<'a>; LEN] = [IoSlice::new(&[]); LEN];
let cnt = self
.inner
.chunks_vectored(&mut slices[..dst.len().min(LEN)]);
let mut limit = self.limit;
for (i, (dst, slice)) in dst[..cnt].iter_mut().zip(slices.iter()).enumerate() {
if let Some(buf) = slice.get(..limit) {
// SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV.
let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(buf) };
*dst = IoSlice::new(buf);
return i + 1;
} else {
// SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV.
let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(slice) };
*dst = IoSlice::new(buf);
limit -= slice.len();
}
}
cnt
}
先让内层往一个栈上的临时数组里填(最多 16 个),再逐个抄到 dst,累计长度碰到 limit 的那一段截短后立即返回。IoSlice 本身不能截短,只能从 &[u8] 重新造一个;而 slice 借的是本函数里的临时数组,slice.get(..limit) 得到的引用生命周期被缩短到了这次借用,transmute 把它恢复成 'a。底层字节确实活 'a 那么久(它们属于 self),注释说明了只是 MSRV 不够、用不了 IoSlice::advance。
Take 曾经让 h2 的 vectored write 退化成一段一段写Take 原先没有覆盖 chunks_vectored,用的是 Buf 的默认实现:只填一个 IoSlice,内容是 self.chunk()。PR #617(「add specialized Buf::chunks_vectored for Take」,1.10.0)的动机是:「The h2 crate uses Take around the data frames to enforce a maximum frame size, which breaks vectored writes since Take doesn't provide a specialized chunks_vectored that calls the underlying chunks_vectored.」内层是一个 Chain 时,包上 Take 之后 writev 每次只能拿到第一段。
作者提到另一种不用 unsafe 的做法:遇到跨过 limit 的那一段直接丢掉而不是截短。但那样可能违反当时 chunks_vectored 文档里的一条保证:「If chunk_vectored does not fill every entry in dst, then dst is guaranteed to contain all remaining slices in self」。所以选择了截短,代价就是上面那两处 transmute。其实就在 #617 合并前三天,PR #754(「Remove incorrect guarantee for chunks_vectored」)已经把那条保证删掉了,两者都在 1.10.0 发布。现在的文档只保证「If Buf::remaining() is non-zero, then this writes at least one non-empty slice to dst」,上面最多只取 16 段的写法也满足这一条。
Limit:最多写 limit 个字节
Limit 是 Take 在写方向上的镜像,BufMut 实现只有三个必需方法:
unsafe impl<T: BufMut> BufMut for Limit<T> {
fn remaining_mut(&self) -> usize {
cmp::min(self.inner.remaining_mut(), self.limit)
}
fn chunk_mut(&mut self) -> &mut UninitSlice {
let bytes = self.inner.chunk_mut();
let end = cmp::min(bytes.len(), self.limit);
&mut bytes[..end]
}
unsafe fn advance_mut(&mut self, cnt: usize) {
assert!(cnt <= self.limit);
self.inner.advance_mut(cnt);
self.limit -= cnt;
}
}
chunk_mut 截短用的是 UninitSlice 的区间下标。它没有覆盖 put_slice,所以即使内层是 Vec<u8>,经过 Limit 写入也走默认的 chunk_mut / advance_mut 循环,而不是 extend_from_slice;默认 put_slice 开头那个 remaining_mut() < src.len() 的检查在这里第一次真正起作用。一个 Vec<u8> 被限制在 4 字节时,chunk_mut 仍会让内层 reserve(64):截短的只是交出去的那一段,不是内层的分配。这两点都在下面的动脑筋里。
Take 能交出几段,Limit 写超了是什么 panic?平台 M2 Max、rustc 1.99.0,--release:
use std::io::IoSlice;
use std::panic;
use bytes::{Buf, BufMut, Bytes};
fn main() {
panic::set_hook(Box::new(|info| println!(" panicked: {}", info.payload_as_str().unwrap_or("?"))));
println!("== Take");
let src = Bytes::from_static(b"abc").chain(Bytes::from_static(b"defgh"));
let mut t = src.take(5);
println!(" remaining {} chunk {:?}", t.remaining(), Bytes::copy_from_slice(t.chunk()));
let mut slices = [IoSlice::new(&[]); 4];
let n = t.chunks_vectored(&mut slices);
println!(" chunks_vectored {n}: {:?}", slices[..n].iter().map(|s| s.len()).collect::<Vec<_>>());
t.advance(4);
println!(" after advance(4): remaining {} limit {} inner remaining {}", t.remaining(), t.limit(), t.get_ref().remaining());
let r = panic::catch_unwind(panic::AssertUnwindSafe(|| t.advance(2)));
println!(" advance(2) ok = {}", r.is_ok());
println!("== Limit");
let mut l = Vec::<u8>::new().limit(4);
println!(" remaining_mut {}", l.remaining_mut());
println!(" chunk_mut len {} (inner cap now {})", l.chunk_mut().len(), l.get_ref().capacity());
let r = panic::catch_unwind(panic::AssertUnwindSafe(|| l.put_slice(b"12345")));
println!(" put_slice(5 bytes) ok = {}", r.is_ok());
l.put_slice(b"1234");
println!(" after put_slice(4): {:?} remaining_mut {}", l.get_ref(), l.remaining_mut());
}
问:chunks_vectored 那一行打印几段、各多长?put_slice(b"12345") 的 panic 信息是哪一句?
Reader 与 Writer:接到 std::io
这两个是给只认 std::io::Read / Write 的代码用的桥,要 std feature。Reader 的全部实现:
impl<B: Buf + Sized> io::Read for Reader<B> {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
let len = cmp::min(self.buf.remaining(), dst.len());
Buf::copy_to_slice(&mut self.buf, &mut dst[0..len]);
Ok(len)
}
}
impl<B: Buf + Sized> io::BufRead for Reader<B> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
Ok(self.buf.chunk())
}
fn consume(&mut self, amt: usize) {
self.buf.advance(amt)
}
}
BufRead 和 Buf 几乎是同一个接口:fill_buf 就是 chunk,consume 就是 advance。所以 fill_buf 也只给一段,但 read_line 这类标准库方法本来就会反复 fill_buf / consume,跨段没有问题。read 从不失败,读完了返回 Ok(0)。
impl<B: BufMut + Sized> io::Write for Writer<B> {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
let n = cmp::min(self.buf.remaining_mut(), src.len());
self.buf.put_slice(&src[..n]);
Ok(n)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
write 写不下就少写,返回实际写入的字节数,这是 io::Write 允许的「短写」;写满之后返回 Ok(0),write_all 会把它变成 WriteZero 错误。对 BytesMut 和 Vec<u8> 来说 remaining_mut 是 isize::MAX - len,实际上不会短写;套上 Limit 才会。1.9.0 之前这里调的是 self.buf.put(&src[0..n]),PR #745 改成 put_slice,理由是 put 「has to handle non-contiguous Bufs」,而 &[u8] 是连续的。
use std::io::{self, BufRead, Read, Write};
use bytes::{Buf, BufMut, Bytes, BytesMut};
fn main() {
println!("== Writer");
let mut w = BytesMut::new().limit(8).writer();
let n = w.write(b"0123456789").unwrap();
println!(" write returned {n}");
let e = w.write_all(b"xyz").unwrap_err();
println!(" write_all: {:?} {}", e.kind(), e);
println!(" inner = {:?}", w.get_ref().get_ref());
println!("== Reader");
let mut r = Bytes::from_static(b"line one\nli").chain(Bytes::from_static(b"ne two\n")).reader();
println!(" fill_buf = {:?}", Bytes::copy_from_slice(r.fill_buf().unwrap()));
let mut s = String::new();
r.read_line(&mut s).unwrap();
r.read_line(&mut s).unwrap();
println!(" two read_line = {s:?}");
let mut rest = Vec::new();
let n = r.read_to_end(&mut rest).unwrap();
println!(" read_to_end = {n}");
println!("== io::copy Reader -> Writer");
let mut src = Bytes::from_static(b"abc").chain(Bytes::from_static(b"def")).reader();
let mut dst = BytesMut::new().writer();
let n = io::copy(&mut src, &mut dst).unwrap();
println!(" copied {n}: {:?}", dst.into_inner());
}
跑 3 次,输出相同:
== Writer
write returned 8
write_all: WriteZero failed to write whole buffer
inner = b"01234567"
== Reader
fill_buf = b"line one\nli"
two read_line = "line one\nline two\n"
read_to_end = 0
== io::copy Reader -> Writer
copied 6: b"abcdef"
write 给了 10 个字节,Limit 只剩 8,返回 8;再 write_all 时 write 返回 0,得到 WriteZero。fill_buf 只给了第一段 line one\nli,第二个 read_line 照样读出了跨段的 line two\n。
IntoIter:逐字节迭代
IntoIter 把任何 Buf 变成 Iterator<Item = u8>:
impl<T: Buf> Iterator for IntoIter<T> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
if !self.inner.has_remaining() {
return None;
}
let b = self.inner.chunk()[0];
self.inner.advance(1);
Some(b)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self.inner.remaining();
(rem, Some(rem))
}
}
每次取当前 chunk 的第一个字节再前进 1。has_remaining 为真时 chunk 一定非空(Buf 的约定),所以 [0] 不会越界,跨段也不用特殊处理。size_hint 是精确的,于是它还实现了 ExactSizeIterator。
得到它的方式是 IntoIterator:Bytes、BytesMut 和 Chain 按值 into_iter() 得到 IntoIter<Self>,产出 u8。按引用就不一样了:&Bytes 和 &BytesMut 的 into_iter() 是切片的 core::slice::Iter,产出 &u8,不经过 Buf。别的 Buf 可以用 IntoIter::new 包(1.4.0 起是公开的,#581)。
use bytes::{Buf, Bytes};
fn main() {
let it = Bytes::from_static(b"ab").chain(Bytes::from_static(b"cd")).into_iter();
println!("size_hint {:?}", it.size_hint());
println!("collect {:?}", it.map(|b| b as char).collect::<String>());
let b = Bytes::from_static(b"xy");
let by_ref: Vec<&u8> = (&b).into_iter().collect();
println!("&Bytes -> {by_ref:?}");
let by_val: Vec<u8> = b.into_iter().collect();
println!("Bytes -> {by_val:?}");
}
跑 3 次,输出相同:
size_hint (4, Some(4))
collect "abcd"
&Bytes -> [120, 121]
Bytes -> [120, 121]
逐字节迭代每一步都是一次 chunk 加一次 advance,解析大块数据时比直接在 chunk() 上操作慢,适合小量的数据或需要 Iterator 接口的地方。
动脑筋答案
== Take
remaining 5 chunk b"abc"
chunks_vectored 2: [3, 2]
after advance(4): remaining 1 limit 1 inner remaining 4
panicked: assertion failed: cnt <= self.limit
advance(2) ok = false
== Limit
remaining_mut 4
chunk_mut len 4 (inner cap now 64)
panicked: advance out of bounds: the len is 4 but advancing by 5
put_slice(5 bytes) ok = false
after put_slice(4): [49, 50, 51, 52] remaining_mut 0
跑了 3 次,输出相同。chunks_vectored 打印 2 段,长度 3 和 2;put_slice(b"12345") 的 panic 是 advance out of bounds: the len is 4 but advancing by 5。
Take的chunk只有abc(内层Chain的第一段,没到limit);chunks_vectored让内层填出abc(3)和defgh(5),抄到dst时第二段碰到剩余的 limit 2,截成 2 并返回。advance(4)之后limit剩 1,内层Chain还剩 8 − 4 = 4;advance(2)超过limit,是那个不带信息的assert!。Limit的remaining_mut是min(isize::MAX, 4)= 4。chunk_mut让内层Vec先reserve(64),内层容量变成 64,交出来的只截到 4。put_slice没被Limit覆盖,走默认实现;开头的检查发现remaining_mut()是 4、要写 5,调用panic_advance,信息原文就是这句(用的是写方向,字面上仍是「advance out of bounds」)。panic 发生在写入之前,所以随后写 4 个字节正好写满,remaining_mut变 0。
小结
- 六个适配器都只改写语义要求改的方法,其余
get_*/put_*继承默认实现。Reader、Writer需要stdfeature。 Chain::chunk只返回第一段非空数据,chunks_vectored才能一次拿到两段。把chunk()当全部数据的解析器在跨段时会找不到分隔符或切片越界;定长字段用get_*,变长的先保证连续或逐段处理。Chain::copy_to_bytes不跨边界时转给那一段,Bytes不拷贝;跨边界时分配一块拷贝(#481,1.1.0)。remaining/remaining_mut用饱和加法,因为会增长的缓冲把remaining_mut报得接近isize::MAX(#488,1.2.0)。Take把remaining、chunk、advance、copy_to_bytes、chunks_vectored都截到limit;chunks_vectored的覆盖是为了 h2 的 vectored write(#617,1.10.0),最多取 16 段。Limit只覆盖三个必需方法,经过它的写入走默认put_slice循环,超限时 panic 在写入之前。Reader的fill_buf/consume就是chunk/advance;Writer::write会短写,写满后write_all得到WriteZero(1.9.0 起用put_slice)。IntoIter每步chunk()[0]加advance(1),长度精确;Bytes按值迭代产出u8,按引用是切片迭代器。