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 
25 namespace Collections {
26 namespace VB {
27 
28 class 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  */
48 class SerialisedManifest {
49 public:
getObjectSize(uint32_t separatorLen)50     static size_t getObjectSize(uint32_t separatorLen) {
51         return sizeof(SerialisedManifest) + separatorLen;
52     }
53 
setEntryCount(uint32_t items)54     void setEntryCount(uint32_t items) {
55         itemCount = items;
56     }
57 
getEntryCount() const58     uint32_t getEntryCount() const {
59         return itemCount;
60     }
61 
getSeparator() const62     std::string getSeparator() const {
63         return {getSeparatorPtr(), separatorLen};
64     }
65 
getSeparatorBuffer() const66     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      */
calculateFinalEntryOffest(const SerialisedManifestEntry* sme)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      */
getManifestEntryBuffer()85     char* getManifestEntryBuffer() {
86         return getSeparatorPtr() + separatorLen;
87     }
88 
89     /**
90      * Return a const pointer from where the entries can be read
91      */
getManifestEntryBuffer() const92     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      */
getFinalManifestEntry() const103     const SerialisedManifestEntry* getFinalManifestEntry() const {
104         return reinterpret_cast<const SerialisedManifestEntry*>(
105                 getManifestEntryBuffer() + finalEntryOffset);
106     }
107 
getManifestUid() const108     uid_t getManifestUid() const {
109         return manifestUid;
110     }
111 
112 private:
113 
114     friend Collections::VB::Manifest;
make(char* address, const std::string& separator, uid_t manifestUid, cb::char_buffer out)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      */
SerialisedManifest(const std::string& separator, uid_t manifestUid, cb::char_buffer out)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 
getSeparatorPtr() const148     const char* getSeparatorPtr() const {
149         return reinterpret_cast<const char*>(this + 1);
150     }
151 
getSeparatorPtr()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 
162 class SerialisedManifestEntry {
163 public:
164     /**
165      * Object is not copyable or movable
166      */
167     SerialisedManifestEntry(const SerialisedManifestEntry& other) = delete;
168     SerialisedManifestEntry(SerialisedManifestEntry&& other) = delete;
169 
getObjectSize(size_t collectionNameLen)170     static size_t getObjectSize(size_t collectionNameLen) {
171         return sizeof(SerialisedManifestEntry) + collectionNameLen;
172     }
173 
getObjectSize() const174     size_t getObjectSize() const {
175         return getObjectSize(getCollectionNameLen());
176     }
177 
getUid() const178     uid_t getUid() const {
179         return uid;
180     }
181 
setUid(uid_t uid)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      */
getCollectionName() const189     cb::const_char_buffer getCollectionName() const {
190         return {getCollectionNamePtr(), size_t(collectionNameLen)};
191     }
192 
getCollectionNameLen() const193     size_t getCollectionNameLen() const {
194         return collectionNameLen;
195     }
196 
nextEntry() const197     const char* nextEntry() const {
198         return reinterpret_cast<const char*>(this) + getObjectSize();
199     }
200 
nextEntry()201     char* nextEntry() {
202         return const_cast<char*>(
203                 static_cast<const SerialisedManifestEntry*>(this)->nextEntry());
204     }
205 
toJson() const206     std::string toJson() const {
207         return toJson(startSeqno, endSeqno);
208     }
209 
toJsonCreateOrDelete(bool isCollectionDelete, int64_t correctedSeqno) const210     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 
toJsonResetEnd() const218     std::string toJsonResetEnd() const {
219         return toJson(startSeqno, StoredValue::state_collection_open);
220     }
221 
222 private:
223     friend Collections::VB::Manifest;
make( char* address, const Collections::VB::ManifestEntry& me, cb::char_buffer out)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 
make(char* address, Identifier identifier, cb::char_buffer out)231     static SerialisedManifestEntry* make(char* address,
232                                          Identifier identifier,
233                                          cb::char_buffer out) {
234         return new (address) SerialisedManifestEntry(identifier, out);
235     }
236 
SerialisedManifestEntry(const Collections::VB::ManifestEntry& me, cb::char_buffer out)237     SerialisedManifestEntry(const Collections::VB::ManifestEntry& me,
238                             cb::char_buffer out) {
239         tryConstruction(
240                 out, me.getIdentifier(), me.getStartSeqno(), me.getEndSeqno());
241     }
242 
SerialisedManifestEntry(Identifier identifier, cb::char_buffer out)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      */
getCollectionNamePtr()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      */
getCollectionNamePtr() const263     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      */
tryConstruction(cb::char_buffer out, Identifier identifier, int64_t startSeqno, int64_t endSeqno)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      */
toJson(int64_t _startSeqno, int64_t _endSeqno) const309     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 
325 static_assert(std::is_standard_layout<SerialisedManifestEntry>::value,
326               "SerialisedManifestEntry must satisfy StandardLayoutType");
327 
328 } // end namespace VB
329 } // end namespace Collections
330