本文捋一下mini-lsm
中的字符相关操作
[u8]
Vec<u8>
Bytes
Buf
KeySlice
KeyBytes
[u8]
和Vec<u8>
这两个是rust内置的数据类型。
[u8]
: 切片本身并不拥有数据,而是引用了数据。它由一个指向数组开始处的指针和一个表示数组长度的计数器组成。[u8]类型通常写作&[u8],这是因为切片通常是以引用的形式出现的。
Vec<u8>
: Vec<u8>
是一个可变的、动态大小的向量,它可以存储任意数量的u8字节。向量拥有它所存储的数据,并且可以动态地在其生命周期内增长或缩小。
所以在
mini-lsm
项目中,解码的入参一般是&[u8]
:
因为用于解码的数据一般是从文件中读出来的不可变的持久化的数据
编码的入参一般是
&mut Vec<u8>
或者返回Bytes
:因为在编码过程中,需要动态的将数据、变量编码成
u8
类型写入Vec
中
相互转换:
尽管[u8]
和Vec<u8>
在概念上紧密相关,但它们之间需要进行适当的转换才能互换使用:
从Vec<u8>
到[u8]
:可以通过简单的引用转换得到切片,例如:let slice: &[u8] = &vec;
从[u8]
到Vec<u8>
:可以通过to_vec()
方法将切片转换为向量,例如:let vector: Vec<u8> = slice.to_vec();
总结来说,Vec<u8>
是用于动态操作字节序列的更通用和灵活的选择,而[u8]
则用于访问现有字节序列的非所有者视图,通常在需要快速访问而不涉及所有权移动的场景下使用。
Bytes
在Rust
中,bytes
是一个非常流行的第三方库,用于高效处理字节序列。这个库提供了Bytes
和BytesMut
类型,它们分别类似于Vec<u8>
和Vec<u8>
的不可变和可变版本,但它们针对字节序列的常见操作进行了优化。
所以在
mini-lsm
项目中Bytes
,常用在可能出现数据共享的情况下。比如MemTable
中的map
他的key
和value
可能被多个地方获取。
相互转换:
Bytes
到Vec<u8>
:
你可以使用to_vec
方法将Bytes
转换为Vec<u8>
use bytes::Bytes;
let b = Bytes::from(vec![1, 2, 3]);
let vec: Vec<u8> = b.to_vec();
当你从Bytes
转换到Vec<u8>
时,这通常会涉及到数据的拷贝,除非你使用一些特殊的技巧或API来避免它。当你调用to_vec
方法时,Bytes
会返回一个包含相同字节的新Vec<u8>
实例。这个操作涉及到数据的拷贝,因为Vec<u8>
需要拥有数据的所有权,而Bytes
不能放弃它的所有权,因为它可能正在被其他引用共享。
Vec<u8>
到Bytes
:
你可以使用Bytes::from
构造函数或者Into<Bytes>
转换将Vec<u8>
转换为Bytes
:
use bytes::Bytes;
let vec = vec![1, 2, 3];
let b: Bytes = Bytes::from(vec);
// 或者
let b: Bytes = vec.into();
当你从Vec<u8>
转换到Bytes
时,Bytes
会采取数据的所有权,这意味着它会拷贝数据或共享数据的引用,具体取决于实现细节。在bytes
库的内部,Bytes
使用Arc<[u8]>
来持有数据,这样多个Bytes
实例可以共享同一个数据缓冲区,从而避免不必要的拷贝。
当你使用Bytes::from
或者Into<Bytes>
转换时,如果底层数据没有被其他任何引用持有,那么Bytes
可能会简单地接管Vec<u8>
的数据,否则它会创建一个新的Arc
来持有数据副本。这是为了保证Bytes
实例的不变性,即它是不可变的,并且在多个线程间安全共享。
避免拷贝的情况
在某些情况下,你可以避免显式的数据拷贝。例如,如果你有一个Vec<u8>
并且想要创建一个Bytes
实例,但是你不再需要原来的Vec<u8>
,你可以使用Bytes::copy_from_slice
方法来避免拷贝,前提是你能够保证Vec<u8>
的数据不再被引用:
use bytes::Bytes;
let vec = vec![1, 2, 3];
let b = Bytes::copy_from_slice(&vec); // 没有拷贝,因为我们只是借用&vec
// 此时,你不能再安全地使用vec,除非你知道它不会被访问直到b超出作用域
Buf
Buf
trait提供了读取字节缓冲区的方法。它适用于只读数据,或者在数据被读取后不需要保留原始状态的场景。Buf
的主要方法包括:
remaining(&self)
:返回剩余未读取的字节数。chunk(&self)
:返回一个可读取的字节切片。advance(&mut self, cnt: usize)
:标记已经读取了cnt个字节,通常在读取数据后调用,以便更新内部状态。split(&mut self)
:从缓冲区中移除已读取的数据,并返回一个包含这些数据的新Bytes对象。get_u32
等
同时bytes
库为&[u8]
实现了Buf
,为&mut [u8]
、Vec<u8>
等实现了BufMut
:
所以我们对Vec<u8>
、[u8]
类型的数据可以直接使用其定义的函数。
KeySlice
、KeyBytes
项目的key.rs
中的实现,定义了一系列结构体和相关的实现,主要围绕着处理字节序列([u8]
、Vec<u8>
和 ·)的封装和操作。Key
结构体被设计成一个通用容器,它可以包含不同类型的字节序列,并提供一系列方法来操作这些数据。