levelDB 代码阅读笔记 01 db.h
Overview
背景
最近在做一个智能算力相关的项目,类似美团外卖广告智能算力的探索与实践 其中实现控制系统需要与数据库交互。 虽然最后技术选型并没有使用到levelDB,但是想趁机把代码读了吧。
很惊讶的发现我大三的时候声称自己读过部分levelDB代码,甚至还写了几篇相关的博客,比如
但是我却一点都没印象了.... 仔细看来很多概念在当时可能都是没有充分理解的,而且从数目上来看,应该并没有完整看完levelDB代码。
所以重新开个坑,看看自己比起毕业前有没有长进【没有
先从入口 include/leveldb/db.h 开始
LEVELDB_EXPORT
看到LEVELDB_EXPORT这个macro
1
2class LEVELDB_EXPORT Snapshot {
3 protected:
4 virtual ~Snapshot();
5};
6
是在 include/leveldb/export.h 中定义的
1
2// 符号可见性问题,使用macro来控制在编译成动态库时暴露,在Link时不暴露符号是一种common 的做法
3//
4#if !defined(LEVELDB_EXPORT)
5
6#if defined(LEVELDB_SHARED_LIBRARY)
7#if defined(_WIN32)
8
9#if defined(LEVELDB_COMPILE_LIBRARY)
10#define LEVELDB_EXPORT __declspec(dllexport)
11#else
12#define LEVELDB_EXPORT __declspec(dllimport)
13#endif // defined(LEVELDB_COMPILE_LIBRARY)
14
15#else // defined(_WIN32)
16#if defined(LEVELDB_COMPILE_LIBRARY)
17#define LEVELDB_EXPORT __attribute__((visibility("default")))
18#else
19#define LEVELDB_EXPORT
20#endif
21#endif // defined(_WIN32)
22
23#else // defined(LEVELDB_SHARED_LIBRARY)
24#define LEVELDB_EXPORT
25#endif
26
27#endif // !defined(LEVELDB_EXPORT)
28
29#endif // STORAGE_LEVELDB_INCLUDE_EXPORT_H_
30
31
通常在编译为动态链接库时,需要将某些符号设置为可见; 在link 这些动态库时,将这些符号设置为hidden
because the same header file is generally used both when compiling the DLL and in client code that consumes the DLL's interface, it is a common pattern to define a macro that automatically resolves to the appropriate attribute specifier at compile-time
可以参考
- what does __declspec(dllimport) really mean?
- gcc-wiki-Why is the new C++ visibility support so useful?
- 记录一次因动态库符号表可见性导致的未定义的引用(undefined reference)
slice
代码位于include/leveldb/slice.h 可以参考 这里的说明
Slice 基本就是一个简单版本的std::string_view,提供一个只读的窗口
与std::string_view相似, Slice的生命周期依赖于外部数据的生命周期
值得一提的可能是Slice::compare 函数,用来做一个三路比较
1
2
3inline int Slice::compare(const Slice& b) const {
4 const size_t min_len = (size_ < b.size_) ? size_ : b.size_;
5 int r = memcmp(data_, b.data_, min_len);
6 if (r == 0) {
7 if (size_ < b.size_)
8 r = -1;
9 else if (size_ > b.size_)
10 r = +1;
11 }
12 return r;
13}
14
实际上这部分可以借助c++20的default_comparisons 做一个简化
Status
Status Class 是对levelDB operation的结果做了一层封装。
比较巧妙的地方在于,实现中将"状态码,错误信息“全部压缩在了一个char*中.
1
2 // OK status has a null state_. Otherwise, state_ is a new[] array
3 // of the following form:
4 // state_[0..3] == length of message
5 // state_[4] == code
6 // state_[5..] == message
7 const char* state_;
8
这个实现主要是性能会有优势,原因之一是传递参数的时候只需要传一个char*,就可以将code和message全部进行传递。
这样的state_实现决定了copy函数的实现,需要先从前4个byte拿到size,然后再进行copy
1
2const char* Status::CopyState(const char* state) {
3 uint32_t size;
4 std::memcpy(&size, state, sizeof(size));
5 // 5是4byte size + 1byte code
6 char* result = new char[size + 5];
7 std::memcpy(result, state, size + 5);
8 return result;
9}
此外,一些静态函数的声明最初有些让人困惑,为啥要有两个Slice? 一个Slice不就够了吗
1 static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) {
2 return Status(kNotFound, msg, msg2);
3 }
4
看了调用时的使用,发现是在一些场景下,错误信息会有两部分组成。 前一部分是大的类别的错误信息,后面可能是错误码(其他第三方的错误码)
这样实现感觉主要是减轻了调用方的负担,因为不需要在调用测自己做message的拼接了。
1
2
3Status::Status(Code code, const Slice& msg, const Slice& msg2) {
4 assert(code != kOk);
5 // 两种情况
6 // 1: msg2为空,len2为0, 返回的结果由msg决定
7 // 2: msg2不为空, size为 size(msg1) + len(": ") + size(msg2). 中间的2是分隔符
8 const uint32_t len1 = static_cast<uint32_t>(msg.size());
9 const uint32_t len2 = static_cast<uint32_t>(msg2.size());
10 const uint32_t size = len1 + (len2 ? (2 + len2) : 0);
11 char* result = new char[size + 5];
12 std::memcpy(result, &size, sizeof(size));
13 result[4] = static_cast<char>(code);
14 std::memcpy(result + 5, msg.data(), len1);
15 if (len2) {
16 result[5 + len1] = ':';
17 result[6 + len1] = ' ';
18 std::memcpy(result + 7 + len1, msg2.data(), len2);
19 }
20 state_ = result;
21}
22
23
注释
include/leveldb/db.h 文件算是整个项目的接口,注释写的全面而详细
大概有以下几个方面:
- 函数是做什么的
- 正常情况下的返回值;异常情况下的返回值
- 参数的生命周期
- 当操作的key不存在时,是否返回错误(如Delete不会返回错误,但似乎Get会返回错误)
- 线程安全性。 哪些部分是可以直接多个thread调用的,哪些需要外部加锁
Posts in this Series
- [施工中] levelDB 代码阅读笔记 06 iterator
- levelDB 代码阅读笔记 05 arena
- levelDB 代码阅读笔记 04 filter
- levelDB 代码阅读笔记 03 cache
- levelDB 代码阅读笔记 02 comparator
- levelDB 代码阅读笔记 01 db.h
- murmurhash源码分析
- 内存屏障(Memory Barriers)
- 文本相似度判断-simhash算法学习笔记