将 `Vec` 进行分割

7

我试图写一个小型的缓冲区来进行解析,这样我就可以在解析时从前面获取记录,理想情况下不需要做任何副本,只需将缓冲区前端的块所有权转移出去即可。以下是我的实现:

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的错误。这段代码基本上与Nomicon中的例子相同,但我试图在Vec而不是对象本身上拆分字段。

是否可能在不复制其中一个Vec的情况下完成此操作?是否有Nomicon或其他文档的某个部分可以解释为什么我在unsafe块中炸毁了一切?


5
如果你无法解释为什么unsafe代码是安全的,那么使用它是一个非常糟糕的想法。 - Shepmaster
你可以尝试使用 splices 来实现你想要的功能。例如将向量分成两个切片,一个是元素 [0,1,2],另一个是 [3...],然后你可以处理 0、1、2。在下一次迭代中,从第二个切片中取出 [0.1.2]。此外,VecDeque 也是一个不错的选择。 - Aleksander Fular
你的解析器真的需要使用Vec吗?我想你的解析器可以使用slice。 - Josh Lee
缓冲区正在从一个非常大的文件中进行读取,所以我无法一次性将所有内容都放入内存(而且随着文件的读取,缓冲区也会发生变化,导致使用切片时存在生命周期问题)。复制是一个可以接受的解决方案,但我想看看是否有更巧妙的方法可以避免复制。 - Roderick
1个回答

5
很遗憾,这不是内存分配器的工作方式。在过去可能有可能实现,因为内存很珍贵,但现在的分配器是以速度为导向而不是内存保护。
内存分配器的常见实现方式是使用slabs。基本上,它是:
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用于拆分部分。

@Shepmaster:它会分配内存,我不喜欢分配内存 :) - Matthieu M.
@Shepmaster: 嗯,楼主要求不使用分配内存 ;) 我们可以编辑标题以反映这一点,或者我可以编辑我的答案以包括 Vec::split_off 作为分配内存的替代方案。我不确定哪个是最好的,你有什么想法? - Matthieu M.
@Shepmaster:我正在看“是否可以在不复制其中一个Vec的情况下完成这个操作?”;我现在可以修改我的答案,似乎更容易。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接