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
27enum 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
36std::string to_string(MutationLogType t);
37
38class 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 */
45class MutationLogEntryV1 {
46public:
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     */
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     */
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     */
104    size_t len() const {
105        return len(keylen);
106    }
107
108    /**
109     * This entry's key.
110     */
111    const std::string key() const {
112        return std::string(_key, keylen);
113    }
114
115    uint8_t getKeylen() const {
116        return keylen;
117    }
118
119    /**
120     * This entry's rowid.
121     */
122    uint64_t rowid() const {
123        return ntohll(_rowid);
124    }
125
126    /**
127     * This entry's vbucket.
128     */
129    uint16_t vbucket() const {
130        return ntohs(_vbucket);
131    }
132
133    /**
134     * The type of this log entry.
135     */
136    MutationLogType type() const {
137        return _type;
138    }
139
140protected:
141    friend MutationLogEntryV2;
142
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
175std::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 */
182class MutationLogEntryV2 {
183public:
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     */
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     */
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
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     */
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     */
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     */
280    size_t len() const {
281        return len(_key.size());
282    }
283
284    /**
285     * This entry's key.
286     */
287    const SerialisedDocKey& key() const {
288        return _key;
289    }
290
291    /**
292     * This entry's vbucket.
293     */
294    uint16_t vbucket() const {
295        return ntohs(_vbucket);
296    }
297
298    /**
299     * The type of this log entry.
300     */
301    MutationLogType type() const {
302        return _type;
303    }
304
305private:
306    friend std::ostream& operator<<(std::ostream& out,
307                                    const MutationLogEntryV2& e);
308
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
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
336using MutationLogEntry = MutationLogEntryV2;
337
338std::ostream& operator<<(std::ostream& out, const MutationLogEntryV2& mle);
339