1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 #pragma once
6 
7 #include <sstream>
8 #include <string>
9 
10 #include "rocksdb/compression_type.h"
11 #include "util/coding.h"
12 #include "util/compression.h"
13 #include "util/string_util.h"
14 
15 namespace ROCKSDB_NAMESPACE {
16 
17 // BlobIndex is a pointer to the blob and metadata of the blob. The index is
18 // stored in base DB as ValueType::kTypeBlobIndex.
19 // There are three types of blob index:
20 //
21 //    kInlinedTTL:
22 //      +------+------------+---------------+
23 //      | type | expiration | value         |
24 //      +------+------------+---------------+
25 //      | char | varint64   | variable size |
26 //      +------+------------+---------------+
27 //
28 //    kBlob:
29 //      +------+-------------+----------+----------+-------------+
30 //      | type | file number | offset   | size     | compression |
31 //      +------+-------------+----------+----------+-------------+
32 //      | char | varint64    | varint64 | varint64 | char        |
33 //      +------+-------------+----------+----------+-------------+
34 //
35 //    kBlobTTL:
36 //      +------+------------+-------------+----------+----------+-------------+
37 //      | type | expiration | file number | offset   | size     | compression |
38 //      +------+------------+-------------+----------+----------+-------------+
39 //      | char | varint64   | varint64    | varint64 | varint64 | char        |
40 //      +------+------------+-------------+----------+----------+-------------+
41 //
42 // There isn't a kInlined (without TTL) type since we can store it as a plain
43 // value (i.e. ValueType::kTypeValue).
44 class BlobIndex {
45  public:
46   enum class Type : unsigned char {
47     kInlinedTTL = 0,
48     kBlob = 1,
49     kBlobTTL = 2,
50     kUnknown = 3,
51   };
52 
BlobIndex()53   BlobIndex() : type_(Type::kUnknown) {}
54 
IsInlined()55   bool IsInlined() const { return type_ == Type::kInlinedTTL; }
56 
HasTTL()57   bool HasTTL() const {
58     return type_ == Type::kInlinedTTL || type_ == Type::kBlobTTL;
59   }
60 
expiration()61   uint64_t expiration() const {
62     assert(HasTTL());
63     return expiration_;
64   }
65 
value()66   const Slice& value() const {
67     assert(IsInlined());
68     return value_;
69   }
70 
file_number()71   uint64_t file_number() const {
72     assert(!IsInlined());
73     return file_number_;
74   }
75 
offset()76   uint64_t offset() const {
77     assert(!IsInlined());
78     return offset_;
79   }
80 
size()81   uint64_t size() const {
82     assert(!IsInlined());
83     return size_;
84   }
85 
compression()86   CompressionType compression() const {
87     assert(!IsInlined());
88     return compression_;
89   }
90 
DecodeFrom(Slice slice)91   Status DecodeFrom(Slice slice) {
92     static const std::string kErrorMessage = "Error while decoding blob index";
93     assert(slice.size() > 0);
94     type_ = static_cast<Type>(*slice.data());
95     if (type_ >= Type::kUnknown) {
96       return Status::Corruption(
97           kErrorMessage,
98           "Unknown blob index type: " + ToString(static_cast<char>(type_)));
99     }
100     slice = Slice(slice.data() + 1, slice.size() - 1);
101     if (HasTTL()) {
102       if (!GetVarint64(&slice, &expiration_)) {
103         return Status::Corruption(kErrorMessage, "Corrupted expiration");
104       }
105     }
106     if (IsInlined()) {
107       value_ = slice;
108     } else {
109       if (GetVarint64(&slice, &file_number_) && GetVarint64(&slice, &offset_) &&
110           GetVarint64(&slice, &size_) && slice.size() == 1) {
111         compression_ = static_cast<CompressionType>(*slice.data());
112       } else {
113         return Status::Corruption(kErrorMessage, "Corrupted blob offset");
114       }
115     }
116     return Status::OK();
117   }
118 
DebugString(bool output_hex)119   std::string DebugString(bool output_hex) const {
120     std::ostringstream oss;
121 
122     if (IsInlined()) {
123       oss << "[inlined blob] value:" << value_.ToString(output_hex);
124     } else {
125       oss << "[blob ref] file:" << file_number_ << " offset:" << offset_
126           << " size:" << size_
127           << " compression: " << CompressionTypeToString(compression_);
128     }
129 
130     if (HasTTL()) {
131       oss << " exp:" << expiration_;
132     }
133 
134     return oss.str();
135   }
136 
EncodeInlinedTTL(std::string * dst,uint64_t expiration,const Slice & value)137   static void EncodeInlinedTTL(std::string* dst, uint64_t expiration,
138                                const Slice& value) {
139     assert(dst != nullptr);
140     dst->clear();
141     dst->reserve(1 + kMaxVarint64Length + value.size());
142     dst->push_back(static_cast<char>(Type::kInlinedTTL));
143     PutVarint64(dst, expiration);
144     dst->append(value.data(), value.size());
145   }
146 
EncodeBlob(std::string * dst,uint64_t file_number,uint64_t offset,uint64_t size,CompressionType compression)147   static void EncodeBlob(std::string* dst, uint64_t file_number,
148                          uint64_t offset, uint64_t size,
149                          CompressionType compression) {
150     assert(dst != nullptr);
151     dst->clear();
152     dst->reserve(kMaxVarint64Length * 3 + 2);
153     dst->push_back(static_cast<char>(Type::kBlob));
154     PutVarint64(dst, file_number);
155     PutVarint64(dst, offset);
156     PutVarint64(dst, size);
157     dst->push_back(static_cast<char>(compression));
158   }
159 
EncodeBlobTTL(std::string * dst,uint64_t expiration,uint64_t file_number,uint64_t offset,uint64_t size,CompressionType compression)160   static void EncodeBlobTTL(std::string* dst, uint64_t expiration,
161                             uint64_t file_number, uint64_t offset,
162                             uint64_t size, CompressionType compression) {
163     assert(dst != nullptr);
164     dst->clear();
165     dst->reserve(kMaxVarint64Length * 4 + 2);
166     dst->push_back(static_cast<char>(Type::kBlobTTL));
167     PutVarint64(dst, expiration);
168     PutVarint64(dst, file_number);
169     PutVarint64(dst, offset);
170     PutVarint64(dst, size);
171     dst->push_back(static_cast<char>(compression));
172   }
173 
174  private:
175   Type type_ = Type::kUnknown;
176   uint64_t expiration_ = 0;
177   Slice value_;
178   uint64_t file_number_ = 0;
179   uint64_t offset_ = 0;
180   uint64_t size_ = 0;
181   CompressionType compression_ = kNoCompression;
182 };
183 
184 }  // namespace ROCKSDB_NAMESPACE
185