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#include "collections/vbucket_manifest_entry.h"
19#include "config.h"
20
21#include <functional>
22
23#pragma once
24
25namespace Collections {
26namespace VB {
27
28class SerialisedManifestEntry;
29
30/**
31 * A VB::Manifest is serialised into an Item when it is updated. The
32 * Item carries a copy of the VB::Manifest down to the flusher so that
33 * a JSON version can be written to persistent storage.
34 *
35 * The serialised data is created by VB::Manifest and is a copy of the
36 * manifest *before* the update is applied. The update being applied to the
37 * manifest is serialised as the final entry. This is done because the seqno
38 * of the final entry needs correcting during the creation of the JSON. This
39 * occurs because the Item is created but cannot be allocated a seqno until it
40 * is queued, and it is not safe to mutate the Item's value after it is queued.
41 *
42 */
43
44/**
45 * SerialisedManifest just stores the entry count and can return a pointer
46 * to the buffer where entries can be written.
47 */
48class SerialisedManifest {
49public:
50    static size_t getObjectSize(uint32_t separatorLen) {
51        return sizeof(SerialisedManifest) + separatorLen;
52    }
53
54    void setEntryCount(uint32_t items) {
55        itemCount = items;
56    }
57
58    uint32_t getEntryCount() const {
59        return itemCount;
60    }
61
62    std::string getSeparator() const {
63        return {getSeparatorPtr(), separatorLen};
64    }
65
66    cb::const_char_buffer getSeparatorBuffer() const {
67        return {getSeparatorPtr(), separatorLen};
68    }
69
70    cb::const_byte_buffer getRevisionBuffer() const {
71        return {reinterpret_cast<const uint8_t*>(&revision), sizeof(revision)};
72    }
73
74    /**
75     * For code to locate the final entry this function is used to calculate
76     * a byte offset so that getFinalManifestEntry can return the final entry
77     * without any iteration.
78     *
79     * @param sme pointer to the final entry constructed in the serial manifest.
80     */
81    void calculateFinalEntryOffest(const SerialisedManifestEntry* sme) {
82        finalEntryOffset =
83                reinterpret_cast<const char*>(sme) - getManifestEntryBuffer();
84    }
85
86    /**
87     * Return a non const pointer to where the entries can be written
88     */
89    char* getManifestEntryBuffer() {
90        return getSeparatorPtr() + separatorLen;
91    }
92
93    /**
94     * Return a const pointer from where the entries can be read
95     */
96    const char* getManifestEntryBuffer() const {
97        return getSeparatorPtr() + separatorLen;
98    }
99
100    /**
101     * Return the final entry in the SerialManifest - this entry is always the
102     * entry which is being created or deleted. DCP event generation can use
103     * this method to obtain the data needed to send to replicas.
104     *
105     * @return The entry which is being added or removed.
106     */
107    const SerialisedManifestEntry* getFinalManifestEntry() const {
108        return reinterpret_cast<const SerialisedManifestEntry*>(
109                getManifestEntryBuffer() + finalEntryOffset);
110    }
111
112private:
113
114    friend Collections::VB::Manifest;
115    static SerialisedManifest* make(char* address,
116                                    const std::string& separator,
117                                    uint32_t revision,
118                                    cb::char_buffer out) {
119        return new (address) SerialisedManifest(separator, revision, out);
120    }
121
122    /**
123     * Construct a SerialisedManifest to have 0 items and the separator string.
124     *
125     * @param separator The separator for the manifest
126     * @param revision The revision of the manifest which triggered the
127     *        creation of this serialised version.
128     * @param out The buffer into which this object is being constructed
129     * @throws length_error if the consruction would access outside of out
130     */
131    SerialisedManifest(const std::string& separator,
132                       uint32_t revision,
133                       cb::char_buffer out) {
134        if (!((out.data() + out.size()) >=
135              (reinterpret_cast<char*>(this) +
136               getObjectSize(separator.size())))) {
137            throw std::length_error("SerialisedManifest::tryConstruction with separator size " +
138                                    std::to_string(separator.size()) +
139                                    " exceeds the buffer of size " +
140                                    std::to_string(out.size()));
141        }
142        itemCount = 0;
143        separatorLen = separator.size();
144        this->revision = revision;
145        finalEntryOffset = 0;
146        std::copy_n(separator.data(), separator.size(), getSeparatorPtr());
147    }
148
149    const char* getSeparatorPtr() const {
150        return reinterpret_cast<const char*>(this + 1);
151    }
152
153    char* getSeparatorPtr() {
154        return reinterpret_cast<char*>(this + 1);
155    }
156
157    uint32_t itemCount;
158    uint32_t separatorLen;
159    uint32_t revision;
160    uint32_t finalEntryOffset;
161};
162
163class SerialisedManifestEntry {
164public:
165    /**
166     * Object is not copyable or movable
167     */
168    SerialisedManifestEntry(const SerialisedManifestEntry& other) = delete;
169    SerialisedManifestEntry(SerialisedManifestEntry&& other) = delete;
170
171    static size_t getObjectSize(size_t collectionNameLen) {
172        return sizeof(SerialisedManifestEntry) + collectionNameLen;
173    }
174
175    size_t getObjectSize() const {
176        return getObjectSize(getCollectionNameLen());
177    }
178
179    uint32_t getRevision() const {
180        return revision;
181    }
182
183    void setRevision(uint32_t rev) {
184        revision = rev;
185    }
186
187    /**
188     * @return the entry's collection name as a const byte buffer
189     */
190    cb::const_char_buffer getCollectionName() const {
191        return {getCollectionNamePtr(), size_t(collectionNameLen)};
192    }
193
194    size_t getCollectionNameLen() const {
195        return collectionNameLen;
196    }
197
198    const char* nextEntry() const {
199        return reinterpret_cast<const char*>(this) + getObjectSize();
200    }
201
202    char* nextEntry() {
203        return const_cast<char*>(
204                static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
205    }
206
207    std::string toJson() const {
208        return toJson(startSeqno, endSeqno);
209    }
210
211    std::string toJson(SystemEvent se, int64_t correctedSeqno) const {
212        switch (se) {
213        case SystemEvent::BeginDeleteCollection:
214            return toJson(startSeqno, correctedSeqno);
215        case SystemEvent::CreateCollection:
216            return toJson(correctedSeqno, endSeqno);
217        case SystemEvent::DeleteCollectionHard:
218            return std::string(); // return nothing - collection gone
219        case SystemEvent::DeleteCollectionSoft:
220            return toJson(startSeqno, StoredValue::state_collection_open);
221        case SystemEvent::CollectionsSeparatorChanged:
222            return std::string(); // return nothing - no effect on entries
223        }
224
225        throw std::invalid_argument(
226                "SerialisedManifestEntry::toJson invalid event " +
227                to_string(se));
228        return {};
229    }
230
231private:
232    friend Collections::VB::Manifest;
233    static SerialisedManifestEntry* make(
234            char* address,
235            const Collections::VB::ManifestEntry& me,
236            cb::char_buffer out) {
237        return new (address) SerialisedManifestEntry(me, out);
238    }
239
240    static SerialisedManifestEntry* make(char* address,
241                                         int32_t revision,
242                                         cb::const_char_buffer collection,
243                                         cb::char_buffer out) {
244        return new (address) SerialisedManifestEntry(revision, collection, out);
245    }
246
247    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
248                            cb::char_buffer out) {
249        tryConstruction(out,
250                        me.getRevision(),
251                        me.getStartSeqno(),
252                        me.getEndSeqno(),
253                        me.getCollectionName());
254    }
255
256    SerialisedManifestEntry(int revision,
257                            cb::const_char_buffer collection,
258                            cb::char_buffer out) {
259        tryConstruction(out,
260                        revision,
261                        0,
262                        StoredValue::state_collection_open,
263                        collection);
264    }
265
266    /**
267     * The collection name is stored in memory allocated after this object.
268     *
269     * @return pointer to the collection name, located at the address after
270     *         'this'.
271     */
272    char* getCollectionNamePtr() {
273        return reinterpret_cast<char*>(this + 1);
274    }
275
276    /**
277     * The collection name is stored in memory allocated after this object.
278     *
279     * @return const pointer to the collection name, located at the address
280     *         after 'this'.
281     */
282    const char* getCollectionNamePtr() const {
283        return reinterpret_cast<const char*>(this + 1);
284    }
285
286    /**
287     * throw an exception if the construction of a serialised object overflows
288     * the memory allocation represented by out.
289     *
290     * @param out The buffer we are writing to
291     * @param entryData The struct to update (which should be enclosed by out)
292     * @param revision Collections::Manifest revision that got us here
293     * @param startSeqno The startSeqno value to be used
294     * @param endSeqno The endSeqno value to be used
295     * @param collection The name of the collection to copy-in
296     * @throws std::length_error if the function would write outside of out's
297     *         bounds.
298     */
299    void tryConstruction(cb::char_buffer out,
300                         uint32_t revision,
301                         int64_t startSeqno,
302                         int64_t endSeqno,
303                         cb::const_char_buffer collection) {
304        if (!((out.data() + out.size()) >=
305              (reinterpret_cast<char*>(this) +
306               getObjectSize(collection.size())))) {
307            throw std::length_error("SerialisedManifestEntry::tryConstruction with collection size " +
308                                    std::to_string(collection.size()) +
309                                    " exceeds the buffer of size " +
310                                    std::to_string(out.size()));
311        }
312        this->revision = revision;
313        this->startSeqno = startSeqno;
314        this->endSeqno = endSeqno;
315        this->collectionNameLen = collection.size();
316        std::memcpy(getCollectionNamePtr(),
317                    collection.data(),
318                    this->collectionNameLen);
319    }
320
321    /**
322     * Return a std::string JSON representation of this object with the callers
323     * chosen startSeqno/endSeqno (based on isDelete)
324     *
325     * @param _startSeqno The startSeqno value to be used
326     * @param _endSeqno The endSeqno value to be used
327     * @return A std::string JSON object for this object.
328     */
329    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
330        std::string json =
331                R"({"name":")" +
332                std::string(getCollectionNamePtr(), collectionNameLen) +
333                R"(","revision":")" + std::to_string(revision) + "\"," +
334                R"("startSeqno":")" + std::to_string(_startSeqno) + "\"," +
335                R"("endSeqno":")" + std::to_string(_endSeqno) + "\"}";
336        return json;
337    }
338
339    uint32_t revision;
340    int32_t collectionNameLen;
341    int64_t startSeqno;
342    int64_t endSeqno;
343};
344
345static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
346              "SerialisedManifestEntry must satisfy StandardLayoutType");
347
348} // end namespace VB
349} // end namespace Collections
350