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
28/**
29 * A VB::Manifest is serialised into an Item when it is updated. The
30 * Item carries a copy of the VB::Manifest down to the flusher so that
31 * a JSON version can be written to persistent storage.
32 *
33 * The serialised data is created by VB::Manifest and is a copy of the
34 * manifest *before* the update is applied. The update being applied to the
35 * manifest is serialised as the final entry. This is done because the seqno
36 * of the final entry needs correcting during the creation of the JSON. This
37 * occurs because the Item is created but cannot be allocated a seqno until it
38 * is queued, and it is not safe to mutate the Item's value after it is queued.
39 *
40 */
41
42/**
43 * SerialisedManifest just stores the entry count and can return a pointer
44 * to the buffer where entries can be written.
45 */
46class SerialisedManifest {
47public:
48    static size_t getObjectSize() {
49        return sizeof(SerialisedManifest);
50    }
51
52    void setEntryCount(uint32_t items) {
53        itemCount = items;
54    }
55
56    uint32_t getEntryCount() const {
57        return itemCount;
58    }
59
60    /**
61     * Return a non const pointer to where the entries can be written
62     */
63    char* getManifestEntryBuffer() {
64        return reinterpret_cast<char*>(this + 1);
65    }
66
67    /**
68     * Return a const pointer from where the entries can be read
69     */
70    const char* getManifestEntryBuffer() const {
71        return reinterpret_cast<const char*>(this + 1);
72    }
73
74private:
75    uint32_t itemCount;
76};
77
78class SerialisedManifestEntry {
79public:
80
81    static size_t getObjectSize(size_t collectionNameLen) {
82        return sizeof(SerialisedManifestEntry) - 1 + collectionNameLen;
83    }
84
85    size_t getObjectSize() const {
86        return getObjectSize(getCollectionNameLen());
87    }
88
89    void setRevision(uint32_t rev) {
90        revision = rev;
91    }
92
93    size_t getCollectionNameLen() const {
94        return collectionNameLen;
95    }
96
97    const char* nextEntry() const {
98        return reinterpret_cast<const char*>(this) + getObjectSize();
99    }
100
101    char* nextEntry() {
102        return const_cast<char*>(
103                static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
104    }
105
106    std::string toJson() const {
107        return toJson(startSeqno, endSeqno);
108    }
109
110    std::string toJson(SystemEvent se, int64_t correctedSeqno) const {
111        switch (se) {
112        case SystemEvent::BeginDeleteCollection:
113            return toJson(startSeqno, correctedSeqno);
114        case SystemEvent::CreateCollection:
115            return toJson(correctedSeqno, endSeqno);
116        }
117
118        throw std::invalid_argument(
119                "SerialisedManifestEntry::toJson invalid event " +
120                to_string(se));
121        return {};
122    }
123
124private:
125    friend Collections::VB::Manifest;
126    static SerialisedManifestEntry* make(
127            char* address,
128            const Collections::VB::ManifestEntry& me,
129            cb::char_buffer out) {
130        return new (address) SerialisedManifestEntry(me, out);
131    }
132
133    static SerialisedManifestEntry* make(char* address,
134                                         int32_t revision,
135                                         const std::string& collection,
136                                         cb::char_buffer out) {
137        return new (address) SerialisedManifestEntry(revision, collection, out);
138    }
139
140    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
141                            cb::char_buffer out) {
142        tryConstruction(out,
143                        me.getRevision(),
144                        me.getStartSeqno(),
145                        me.getEndSeqno(),
146                        me.getCollectionName());
147    }
148
149    SerialisedManifestEntry(int revision,
150                            const std::string& collection,
151                            cb::char_buffer out) {
152        tryConstruction(out,
153                        revision,
154                        0,
155                        StoredValue::state_collection_open,
156                        collection);
157    }
158
159    /**
160     * throw an exception if the construction of a serialised object overflows
161     * the memory allocation represented by out.
162     *
163     * @param out The buffer we are writing to
164     * @param entryData The struct to update (which should be enclosed by out)
165     * @param revision Collections::Manifest revision that got us here
166     * @param startSeqno The startSeqno value to be used
167     * @param endSeqno The endSeqno value to be used
168     * @param collection The name of the collection to copy-in
169     */
170    void tryConstruction(cb::char_buffer out,
171                         uint32_t revision,
172                         int64_t startSeqno,
173                         int64_t endSeqno,
174                         const std::string collection) {
175        if (!((out.data() + out.size()) >=
176              (reinterpret_cast<char*>(this) +
177               getObjectSize(collection.size())))) {
178            throw std::length_error("tryConstruction with collection size " +
179                                    std::to_string(collection.size()) +
180                                    " exceeds the buffer of size " +
181                                    std::to_string(out.size()));
182        }
183        this->revision = revision;
184        this->startSeqno = startSeqno;
185        this->endSeqno = endSeqno;
186        this->collectionNameLen = collection.size();
187        std::memcpy(this->collectionName,
188                    collection.data(),
189                    this->collectionNameLen);
190    }
191
192    /**
193     * Return a std::string JSON representation of this object with the callers
194     * chosen startSeqno/endSeqno (based on isDelete)
195     *
196     * @param _startSeqno The startSeqno value to be used
197     * @param _endSeqno The endSeqno value to be used
198     */
199    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
200        std::string json =
201                R"({"name":")" +
202                std::string(collectionName, collectionNameLen) +
203                R"(","revision":")" + std::to_string(revision) + "\"," +
204                R"("startSeqno":")" + std::to_string(_startSeqno) + "\"," +
205                R"("endSeqno":")" + std::to_string(_endSeqno) + "\"}";
206        return json;
207    }
208
209    uint32_t revision;
210    int64_t startSeqno;
211    int64_t endSeqno;
212    int32_t collectionNameLen;
213    char collectionName[1];
214};
215
216static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
217              "SerialisedManifestEntry must satisfy StandardLayoutType");
218
219} // end namespace VB
220} // end namespace Collections