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 <memcached/dockey.h>
19
20#pragma once
21
22namespace Collections {
23
24/**
25 * Collections::DocKey extends a DocKey so that the length of the collection
26 * can be recorded (the collection is a prefix on the key).
27 * The length of the collection only describes how many bytes are before
28 * the collection separator, whether the collection is valid or not is not
29 * something this class determines.
30 */
31class DocKey : public ::DocKey {
32public:
33
34    uint32_t hash() const {
35        return ::DocKey::hash(collectionLen);
36    }
37
38    /**
39     * Factory method to create a Collections::DocKey from a DocKey
40     */
41    static DocKey make(const ::DocKey& key, const std::string& separator) {
42        const uint8_t* collection = findCollection(key, separator);
43        if (collection) {
44            return DocKey(key, collection - key.data());
45        } else {
46            // No collection found, not an error - ok for DefaultNamespace.
47            return DocKey(key, 0);
48        }
49    }
50
51    /**
52     * @return how many bytes of the key are a collection
53     */
54    size_t getCollectionLen() const {
55        return collectionLen;
56    }
57
58private:
59    DocKey(const ::DocKey& key, size_t _collectionLen)
60        : ::DocKey(key), collectionLen(_collectionLen) {
61    }
62
63    /**
64     * Perform a search on the DocKey looking for the separator.
65     *
66     * @returns pointer to the start of the separator or nullptr if none found.
67     */
68    static const cb::const_byte_buffer::iterator findCollection(
69            const ::DocKey& key, const std::string& separator) {
70        if (key.size() == 0 || separator.size() == 0 ||
71            separator.size() > key.size()) {
72            return nullptr;
73        }
74
75        auto rv = std::search(key.data(),
76                              key.data() + key.size(),
77                              separator.begin(),
78                              separator.end());
79        if (rv != (key.data() + key.size())) {
80            return rv;
81        }
82        return nullptr;
83    }
84
85    uint8_t collectionLen;
86};
87} // end namespace Collections
88
89namespace std {
90template <>
91struct hash<Collections::DocKey> {
92    std::size_t operator()(const Collections::DocKey& key) const {
93        return key.hash();
94    }
95};
96}