in cachelib/navy/serialization/RecordIO.cpp [150:218]
std::unique_ptr<folly::IOBuf> readRecord() override {
bool readHeader = true;
std::unique_ptr<folly::IOBuf> buf = nullptr;
uint8_t* bufferData = buffer_.data();
uint64_t size = 0;
uint8_t* data = nullptr;
auto dataOffset = 0;
do {
// This is true when we have to read a header and there are not
// enough bytes in the buffer OR we have to read the next block
// in the multi-block read
if (bufIndex_ + headerSize() > kBlockSize) {
// read new block from the device if the number of bytes left from
// previous read are less than header size.
if (offset_ + kBlockSize > metadataSize_) {
throw std::logic_error("exceeding metadata limit");
}
// read from device to the middle of the buffer 'kReadOffset'
if (!dev_.read(offset_, kBlockSize, bufferData)) {
throw std::invalid_argument(
folly::sformat("read failed: offset = {}", offset_));
}
offset_ += kBlockSize;
bufIndex_ = 0;
}
// Parse the header if we are expecting header
if (readHeader) {
readHeader = false;
auto valid = validateRecordHeader(
folly::Range<unsigned char*>(&bufferData[bufIndex_],
kBlockSize - bufIndex_),
kMetadataHeaderFileId);
if (!valid) {
throw std::logic_error("Invalid record header");
}
recordio_detail::Header* h =
reinterpret_cast<recordio_detail::Header*>(&bufferData[bufIndex_]);
size = headerSize() + h->dataLength;
// copy the header also to IOBuf so that we can do validation
buf = folly::IOBuf::create(size);
if (buf == nullptr) {
return nullptr;
}
buf->append(size);
data = buf->writableData();
dataOffset = 0;
}
auto cpSize =
std::min(static_cast<uint64_t>(kBlockSize - bufIndex_), size);
memcpy(data + dataOffset, &bufferData[bufIndex_], cpSize);
bufIndex_ += cpSize;
dataOffset += cpSize;
size -= cpSize;
} while (size > 0);
// Validate the what we just read from the device
auto record =
validateRecordData(folly::Range<unsigned char*>(data, buf->length()));
if (record.fileId == 0) {
throw std::invalid_argument(folly::sformat(
"Invalid record : offset = {}, length = {}", offset_, buf->length()));
}
// skip the header part and return
buf->trimStart(headerSize());
return buf;
}