1797cb301SJim Walker/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2797cb301SJim Walker/*
3797cb301SJim Walker *     Copyright 2017 Couchbase, Inc
4797cb301SJim Walker *
5797cb301SJim Walker *   Licensed under the Apache License, Version 2.0 (the "License");
6797cb301SJim Walker *   you may not use this file except in compliance with the License.
7797cb301SJim Walker *   You may obtain a copy of the License at
8797cb301SJim Walker *
9797cb301SJim Walker *       http://www.apache.org/licenses/LICENSE-2.0
10797cb301SJim Walker *
11797cb301SJim Walker *   Unless required by applicable law or agreed to in writing, software
12797cb301SJim Walker *   distributed under the License is distributed on an "AS IS" BASIS,
13797cb301SJim Walker *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14797cb301SJim Walker *   See the License for the specific language governing permissions and
15797cb301SJim Walker *   limitations under the License.
16797cb301SJim Walker */
17797cb301SJim Walker
18797cb301SJim Walker#include <memcached/dockey.h>
19fa526109STim Bradgate#include <gsl/gsl>
20797cb301SJim Walker
21dea18910SJim Walker#include "collections/collections_types.h"
22dea18910SJim Walker
23797cb301SJim Walker#pragma once
24797cb301SJim Walker
25797cb301SJim Walkernamespace Collections {
26797cb301SJim Walker
27797cb301SJim Walker/**
28797cb301SJim Walker * Collections::DocKey extends a DocKey so that the length of the collection
29797cb301SJim Walker * can be recorded (the collection is a prefix on the key).
30797cb301SJim Walker * The length of the collection only describes how many bytes are before
31797cb301SJim Walker * the collection separator, whether the collection is valid or not is not
32797cb301SJim Walker * something this class determines.
33797cb301SJim Walker */
34797cb301SJim Walkerclass DocKey : public ::DocKey {
35797cb301SJim Walkerpublic:
36797cb301SJim Walker
37797cb301SJim Walker    uint32_t hash() const {
38797cb301SJim Walker        return ::DocKey::hash(collectionLen);
39797cb301SJim Walker    }
40797cb301SJim Walker
41797cb301SJim Walker    /**
42797cb301SJim Walker     * Factory method to create a Collections::DocKey from a DocKey
43797cb301SJim Walker     */
44797cb301SJim Walker    static DocKey make(const ::DocKey& key, const std::string& separator) {
4512e87fb6SDave Rigby        switch (key.getDocNamespace()) {
4612e87fb6SDave Rigby        case DocNamespace::DefaultCollection:
4712e87fb6SDave Rigby            return DocKey(key, 0, 0);
4812e87fb6SDave Rigby        case DocNamespace::Collections: {
4912e87fb6SDave Rigby            const uint8_t* collection = findCollection(key, separator);
5012e87fb6SDave Rigby            if (collection) {
5112e87fb6SDave Rigby                return DocKey(key,
5212e87fb6SDave Rigby                              gsl::narrow<uint8_t>(collection - key.data()),
5312e87fb6SDave Rigby                              gsl::narrow<uint8_t>(separator.size()));
5412e87fb6SDave Rigby            }
5512e87fb6SDave Rigby            // No collection found in this key, so return empty (len 0)
5612e87fb6SDave Rigby            return DocKey(key, 0, 0);
5712e87fb6SDave Rigby        }
5812e87fb6SDave Rigby        case DocNamespace::System:
59dea18910SJim Walker            throw std::invalid_argument(
60dea18910SJim Walker                    "DocKey::make incorrect use of SystemKey");
61dea18910SJim Walker        }
6212e87fb6SDave Rigby        throw std::invalid_argument("DocKey::make invalid key.namespace: " +
6312e87fb6SDave Rigby                                    std::to_string(int(key.getDocNamespace())));
64797cb301SJim Walker    }
65797cb301SJim Walker
66dea18910SJim Walker    /**
67dea18910SJim Walker     * Factory method to create a Collections::DocKey from a DocKey
68dea18910SJim Walker     */
69dea18910SJim Walker    static Collections::DocKey make(const ::DocKey& key);
70dea18910SJim Walker
71797cb301SJim Walker    /**
72797cb301SJim Walker     * @return how many bytes of the key are a collection
73797cb301SJim Walker     */
74797cb301SJim Walker    size_t getCollectionLen() const {
75797cb301SJim Walker        return collectionLen;
76797cb301SJim Walker    }
77797cb301SJim Walker
7855e05834SJim Walker    /**
7955e05834SJim Walker     * @return how the length of the separator used in creating this
8055e05834SJim Walker     */
8155e05834SJim Walker    size_t getSeparatorLen() const {
8255e05834SJim Walker        return separatorLen;
8355e05834SJim Walker    }
8455e05834SJim Walker
8555e05834SJim Walker    /**
8655e05834SJim Walker     * @return const_char_buffer representing the collection part of the DocKey
8755e05834SJim Walker     */
8855e05834SJim Walker    cb::const_char_buffer getCollection() const {
8955e05834SJim Walker        return {reinterpret_cast<const char*>(data()), getCollectionLen()};
9055e05834SJim Walker    }
9155e05834SJim Walker
9255e05834SJim Walker    /**
9355e05834SJim Walker     * @return const_char_buffer representing the 'key' part of the DocKey
9455e05834SJim Walker     */
9555e05834SJim Walker    cb::const_char_buffer getKey() const {
9655e05834SJim Walker        return {reinterpret_cast<const char*>(data() + getCollectionLen() +
9755e05834SJim Walker                                              getSeparatorLen()),
9855e05834SJim Walker                size() - getCollectionLen() - getSeparatorLen()};
9955e05834SJim Walker    }
10055e05834SJim Walker
101797cb301SJim Walkerprivate:
102fa526109STim Bradgate    DocKey(const ::DocKey& key, uint8_t _collectionLen, uint8_t _separatorLen)
10355e05834SJim Walker        : ::DocKey(key),
10455e05834SJim Walker          collectionLen(_collectionLen),
10555e05834SJim Walker          separatorLen(_separatorLen) {
10655e05834SJim Walker        if (_separatorLen > std::numeric_limits<uint8_t>::max()) {
10755e05834SJim Walker            throw std::invalid_argument(
10855e05834SJim Walker                    "Collections::DocKey invalid separatorLen:" +
10955e05834SJim Walker                    std::to_string(_separatorLen));
11055e05834SJim Walker        }
111797cb301SJim Walker    }
112797cb301SJim Walker
113797cb301SJim Walker    /**
114797cb301SJim Walker     * Perform a search on the DocKey looking for the separator.
115797cb301SJim Walker     *
116797cb301SJim Walker     * @returns pointer to the start of the separator or nullptr if none found.
117797cb301SJim Walker     */
118797cb301SJim Walker    static const cb::const_byte_buffer::iterator findCollection(
119797cb301SJim Walker            const ::DocKey& key, const std::string& separator) {
120797cb301SJim Walker        if (key.size() == 0 || separator.size() == 0 ||
121797cb301SJim Walker            separator.size() > key.size()) {
122797cb301SJim Walker            return nullptr;
123797cb301SJim Walker        }
124797cb301SJim Walker
125a84cdbb7SJim Walker        // SystemEvent Doc keys all start with $ (SystemEventFactory::make does)
126a84cdbb7SJim Walker        // this. The collections separator could legally be $, so we need to
127a84cdbb7SJim Walker        // ensure we skip character 0 to correctly split the key.
128a84cdbb7SJim Walker        const int start = key.getDocNamespace() == DocNamespace::System ? 1 : 0;
129a84cdbb7SJim Walker
130a84cdbb7SJim Walker        auto rv = std::search(key.data() + start,
131797cb301SJim Walker                              key.data() + key.size(),
132797cb301SJim Walker                              separator.begin(),
133797cb301SJim Walker                              separator.end());
134797cb301SJim Walker        if (rv != (key.data() + key.size())) {
135797cb301SJim Walker            return rv;
136797cb301SJim Walker        }
137797cb301SJim Walker        return nullptr;
138797cb301SJim Walker    }
139797cb301SJim Walker
140797cb301SJim Walker    uint8_t collectionLen;
14155e05834SJim Walker    uint8_t separatorLen;
142797cb301SJim Walker};
143797cb301SJim Walker} // end namespace Collections
144797cb301SJim Walker
145797cb301SJim Walkernamespace std {
146797cb301SJim Walkertemplate <>
147797cb301SJim Walkerstruct hash<Collections::DocKey> {
148797cb301SJim Walker    std::size_t operator()(const Collections::DocKey& key) const {
149797cb301SJim Walker        return key.hash();
150797cb301SJim Walker    }
151797cb301SJim Walker};
15212e87fb6SDave Rigby}