xref: /leveldb-1.20/table/block.cc (revision f122c6df)
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
4 //
5 // Decodes the blocks generated by block_builder.cc.
6 
7 #include "table/block.h"
8 
9 #include <vector>
10 #include <algorithm>
11 #include "leveldb/comparator.h"
12 #include "util/coding.h"
13 #include "util/logging.h"
14 
15 namespace leveldb {
16 
17 inline uint32_t Block::NumRestarts() const {
18   assert(size_ >= 2*sizeof(uint32_t));
19   return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
20 }
21 
22 Block::Block(const char* data, size_t size)
23     : data_(data),
24       size_(size) {
25   if (size_ < sizeof(uint32_t)) {
26     size_ = 0;  // Error marker
27   } else {
28     restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
29     if (restart_offset_ > size_ - sizeof(uint32_t)) {
30       // The size is too small for NumRestarts() and therefore
31       // restart_offset_ wrapped around.
32       size_ = 0;
33     }
34   }
35 }
36 
37 Block::~Block() {
38   delete[] data_;
39 }
40 
41 // Helper routine: decode the next block entry starting at "p",
42 // storing the number of shared key bytes, non_shared key bytes,
43 // and the length of the value in "*shared", "*non_shared", and
44 // "*value_length", respectively.  Will not derefence past "limit".
45 //
46 // If any errors are detected, returns NULL.  Otherwise, returns a
47 // pointer to the key delta (just past the three decoded values).
48 static inline const char* DecodeEntry(const char* p, const char* limit,
49                                       uint32_t* shared,
50                                       uint32_t* non_shared,
51                                       uint32_t* value_length) {
52   if (limit - p < 3) return NULL;
53   *shared = reinterpret_cast<const unsigned char*>(p)[0];
54   *non_shared = reinterpret_cast<const unsigned char*>(p)[1];
55   *value_length = reinterpret_cast<const unsigned char*>(p)[2];
56   if ((*shared | *non_shared | *value_length) < 128) {
57     // Fast path: all three values are encoded in one byte each
58     p += 3;
59   } else {
60     if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL;
61     if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL;
62     if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL;
63   }
64 
65   if (static_cast<uint32_t>(limit - p) < (*non_shared + *value_length)) {
66     return NULL;
67   }
68   return p;
69 }
70 
71 class Block::Iter : public Iterator {
72  private:
73   const Comparator* const comparator_;
74   const char* const data_;      // underlying block contents
75   uint32_t const restarts_;     // Offset of restart array (list of fixed32)
76   uint32_t const num_restarts_; // Number of uint32_t entries in restart array
77 
78   // current_ is offset in data_ of current entry.  >= restarts_ if !Valid
79   uint32_t current_;
80   uint32_t restart_index_;  // Index of restart block in which current_ falls
81   std::string key_;
82   Slice value_;
83   Status status_;
84 
85   inline int Compare(const Slice& a, const Slice& b) const {
86     return comparator_->Compare(a, b);
87   }
88 
89   // Return the offset in data_ just past the end of the current entry.
90   inline uint32_t NextEntryOffset() const {
91     return (value_.data() + value_.size()) - data_;
92   }
93 
94   uint32_t GetRestartPoint(uint32_t index) {
95     assert(index < num_restarts_);
96     return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t));
97   }
98 
99   void SeekToRestartPoint(uint32_t index) {
100     key_.clear();
101     restart_index_ = index;
102     // current_ will be fixed by ParseNextKey();
103 
104     // ParseNextKey() starts at the end of value_, so set value_ accordingly
105     uint32_t offset = GetRestartPoint(index);
106     value_ = Slice(data_ + offset, 0);
107   }
108 
109  public:
110   Iter(const Comparator* comparator,
111        const char* data,
112        uint32_t restarts,
113        uint32_t num_restarts)
114       : comparator_(comparator),
115         data_(data),
116         restarts_(restarts),
117         num_restarts_(num_restarts),
118         current_(restarts_),
119         restart_index_(num_restarts_) {
120     assert(num_restarts_ > 0);
121   }
122 
123   virtual bool Valid() const { return current_ < restarts_; }
124   virtual Status status() const { return status_; }
125   virtual Slice key() const {
126     assert(Valid());
127     return key_;
128   }
129   virtual Slice value() const {
130     assert(Valid());
131     return value_;
132   }
133 
134   virtual void Next() {
135     assert(Valid());
136     ParseNextKey();
137   }
138 
139   virtual void Prev() {
140     assert(Valid());
141 
142     // Scan backwards to a restart point before current_
143     const uint32_t original = current_;
144     while (GetRestartPoint(restart_index_) >= original) {
145       if (restart_index_ == 0) {
146         // No more entries
147         current_ = restarts_;
148         restart_index_ = num_restarts_;
149         return;
150       }
151       restart_index_--;
152     }
153 
154     SeekToRestartPoint(restart_index_);
155     do {
156       // Loop until end of current entry hits the start of original entry
157     } while (ParseNextKey() && NextEntryOffset() < original);
158   }
159 
160   virtual void Seek(const Slice& target) {
161     // Binary search in restart array to find the first restart point
162     // with a key >= target
163     uint32_t left = 0;
164     uint32_t right = num_restarts_ - 1;
165     while (left < right) {
166       uint32_t mid = (left + right + 1) / 2;
167       uint32_t region_offset = GetRestartPoint(mid);
168       uint32_t shared, non_shared, value_length;
169       const char* key_ptr = DecodeEntry(data_ + region_offset,
170                                         data_ + restarts_,
171                                         &shared, &non_shared, &value_length);
172       if (key_ptr == NULL || (shared != 0)) {
173         CorruptionError();
174         return;
175       }
176       Slice mid_key(key_ptr, non_shared);
177       if (Compare(mid_key, target) < 0) {
178         // Key at "mid" is smaller than "target".  Therefore all
179         // blocks before "mid" are uninteresting.
180         left = mid;
181       } else {
182         // Key at "mid" is >= "target".  Therefore all blocks at or
183         // after "mid" are uninteresting.
184         right = mid - 1;
185       }
186     }
187 
188     // Linear search (within restart block) for first key >= target
189     SeekToRestartPoint(left);
190     while (true) {
191       if (!ParseNextKey()) {
192         return;
193       }
194       if (Compare(key_, target) >= 0) {
195         return;
196       }
197     }
198   }
199 
200   virtual void SeekToFirst() {
201     SeekToRestartPoint(0);
202     ParseNextKey();
203   }
204 
205   virtual void SeekToLast() {
206     SeekToRestartPoint(num_restarts_ - 1);
207     while (ParseNextKey() && NextEntryOffset() < restarts_) {
208       // Keep skipping
209     }
210   }
211 
212  private:
213   void CorruptionError() {
214     current_ = restarts_;
215     restart_index_ = num_restarts_;
216     status_ = Status::Corruption("bad entry in block");
217     key_.clear();
218     value_.clear();
219   }
220 
221   bool ParseNextKey() {
222     current_ = NextEntryOffset();
223     const char* p = data_ + current_;
224     const char* limit = data_ + restarts_;  // Restarts come right after data
225     if (p >= limit) {
226       // No more entries to return.  Mark as invalid.
227       current_ = restarts_;
228       restart_index_ = num_restarts_;
229       return false;
230     }
231 
232     // Decode next entry
233     uint32_t shared, non_shared, value_length;
234     p = DecodeEntry(p, limit, &shared, &non_shared, &value_length);
235     if (p == NULL || key_.size() < shared) {
236       CorruptionError();
237       return false;
238     } else {
239       key_.resize(shared);
240       key_.append(p, non_shared);
241       value_ = Slice(p + non_shared, value_length);
242       while (restart_index_ + 1 < num_restarts_ &&
243              GetRestartPoint(restart_index_ + 1) < current_) {
244         ++restart_index_;
245       }
246       return true;
247     }
248   }
249 };
250 
251 Iterator* Block::NewIterator(const Comparator* cmp) {
252   if (size_ < 2*sizeof(uint32_t)) {
253     return NewErrorIterator(Status::Corruption("bad block contents"));
254   }
255   const uint32_t num_restarts = NumRestarts();
256   if (num_restarts == 0) {
257     return NewEmptyIterator();
258   } else {
259     return new Iter(cmp, data_, restart_offset_, num_restarts);
260   }
261 }
262 
263 }
264