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 toJsonCreateOrDelete(bool isCollectionDelete,
210                                     int64_t correctedSeqno) const {
211        if (isCollectionDelete) {
212            return toJson(startSeqno, correctedSeqno);
213        }
214        return toJson(correctedSeqno, endSeqno);
215    }
216
217    std::string toJsonResetEnd() const {
218        return toJson(startSeqno, StoredValue::state_collection_open);
219    }
220
221private:
222    friend Collections::VB::Manifest;
223    static SerialisedManifestEntry* make(
224            char* address,
225            const Collections::VB::ManifestEntry& me,
226            cb::char_buffer out) {
227        return new (address) SerialisedManifestEntry(me, out);
228    }
229
230    static SerialisedManifestEntry* make(char* address,
231                                         Identifier identifier,
232                                         cb::char_buffer out) {
233        return new (address) SerialisedManifestEntry(identifier, out);
234    }
235
236    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
237                            cb::char_buffer out) {
238        tryConstruction(
239                out, me.getIdentifier(), me.getStartSeqno(), me.getEndSeqno());
240    }
241
242    SerialisedManifestEntry(Identifier identifier, cb::char_buffer out) {
243        tryConstruction(out, identifier, 0, StoredValue::state_collection_open);
244    }
245
246    /**
247     * The collection name is stored in memory allocated after this object.
248     *
249     * @return pointer to the collection name, located at the address after
250     *         'this'.
251     */
252    char* getCollectionNamePtr() {
253        return reinterpret_cast<char*>(this + 1);
254    }
255
256    /**
257     * The collection name is stored in memory allocated after this object.
258     *
259     * @return const pointer to the collection name, located at the address
260     *         after 'this'.
261     */
262    const char* getCollectionNamePtr() const {
263        return reinterpret_cast<const char*>(this + 1);
264    }
265
266    /**
267     * throw an exception if the construction of a serialised object overflows
268     * the memory allocation represented by out.
269     *
270     * @param out The buffer we are writing to
271     * @param identifier The Identifier of the collection to save in this entry
272     * @param startSeqno The startSeqno value to be used
273     * @param endSeqno The endSeqno value to be used
274     * @throws std::length_error if the function would write outside of out's
275     *         bounds.
276     */
277    void tryConstruction(cb::char_buffer out,
278                         Identifier identifier,
279                         int64_t startSeqno,
280                         int64_t endSeqno) {
281        if (!((out.data() + out.size()) >=
282              (reinterpret_cast<char*>(this) +
283               getObjectSize(identifier.getName().size())))) {
284            throw std::length_error(
285                    "SerialisedManifestEntry::tryConstruction with collection "
286                    "size " +
287                    std::to_string(identifier.getName().size()) +
288                    " exceeds the buffer of size " +
289                    std::to_string(out.size()));
290        }
291        this->uid = identifier.getUid();
292        this->startSeqno = startSeqno;
293        this->endSeqno = endSeqno;
294        this->collectionNameLen = identifier.getName().size();
295        std::memcpy(getCollectionNamePtr(),
296                    identifier.getName().data(),
297                    this->collectionNameLen);
298    }
299
300    /**
301     * Return a std::string JSON representation of this object with the callers
302     * chosen startSeqno/endSeqno (based on isDelete)
303     *
304     * @param _startSeqno The startSeqno value to be used
305     * @param _endSeqno The endSeqno value to be used
306     * @return A std::string JSON object for this object.
307     */
308    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
309        std::stringstream json;
310        json << R"({"name":")"
311             << std::string(getCollectionNamePtr(), collectionNameLen)
312             << R"(","uid":")" << std::hex << uid << "\"," << std::dec
313             << R"("startSeqno":")" << _startSeqno << "\","
314             << R"("endSeqno":")" << _endSeqno << "\"}";
315        return json.str();
316    }
317
318    int32_t collectionNameLen;
319    uid_t uid;
320    int64_t startSeqno;
321    int64_t endSeqno;
322};
323
324static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
325              "SerialisedManifestEntry must satisfy StandardLayoutType");
326
327} // end namespace VB
328} // end namespace Collections
329