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        case SystemEvent::DeleteCollectionHard:
117            return std::string(); // return nothing - collection gone
118        case SystemEvent::DeleteCollectionSoft:
119            return toJson(startSeqno, StoredValue::state_collection_open);
120        }
121
122        throw std::invalid_argument(
123                "SerialisedManifestEntry::toJson invalid event " +
124                to_string(se));
125        return {};
126    }
127
128private:
129    friend Collections::VB::Manifest;
130    static SerialisedManifestEntry* make(
131            char* address,
132            const Collections::VB::ManifestEntry& me,
133            cb::char_buffer out) {
134        return new (address) SerialisedManifestEntry(me, out);
135    }
136
137    static SerialisedManifestEntry* make(char* address,
138                                         int32_t revision,
139                                         cb::const_char_buffer collection,
140                                         cb::char_buffer out) {
141        return new (address) SerialisedManifestEntry(revision, collection, out);
142    }
143
144    SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
145                            cb::char_buffer out) {
146        tryConstruction(out,
147                        me.getRevision(),
148                        me.getStartSeqno(),
149                        me.getEndSeqno(),
150                        me.getCollectionName());
151    }
152
153    SerialisedManifestEntry(int revision,
154                            cb::const_char_buffer collection,
155                            cb::char_buffer out) {
156        tryConstruction(out,
157                        revision,
158                        0,
159                        StoredValue::state_collection_open,
160                        collection);
161    }
162
163    /**
164     * throw an exception if the construction of a serialised object overflows
165     * the memory allocation represented by out.
166     *
167     * @param out The buffer we are writing to
168     * @param entryData The struct to update (which should be enclosed by out)
169     * @param revision Collections::Manifest revision that got us here
170     * @param startSeqno The startSeqno value to be used
171     * @param endSeqno The endSeqno value to be used
172     * @param collection The name of the collection to copy-in
173     */
174    void tryConstruction(cb::char_buffer out,
175                         uint32_t revision,
176                         int64_t startSeqno,
177                         int64_t endSeqno,
178                         cb::const_char_buffer collection) {
179        if (!((out.data() + out.size()) >=
180              (reinterpret_cast<char*>(this) +
181               getObjectSize(collection.size())))) {
182            throw std::length_error("tryConstruction with collection size " +
183                                    std::to_string(collection.size()) +
184                                    " exceeds the buffer of size " +
185                                    std::to_string(out.size()));
186        }
187        this->revision = revision;
188        this->startSeqno = startSeqno;
189        this->endSeqno = endSeqno;
190        this->collectionNameLen = collection.size();
191        std::memcpy(this->collectionName,
192                    collection.data(),
193                    this->collectionNameLen);
194    }
195
196    /**
197     * Return a std::string JSON representation of this object with the callers
198     * chosen startSeqno/endSeqno (based on isDelete)
199     *
200     * @param _startSeqno The startSeqno value to be used
201     * @param _endSeqno The endSeqno value to be used
202     */
203    std::string toJson(int64_t _startSeqno, int64_t _endSeqno) const {
204        std::string json =
205                R"({"name":")" +
206                std::string(collectionName, collectionNameLen) +
207                R"(","revision":")" + std::to_string(revision) + "\"," +
208                R"("startSeqno":")" + std::to_string(_startSeqno) + "\"," +
209                R"("endSeqno":")" + std::to_string(_endSeqno) + "\"}";
210        return json;
211    }
212
213    uint32_t revision;
214    int64_t startSeqno;
215    int64_t endSeqno;
216    int32_t collectionNameLen;
217    char collectionName[1];
218};
219
220static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
221              "SerialisedManifestEntry must satisfy StandardLayoutType");
222
223} // end namespace VB
224} // end namespace Collections