我正在尝试编写一个用于解析的小缓冲区,这样我就可以在解析它们时从前端提取记录,理想情况下不制作任何副本,而只是将缓冲区前端 block 的所有权转移为我跑。这是我的实现:
struct BufferThing {
buf: Vec<u8>,
}
impl BufferThing {
fn extract(&mut self, size: usize) -> Vec<u8> {
assert!(size <= self.buf.len());
let remaining: usize = self.buf.len() - size;
let ptr: *mut u8 = self.buf.as_mut_ptr();
unsafe {
self.buf = Vec::from_raw_parts(ptr.offset(size as isize), remaining, remaining);
Vec::from_raw_parts(ptr, size, size)
}
}
}
这会编译,但会在开始运行时出现 信号:11,SIGSEGV:无效内存引用
的 panic 。这与 the Nomicon 中的示例代码基本相同。 ,但我正在尝试在 Vec
上执行此操作,并且我正在尝试拆分一个字段而不是对象本身。
是否可以在不复制其中一个 Vec
的情况下执行此操作?是否有 Nomicon 或其他文档的某些部分解释了为什么我要炸掉 unsafe
block 中的所有内容?
最佳答案
不幸的是,这不是内存分配器的工作方式。这在过去可能是可行的,当时内存非常宝贵,但今天的分配器更注重速度而不是内存保存。
内存分配器的一个常见实现是使用 slab。基本上,它是:
struct Allocator {
less_than_32_bytes: List<[u8; 32]>,
less_than_64_bytes: List<[u8; 64]>,
less_than_128_bytes: List<[u8; 128]>,
less_than_256_bytes: List<[u8; 256]>,
less_than_512_bytes: List<[u8; 512]>,
...
}
当你请求96字节时,它从less_than_128_bytes
中取一个元素.
当您释放该元素时,它会释放所有 元素,而不仅仅是前 N 个字节,并且整个 block 现在都可以重复使用。 block 内的任何指针现在都是悬空的,不应取消引用。
此外,尝试释放 block 中间的指针只会混淆分配器:它不会找到它,因为约定是您按 block 的第一个字节寻址。
您使用 unsafe
违反了契约(Contract)代码,BOOM。
我提出的解决方案很简单:
- 使用单个
Vec<u8>
包含要解析的整个缓冲区 - 在这个
Vec
中使用切片用于解析
Rust 将检查生命周期,因此您的切片不能超过缓冲区,并且进一步切片(s[..offset]
,s[offset..]
)不会分配。
如果你不介意一个分配,那就是 Vec::split_off
它分配一个新的 Vec
足够大的 split 部分。
关于rust - 拆分 `Vec`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42163975/