我正在尝试编写一个用于解析的小缓冲区,因此我可以在解析它们时从前面拉出记录,理想情况下不需要复制任何副本,只需将缓冲区前面的块的所有权转移为我跑这是我的实施:
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)
}
}
}
这会编译,但在开始运行时会出现signal: 11, SIGSEGV: invalid memory reference
的恐慌。这与the Nomicon中的示例大致相同,但我试图在Vec
上进行,我正在尝试拆分字段而不是对象本身。
是否可以在不复制Vec
之一的情况下执行此操作? Nomicon或其他文档中是否有一些部分解释了为什么我在unsafe
块中炸毁了所有内容?
答案 0 :(得分:3)
不幸的是,这不是内存分配器的工作方式。在过去,当内存非常宝贵时,它可能是可能的,但今天的分配器是为了速度而不是内存保存。
内存分配器的常见实现是使用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个字节,整个块现在可以重复使用。块内的任何指针现在悬空,不应该被解除引用。
此外,尝试释放块中间的指针只会混淆分配器:它不会找到它,因为合同是你通过它们的第一个字节来寻址块。
您使用unsafe
代码 BOOM 违反了合同。
我提出的解决方案很简单:
Vec<u8>
来解析Vec
中使用切片进行解析 Rust将检查生命周期,因此切片不能超过缓冲区,并且进一步切片(s[..offset]
,s[offset..]
)不会分配。
如果您不介意一个分配,那么Vec::split_off
会为分割部分分配一个足够大的新Vec
。