
writer的工作就是将上層拼裝好的資料,按照log format的格式,進行二次拼裝,然後寫進檔案裡面。
【資料成員介紹】
block_offset_:目前block的偏移位,通過它就能知道目前資料寫到這一塊的什麼位置了;
dest_:增加記錄,就是将資料追加到dest_;
type_crc_:因為每個record都會有一個header,而header裡面就會有checksum,在調用這個類的構造函數的時候,就會預先将RecordType的crc生成好,然後儲存在type_crc_;
【方法成員介紹】
Status Writer::AddRecord(const Slice& slice)
作用:将記錄按照log format的方式組裝,并刷到磁盤。
Status Writer::AddRecord(const Slice& slice) {
//擷取資料的開始位置
const char* ptr = slice.data();
//資料的剩餘大小
size_t left = slice.size();
Status s;
//資料在這塊中開始
bool begin = true;
do {
//目前塊的剩餘大小
const int leftover = kBlockSize - block_offset_;
assert(leftover >= 0);
//當塊的剩餘大小不足header的大小時,用\x00填充塊剩餘的空間,并重新開始一個新的block,block_offset_=0
if (leftover < kHeaderSize) {
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;
}
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);
//資料在這塊中開始,也在這塊中結束,那麼資料整個都在這塊中,那麼類型自然就是kFullType
if (begin && end) {
type = kFullType;
//資料隻是在這塊中開始,并沒有結束,那麼資料整個都在這塊中,那麼類型自然就是kFirstType
} else if (begin) {
type = kFirstType;
//資料隻是在這塊中結束,并沒有開始,那麼資料整個都在這塊中,那麼類型自然就是kLastType
} else if (end) {
type = kLastType;
//資料在目前塊即沒有開始,也沒有結束,那自然就是中間了kMiddleType
} else {
type = kMiddleType;
}
s = EmitPhysicalRecord(type, ptr, fragment_length);
ptr += fragment_length;
left -= fragment_length;
begin = false;
} while (s.ok() && left > 0);
return s;
}
這個方法的重點就是,始終把目前塊當做參照物,注意目前block在record中的type和block_offset_就可以了;
每次寫一個record,要麼是把整個record寫完,要麼是把整個block寫完。
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n)
作用:根據本次寫的資料,生成header,并将資料刷到磁盤上。
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
assert(n <= 0xffff);
assert(block_offset_ + kHeaderSize + n <= kBlockSize);
// 填充header
char buf[kHeaderSize];
//本次寫入data的大小,即fragment_length,小端方式儲存
buf[4] = static_cast<char>(n & 0xff);
buf[5] = static_cast<char>(n >> 8);
//RecordType
buf[6] = static_cast<char>(t);
// 計算資料的crc
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n);
crc = crc32c::Mask(crc);
//将校驗碼插入header中
EncodeFixed32(buf, crc);
// 将header和資料追加到dest_,并寫到磁盤
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;
}