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    cb::const_char_buffer getCollectionName() const {
188        return {getCollectionNamePtr(), size_t(collectionNameLen)};
189    }
190
191    size_t getCollectionNameLen() const {
192        return collectionNameLen;
193    }
194
195    const char* nextEntry() const {
196        return reinterpret_cast<const char*>(this) + getObjectSize();
197    }
198
199    char* nextEntry() {
200        return const_cast<char*>(
201                static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
202    }
203
204    std::string toJson() const {
205        return toJson(startSeqno, endSeqno);
206    }
207
208    std::string toJson(SystemEvent se, int64_t correctedSeqno) const {
209        switch (se) {
210        case SystemEvent::BeginDeleteCollection:
211            return toJson(startSeqno, correctedSeqno);
212        case SystemEvent::CreateCollection:
213            return toJson(correctedSeqno, endSeqno);
214        case SystemEvent::DeleteCollectionHard:
215            return std::string(); // return nothing - collection gone
216        case SystemEvent::DeleteCollectionSoft:
217            return toJson(startSeqno, StoredValue::state_collection_open);
218        case SystemEvent::CollectionsSeparatorChanged:
219            return std::string(); // return nothing - no effect on entries
220        }
221
222        throw std::invalid_argument(
223                "SerialisedManifestEntry::toJson invalid event " +
224                to_string(se));
225        return {};
226    }
227
228private:
229    friend Collections::VB::Manifest;
230    static SerialisedManifestEntry* make(
231            char* address,
232            const Collections::VB::ManifestEntry& me,
233            cb::char_buffer out) {
234        return new (address) SerialisedManifestEntry(me, out);
235    }
236
237    static SerialisedManifestEntry* make(char* address,
238                                         int32_t revision,
239                                         cb::const_char_buffer collection,
240                                         cb::char_buffer out) {
241        return new (address) SerialisedManifestEntry(revision, collection, out);
242    }
243
244    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
245                            cb::char_buffer out) {
246        tryConstruction(out,
247                        me.getRevision(),
248                        me.getStartSeqno(),
249                        me.getEndSeqno(),
250                        me.getCollectionName());
251    }
252
253    SerialisedManifestEntry(int revision,
254                            cb::const_char_buffer collection,
255                            cb::char_buffer out) {
256        tryConstruction(out,
257                        revision,
258                        0,
259                        StoredValue::state_collection_open,
260                        collection);
261    }
262
263    /**
264     * The collection name is stored in memory allocated after this object.
265     *
266     * @return pointer to the collection name, located at the address after
267     *         'this'.
268     */
269    char* getCollectionNamePtr() {
270        return reinterpret_cast<char*>(this + 1);
271    }
272
273    /**
274     * The collection name is stored in memory allocated after this object.
275     *
276     * @return const pointer to the collection name, located at the address
277     *         after 'this'.
278     */
279    const char* getCollectionNamePtr() const {
280        return reinterpret_cast<const char*>(this + 1);
281    }
282
283    /**
284     * throw an exception if the construction of a serialised object overflows
285     * the memory allocation represented by out.
286     *
287     * @param out The buffer we are writing to
288     * @param entryData The struct to update (which should be enclosed by out)
289     * @param revision Collections::Manifest revision that got us here
290     * @param startSeqno The startSeqno value to be used
291     * @param endSeqno The endSeqno value to be used
292     * @param collection The name of the collection to copy-in
293     * @throws std::length_error if the function would write outside of out's
294     *         bounds.
295     */
296    void tryConstruction(cb::char_buffer out,
297                         uint32_t revision,
298                         int64_t startSeqno,
299                         int64_t endSeqno,
300                         cb::const_char_buffer collection) {
301        if (!((out.data() + out.size()) >=
302              (reinterpret_cast<char*>(this) +
303               getObjectSize(collection.size())))) {
304            throw std::length_error("SerialisedManifestEntry::tryConstruction with collection size " +
305                                    std::to_string(collection.size()) +
306                                    " exceeds the buffer of size " +
307                                    std::to_string(out.size()));
308        }
309        this->revision = revision;
310        this->startSeqno = startSeqno;
311        this->endSeqno = endSeqno;
312        this->collectionNameLen = collection.size();
313        std::memcpy(getCollectionNamePtr(),
314                    collection.data(),
315                    this->collectionNameLen);
316    }
317
318    /**
319     * Return a std::string JSON representation of this object with the callers
320     * chosen startSeqno/endSeqno (based on isDelete)
321     *
322     * @param _startSeqno The startSeqno value to be used
323     * @param _endSeqno The endSeqno value to be used
324     * @return A std::string JSON object for this object.
325     */
326    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
327        std::string json =
328                R"({"name":")" +
329                std::string(getCollectionNamePtr(), collectionNameLen) +
330                R"(","revision":")" + std::to_string(revision) + "\"," +
331                R"("startSeqno":")" + std::to_string(_startSeqno) + "\"," +
332                R"("endSeqno":")" + std::to_string(_endSeqno) + "\"}";
333        return json;
334    }
335
336    uint32_t revision;
337    int32_t collectionNameLen;
338    int64_t startSeqno;
339    int64_t endSeqno;
340};
341
342static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
343              "SerialisedManifestEntry must satisfy StandardLayoutType");
344
345} // end namespace VB
346} // end namespace Collections
347