2017-05-10 23:54:35 +02:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-16 01:03:42 +02:00
|
|
|
// 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).
|
2017-05-10 23:54:35 +02:00
|
|
|
//
|
|
|
|
#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) {}
|
|
|
|
|
2017-05-23 19:30:04 +02:00
|
|
|
Status BlobLogFooter::DecodeFrom(const Slice& input) {
|
|
|
|
Slice slice(input);
|
2017-05-10 23:54:35 +02:00
|
|
|
uint32_t val;
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed32(&slice, &val)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Footer: flags");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_ttl = false;
|
|
|
|
bool has_ts = false;
|
|
|
|
val >>= 8;
|
|
|
|
RecordSubType st = static_cast<RecordSubType>(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");
|
|
|
|
}
|
|
|
|
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed64(&slice, &blob_count_)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Footer: blob_count");
|
|
|
|
}
|
|
|
|
|
|
|
|
ttlrange_t temp_ttl;
|
2017-08-04 02:46:00 +02:00
|
|
|
if (!GetFixed64(&slice, &temp_ttl.first) ||
|
|
|
|
!GetFixed64(&slice, &temp_ttl.second)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Footer: ttl_range");
|
|
|
|
}
|
|
|
|
if (has_ttl) {
|
|
|
|
ttl_range_.reset(new ttlrange_t(temp_ttl));
|
|
|
|
}
|
|
|
|
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed64(&slice, &sn_range_.first) ||
|
|
|
|
!GetFixed64(&slice, &sn_range_.second)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Footer: sn_range");
|
|
|
|
}
|
|
|
|
|
|
|
|
tsrange_t temp_ts;
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed64(&slice, &temp_ts.first) ||
|
|
|
|
!GetFixed64(&slice, &temp_ts.second)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Footer: ts_range");
|
|
|
|
}
|
2017-05-23 19:30:04 +02:00
|
|
|
if (has_ts) {
|
|
|
|
ts_range_.reset(new tsrange_t(temp_ts));
|
|
|
|
}
|
2017-05-10 23:54:35 +02:00
|
|
|
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed32(&slice, &magic_number_) || magic_number_ != kMagicNumber) {
|
2017-05-10 23:54:35 +02:00
|
|
|
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<uint32_t>(rt) | (static_cast<uint32_t>(st) << 8);
|
|
|
|
PutFixed32(dst, val);
|
|
|
|
|
|
|
|
PutFixed64(dst, blob_count_);
|
|
|
|
bool has_ttl = HasTTL();
|
|
|
|
bool has_ts = HasTimestamp();
|
|
|
|
|
|
|
|
if (has_ttl) {
|
2017-08-04 02:46:00 +02:00
|
|
|
PutFixed64(dst, ttl_range_.get()->first);
|
|
|
|
PutFixed64(dst, ttl_range_.get()->second);
|
2017-05-10 23:54:35 +02:00
|
|
|
} else {
|
2017-08-04 02:46:00 +02:00
|
|
|
PutFixed64(dst, 0);
|
|
|
|
PutFixed64(dst, 0);
|
2017-05-10 23:54:35 +02:00
|
|
|
}
|
|
|
|
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<uint32_t>(st) | (static_cast<uint32_t>(compression_) << 8);
|
|
|
|
PutFixed32(dst, val);
|
|
|
|
|
|
|
|
if (has_ttl) {
|
2017-08-04 02:46:00 +02:00
|
|
|
PutFixed64(dst, ttl_guess_.get()->first);
|
|
|
|
PutFixed64(dst, ttl_guess_.get()->second);
|
2017-05-10 23:54:35 +02:00
|
|
|
} else {
|
2017-08-04 02:46:00 +02:00
|
|
|
PutFixed64(dst, 0);
|
|
|
|
PutFixed64(dst, 0);
|
2017-05-10 23:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (has_ts) {
|
|
|
|
PutFixed64(dst, ts_guess_.get()->first);
|
|
|
|
PutFixed64(dst, ts_guess_.get()->second);
|
|
|
|
} else {
|
|
|
|
PutFixed64(dst, 0);
|
|
|
|
PutFixed64(dst, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-23 19:30:04 +02:00
|
|
|
Status BlobLogHeader::DecodeFrom(const Slice& input) {
|
|
|
|
Slice slice(input);
|
|
|
|
if (!GetFixed32(&slice, &magic_number_) || magic_number_ != kMagicNumber) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Log Header: magic");
|
|
|
|
}
|
|
|
|
|
|
|
|
// as of today, we only support 1 version
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed32(&slice, &version_) || version_ != kVersion1) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Log Header: version");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t val;
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed32(&slice, &val)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Log Header: subtype");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_ttl = false;
|
|
|
|
bool has_ts = false;
|
|
|
|
RecordSubType st = static_cast<RecordSubType>(val & 0xff);
|
|
|
|
compression_ = static_cast<CompressionType>((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;
|
2017-08-04 02:46:00 +02:00
|
|
|
if (!GetFixed64(&slice, &temp_ttl.first) ||
|
|
|
|
!GetFixed64(&slice, &temp_ttl.second)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
return Status::Corruption("Invalid Blob Log Header: ttl");
|
|
|
|
}
|
2017-08-04 02:46:00 +02:00
|
|
|
if (has_ttl) {
|
|
|
|
set_ttl_guess(temp_ttl);
|
|
|
|
}
|
2017-05-10 23:54:35 +02:00
|
|
|
|
|
|
|
tsrange_t temp_ts;
|
2017-05-23 19:30:04 +02:00
|
|
|
if (!GetFixed64(&slice, &temp_ts.first) ||
|
|
|
|
!GetFixed64(&slice, &temp_ts.second)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
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),
|
|
|
|
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;
|
|
|
|
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");
|
|
|
|
}
|
2017-08-04 02:46:00 +02:00
|
|
|
if (!GetFixed64(&input, &ttl_val_)) {
|
2017-05-10 23:54:35 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace blob_db
|
|
|
|
} // namespace rocksdb
|
|
|
|
#endif // ROCKSDB_LITE
|