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
108    uid_t getManifestUid() const {
109        return manifestUid;
110    }
111
112private:
113
114    friend Collections::VB::Manifest;
115    static SerialisedManifest* make(char* address,
116                                    const std::string& separator,
117                                    uid_t manifestUid,
118                                    cb::char_buffer out) {
119        return new (address) SerialisedManifest(separator, manifestUid, 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 out The buffer into which this object is being constructed
127     * @throws length_error if the consruction would access outside of out
128     */
129    SerialisedManifest(const std::string& separator,
130                       uid_t manifestUid,
131                       cb::char_buffer out)
132        : itemCount(0),
133          separatorLen(separator.size()),
134          finalEntryOffset(0),
135          manifestUid(manifestUid) {
136        if (!((out.data() + out.size()) >=
137              (reinterpret_cast<char*>(this) +
138               getObjectSize(separator.size())))) {
139            throw std::length_error(
140                    "SerialisedManifest::tryConstruction with separator.size " +
141                    std::to_string(separator.size()) +
142                    " exceeds the buffer of size " +
143                    std::to_string(out.size()));
144        }
145        std::copy_n(separator.data(), separator.size(), getSeparatorPtr());
146    }
147
148    const char* getSeparatorPtr() const {
149        return reinterpret_cast<const char*>(this + 1);
150    }
151
152    char* getSeparatorPtr() {
153        return reinterpret_cast<char*>(this + 1);
154    }
155
156    uint32_t itemCount;
157    uint32_t separatorLen;
158    uint32_t finalEntryOffset;
159    uid_t manifestUid;
160};
161
162class SerialisedManifestEntry {
163public:
164    /**
165     * Object is not copyable or movable
166     */
167    SerialisedManifestEntry(const SerialisedManifestEntry& other) = delete;
168    SerialisedManifestEntry(SerialisedManifestEntry&& other) = delete;
169
170    static size_t getObjectSize(size_t collectionNameLen) {
171        return sizeof(SerialisedManifestEntry) + collectionNameLen;
172    }
173
174    size_t getObjectSize() const {
175        return getObjectSize(getCollectionNameLen());
176    }
177
178    uid_t getUid() const {
179        return uid;
180    }
181
182    void setUid(uid_t uid) {
183        this->uid = uid;
184    }
185
186    /**
187     * @return the entry's collection name as a const char buffer
188     */
189    cb::const_char_buffer getCollectionName() const {
190        return {getCollectionNamePtr(), size_t(collectionNameLen)};
191    }
192
193    size_t getCollectionNameLen() const {
194        return collectionNameLen;
195    }
196
197    const char* nextEntry() const {
198        return reinterpret_cast<const char*>(this) + getObjectSize();
199    }
200
201    char* nextEntry() {
202        return const_cast<char*>(
203                static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
204    }
205
206    std::string toJson() const {
207        return toJson(startSeqno, endSeqno);
208    }
209
210    std::string toJsonCreateOrDelete(bool isCollectionDelete,
211                                     int64_t correctedSeqno) const {
212        if (isCollectionDelete) {
213            return toJson(startSeqno, correctedSeqno);
214        }
215        return toJson(correctedSeqno, endSeqno);
216    }
217
218    std::string toJsonResetEnd() const {
219        return toJson(startSeqno, StoredValue::state_collection_open);
220    }
221
222private:
223    friend Collections::VB::Manifest;
224    static SerialisedManifestEntry* make(
225            char* address,
226            const Collections::VB::ManifestEntry& me,
227            cb::char_buffer out) {
228        return new (address) SerialisedManifestEntry(me, out);
229    }
230
231    static SerialisedManifestEntry* make(char* address,
232                                         Identifier identifier,
233                                         cb::char_buffer out) {
234        return new (address) SerialisedManifestEntry(identifier, out);
235    }
236
237    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
238                            cb::char_buffer out) {
239        tryConstruction(
240                out, me.getIdentifier(), me.getStartSeqno(), me.getEndSeqno());
241    }
242
243    SerialisedManifestEntry(Identifier identifier, cb::char_buffer out) {
244        tryConstruction(out, identifier, 0, StoredValue::state_collection_open);
245    }
246
247    /**
248     * The collection name is stored in memory allocated after this object.
249     *
250     * @return pointer to the collection name, located at the address after
251     *         'this'.
252     */
253    char* getCollectionNamePtr() {
254        return reinterpret_cast<char*>(this + 1);
255    }
256
257    /**
258     * The collection name is stored in memory allocated after this object.
259     *
260     * @return const pointer to the collection name, located at the address
261     *         after 'this'.
262     */
263    const char* getCollectionNamePtr() const {
264        return reinterpret_cast<const char*>(this + 1);
265    }
266
267    /**
268     * throw an exception if the construction of a serialised object overflows
269     * the memory allocation represented by out.
270     *
271     * @param out The buffer we are writing to
272     * @param identifier The Identifier of the collection to save in this entry
273     * @param startSeqno The startSeqno value to be used
274     * @param endSeqno The endSeqno value to be used
275     * @throws std::length_error if the function would write outside of out's
276     *         bounds.
277     */
278    void tryConstruction(cb::char_buffer out,
279                         Identifier identifier,
280                         int64_t startSeqno,
281                         int64_t endSeqno) {
282        if (!((out.data() + out.size()) >=
283              (reinterpret_cast<char*>(this) +
284               getObjectSize(identifier.getName().size())))) {
285            throw std::length_error(
286                    "SerialisedManifestEntry::tryConstruction with collection "
287                    "size " +
288                    std::to_string(identifier.getName().size()) +
289                    " exceeds the buffer of size " +
290                    std::to_string(out.size()));
291        }
292        this->uid = identifier.getUid();
293        this->startSeqno = startSeqno;
294        this->endSeqno = endSeqno;
295        this->collectionNameLen = identifier.getName().size();
296        std::memcpy(getCollectionNamePtr(),
297                    identifier.getName().data(),
298                    this->collectionNameLen);
299    }
300
301    /**
302     * Return a std::string JSON representation of this object with the callers
303     * chosen startSeqno/endSeqno (based on isDelete)
304     *
305     * @param _startSeqno The startSeqno value to be used
306     * @param _endSeqno The endSeqno value to be used
307     * @return A std::string JSON object for this object.
308     */
309    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
310        std::stringstream json;
311        json << R"({"name":")"
312             << std::string(getCollectionNamePtr(), collectionNameLen)
313             << R"(","uid":")" << std::hex << uid << "\"," << std::dec
314             << R"("startSeqno":")" << _startSeqno << "\","
315             << R"("endSeqno":")" << _endSeqno << "\"}";
316        return json.str();
317    }
318
319    int32_t collectionNameLen;
320    uid_t uid;
321    int64_t startSeqno;
322    int64_t endSeqno;
323};
324
325static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
326              "SerialisedManifestEntry must satisfy StandardLayoutType");
327
328} // end namespace VB
329} // end namespace Collections
330