// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // #ifndef ROCKSDB_LITE #include "utilities/blob_db/blob_log_format.h" #include "util/coding.h" #include "util/crc32c.h" namespace rocksdb { namespace blob_db { const uint32_t kMagicNumber = 2395959; const uint32_t kVersion1 = 1; const size_t kBlockSize = 32768; BlobLogHeader::BlobLogHeader() : magic_number_(kMagicNumber), compression_(kNoCompression) {} BlobLogHeader& BlobLogHeader::operator=(BlobLogHeader&& in) noexcept { if (this != &in) { magic_number_ = in.magic_number_; version_ = in.version_; ttl_guess_ = std::move(in.ttl_guess_); ts_guess_ = std::move(in.ts_guess_); compression_ = in.compression_; } return *this; } BlobLogFooter::BlobLogFooter() : magic_number_(kMagicNumber), blob_count_(0) {} Status BlobLogFooter::DecodeFrom(const Slice& input) { Slice slice(input); uint32_t val; if (!GetFixed32(&slice, &val)) { return Status::Corruption("Invalid Blob Footer: flags"); } bool has_ttl = false; bool has_ts = false; val >>= 8; RecordSubType st = static_cast(val); switch (st) { case kRegularType: break; case kTTLType: has_ttl = true; break; case kTimestampType: has_ts = true; break; default: return Status::Corruption("Invalid Blob Footer: flags_val"); } if (!GetFixed64(&slice, &blob_count_)) { return Status::Corruption("Invalid Blob Footer: blob_count"); } ttlrange_t temp_ttl; if (!GetFixed32(&slice, &temp_ttl.first) || !GetFixed32(&slice, &temp_ttl.second)) { return Status::Corruption("Invalid Blob Footer: ttl_range"); } if (has_ttl) { ttl_range_.reset(new ttlrange_t(temp_ttl)); } if (!GetFixed64(&slice, &sn_range_.first) || !GetFixed64(&slice, &sn_range_.second)) { return Status::Corruption("Invalid Blob Footer: sn_range"); } tsrange_t temp_ts; if (!GetFixed64(&slice, &temp_ts.first) || !GetFixed64(&slice, &temp_ts.second)) { return Status::Corruption("Invalid Blob Footer: ts_range"); } if (has_ts) { ts_range_.reset(new tsrange_t(temp_ts)); } if (!GetFixed32(&slice, &magic_number_) || magic_number_ != kMagicNumber) { return Status::Corruption("Invalid Blob Footer: magic"); } return Status::OK(); } void BlobLogFooter::EncodeTo(std::string* dst) const { dst->reserve(kFooterSize); RecordType rt = kFullType; RecordSubType st = kRegularType; if (HasTTL()) { st = kTTLType; } else if (HasTimestamp()) { st = kTimestampType; } uint32_t val = static_cast(rt) | (static_cast(st) << 8); PutFixed32(dst, val); PutFixed64(dst, blob_count_); bool has_ttl = HasTTL(); bool has_ts = HasTimestamp(); if (has_ttl) { PutFixed32(dst, ttl_range_.get()->first); PutFixed32(dst, ttl_range_.get()->second); } else { PutFixed32(dst, 0); PutFixed32(dst, 0); } PutFixed64(dst, sn_range_.first); PutFixed64(dst, sn_range_.second); if (has_ts) { PutFixed64(dst, ts_range_.get()->first); PutFixed64(dst, ts_range_.get()->second); } else { PutFixed64(dst, 0); PutFixed64(dst, 0); } PutFixed32(dst, magic_number_); } void BlobLogHeader::EncodeTo(std::string* dst) const { dst->reserve(kHeaderSize); PutFixed32(dst, magic_number_); PutFixed32(dst, version_); RecordSubType st = kRegularType; bool has_ttl = HasTTL(); bool has_ts = HasTimestamp(); if (has_ttl) { st = kTTLType; } else if (has_ts) { st = kTimestampType; } uint32_t val = static_cast(st) | (static_cast(compression_) << 8); PutFixed32(dst, val); if (has_ttl) { PutFixed32(dst, ttl_guess_.get()->first); PutFixed32(dst, ttl_guess_.get()->second); } else { PutFixed32(dst, 0); PutFixed32(dst, 0); } if (has_ts) { PutFixed64(dst, ts_guess_.get()->first); PutFixed64(dst, ts_guess_.get()->second); } else { PutFixed64(dst, 0); PutFixed64(dst, 0); } } Status BlobLogHeader::DecodeFrom(const Slice& input) { Slice slice(input); if (!GetFixed32(&slice, &magic_number_) || magic_number_ != kMagicNumber) { return Status::Corruption("Invalid Blob Log Header: magic"); } // as of today, we only support 1 version if (!GetFixed32(&slice, &version_) || version_ != kVersion1) { return Status::Corruption("Invalid Blob Log Header: version"); } uint32_t val; if (!GetFixed32(&slice, &val)) { return Status::Corruption("Invalid Blob Log Header: subtype"); } bool has_ttl = false; bool has_ts = false; RecordSubType st = static_cast(val & 0xff); compression_ = static_cast((val >> 8) & 0xff); switch (st) { case kRegularType: break; case kTTLType: has_ttl = true; break; case kTimestampType: has_ts = true; break; default: return Status::Corruption("Invalid Blob Log Header: subtype_2"); } ttlrange_t temp_ttl; if (!GetFixed32(&slice, &temp_ttl.first) || !GetFixed32(&slice, &temp_ttl.second)) { return Status::Corruption("Invalid Blob Log Header: ttl"); } if (has_ttl) set_ttl_guess(temp_ttl); tsrange_t temp_ts; if (!GetFixed64(&slice, &temp_ts.first) || !GetFixed64(&slice, &temp_ts.second)) { return Status::Corruption("Invalid Blob Log Header: timestamp"); } if (has_ts) set_ts_guess(temp_ts); return Status::OK(); } BlobLogRecord::BlobLogRecord() : checksum_(0), header_cksum_(0), key_size_(0), blob_size_(0), time_val_(0), ttl_val_(0), sn_(0), type_(0), subtype_(0) {} BlobLogRecord::~BlobLogRecord() {} void BlobLogRecord::ResizeKeyBuffer(size_t kbs) { if (kbs > key_buffer_.size()) { key_buffer_.resize(kbs); } } void BlobLogRecord::ResizeBlobBuffer(size_t bbs) { if (bbs > blob_buffer_.size()) { blob_buffer_.resize(bbs); } } void BlobLogRecord::Clear() { checksum_ = 0; header_cksum_ = 0; key_size_ = 0; blob_size_ = 0; time_val_ = 0; ttl_val_ = 0; sn_ = 0; type_ = subtype_ = 0; key_.clear(); blob_.clear(); } Status BlobLogRecord::DecodeHeaderFrom(const Slice& hdrslice) { Slice input = hdrslice; if (input.size() < kHeaderSize) { return Status::Corruption("Invalid Blob Record Header: size"); } if (!GetFixed32(&input, &key_size_)) { return Status::Corruption("Invalid Blob Record Header: key_size"); } if (!GetFixed64(&input, &blob_size_)) { return Status::Corruption("Invalid Blob Record Header: blob_size"); } if (!GetFixed32(&input, &ttl_val_)) { return Status::Corruption("Invalid Blob Record Header: ttl_val"); } if (!GetFixed64(&input, &time_val_)) { return Status::Corruption("Invalid Blob Record Header: time_val"); } type_ = *(input.data()); input.remove_prefix(1); subtype_ = *(input.data()); input.remove_prefix(1); if (!GetFixed32(&input, &header_cksum_)) { return Status::Corruption("Invalid Blob Record Header: header_cksum"); } if (!GetFixed32(&input, &checksum_)) { return Status::Corruption("Invalid Blob Record Header: checksum"); } return Status::OK(); } Status BlobLogRecord::DecodeFooterFrom(const Slice& footerslice) { Slice input = footerslice; if (input.size() < kFooterSize) { return Status::Corruption("Invalid Blob Record Footer: size"); } uint32_t f_crc = crc32c::Extend(0, input.data(), 8); f_crc = crc32c::Mask(f_crc); if (!GetFixed64(&input, &sn_)) { return Status::Corruption("Invalid Blob Record Footer: sn"); } if (!GetFixed32(&input, &footer_cksum_)) { return Status::Corruption("Invalid Blob Record Footer: cksum"); } if (f_crc != footer_cksum_) { return Status::Corruption("Record Checksum mismatch: footer_cksum"); } return Status::OK(); } } // namespace blob_db } // namespace rocksdb #endif // ROCKSDB_LITE