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    /**
71     * For code to locate the final entry this function is used to calculate
72     * a byte offset so that getFinalManifestEntry can return the final entry
73     * without any iteration.
74     *
75     * @param sme pointer to the final entry constructed in the serial manifest.
76     */
77    void calculateFinalEntryOffest(const SerialisedManifestEntry* sme) {
78        finalEntryOffset =
79                reinterpret_cast<const char*>(sme) - getManifestEntryBuffer();
80    }
81
82    /**
83     * Return a non const pointer to where the entries can be written
84     */
85    char* getManifestEntryBuffer() {
86        return getSeparatorPtr() + separatorLen;
87    }
88
89    /**
90     * Return a const pointer from where the entries can be read
91     */
92    const char* getManifestEntryBuffer() const {
93        return getSeparatorPtr() + separatorLen;
94    }
95
96    /**
97     * Return the final entry in the SerialManifest - this entry is always the
98     * entry which is being created or deleted. DCP event generation can use
99     * this method to obtain the data needed to send to replicas.
100     *
101     * @return The entry which is being added or removed.
102     */
103    const SerialisedManifestEntry* getFinalManifestEntry() const {
104        return reinterpret_cast<const SerialisedManifestEntry*>(
105                getManifestEntryBuffer() + finalEntryOffset);
106    }
107
108private:
109
110    friend Collections::VB::Manifest;
111    static SerialisedManifest* make(char* address,
112                                    const std::string& separator,
113                                    cb::char_buffer out) {
114        return new (address) SerialisedManifest(separator, out);
115    }
116
117    /**
118     * Construct a SerialisedManifest to have 0 items and the separator string.
119     *
120     * @param separator The separator for the manifest
121     * @param out The buffer into which this object is being constructed
122     * @throws length_error if the consruction would access outside of out
123     */
124    SerialisedManifest(const std::string& separator,
125                       cb::char_buffer out) {
126        if (!((out.data() + out.size()) >=
127              (reinterpret_cast<char*>(this) +
128               getObjectSize(separator.size())))) {
129            throw std::length_error(
130                    "SerialisedManifest::tryConstruction with separator.size " +
131                    std::to_string(separator.size()) +
132                    " exceeds the buffer of size " +
133                    std::to_string(out.size()));
134        }
135        itemCount = 0;
136        separatorLen = separator.size();
137        finalEntryOffset = 0;
138        std::copy_n(separator.data(), separator.size(), getSeparatorPtr());
139    }
140
141    const char* getSeparatorPtr() const {
142        return reinterpret_cast<const char*>(this + 1);
143    }
144
145    char* getSeparatorPtr() {
146        return reinterpret_cast<char*>(this + 1);
147    }
148
149    uint32_t itemCount;
150    uint32_t separatorLen;
151    uint32_t finalEntryOffset;
152};
153
154class SerialisedManifestEntry {
155public:
156    /**
157     * Object is not copyable or movable
158     */
159    SerialisedManifestEntry(const SerialisedManifestEntry& other) = delete;
160    SerialisedManifestEntry(SerialisedManifestEntry&& other) = delete;
161
162    static size_t getObjectSize(size_t collectionNameLen) {
163        return sizeof(SerialisedManifestEntry) + collectionNameLen;
164    }
165
166    size_t getObjectSize() const {
167        return getObjectSize(getCollectionNameLen());
168    }
169
170    uid_t getUid() const {
171        return uid;
172    }
173
174    void setUid(uid_t uid) {
175        this->uid = uid;
176    }
177
178    /**
179     * @return the entry's collection name as a const char buffer
180     */
181    cb::const_char_buffer getCollectionName() const {
182        return {getCollectionNamePtr(), size_t(collectionNameLen)};
183    }
184
185    /**
186     * @return the entry's UID as a const byte buffer
187     */
188    cb::const_byte_buffer getUidBuffer() const {
189        return {reinterpret_cast<const uint8_t*>(&uid), sizeof(uid)};
190    }
191
192    size_t getCollectionNameLen() const {
193        return collectionNameLen;
194    }
195
196    const char* nextEntry() const {
197        return reinterpret_cast<const char*>(this) + getObjectSize();
198    }
199
200    char* nextEntry() {
201        return const_cast<char*>(
202                static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
203    }
204
205    std::string toJson() const {
206        return toJson(startSeqno, endSeqno);
207    }
208
209    std::string toJson(SystemEvent se, int64_t correctedSeqno) const {
210        switch (se) {
211        case SystemEvent::BeginDeleteCollection:
212            return toJson(startSeqno, correctedSeqno);
213        case SystemEvent::CreateCollection:
214            return toJson(correctedSeqno, endSeqno);
215        case SystemEvent::DeleteCollectionHard:
216            return std::string(); // return nothing - collection gone
217        case SystemEvent::DeleteCollectionSoft:
218            return toJson(startSeqno, StoredValue::state_collection_open);
219        case SystemEvent::CollectionsSeparatorChanged:
220            return std::string(); // return nothing - no effect on entries
221        }
222
223        throw std::invalid_argument(
224                "SerialisedManifestEntry::toJson invalid event " +
225                to_string(se));
226        return {};
227    }
228
229private:
230    friend Collections::VB::Manifest;
231    static SerialisedManifestEntry* make(
232            char* address,
233            const Collections::VB::ManifestEntry& me,
234            cb::char_buffer out) {
235        return new (address) SerialisedManifestEntry(me, out);
236    }
237
238    static SerialisedManifestEntry* make(char* address,
239                                         Identifier identifier,
240                                         cb::char_buffer out) {
241        return new (address) SerialisedManifestEntry(identifier, out);
242    }
243
244    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
245                            cb::char_buffer out) {
246        tryConstruction(
247                out, me.getIdentifier(), me.getStartSeqno(), me.getEndSeqno());
248    }
249
250    SerialisedManifestEntry(Identifier identifier, cb::char_buffer out) {
251        tryConstruction(out, identifier, 0, StoredValue::state_collection_open);
252    }
253
254    /**
255     * The collection name is stored in memory allocated after this object.
256     *
257     * @return pointer to the collection name, located at the address after
258     *         'this'.
259     */
260    char* getCollectionNamePtr() {
261        return reinterpret_cast<char*>(this + 1);
262    }
263
264    /**
265     * The collection name is stored in memory allocated after this object.
266     *
267     * @return const pointer to the collection name, located at the address
268     *         after 'this'.
269     */
270    const char* getCollectionNamePtr() const {
271        return reinterpret_cast<const char*>(this + 1);
272    }
273
274    /**
275     * throw an exception if the construction of a serialised object overflows
276     * the memory allocation represented by out.
277     *
278     * @param out The buffer we are writing to
279     * @param identifier The Identifier of the collection to save in this entry
280     * @param startSeqno The startSeqno value to be used
281     * @param endSeqno The endSeqno value to be used
282     * @throws std::length_error if the function would write outside of out's
283     *         bounds.
284     */
285    void tryConstruction(cb::char_buffer out,
286                         Identifier identifier,
287                         int64_t startSeqno,
288                         int64_t endSeqno) {
289        if (!((out.data() + out.size()) >=
290              (reinterpret_cast<char*>(this) +
291               getObjectSize(identifier.getName().size())))) {
292            throw std::length_error(
293                    "SerialisedManifestEntry::tryConstruction with collection "
294                    "size " +
295                    std::to_string(identifier.getName().size()) +
296                    " exceeds the buffer of size " +
297                    std::to_string(out.size()));
298        }
299        this->uid = identifier.getUid();
300        this->startSeqno = startSeqno;
301        this->endSeqno = endSeqno;
302        this->collectionNameLen = identifier.getName().size();
303        std::memcpy(getCollectionNamePtr(),
304                    identifier.getName().data(),
305                    this->collectionNameLen);
306    }
307
308    /**
309     * Return a std::string JSON representation of this object with the callers
310     * chosen startSeqno/endSeqno (based on isDelete)
311     *
312     * @param _startSeqno The startSeqno value to be used
313     * @param _endSeqno The endSeqno value to be used
314     * @return A std::string JSON object for this object.
315     */
316    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
317        std::string json =
318                R"({"name":")" +
319                std::string(getCollectionNamePtr(), collectionNameLen) +
320                R"(","uid":")" + std::to_string(uid) + "\"," +
321                R"("startSeqno":")" + std::to_string(_startSeqno) + "\"," +
322                R"("endSeqno":")" + std::to_string(_endSeqno) + "\"}";
323        return json;
324    }
325
326    uid_t uid;
327    int32_t collectionNameLen;
328    int64_t startSeqno;
329    int64_t endSeqno;
330};
331
332static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
333              "SerialisedManifestEntry must satisfy StandardLayoutType");
334
335} // end namespace VB
336} // end namespace Collections
337