http://blog.csdn.net/sparkliang/article/details/8635821
BlockBuilder的接口
首先從Block的構建開始,這就是BlockBuilder類,來看下BlockBuilder的函數接口,一共有5個:算法
- void Reset();
- void Add(const Slice& key,const Slice& value);
- Slice Finish();
- size_t CurrentSizeEstimate()const;
- bool empty() const { returnbuffer_.empty();}
主要成員變量以下:數組
- std::string buffer_;
- std::vector<uint32_t> restarts_;
- int counter_;
- std::string last_key_;
6.3.2 BlockBuilder::Add()
調用Add函數向當前Block中新加入一個k/v對{key, value}。函數處理邏輯以下:app
S1 保證新加入的key > 已加入的任何一個key;函數
- assert(!finished_);
- assert(counter_ <= options_->block_restart_interval);
- assert(buffer_.empty() || options_->comparator->Compare(key,last_key_piece) > 0);
S2 若是計數器counter < opions->block_restart_interval,則使用前綴算法壓縮key,不然就把key做爲一個重啓點,無壓縮存儲;ui
- Slice last_key_piece(last_key_);
- if (counter_ < options_->block_restart_interval) {
-
- const size_t min_length= std::min(last_key_piece.size(), key.size());
- while ((shared < min_length)&& (last_key_piece[shared] == key[shared])) {
- shared++;
- }else{
- restarts_.push_back(buffer_.size());
- counter_ = 0;
- }
S3根據上面的數據格式存儲k/v對,追加到buffer中,並更新block狀態。spa
- const size_t non_shared = key.size() - shared;
- PutVarint32(&buffer_, shared);
- PutVarint32(&buffer_, non_shared);
- PutVarint32(&buffer_, value.size());
- buffer_.append(key.data() + shared, non_shared);
- buffer_.append(value.data(), value.size());
- last_key_.resize(shared);
- last_key_.append(key.data() + shared, non_shared);
- assert(Slice(last_key_) == key);
- counter_++;
6.3.3 BlockBuilder::Finish()
調用該函數完成Block的構建,很簡單,壓入重啓點信息,並返回buffer_,設置結束標記finished_:.net
- for (size_t i = 0; i < restarts_.size(); i++) {
- PutFixed32(&buffer_, restarts_[i]);
- }
- PutFixed32(&buffer_, restarts_.size());
- finished_ = true;
- return Slice(buffer_);
6.3.4 BlockBuilder::Reset() & 大小
還有Reset和CurrentSizeEstimate兩個函數,Reset復位函數,清空各個信息;函數CurrentSizeEstimate返回block的預計大小,從函數實現來看,應該在調用Finish以前調用該函數。指針
- void BlockBuilder::Reset() {
- buffer_.clear(); restarts_.clear(); last_key_.clear();
- restarts_.push_back(0);
- counter_ = 0;
- finished_ = false;
- }
-
- size_t BlockBuilder::CurrentSizeEstimate () const {
-
- return (buffer_.size() + restarts_.size() * sizeof(uint32_t) + sizeof(uint32_t));
- }
Block的構建就這些內容了,下面開始分析Block的讀取,就是類Block。rest
對Block的讀取是由類Block完成的,先來看看其函數接口和關鍵成員變量。code
Block只有兩個函數接口,經過Iterator對象,調用者就能夠遍歷訪問Block的存儲的k/v對了;以及幾個成員變量,以下:
- size_t size() const { returnsize_; }
- Iterator* NewIterator(constComparator* comparator);
-
- const char* data_;
- size_t size_;
- uint32_t restart_offset_;
- bool owned_;
Block的構造函數接受一個BlockContents對象contents初始化,BlockContents是一個有3個成員的結構體。
- >data = Slice();
- >cachable = false;
- >heap_allocated = false;
- 根據contents爲成員賦值
- data_ = contents.data.data(), size_ =contents.data.size(),owned_ = contents.heap_allocated;
而後從data中解析出重啓點數組,若是數據過小,或者重啓點計算出錯,就設置size_=0,代表該block data解析失敗.
- if (size_ < sizeof(uint32_t)){
- size_ = 0;
- } else {
- restart_offset_ = size_ - (1 +NumRestarts()) * sizeof(uint32_t);
- if (restart_offset_ > size_- sizeof(uint32_t)) size_ = 0;
- }
NumRestarts()函數就是從最後的uint32解析出重啓點的個數,並返回:
return DecodeFixed32(data_ +size_ - sizeof(uint32_t))
這是一個用以遍歷Block內部數據的內部類,它繼承了Iterator接口。函數NewIterator返回Block::Iter對象:return new Iter(cmp, data_,restart_offset_, num_restarts);
下面咱們就分析Iter的實現。
主要成員變量有:
- const Comparator* constcomparator_;
- const char* const data_;
- uint32_t const restarts_;
- uint32_t const num_restarts_;
- uint32_t current_;
- uint32_t restart_index_;
下面來看看對Iterator接口的實現,簡單函數略過。
>首先是Next()函數,直接調用private函數ParseNextKey()跳到下一個k/v對,函數實現以下:
S1 跳到下一個entry,其位置緊鄰在當前value_以後。若是已是最後一個entry了,返回false,標記current_爲invalid。
- current_ = NextEntryOffset();
- const char* p = data_ +current_;
- const char* limit = data_ +restarts_;
- if (p >= limit) {
- current_ = restarts_;
- restart_index_ =num_restarts_;
- return false;
- }
S2 解析出entry,解析出錯則設置錯誤狀態,記錄錯誤並返回false。解析成功則根據信息組成key和value,並更新重啓點index。
- uint32_t shared, non_shared,value_length;
- p = DecodeEntry(p, limit,&shared, &non_shared, &value_length);
- if (p == NULL || key_.size()< shared) {
- CorruptionError();
- return false;
- } else {
- key_.resize(shared);
- key_.append(p, non_shared);
- value_ = Slice(p +non_shared, value_length);
- while (restart_index_ + 1< num_restarts_ && GetRestartPoint(restart_index_ + 1) < current_) {
- ++restart_index_;
- }
- return true;
- }
函數DecodeEntry從字符串[p, limit)解析出key的前綴長度、key前綴以後的字符串長度和value的長度這三個vint32值,代碼很簡單。
函數CorruptionError將current_和restart_index_都設置爲invalid狀態,並在status中設置錯誤狀態。
函數GetRestartPoint從data中讀取指定restart index的偏移值restart[index],並返回:DecodeFixed32(data_ + restarts_ +index * sizeof(uint32_t);
>接下來看看Prev函數,Previous操做分爲兩步:首先回到current_以前的重啓點,而後再向後直到current_,實現以下:
S1首先向前回跳到在current_前面的那個重啓點,並定位到重啓點的k/v對開始位置。
- const uint32_t original =current_;
- while (GetRestartPoint(restart_index_)>= original) {
- if (restart_index_ == 0) {
- current_ = restarts_;
- restart_index_ =num_restarts_;
- return;
- }
- restart_index_--;
- }
- SeekToRestartPoint(restart_index_);
S2 第二步,從重啓點位置開始向後遍歷,直到遇到original前面的那個k/v對。
do {} while (ParseNextKey() &&NextEntryOffset() < original);
說說上面遇到的SeekToRestartPoint函數,它只是設置了幾個有限的狀態,其它值將在函數ParseNextKey()中設置。感受這有點tricky,這裏的value_並非k/v對的value,而只是一個指向k/v對起始位置的0長度指針,這樣後面的ParseNextKey函數將會取出重啓點的k/v值。
- void SeekToRestartPoint(uint32_tindex) {
- key_.clear();
- restart_index_ = index;
-
-
- uint32_t offset =GetRestartPoint(index);
- value_ = Slice(data_ + offset,0);
- }
> SeekToFirst/Last,這兩個函數都很簡單,藉助於前面的SeekToResartPoint函數就能夠完成。
- virtual void SeekToFirst() {
- SeekToRestartPoint(0);
- ParseNextKey();
- }
-
- virtual void SeekToLast() {
- SeekToRestartPoint(num_restarts_ - 1);
- while (ParseNextKey()&& NextEntryOffset() < restarts_) {}
- }
> 最後一個Seek函數,跳到指定的target(Slice),函數邏輯以下:
S1 二分查找,找到key < target的最後一個重啓點,典型的二分查找算法,代碼就再也不貼了。
S2 找到後,跳轉到重啓點,其索引由left指定,這是前面二分查找到的結果。如前面所分析的,value_指向重啓點的地址,而size_指定爲0,這樣ParseNextKey函數將會取出重啓點的k/v值。
SeekToRestartPoint(left);
S3 自重啓點線性向下,直到遇到key>= target的k/v對。
- while (true) {
- if (!ParseNextKey()) return;
- if (Compare(key_, target)>= 0) return;
- }
上面就是Block::Iter的所有實現邏輯,這樣Block的建立和讀取遍歷都已經分析完畢。