1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include "config.h"
21 
22 #include "storeddockey.h"
23 #include "utility.h"
24 
25 #include <type_traits>
26 
27 enum class MutationLogType : uint8_t {
28     New = 0,
29     /* removed: ML_DEL = 1 */
30     /* removed: ML_DEL_ALL = 2 */
31     Commit1 = 3,
32     Commit2 = 4,
33     NumberOfTypes
34 };
35 
36 std::string to_string(MutationLogType t);
37 
38 class MutationLogEntryV2;
39 
40 /**
41  * An entry in the MutationLog.
42  * This is the V1 layout which pre-dates the addition of document namespaces and
43  * is only defined to permit upgrading to V2.
44  */
45 class MutationLogEntryV1 {
46 public:
47     static const uint8_t MagicMarker = 0x45;
48 
49     /**
50      * Initialize a new entry using the contents of the given buffer.
51      *
52      * @param buf a chunk of memory thought to contain a valid
53      *        MutationLogEntryV1
54      * @param buflen the length of said buf
55      */
newEntry( std::vector<uint8_t>::const_iterator itr, size_t buflen)56     static const MutationLogEntryV1* newEntry(
57             std::vector<uint8_t>::const_iterator itr, size_t buflen) {
58         if (buflen < len(0)) {
59             throw std::invalid_argument(
60                     "MutationLogEntryV1::newEntry: buflen "
61                     "(which is " +
62                     std::to_string(buflen) +
63                     ") is less than minimum required (which is " +
64                     std::to_string(len(0)) + ")");
65         }
66 
67         const auto* me = reinterpret_cast<const MutationLogEntryV1*>(&(*itr));
68 
69         if (me->magic != MagicMarker) {
70             throw std::invalid_argument(
71                     "MutationLogEntryV1::newEntry: "
72                     "magic (which is " +
73                     std::to_string(me->magic) + ") is not equal to " +
74                     std::to_string(MagicMarker));
75         }
76         if (me->len() > buflen) {
77             throw std::invalid_argument(
78                     "MutationLogEntryV1::newEntry: "
79                     "entry length (which is " +
80                     std::to_string(me->len()) +
81                     ") is greater than available buflen (which is " +
82                     std::to_string(buflen) + ")");
83         }
84         return me;
85     }
86 
87     // Statically buffered.  There is no delete.
88     void operator delete(void*) = delete;
89 
90     /**
91      * The size of a MutationLogEntryV1, in bytes, containing a key of
92      * the specified length.
93      */
len(size_t klen)94     static size_t len(size_t klen) {
95         // 13 == the exact empty record size as will be packed into
96         // the layout
97         return 13 + klen;
98     }
99 
100     /**
101      * The number of bytes of the serialized form of this
102      * MutationLogEntryV1.
103      */
len() const104     size_t len() const {
105         return len(keylen);
106     }
107 
108     /**
109      * This entry's key.
110      */
key() const111     const std::string key() const {
112         return std::string(_key, keylen);
113     }
114 
getKeylen() const115     uint8_t getKeylen() const {
116         return keylen;
117     }
118 
119     /**
120      * This entry's rowid.
121      */
rowid() const122     uint64_t rowid() const {
123         return ntohll(_rowid);
124     }
125 
126     /**
127      * This entry's vbucket.
128      */
vbucket() const129     uint16_t vbucket() const {
130         return ntohs(_vbucket);
131     }
132 
133     /**
134      * The type of this log entry.
135      */
type() const136     MutationLogType type() const {
137         return _type;
138     }
139 
140 protected:
141     friend MutationLogEntryV2;
142 
MutationLogEntryV1(uint64_t r, MutationLogType t, uint16_t vb, const std::string& k)143     MutationLogEntryV1(uint64_t r,
144                        MutationLogType t,
145                        uint16_t vb,
146                        const std::string& k)
147         : _rowid(htonll(r)),
148           _vbucket(htons(vb)),
149           magic(MagicMarker),
150           _type(t),
151           keylen(static_cast<uint8_t>(k.length())) {
152         if (k.length() > std::numeric_limits<uint8_t>::max()) {
153             throw std::invalid_argument(
154                     "MutationLogEntryV1(): key length "
155                     "(which is " +
156                     std::to_string(k.length()) + ") is greater than " +
157                     std::to_string(std::numeric_limits<uint8_t>::max()));
158         }
159         memcpy(_key, k.data(), k.length());
160     }
161 
162     friend std::ostream& operator<<(std::ostream& out,
163                                     const MutationLogEntryV1& e);
164 
165     const uint64_t _rowid;
166     const uint16_t _vbucket;
167     const uint8_t magic;
168     const MutationLogType _type;
169     const uint8_t keylen;
170     char _key[1];
171 
172     DISALLOW_COPY_AND_ASSIGN(MutationLogEntryV1);
173 };
174 
175 std::ostream& operator<<(std::ostream& out, const MutationLogEntryV1& mle);
176 
177 /**
178  * An entry in the MutationLog.
179  * This is the V2 layout which stores document namespaces and removes the rowid
180  * (sequence number) as it was unused.
181  */
182 class MutationLogEntryV2 {
183 public:
184     static const uint8_t MagicMarker = 0x46;
185 
186     /**
187      * Construct a V2 from V1, this places the key into the default collection
188      * No constructor delegation, copy the values raw (so no byte swaps occur)
189      * key is initialised into the default collection
190      */
MutationLogEntryV2(const MutationLogEntryV1& mleV1)191     MutationLogEntryV2(const MutationLogEntryV1& mleV1)
192         : _vbucket(mleV1._vbucket),
193           magic(MagicMarker),
194           _type(mleV1._type),
195           pad(0),
196           _key({(const uint8_t*)mleV1._key,
197                 mleV1.keylen,
198                 DocNamespace::DefaultCollection}) {
199     }
200 
201     /**
202      * Initialize a new entry inside the given buffer.
203      *
204      * @param r the rowid
205      * @param t the type of log entry
206      * @param vb the vbucket
207      * @param k the key
208      */
newEntry(uint8_t* buf, MutationLogType t, uint16_t vb, const DocKey& k)209     static MutationLogEntryV2* newEntry(uint8_t* buf,
210                                         MutationLogType t,
211                                         uint16_t vb,
212                                         const DocKey& k) {
213         return new (buf) MutationLogEntryV2(t, vb, k);
214     }
215 
newEntry(uint8_t* buf, MutationLogType t, uint16_t vb)216     static MutationLogEntryV2* newEntry(uint8_t* buf,
217                                         MutationLogType t,
218                                         uint16_t vb) {
219         if (MutationLogType::Commit1 != t && MutationLogType::Commit2 != t) {
220             throw std::invalid_argument(
221                     "MutationLogEntryV2::newEntry: invalid type");
222         }
223         return new (buf) MutationLogEntryV2(t, vb);
224     }
225 
226     /**
227      * Initialize a new entry using the contents of the given buffer.
228      *
229      * @param buf a chunk of memory thought to contain a valid
230      *        MutationLogEntryV2
231      * @param buflen the length of said buf
232      */
newEntry( std::vector<uint8_t>::const_iterator itr, size_t buflen)233     static const MutationLogEntryV2* newEntry(
234             std::vector<uint8_t>::const_iterator itr, size_t buflen) {
235         if (buflen < len(0)) {
236             throw std::invalid_argument(
237                     "MutationLogEntryV2::newEntry: buflen "
238                     "(which is " +
239                     std::to_string(buflen) +
240                     ") is less than minimum required (which is " +
241                     std::to_string(len(0)) + ")");
242         }
243 
244         const auto* me = reinterpret_cast<const MutationLogEntryV2*>(&(*itr));
245 
246         if (me->magic != MagicMarker) {
247             throw std::invalid_argument(
248                     "MutationLogEntryV2::newEntry: "
249                     "magic (which is " +
250                     std::to_string(me->magic) + ") is not equal to " +
251                     std::to_string(MagicMarker));
252         }
253         if (me->len() > buflen) {
254             throw std::invalid_argument(
255                     "MutationLogEntryV2::newEntry: "
256                     "entry length (which is " +
257                     std::to_string(me->len()) +
258                     ") is greater than available buflen (which is " +
259                     std::to_string(buflen) + ")");
260         }
261         return me;
262     }
263 
264     // Statically buffered.  There is no delete.
265     void operator delete(void*) = delete;
266 
267     /**
268      * The size of a MutationLogEntryV2, in bytes, containing a key of
269      * the specified length.
270      */
len(size_t klen)271     static size_t len(size_t klen) {
272         // the exact empty record size as will be packed into the layout
273         return sizeof(MutationLogEntryV2) + (klen - 1);
274     }
275 
276     /**
277      * The number of bytes of the serialized form of this
278      * MutationLogEntryV2.
279      */
len() const280     size_t len() const {
281         return len(_key.size());
282     }
283 
284     /**
285      * This entry's key.
286      */
key() const287     const SerialisedDocKey& key() const {
288         return _key;
289     }
290 
291     /**
292      * This entry's vbucket.
293      */
vbucket() const294     uint16_t vbucket() const {
295         return ntohs(_vbucket);
296     }
297 
298     /**
299      * The type of this log entry.
300      */
type() const301     MutationLogType type() const {
302         return _type;
303     }
304 
305 private:
306     friend std::ostream& operator<<(std::ostream& out,
307                                     const MutationLogEntryV2& e);
308 
MutationLogEntryV2(MutationLogType t, uint16_t vb, const DocKey& k)309     MutationLogEntryV2(MutationLogType t, uint16_t vb, const DocKey& k)
310         : _vbucket(htons(vb)), magic(MagicMarker), _type(t), pad(0), _key(k) {
311         (void)pad;
312         // Assert that _key is the final member
313         static_assert(
314                 offsetof(MutationLogEntryV2, _key) ==
315                         (sizeof(MutationLogEntryV2) - sizeof(SerialisedDocKey)),
316                 "_key must be the final member of MutationLogEntryV2");
317     }
318 
MutationLogEntryV2(MutationLogType t, uint16_t vb)319     MutationLogEntryV2(MutationLogType t, uint16_t vb)
320         : MutationLogEntryV2(
321                   t, vb, {nullptr, 0, DocNamespace::DefaultCollection}) {
322     }
323 
324     const uint16_t _vbucket;
325     const uint8_t magic;
326     const MutationLogType _type;
327     const uint8_t pad; // explicit padding to ensure _key is the final member
328     const SerialisedDocKey _key;
329 
330     DISALLOW_COPY_AND_ASSIGN(MutationLogEntryV2);
331 
332     static_assert(sizeof(MutationLogType) == sizeof(uint8_t),
333                   "_type must be a uint8_t");
334 };
335 
336 using MutationLogEntry = MutationLogEntryV2;
337 
338 std::ostream& operator<<(std::ostream& out, const MutationLogEntryV2& mle);
339