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