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.h"
19#include "checkpoint.h"
20#include "collections/manifest.h"
21#include "collections/vbucket_serialised_manifest_entry.h"
22#include "item.h"
23#include "vbucket.h"
24
25#include <JSON_checker.h>
26#include <cJSON.h>
27#include <cJSON_utils.h>
28#include <platform/make_unique.h>
29
30namespace Collections {
31namespace VB {
32
33Manifest::Manifest(const std::string& manifest)
34    : defaultCollectionExists(false),
35      separator(DefaultSeparator),
36      greatestEndSeqno(StoredValue::state_collection_open),
37      nDeletingCollections(0) {
38    if (manifest.empty()) {
39        // Empty manifest, initialise the manifest with the default collection
40        addNewCollectionEntry({DefaultCollectionIdentifier, 0},
41                              0,
42                              StoredValue::state_collection_open);
43        defaultCollectionExists = true;
44        return;
45    }
46
47    if (!checkUTF8JSON(reinterpret_cast<const unsigned char*>(manifest.data()),
48                       manifest.size())) {
49        throwException<std::invalid_argument>(__FUNCTION__,
50                                              "input not valid json");
51    }
52
53    unique_cJSON_ptr cjson(cJSON_Parse(manifest.c_str()));
54    if (!cjson) {
55        throwException<std::invalid_argument>(__FUNCTION__,
56                                              "cJSON cannot parse json");
57    }
58
59    // Load the uid
60    manifestUid = makeUid(getJsonEntry(cjson.get(), "uid"));
61
62    // Load the separator
63    separator = getJsonEntry(cjson.get(), "separator");
64
65    // Load the collections array
66    auto jsonCollections = cJSON_GetObjectItem(cjson.get(), "collections");
67    if (!jsonCollections || jsonCollections->type != cJSON_Array) {
68        throwException<std::invalid_argument>(
69                __FUNCTION__,
70                "cannot find valid "
71                "collections: " +
72                        (!jsonCollections
73                                 ? "nullptr"
74                                 : std::to_string(jsonCollections->type)));
75    }
76
77    // Iterate the collections and load-em up.
78    for (int ii = 0; ii < cJSON_GetArraySize(jsonCollections); ii++) {
79        auto collection = cJSON_GetArrayItem(jsonCollections, ii);
80        uid_t uid = makeUid(getJsonEntry(collection, "uid"));
81        int64_t startSeqno = std::stoll(getJsonEntry(collection, "startSeqno"));
82        int64_t endSeqno = std::stoll(getJsonEntry(collection, "endSeqno"));
83        std::string collectionName(getJsonEntry(collection, "name"));
84        auto& entry = addNewCollectionEntry(
85                {collectionName, uid}, startSeqno, endSeqno);
86
87        if (DefaultCollectionIdentifier == collectionName.c_str()) {
88            defaultCollectionExists = entry.isOpen();
89        }
90    }
91}
92
93void Manifest::update(::VBucket& vb, const Collections::Manifest& manifest) {
94    std::vector<Collections::Manifest::Identifier> additions, deletions;
95    std::tie(additions, deletions) = processManifest(manifest);
96
97    if (separator != manifest.getSeparator()) {
98        changeSeparator(vb,
99                        manifest.getUid(),
100                        manifest.getSeparator(),
101                        OptionalSeqno{/*no-seqno*/});
102    }
103
104    // Process deletions to the manifest
105    for (const auto& collection : deletions) {
106        beginCollectionDelete(vb,
107                              manifest.getUid(),
108                              {collection.getName(), collection.getUid()},
109                              OptionalSeqno{/*no-seqno*/});
110    }
111
112    // Process additions to the manifest
113    for (const auto& collection : additions) {
114        addCollection(vb,
115                      manifest.getUid(),
116                      {collection.getName(), collection.getUid()},
117                      OptionalSeqno{/*no-seqno*/});
118    }
119}
120
121void Manifest::addCollection(::VBucket& vb,
122                             uid_t manifestUid,
123                             Identifier identifier,
124                             OptionalSeqno optionalSeqno) {
125    // 1. Update the manifest, adding or updating an entry in the map. Specify a
126    //    non-zero start
127    auto& entry = addCollectionEntry(identifier);
128
129    // 2. Queue a system event, this will take a copy of the manifest ready
130    //    for persistence into the vb state file.
131    auto seqno = queueSystemEvent(vb,
132                                  SystemEvent::Collection,
133                                  identifier,
134                                  false /*deleted*/,
135                                  optionalSeqno);
136
137    // record the uid of the manifest which is adding the collection
138    this->manifestUid = manifestUid;
139
140    LOG(EXTENSION_LOG_NOTICE,
141        "collections: vb:%" PRIu16 " adding collection:%.*s, uid:%" PRIx64
142        ", replica:%s, backfill:%s, seqno:%" PRId64 ", manifest:%" PRIx64,
143        vb.getId(),
144        int(identifier.getName().size()),
145        identifier.getName().data(),
146        identifier.getUid(),
147        optionalSeqno.is_initialized() ? "true" : "false",
148        vb.isBackfillPhase() ? "true" : "false",
149        seqno,
150        manifestUid);
151
152    // 3. Now patch the entry with the seqno of the system event, note the copy
153    //    of the manifest taken at step 1 gets the correct seqno when the system
154    //    event is flushed.
155    entry.setStartSeqno(seqno);
156}
157
158ManifestEntry& Manifest::addCollectionEntry(Identifier identifier) {
159    auto itr = map.find(identifier.getName());
160    if (itr == map.end()) {
161        if (identifier.isDefaultCollection()) {
162            defaultCollectionExists = true;
163        }
164        // Add new collection with 0,-6 start,end. The caller will correct the
165        // seqno based on what the checkpoint manager returns.
166        return addNewCollectionEntry(
167                identifier, 0, StoredValue::state_collection_open);
168    } else if (!itr->second->isOpen()) {
169        if (identifier.isDefaultCollection()) {
170            defaultCollectionExists = true;
171        }
172
173        itr->second->setUid(identifier.getUid());
174        return *itr->second;
175    }
176    throwException<std::logic_error>(
177            __FUNCTION__, "cannot add collection:" + to_string(identifier));
178}
179
180ManifestEntry& Manifest::addNewCollectionEntry(Identifier identifier,
181                                               int64_t startSeqno,
182                                               int64_t endSeqno) {
183    // This method is only for when the map does not have the collection
184    if (map.count(identifier.getName()) > 0) {
185        throwException<std::logic_error>(
186                __FUNCTION__,
187                "collection already exists, collection:" +
188                        to_string(identifier) + ", startSeqno:" +
189                        std::to_string(startSeqno) + ", endSeqno:" +
190                        std::to_string(endSeqno));
191    }
192    auto m = std::make_unique<ManifestEntry>(identifier, startSeqno, endSeqno);
193    auto* newEntry = m.get();
194    map.emplace(m->getCharBuffer(), std::move(m));
195
196    if (newEntry->isDeleting()) {
197        trackEndSeqno(endSeqno);
198    }
199
200    return *newEntry;
201}
202
203void Manifest::beginCollectionDelete(::VBucket& vb,
204                                     uid_t manifestUid,
205                                     Identifier identifier,
206                                     OptionalSeqno optionalSeqno) {
207    auto& entry = beginDeleteCollectionEntry(identifier);
208    auto seqno = queueSystemEvent(vb,
209                                  SystemEvent::Collection,
210                                  identifier,
211                                  true /*deleted*/,
212                                  optionalSeqno);
213
214    // record the uid of the manifest which removed the collection
215    this->manifestUid = manifestUid;
216
217    LOG(EXTENSION_LOG_NOTICE,
218        "collections: vb:%" PRIu16
219        " begin delete of collection:%.*s, uid:%" PRIx64
220        ", replica:%s, backfill:%s, seqno:%" PRId64 ", manifest:%" PRIx64,
221        vb.getId(),
222        int(identifier.getName().size()),
223        identifier.getName().data(),
224        identifier.getUid(),
225        optionalSeqno.is_initialized() ? "true" : "false",
226        vb.isBackfillPhase() ? "true" : "false",
227        seqno,
228        manifestUid);
229
230    if (identifier.isDefaultCollection()) {
231        defaultCollectionExists = false;
232    }
233
234    entry.setEndSeqno(seqno);
235
236    trackEndSeqno(seqno);
237}
238
239ManifestEntry& Manifest::beginDeleteCollectionEntry(Identifier identifier) {
240    auto itr = map.find(identifier.getName());
241    if (itr == map.end()) {
242            throwException<std::logic_error>(
243                    __FUNCTION__,
244                    "did not find collection:" + to_string(identifier));
245    }
246
247    return *itr->second;
248}
249
250void Manifest::completeDeletion(::VBucket& vb,
251                                cb::const_char_buffer collection) {
252    auto itr = map.find(collection);
253
254    LOG(EXTENSION_LOG_NOTICE,
255        "collections: vb:%" PRIu16 " complete delete of collection:%.*s",
256        vb.getId(),
257        int(collection.size()),
258        collection.data());
259
260    if (itr == map.end()) {
261        throwException<std::logic_error>(
262                __FUNCTION__,
263                "could not find collection:" + cb::to_string(collection));
264    }
265
266    auto se = itr->second->completeDeletion();
267    auto uid = itr->second->getUid();
268
269    if (se == SystemEvent::DeleteCollectionHard) {
270        map.erase(itr); // wipe out
271    }
272
273    nDeletingCollections--;
274    if (nDeletingCollections == 0) {
275        greatestEndSeqno = StoredValue::state_collection_open;
276    }
277
278    queueSystemEvent(vb,
279                     se,
280                     {collection, uid},
281                     false /*delete*/,
282                     OptionalSeqno{/*none*/});
283}
284
285void Manifest::changeSeparator(::VBucket& vb,
286                               uid_t manifestUid,
287                               cb::const_char_buffer newSeparator,
288                               OptionalSeqno optionalSeqno) {
289    // Can we change the separator? Only allowed to change if there are no
290    // collections or the only collection is the default collection
291    if (cannotChangeSeparator()) {
292        throwException<std::logic_error>(__FUNCTION__,
293                                         "cannot change "
294                                         "separator to " +
295                                                 cb::to_string(newSeparator));
296    } else {
297        LOG(EXTENSION_LOG_NOTICE,
298            "collections: vb:%" PRIu16
299            " changing collection separator from:%s, to:%.*s, replica:%s, "
300            "backfill:%s, manifest:%" PRIx64,
301            vb.getId(),
302            separator.c_str(),
303            int(newSeparator.size()),
304            newSeparator.data(),
305            optionalSeqno.is_initialized() ? "true" : "false",
306            vb.isBackfillPhase() ? "true" : "false",
307            manifestUid);
308
309        // record the uid of the manifest which changed the separator
310        this->manifestUid = manifestUid;
311
312        std::string oldSeparator = separator;
313        // Change the separator then queue the event so the new separator
314        // is recorded in the serialised manifest
315        separator = std::string(newSeparator.data(), newSeparator.size());
316
317        // Queue an event so that the manifest is flushed and DCP can
318        // replicate the change.
319        (void)queueSeparatorChanged(vb, oldSeparator, optionalSeqno);
320    }
321}
322
323Manifest::processResult Manifest::processManifest(
324        const Collections::Manifest& manifest) const {
325    std::vector<Collections::Manifest::Identifier> additions, deletions;
326
327    for (const auto& entry : map) {
328        // If the entry is open and not found in the new manifest it must be
329        // deleted.
330        if (entry.second->isOpen() &&
331            manifest.find(entry.second->getIdentifier()) == manifest.end()) {
332            deletions.push_back(entry.second->getIdentifier());
333        }
334    }
335
336    // iterate manifest and add all non-existent collection
337    for (const auto& m : manifest) {
338        // if we don't find the collection, then it must be an addition.
339        // if we do find a name match, then check if the collection is in the
340        //  process of being deleted or has a new UID
341        auto itr = map.find(m.getName());
342
343        if (itr == map.end() || !itr->second->isOpen() ||
344            itr->second->getUid() != m.getUid()) {
345            additions.push_back(m);
346        }
347    }
348    return std::make_pair(additions, deletions);
349}
350
351bool Manifest::doesKeyContainValidCollection(const ::DocKey& key) const {
352    if (defaultCollectionExists &&
353        key.getDocNamespace() == DocNamespace::DefaultCollection) {
354        return true;
355    } else if (key.getDocNamespace() == DocNamespace::Collections) {
356        const auto cKey = Collections::DocKey::make(key, separator);
357        if (cKey.getCollectionLen()) {
358            auto itr = map.find({reinterpret_cast<const char*>(cKey.data()),
359                                 cKey.getCollectionLen()});
360            if (itr != map.end()) {
361                return itr->second->isOpen();
362            }
363        }
364    }
365    return false;
366}
367
368Manifest::container::const_iterator Manifest::getManifestEntry(
369        const ::DocKey& key) const {
370    return getManifestEntry(key, separator);
371}
372
373Manifest::container::const_iterator Manifest::getManifestEntry(
374        const ::DocKey& key, const std::string& separator) const {
375    cb::const_char_buffer identifier;
376    if (defaultCollectionExists &&
377        key.getDocNamespace() == DocNamespace::DefaultCollection) {
378        identifier = DefaultCollectionIdentifier;
379    } else if (key.getDocNamespace() == DocNamespace::Collections) {
380        const auto cKey = Collections::DocKey::make(key, separator);
381        identifier = cKey.getCollection();
382    } else if (key.getDocNamespace() == DocNamespace::System) {
383        const auto cKey = Collections::DocKey::make(key);
384
385        if (cKey.getCollection() == SystemEventPrefix) {
386            identifier = cKey.getKey();
387        } else {
388            std::string sysKey(reinterpret_cast<const char*>(key.data()),
389                               key.size());
390            throwException<std::invalid_argument>(
391                    __FUNCTION__, "Use of system key invalid, key:" + sysKey);
392        }
393    }
394
395    return map.find(identifier);
396}
397
398bool Manifest::isLogicallyDeleted(const ::DocKey& key, int64_t seqno) const {
399    return isLogicallyDeleted(key, seqno, separator);
400}
401
402bool Manifest::isLogicallyDeleted(const ::DocKey& key,
403                                  int64_t seqno,
404                                  const std::string& separator) const {
405    // Only do the searching/scanning work for keys in the deleted range.
406    if (seqno <= greatestEndSeqno) {
407        switch (key.getDocNamespace()) {
408        case DocNamespace::DefaultCollection:
409            return !defaultCollectionExists;
410        case DocNamespace::Collections: {
411            const auto cKey = Collections::DocKey::make(key, separator);
412            auto itr = map.find(cKey.getCollection());
413            if (itr != map.end()) {
414                return seqno <= itr->second->getEndSeqno();
415            }
416            break;
417        }
418        case DocNamespace::System: {
419            const auto cKey = Collections::DocKey::make(key);
420            if (cKey.getCollection() == SystemEventPrefix) {
421                auto itr = map.find(cKey.getKey());
422                if (itr != map.end()) {
423                    return seqno <= itr->second->getEndSeqno();
424                }
425            }
426        }
427        }
428    }
429    return false;
430}
431
432bool Manifest::isLogicallyDeleted(const container::const_iterator entry,
433                                  int64_t seqno) const {
434    if (entry == map.end()) {
435        throwException<std::invalid_argument>(
436                __FUNCTION__,
437                "iterator is invalid, seqno:" + std::to_string(seqno));
438    }
439
440    if (seqno <= greatestEndSeqno) {
441        return seqno <= entry->second->getEndSeqno();
442    }
443    return false;
444}
445
446boost::optional<cb::const_char_buffer> Manifest::shouldCompleteDeletion(
447        const ::DocKey& key) const {
448    // If this is a SystemEvent key then...
449    if (key.getDocNamespace() == DocNamespace::System) {
450        const auto cKey = Collections::DocKey::make(key);
451        // 1. Check it's a collection's event
452        if (cKey.getCollection() == SystemEventPrefix) {
453            // 2. Lookup the collection entry
454            auto itr = map.find(cKey.getKey());
455            if (itr == map.end()) {
456                throwException<std::logic_error>(
457                        __FUNCTION__,
458                        "SystemEvent found which didn't match a collection " +
459                                cb::to_string(cKey.getKey()));
460            }
461
462            // 3. If this collection is deleting, return the collection name.
463            if (itr->second->isDeleting()) {
464                return {cKey.getKey()};
465            }
466        }
467    }
468    return {};
469}
470
471std::unique_ptr<Item> Manifest::createSystemEvent(SystemEvent se,
472                                                  Identifier identifier,
473                                                  bool deleted,
474                                                  OptionalSeqno seqno) const {
475    // Create an item (to be queued and written to disk) that represents
476    // the update of a collection and allows the checkpoint to update
477    // the _local document with a persisted version of this object (the entire
478    // manifest is persisted to disk as JSON).
479    // The key for the item includes the name and revision to ensure a
480    // checkpoint consumer (e.g. DCP) can transmit the full collection info.
481
482    auto item = SystemEventFactory::make(
483            se,
484            cb::to_string(identifier.getName()),
485            getSerialisedDataSize(identifier.getName()),
486            seqno);
487
488    // Quite rightly an Item's value is const, but in this case the Item is
489    // owned only by the local scope so is safe to mutate (by const_cast force)
490    populateWithSerialisedData(
491            {const_cast<char*>(item->getData()), item->getNBytes()},
492            identifier);
493
494    if (deleted) {
495        item->setDeleted();
496    }
497
498    return item;
499}
500
501std::unique_ptr<Item> Manifest::createSeparatorChangedEvent(
502        int64_t highSeqno,
503        OptionalSeqno seqno) const {
504    // Create an item (to be queued and written to disk) that represents
505    // the change of the separator. The item always has the same key.
506    // We serialise the state of this object  into the Item's value.
507    auto item = SystemEventFactory::make(
508            SystemEvent::CollectionsSeparatorChanged,
509            std::to_string(highSeqno) + SystemSeparator + separator,
510            getSerialisedDataSize(),
511            seqno);
512
513    // Quite rightly an Item's value is const, but in this case the Item is
514    // owned only by the local scope so is safe to mutate (by const_cast force)
515    populateWithSerialisedData(
516            {const_cast<char*>(item->getData()), item->getNBytes()});
517
518    return item;
519}
520
521int64_t Manifest::queueSystemEvent(::VBucket& vb,
522                                   SystemEvent se,
523                                   Identifier identifier,
524                                   bool deleted,
525                                   OptionalSeqno seq) const {
526    // Create and transfer Item ownership to the VBucket
527    auto rv = vb.queueItem(
528            createSystemEvent(se, identifier, deleted, seq).release(), seq);
529
530    // If seq is not set, then this is an active vbucket queueing the event.
531    // Collection events will end the CP so they don't de-dep.
532    if (!seq.is_initialized()) {
533        vb.checkpointManager->createNewCheckpoint();
534    }
535    return rv;
536}
537
538int64_t Manifest::queueSeparatorChanged(::VBucket& vb,
539                                        const std::string& oldSeparator,
540                                        OptionalSeqno seqno) const {
541    // Create and transfer Item ownership to the VBucket
542    return vb.queueItem(
543            createSeparatorChangedEvent(vb.getHighSeqno(), seqno).release(),
544            seqno);
545}
546
547size_t Manifest::getSerialisedDataSize(cb::const_char_buffer collection) const {
548    size_t bytesNeeded = SerialisedManifest::getObjectSize(separator.size());
549    for (const auto& collectionEntry : map) {
550        // Skip if a collection in the map matches the collection being changed
551        if (collectionEntry.second->getCharBuffer() == collection) {
552            continue;
553        }
554        bytesNeeded += SerialisedManifestEntry::getObjectSize(
555                collectionEntry.second->getCollectionName().size());
556    }
557
558    return bytesNeeded +
559           SerialisedManifestEntry::getObjectSize(collection.size());
560}
561
562size_t Manifest::getSerialisedDataSize() const {
563    size_t bytesNeeded = SerialisedManifest::getObjectSize(separator.size());
564    for (const auto& collectionEntry : map) {
565        // Skip if a collection in the map matches the collection being changed
566        bytesNeeded += SerialisedManifestEntry::getObjectSize(
567                collectionEntry.second->getCollectionName().size());
568    }
569
570    return bytesNeeded;
571}
572
573void Manifest::populateWithSerialisedData(cb::char_buffer out,
574                                          Identifier identifier) const {
575    auto* sMan = SerialisedManifest::make(
576            out.data(), separator, getManifestUid(), out);
577    uint32_t itemCounter = 1; // always a final entry
578    char* serial = sMan->getManifestEntryBuffer();
579
580    const std::unique_ptr<ManifestEntry>* finalEntry = nullptr;
581    for (const auto& collectionEntry : map) {
582        // Check if we find the mutated entry in the map (so we know if we're
583        // mutating it)
584        if (collectionEntry.second->getCharBuffer() == identifier.getName()) {
585            // If a collection in the map matches the collection being changed
586            // save the iterator so we can use it when creating the final entry
587            finalEntry = &collectionEntry.second;
588        } else {
589            itemCounter++;
590            auto* sme = SerialisedManifestEntry::make(
591                    serial, *collectionEntry.second, out);
592            serial = sme->nextEntry();
593        }
594    }
595
596    SerialisedManifestEntry* finalSme = nullptr;
597    if (finalEntry) {
598        // delete
599        finalSme =
600                SerialisedManifestEntry::make(serial, *finalEntry->get(), out);
601    } else {
602        // create
603        finalSme = SerialisedManifestEntry::make(serial, identifier, out);
604    }
605
606    sMan->setEntryCount(itemCounter);
607    sMan->calculateFinalEntryOffest(finalSme);
608}
609
610void Manifest::populateWithSerialisedData(cb::char_buffer out) const {
611    auto* sMan = SerialisedManifest::make(
612            out.data(), separator, getManifestUid(), out);
613    char* serial = sMan->getManifestEntryBuffer();
614
615    for (const auto& collectionEntry : map) {
616        auto* sme = SerialisedManifestEntry::make(
617                serial, *collectionEntry.second, out);
618        serial = sme->nextEntry();
619    }
620
621    sMan->setEntryCount(map.size());
622}
623
624std::string Manifest::serialToJson(const Item& collectionsEventItem) {
625    cb::const_char_buffer buffer(collectionsEventItem.getData(),
626                                 collectionsEventItem.getNBytes());
627    const auto se = SystemEvent(collectionsEventItem.getFlags());
628
629    if (se == SystemEvent::CollectionsSeparatorChanged) {
630        return serialToJson(buffer);
631    }
632
633    const auto* sMan =
634            reinterpret_cast<const SerialisedManifest*>(buffer.data());
635    const char* serial = sMan->getManifestEntryBuffer();
636
637    std::stringstream json;
638    json << R"({"separator":")" << sMan->getSeparator() << R"(","uid":")"
639         << std::hex << sMan->getManifestUid() << R"(","collections":[)";
640
641    if (sMan->getEntryCount() > 1) {
642        // Iterate and produce an comma separated list
643        for (uint32_t ii = 1; ii < sMan->getEntryCount(); ii++) {
644            const auto* sme =
645                    reinterpret_cast<const SerialisedManifestEntry*>(serial);
646            json << sme->toJson();
647            serial = sme->nextEntry();
648
649            if (ii < sMan->getEntryCount() - 1) {
650                json << ",";
651            }
652        }
653
654        // DeleteCollectionHard removes this last entry so no comma
655        if (se != SystemEvent::DeleteCollectionHard) {
656            json << ",";
657        }
658    }
659
660    const auto* sme = reinterpret_cast<const SerialisedManifestEntry*>(serial);
661    // Last entry is the collection which changed. How did it change?
662    if (se == SystemEvent::Collection) {
663        // Collection start/end (create/delete)
664        json << sme->toJsonCreateOrDelete(collectionsEventItem.isDeleted(),
665                                          collectionsEventItem.getBySeqno());
666    } else if (se == SystemEvent::DeleteCollectionSoft) {
667        // Collection delete completed, but collection has been recreated
668        json << sme->toJsonResetEnd();
669    }
670
671    json << "]}";
672    return json.str();
673}
674
675std::string Manifest::serialToJson(cb::const_char_buffer buffer) {
676    const auto* sMan =
677            reinterpret_cast<const SerialisedManifest*>(buffer.data());
678    const char* serial = sMan->getManifestEntryBuffer();
679
680    std::stringstream json;
681    json << R"({"separator":")" << sMan->getSeparator() << R"(","uid":")"
682         << std::hex << sMan->getManifestUid() << R"(","collections":[)";
683
684    for (uint32_t ii = 0; ii < sMan->getEntryCount(); ii++) {
685        const auto* sme =
686                reinterpret_cast<const SerialisedManifestEntry*>(serial);
687        json << sme->toJson();
688        serial = sme->nextEntry();
689
690        if (ii < sMan->getEntryCount() - 1) {
691            json << ",";
692        }
693    }
694
695    json << "]}";
696    return json.str();
697}
698
699const char* Manifest::getJsonEntry(cJSON* cJson, const char* key) {
700    auto jsonEntry = cJSON_GetObjectItem(cJson, key);
701    if (!jsonEntry || jsonEntry->type != cJSON_String) {
702        throwException<std::invalid_argument>(
703                __FUNCTION__,
704                "null or not string, key:" + std::string(key) + " " +
705                        (!jsonEntry ? "nullptr"
706                                    : std::to_string(jsonEntry->type)));
707    }
708    return jsonEntry->valuestring;
709}
710
711void Manifest::trackEndSeqno(int64_t seqno) {
712    nDeletingCollections++;
713    if (seqno > greatestEndSeqno ||
714        greatestEndSeqno == StoredValue::state_collection_open) {
715        greatestEndSeqno = seqno;
716    }
717}
718
719bool Manifest::cannotChangeSeparator() const {
720    // If any non-default collection exists that isOpen, cannot change separator
721    for (const auto& manifestEntry : map) {
722        // If the manifestEntry is open and not found in the new manifest it
723        // must be deleted.
724        if (manifestEntry.second->isOpen() &&
725            manifestEntry.second->getCharBuffer() !=
726                    DefaultCollectionIdentifier) {
727            // Collection is open and is not $default - cannot change
728            return true;
729        }
730    }
731
732    return false;
733}
734
735SystemEventData Manifest::getSystemEventData(
736        cb::const_char_buffer serialisedManifest) {
737    const auto* sm = reinterpret_cast<const SerialisedManifest*>(
738            serialisedManifest.data());
739    const auto* sme = sm->getFinalManifestEntry();
740    return {sm->getManifestUid(), {sme->getCollectionName(), sme->getUid()}};
741}
742
743SystemEventSeparatorData Manifest::getSystemEventSeparatorData(
744        cb::const_char_buffer serialisedManifest) {
745    const auto* sm = reinterpret_cast<const SerialisedManifest*>(
746            serialisedManifest.data());
747    return {sm->getManifestUid(), sm->getSeparatorBuffer()};
748}
749
750std::string Manifest::getExceptionString(const std::string& thrower,
751                                         const std::string& error) const {
752    std::stringstream ss;
753    ss << "VB::Manifest:" << thrower << ": " << error << ", this:" << *this;
754    return ss.str();
755}
756
757std::ostream& operator<<(std::ostream& os, const Manifest& manifest) {
758    os << "VB::Manifest"
759       << ": defaultCollectionExists:" << manifest.defaultCollectionExists
760       << ", separator:" << manifest.separator
761       << ", greatestEndSeqno:" << manifest.greatestEndSeqno
762       << ", nDeletingCollections:" << manifest.nDeletingCollections
763       << ", map.size:" << manifest.map.size() << std::endl;
764    for (auto& m : manifest.map) {
765        os << *m.second << std::endl;
766    }
767
768    return os;
769}
770
771std::ostream& operator<<(std::ostream& os,
772                         const Manifest::ReadHandle& readHandle) {
773    os << "VB::Manifest::ReadHandle: manifest:" << readHandle.manifest;
774    return os;
775}
776
777std::ostream& operator<<(std::ostream& os,
778                         const Manifest::CachingReadHandle& readHandle) {
779    os << "VB::Manifest::CachingReadHandle: itr:";
780    if (readHandle.iteratorValid()) {
781        os << *readHandle.itr->second;
782    } else {
783        os << "end";
784    }
785    os << ", manifest:" << readHandle.manifest;
786    return os;
787}
788
789} // end namespace VB
790} // end namespace Collections
791