You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
2.8 KiB
103 lines
2.8 KiB
// Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style license that can be |
|
// found in the LICENSE file. See the AUTHORS file for names of contributors. |
|
|
|
#include "db/log_writer.h" |
|
|
|
#include <stdint.h> |
|
#include "leveldb/env.h" |
|
#include "util/coding.h" |
|
#include "util/crc32c.h" |
|
|
|
namespace leveldb { |
|
namespace log { |
|
|
|
Writer::Writer(WritableFile* dest) |
|
: dest_(dest), |
|
block_offset_(0) { |
|
for (int i = 0; i <= kMaxRecordType; i++) { |
|
char t = static_cast<char>(i); |
|
type_crc_[i] = crc32c::Value(&t, 1); |
|
} |
|
} |
|
|
|
Writer::~Writer() { |
|
} |
|
|
|
Status Writer::AddRecord(const Slice& slice) { |
|
const char* ptr = slice.data(); |
|
size_t left = slice.size(); |
|
|
|
// Fragment the record if necessary and emit it. Note that if slice |
|
// is empty, we still want to iterate once to emit a single |
|
// zero-length record |
|
Status s; |
|
bool begin = true; |
|
do { |
|
const int leftover = kBlockSize - block_offset_; |
|
assert(leftover >= 0); |
|
if (leftover < kHeaderSize) { |
|
// Switch to a new block |
|
if (leftover > 0) { |
|
// Fill the trailer (literal below relies on kHeaderSize being 7) |
|
assert(kHeaderSize == 7); |
|
dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); |
|
} |
|
block_offset_ = 0; |
|
} |
|
|
|
// Invariant: we never leave < kHeaderSize bytes in a block. |
|
assert(kBlockSize - block_offset_ - kHeaderSize >= 0); |
|
|
|
const size_t avail = kBlockSize - block_offset_ - kHeaderSize; |
|
const size_t fragment_length = (left < avail) ? left : avail; |
|
|
|
RecordType type; |
|
const bool end = (left == fragment_length); |
|
if (begin && end) { |
|
type = kFullType; |
|
} else if (begin) { |
|
type = kFirstType; |
|
} else if (end) { |
|
type = kLastType; |
|
} else { |
|
type = kMiddleType; |
|
} |
|
|
|
s = EmitPhysicalRecord(type, ptr, fragment_length); |
|
ptr += fragment_length; |
|
left -= fragment_length; |
|
begin = false; |
|
} while (s.ok() && left > 0); |
|
return s; |
|
} |
|
|
|
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { |
|
assert(n <= 0xffff); // Must fit in two bytes |
|
assert(block_offset_ + kHeaderSize + n <= kBlockSize); |
|
|
|
// Format the header |
|
char buf[kHeaderSize]; |
|
buf[4] = static_cast<char>(n & 0xff); |
|
buf[5] = static_cast<char>(n >> 8); |
|
buf[6] = static_cast<char>(t); |
|
|
|
// Compute the crc of the record type and the payload. |
|
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); |
|
crc = crc32c::Mask(crc); // Adjust for storage |
|
EncodeFixed32(buf, crc); |
|
|
|
// Write the header and the payload |
|
Status s = dest_->Append(Slice(buf, kHeaderSize)); |
|
if (s.ok()) { |
|
s = dest_->Append(Slice(ptr, n)); |
|
if (s.ok()) { |
|
s = dest_->Flush(); |
|
} |
|
} |
|
block_offset_ += kHeaderSize + n; |
|
return s; |
|
} |
|
|
|
} // namespace log |
|
} // namespace leveldb
|
|
|