bitcoinleveldb_log/reader.rs
1crate::ix!();
2
3/**
4 | Interface for reporting errors.
5 |
6 */
7pub trait LogReaderReporter {
8
9 /**
10 | Some corruption was detected. "size"
11 | is the approximate number of bytes dropped
12 | due to the corruption.
13 |
14 */
15 fn corruption(&mut self,
16 bytes: usize,
17 status: &Status);
18
19}
20
21/**
22 | Extend record types with the following
23 | special values
24 |
25 */
26bitflags!{
27 pub struct ExtendedRecordTypes: i32 {
28 const Eof = LOG_MAX_RECORD_TYPE as i32 + 1;
29
30 /*
31 | Returned whenever we find an invalid
32 | physical record.
33 |
34 | Currently there are three situations
35 | in which this happens:
36 |
37 | - The record has an invalid CRC (ReadPhysicalRecord
38 | reports a drop)
39 |
40 | - The record is a 0-length record (No
41 | drop is reported)
42 |
43 | - The record is below constructor's
44 | initial_offset (No drop is reported)
45 |
46 */
47 const BadRecord = LOG_MAX_RECORD_TYPE as i32 + 2;
48 }
49}
50
51//-------------------------------------------[.cpp/bitcoin/src/leveldb/db/log_reader.h]
52//-------------------------------------------[.cpp/bitcoin/src/leveldb/db/log_reader.cc]
53
54pub struct LogReader {
55
56 file: Box<dyn SequentialFile>,
57 reporter: Box<dyn LogReaderReporter>,
58 checksum: bool,
59 backing_store: *const u8,
60 buffer: Slice,
61
62 /**
63 | Last Read() indicated EOF by returning
64 | < kBlockSize
65 |
66 */
67 eof: bool,
68
69 /**
70 | Offset of the last record returned by
71 |
72 | ReadRecord.
73 |
74 */
75 last_record_offset: u64,
76
77 /**
78 | Offset of the first location past the
79 | end of buffer_.
80 |
81 */
82 end_of_buffer_offset: u64,
83
84 /**
85 | Offset at which to start looking for
86 | the first record to return
87 |
88 */
89 initial_offset: u64,
90
91 /**
92 | True if we are resynchronizing after
93 | a seek (initial_offset_ > 0). In particular,
94 | a run of kMiddleType and kLastType records
95 | can be silently skipped in this mode
96 |
97 */
98 resyncing: bool,
99}
100
101impl Drop for LogReader {
102 fn drop(&mut self) {
103 todo!();
104 /*
105 delete[] backing_store_;
106 */
107 }
108}
109
110impl LogReader {
111
112 /**
113 | Create a reader that will return log records
114 | from "*file". "*file" must remain live while
115 | this Reader is in use.
116 |
117 | If "reporter" is non-null, it is notified
118 | whenever some data is dropped due to
119 | a detected corruption. "*reporter" must
120 | remain live while this Reader is in use.
121 |
122 | If "checksum" is true, verify checksums if
123 | available.
124 |
125 | The Reader will start reading at the first
126 | record located at physical position >=
127 | initial_offset within the file.
128 */
129 pub fn new(
130 file: Rc<RefCell<dyn SequentialFile>>,
131 reporter: Rc<RefCell<dyn LogReaderReporter>>,
132 checksum: bool,
133 initial_offset: u64) -> Self {
134
135 todo!();
136 /*
137
138
139 : file_(file),
140 reporter_(reporter),
141 checksum_(checksum),
142 backing_store_(new char[kBlockSize]),
143 buffer_(),
144 eof_(false),
145 last_record_offset_(0),
146 end_of_buffer_offset_(0),
147 initial_offset_(initial_offset),
148 resyncing_(initial_offset > 0)
149 */
150 }
151
152 /**
153 | Skips all blocks that are completely before
154 | "initial_offset_".
155 |
156 | Returns true on success. Handles reporting.
157 */
158 pub fn skip_to_initial_block(&mut self) -> bool {
159
160 todo!();
161 /*
162 const size_t offset_in_block = initial_offset_ % kBlockSize;
163 uint64_t block_start_location = initial_offset_ - offset_in_block;
164
165 // Don't search a block if we'd be in the trailer
166 if (offset_in_block > kBlockSize - 6) {
167 block_start_location += kBlockSize;
168 }
169
170 end_of_buffer_offset_ = block_start_location;
171
172 // Skip to start of first block that can contain the initial record
173 if (block_start_location > 0) {
174 Status skip_status = file_->Skip(block_start_location);
175 if (!skip_status.ok()) {
176 ReportDrop(block_start_location, skip_status);
177 return false;
178 }
179 }
180
181 return true;
182 */
183 }
184
185 /**
186 | Read the next record into *record. Returns
187 | true if read successfully, false if we hit
188 | end of the input. May use "*scratch" as
189 | temporary storage. The contents filled in
190 | *record will only be valid until the next
191 | mutating operation on this reader or the next
192 | mutation to *scratch.
193 */
194 pub fn read_record(&mut self,
195 record: *mut Slice,
196 scratch: *mut String) -> bool {
197
198 todo!();
199 /*
200 if (last_record_offset_ < initial_offset_) {
201 if (!SkipToInitialBlock()) {
202 return false;
203 }
204 }
205
206 scratch->clear();
207 record->clear();
208 bool in_fragmented_record = false;
209 // Record offset of the logical record that we're reading
210 // 0 is a dummy value to make compilers happy
211 uint64_t prospective_record_offset = 0;
212
213 Slice fragment;
214 while (true) {
215 const unsigned int record_type = ReadPhysicalRecord(&fragment);
216
217 // ReadPhysicalRecord may have only had an empty trailer remaining in its
218 // internal buffer. Calculate the offset of the next physical record now
219 // that it has returned, properly accounting for its header size.
220 uint64_t physical_record_offset =
221 end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size();
222
223 if (resyncing_) {
224 if (record_type == kMiddleType) {
225 continue;
226 } else if (record_type == kLastType) {
227 resyncing_ = false;
228 continue;
229 } else {
230 resyncing_ = false;
231 }
232 }
233
234 switch (record_type) {
235 case kFullType:
236 if (in_fragmented_record) {
237 // Handle bug in earlier versions of LogWriter where
238 // it could emit an empty kFirstType record at the tail end
239 // of a block followed by a kFullType or kFirstType record
240 // at the beginning of the next block.
241 if (!scratch->empty()) {
242 ReportCorruption(scratch->size(), "partial record without end(1)");
243 }
244 }
245 prospective_record_offset = physical_record_offset;
246 scratch->clear();
247 *record = fragment;
248 last_record_offset_ = prospective_record_offset;
249 return true;
250
251 case kFirstType:
252 if (in_fragmented_record) {
253 // Handle bug in earlier versions of LogWriter where
254 // it could emit an empty kFirstType record at the tail end
255 // of a block followed by a kFullType or kFirstType record
256 // at the beginning of the next block.
257 if (!scratch->empty()) {
258 ReportCorruption(scratch->size(), "partial record without end(2)");
259 }
260 }
261 prospective_record_offset = physical_record_offset;
262 scratch->assign(fragment.data(), fragment.size());
263 in_fragmented_record = true;
264 break;
265
266 case kMiddleType:
267 if (!in_fragmented_record) {
268 ReportCorruption(fragment.size(),
269 "missing start of fragmented record(1)");
270 } else {
271 scratch->append(fragment.data(), fragment.size());
272 }
273 break;
274
275 case kLastType:
276 if (!in_fragmented_record) {
277 ReportCorruption(fragment.size(),
278 "missing start of fragmented record(2)");
279 } else {
280 scratch->append(fragment.data(), fragment.size());
281 *record = Slice(*scratch);
282 last_record_offset_ = prospective_record_offset;
283 return true;
284 }
285 break;
286
287 case kEof:
288 if (in_fragmented_record) {
289 // This can be caused by the writer dying immediately after
290 // writing a physical record but before completing the next; don't
291 // treat it as a corruption, just ignore the entire logical record.
292 scratch->clear();
293 }
294 return false;
295
296 case kBadRecord:
297 if (in_fragmented_record) {
298 ReportCorruption(scratch->size(), "error in middle of record");
299 in_fragmented_record = false;
300 scratch->clear();
301 }
302 break;
303
304 default: {
305 char buf[40];
306 snprintf(buf, sizeof(buf), "unknown record type %u", record_type);
307 ReportCorruption(
308 (fragment.size() + (in_fragmented_record ? scratch->size() : 0)),
309 buf);
310 in_fragmented_record = false;
311 scratch->clear();
312 break;
313 }
314 }
315 }
316 return false;
317 */
318 }
319
320 /**
321 | Returns the physical offset of the last
322 | record returned by ReadRecord.
323 |
324 | Undefined before the first call to
325 | ReadRecord.
326 */
327 pub fn last_record_offset(&mut self) -> u64 {
328
329 todo!();
330 /*
331 return last_record_offset_;
332 */
333 }
334
335 /**
336 | Reports dropped bytes to the reporter.
337 | buffer_ must be updated to remove the
338 | dropped bytes prior to invocation.
339 |
340 */
341 pub fn report_corruption(&mut self,
342 bytes: u64,
343 reason: *const u8) {
344
345 todo!();
346 /*
347 ReportDrop(bytes, Status::Corruption(reason, file_->GetName()));
348 */
349 }
350
351 pub fn report_drop(&mut self,
352 bytes: u64,
353 reason: &Status) {
354
355 todo!();
356 /*
357 if (reporter_ != nullptr &&
358 end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {
359 reporter_->Corruption(static_cast<size_t>(bytes), reason);
360 }
361 */
362 }
363
364 /**
365 | Return type, or one of the preceding
366 | special values
367 |
368 */
369 pub fn read_physical_record(&mut self, result: *mut Slice) -> u32 {
370
371 todo!();
372 /*
373 while (true) {
374 if (buffer_.size() < kHeaderSize) {
375 if (!eof_) {
376 // Last read was a full read, so this is a trailer to skip
377 buffer_.clear();
378 Status status = file_->Read(kBlockSize, &buffer_, backing_store_);
379 end_of_buffer_offset_ += buffer_.size();
380 if (!status.ok()) {
381 buffer_.clear();
382 ReportDrop(kBlockSize, status);
383 eof_ = true;
384 return kEof;
385 } else if (buffer_.size() < kBlockSize) {
386 eof_ = true;
387 }
388 continue;
389 } else {
390 // Note that if buffer_ is non-empty, we have a truncated header at the
391 // end of the file, which can be caused by the writer crashing in the
392 // middle of writing the header. Instead of considering this an error,
393 // just report EOF.
394 buffer_.clear();
395 return kEof;
396 }
397 }
398
399 // Parse the header
400 const char* header = buffer_.data();
401 const uint32_t a = static_cast<uint32_t>(header[4]) & 0xff;
402 const uint32_t b = static_cast<uint32_t>(header[5]) & 0xff;
403 const unsigned int type = header[6];
404 const uint32_t length = a | (b << 8);
405 if (kHeaderSize + length > buffer_.size()) {
406 size_t drop_size = buffer_.size();
407 buffer_.clear();
408 if (!eof_) {
409 ReportCorruption(drop_size, "bad record length");
410 return kBadRecord;
411 }
412 // If the end of the file has been reached without reading |length| bytes
413 // of payload, assume the writer died in the middle of writing the record.
414 // Don't report a corruption.
415 return kEof;
416 }
417
418 if (type == kZeroType && length == 0) {
419 // Skip zero length record without reporting any drops since
420 // such records are produced by the mmap based writing code in
421 // env_posix.cc that preallocates file regions.
422 buffer_.clear();
423 return kBadRecord;
424 }
425
426 // Check crc
427 if (checksum_) {
428 uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header));
429 uint32_t actual_crc = crc32c::Value(header + 6, 1 + length);
430 if (actual_crc != expected_crc) {
431 // Drop the rest of the buffer since "length" itself may have
432 // been corrupted and if we trust it, we could find some
433 // fragment of a real log record that just happens to look
434 // like a valid log record.
435 size_t drop_size = buffer_.size();
436 buffer_.clear();
437 ReportCorruption(drop_size, "checksum mismatch");
438 return kBadRecord;
439 }
440 }
441
442 buffer_.remove_prefix(kHeaderSize + length);
443
444 // Skip physical record that started before initial_offset_
445 if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length <
446 initial_offset_) {
447 result->clear();
448 return kBadRecord;
449 }
450
451 *result = Slice(header + kHeaderSize, length);
452 return type;
453 }
454 */
455 }
456}