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;
}