RocksDB Slice 类定义在 slice.h
头文件中。点此查看 slice.h
。
1. Slice 类的成员
Slice 是 RocksDB 中一个非常简单又很常用的类,其中只有两个成员:
1 | // private: make these public for rocksdbjni access |
data_
: 这是一个指向外部存储的指针;size_
: 记录data_
指向的存储空间大小。
在目前 RocksDB 的版本中,这两个成员默认都是 public
的,
可以注意到,源码中的 private
被注释掉了,原因也一并写在了注释中,当然我们暂且不关注这个。
2. Slice 类的方法
Slice 类中的大多方法都直接实现在 slice.h
头文件中,部分实现在 slice.cc
源文件中。点此查看 slice.cc
。
下面按照 slice.h
中的顺序,依次介绍其中方法。
2.1. 构造函数
构造函数有 6 个重载,下面按代码中的顺序依次说。
1. 创建一个空的 Slice
1 | Slice() : data_(""), size_(0) {} |
这个看起来很简单,就是把 data_
初始化为空,size_
初始化为 0。
2. 从 char 字符串创建 Slice
1 | Slice(const char* d, size_t n) : data_(d), size_(n) {} |
d
: char 字符串的起始地址;n
: char 字符串的长度。
即把 d[0,n-1]
的内容初始化到 Slice。
3. 从 std::string 字符串创建 Slice
1 | Slice(const std::string& s) : data_(s.data()), size_(s.size()) {} |
s
: std::string 字符串。
这个和上一个没啥区别。
4. 从 std::string_view 字符串创建 Slice
1 |
|
std::string_view 是 C++17 中的新特性,就是一个只读的 std::string。
和上一个没啥区别。
5. 从 char 字符串创建 Slice(自动计算长度)
1 | Slice(const char* s) : data_(s) { size_ = (s == nullptr) ? 0 : strlen(s); } |
这个和上面第 2 个构造函数的唯一区别就是,无需你指定 char 字符串的长度,使用 strlen()
函数来计算。
6. 从 SliceParts 创建一个 Slice
1 | Slice(const struct SliceParts& parts, std::string* buf); |
这个是唯一一个相对复杂的构造函数,其定义在 slice.cc
文件中,如下:
1 | Slice::Slice(const SliceParts& parts, std::string* buf) { |
-
其中 SliceParts 就是一个 Slice 数组,里面存了很多个 Slice,其定义也在 slice.h
中,如下:
1 | struct SliceParts { |
_part
: 指向 Slice 数组的指针;_num_parts
: Slice 数组的长度,即数组中 Slice 对象的个数。
-
现在再看上面的构造函数,就很明了了,这个构造函数,就是把 SliceParts 中的所有 Slice 拼接起来,搞了一个大的 Slice。
要注意,代码中的 data_ = buf->data();
执行的是浅拷贝!所以,一旦 buf
失效,这个 Slice 也会失效!
2.2. 其他方法
1. 获取数据指针
1 | const char* data() const { return data_; } |
2. 获取数据长度
1 | size_t size() const { return size_; } |
3. 判断 Slice 是否为空
1 | bool empty() const { return size_ == 0; } |
4. 重载 []
运算符
1 | char operator[](size_t n) const { |
使 Slice 可以像普通数组那样通过 []
运算符使用索引访问元素。
5. 清空 Slice
1 | void clear() { |
6. 移除 Slice 中数据的前 n 个字符
1 | void remove_prefix(size_t n) { |
7. 移除 Slice 中数据的后 n 个字符
1 | void remove_suffix(size_t n) { |
8. 获取 Slice 中数据的 std::string 类型的拷贝
1 | std::string ToString(bool hex = false) const; |
hex
: 当 hex 为 true 时,返回的 std::string 将进行 16 进制编码。
这里要注意的是,如果 hex 为 true,那么返回的 std::string 的长度将是 Slice 中数据长度的两倍。原因是原来是一个字符 1 个字节(8 位),用 16 进制编码,是将原来的数据每 4 位编成一个 16 进制值,所以返回的 std::string 长度将是原来数据长度的两倍。
这个函数的实现较为复杂,而且不是很重要,这里不列实现了,有兴趣可以自己去看源码。
9. 获取 Slice 中数据的 std::string_view 类型的拷贝
1 |
|
这个比较简单了,因为没有像上一个方法那样提供 hex 参数。
10. 判断一个 std::string 字符串是不是有效的 16 进制串
1 | bool DecodeHex(std::string* result) const; |
这个函数检查 result 是不是一个有效的 16 进制串,即其中只含有 0-9A-F 字符,大小写均可。
如果是一个有效的 16 进制串,返回 true,否则返回 false。
其实现在 slice.cc
中,和之前的 16 进制编码实现一样,这个不是很重要,这里就不说了,有兴趣的可以自己去看看源码。
11. 比较两个 Slice 中的数据
1 | int compare(const Slice& b) const; |
其实现和声明都在 slice.h
头文件中,但是没写在一起,其实现如下:
1 | inline int Slice::compare(const Slice& b) const { |
从代码可以看到,其实就是两个 Slice 中数据的字典序比较,即:
- 返回值小于 0 如果 “*this” < “b”;
- 返回值等于 0 如果 “*this” == “b”;
- 返回值大于 0 如果 “*this” > “b”;
- 如果一个 Slice a 中数据是另一个 Slice b 中数据的前缀,则 a < b,返回值小于 0。
12. 判断一个 Slice 中数据的开头是否和另一个 Slice 中的数据一样
1 | bool starts_with(const Slice& x) const { |
13. 判断一个 Slice 中数据的结尾是否和另一个 Slice 中的数据一样
1 | bool ends_with(const Slice& x) const { |
14. 比较两个 Slice 并返回第一个不同字符的位置
1 | size_t difference_offset(const Slice& b) const; |
其实现和声明都在 slice.h
头文件中,但是没写在一起,其实现如下:
1 | inline size_t Slice::difference_offset(const Slice& b) const { |
通过代码可以看到,difference_offset()
方法比较两个 Slice 中的数据。
如果两个 Slice 中的数据有不同,就返回不同位置的索引;如果两个 Slice 中的数据完全相同,则返回数据的长度。
3. 比较 Slice 的方法
slice.h
中还定义了两个运算符重载(在头文件最后的位置),用以比较两个 Slice。
1 | inline bool operator==(const Slice& x, const Slice& y) { |
1 | inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); } |
上面的代码写的还是很巧妙的,实际上主要重载的是 ==
运算符,然后 !=
的重载用了 ==
重载的结果。
这两个运算符重载后,两个 Slice 比较的是其中的具体数据。