107df3775SJim Walker/*
207df3775SJim Walker *     Copyright 2016 Couchbase, Inc
307df3775SJim Walker *
407df3775SJim Walker *   Licensed under the Apache License, Version 2.0 (the "License");
507df3775SJim Walker *   you may not use this file except in compliance with the License.
607df3775SJim Walker *   You may obtain a copy of the License at
707df3775SJim Walker *
807df3775SJim Walker *       http://www.apache.org/licenses/LICENSE-2.0
907df3775SJim Walker *
1007df3775SJim Walker *   Unless required by applicable law or agreed to in writing, software
1107df3775SJim Walker *   distributed under the License is distributed on an "AS IS" BASIS,
1207df3775SJim Walker *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307df3775SJim Walker *   See the License for the specific language governing permissions and
1407df3775SJim Walker *   limitations under the License.
1507df3775SJim Walker */
1607df3775SJim Walker
1707df3775SJim Walker#pragma once
1807df3775SJim Walker
198ca2bbe4SJim Walker#include <limits>
208ca2bbe4SJim Walker#include <memory>
2107df3775SJim Walker#include <string>
2207df3775SJim Walker#include <type_traits>
2307df3775SJim Walker
2407df3775SJim Walker#include <memcached/dockey.h>
2507df3775SJim Walker
26ab711953STim Bradgate#include <gsl/gsl>
27ab711953STim Bradgate
2807df3775SJim Walker#include "ep_types.h"
2907df3775SJim Walker
308ca2bbe4SJim Walkerclass SerialisedDocKey;
318ca2bbe4SJim Walker
3207df3775SJim Walker/**
3307df3775SJim Walker * StoredDocKey is a container for key data
3407df3775SJim Walker *
3507df3775SJim Walker * Internally an n byte key is stored in a n + 1 std::string.
3607df3775SJim Walker *  a) We zero terminate so that data() is safe for printing as a c-string.
3707df3775SJim Walker *  b) We store the DocNamespace in byte 0 (duplicated in parent class DocKey).
3807df3775SJim Walker *    This is because StoredDocKey typically ends up being written to disk and
3907df3775SJim Walker *    the DocNamespace forms part of the on-disk key. Pre-allocating space
4007df3775SJim Walker *    for the DocNamespace means storage components don't have to create a
4107df3775SJim Walker *    new buffer into which we layout DocNamespace and key data.
4207df3775SJim Walker */
4307df3775SJim Walkerclass StoredDocKey : public DocKeyInterface<StoredDocKey> {
4407df3775SJim Walkerpublic:
45dea18910SJim Walker    /**
46dea18910SJim Walker     * Construct empty - required for some std containers
47dea18910SJim Walker     */
48dea18910SJim Walker    StoredDocKey() : keydata() {
49dea18910SJim Walker    }
50dea18910SJim Walker
5107df3775SJim Walker    /**
5207df3775SJim Walker     * Create a StoredDocKey from key to key+nkey in docNamespace.
5307df3775SJim Walker     * @param key pointer to key data
5407df3775SJim Walker     * @param nkey byte length of key (which will be copied in)
5507df3775SJim Walker     * @docNamespace the namespace the key applies to
5607df3775SJim Walker     */
5707df3775SJim Walker    StoredDocKey(const uint8_t* key, size_t nkey, DocNamespace docNamespace) {
5807df3775SJim Walker        keydata.resize(nkey + namespaceBytes);
5907df3775SJim Walker        keydata.replace(
6007df3775SJim Walker                namespaceBytes, nkey, reinterpret_cast<const char*>(key), nkey);
6112e87fb6SDave Rigby        keydata[0] = static_cast<std::underlying_type<DocNamespace>::type>(
6207df3775SJim Walker                docNamespace);
6307df3775SJim Walker    }
6407df3775SJim Walker
6507df3775SJim Walker    /**
6607df3775SJim Walker     * Create a StoredDocKey from a DocKey
6707df3775SJim Walker     * @param key DocKey that is to be copied-in
6807df3775SJim Walker     */
69f734f13fSJim Walker    StoredDocKey(const DocKey& key)
7007df3775SJim Walker        : StoredDocKey(key.data(), key.size(), key.getDocNamespace()) {
7107df3775SJim Walker    }
7207df3775SJim Walker
7307df3775SJim Walker    /**
7407df3775SJim Walker     * Create a StoredDocKey from a std::string. Post
7507df3775SJim Walker     * construction size will return std::string::size();
7607df3775SJim Walker     * @param key std::string to be copied-in
7707df3775SJim Walker     * @param docNamespace the namespace that the key applies to
7807df3775SJim Walker     */
7907df3775SJim Walker    StoredDocKey(const std::string& key, DocNamespace docNamespace)
8007df3775SJim Walker        : StoredDocKey(reinterpret_cast<const uint8_t*>(key.c_str()),
8107df3775SJim Walker                       key.size(),
8207df3775SJim Walker                       docNamespace) {
8307df3775SJim Walker    }
8407df3775SJim Walker
8507df3775SJim Walker    /**
8607df3775SJim Walker     * Create a StoredDocKey from a buffer that was previously
8707df3775SJim Walker     * obtained from getNamespacedData()
8807df3775SJim Walker     * Post construction size() will return nkey - 1
8907df3775SJim Walker     * @param key pointer to data to be copied in
9007df3775SJim Walker     * @param nkey the number of bytes to be copied
9107df3775SJim Walker     */
9207df3775SJim Walker    StoredDocKey(const uint8_t* key, size_t nkey)
9307df3775SJim Walker        : keydata(reinterpret_cast<const char*>(key), nkey) {
9407df3775SJim Walker    }
9507df3775SJim Walker
9607df3775SJim Walker    const uint8_t* data() const {
9707df3775SJim Walker        return reinterpret_cast<const uint8_t*>(
9807df3775SJim Walker                &keydata.data()[namespaceBytes]);
9907df3775SJim Walker    }
10007df3775SJim Walker
10107df3775SJim Walker    size_t size() const {
10207df3775SJim Walker        return keydata.size() - namespaceBytes;
10307df3775SJim Walker    }
10407df3775SJim Walker
10507df3775SJim Walker    DocNamespace getDocNamespace() const {
10612e87fb6SDave Rigby        return DocNamespace(keydata[0]);
10707df3775SJim Walker    }
10807df3775SJim Walker
10907df3775SJim Walker    const char* c_str() const {
11007df3775SJim Walker        return &keydata.c_str()[namespaceBytes];
11107df3775SJim Walker    }
11207df3775SJim Walker
113130763a5SDave Rigby    int compare(const StoredDocKey& rhs) const {
114130763a5SDave Rigby        return keydata.compare(rhs.keydata);
115130763a5SDave Rigby    }
116130763a5SDave Rigby
11707df3775SJim Walker    /**
11807df3775SJim Walker     * Return the key with the DocNamespace as a prefix.
11907df3775SJim Walker     * Thus the "beer::bud" in the Collections namespace becomes "\1beer::bud"
12007df3775SJim Walker     */
12107df3775SJim Walker    const uint8_t* getDocNameSpacedData() const {
12207df3775SJim Walker        return reinterpret_cast<const uint8_t*>(keydata.data());
12307df3775SJim Walker    }
12407df3775SJim Walker
125dea18910SJim Walker    /**
126dea18910SJim Walker     * @return true if StoredDocKey was constructed empty from StoredDocKey()
127dea18910SJim Walker     */
128dea18910SJim Walker    bool empty() const {
129dea18910SJim Walker        return keydata.empty();
130dea18910SJim Walker    }
131dea18910SJim Walker
13207df3775SJim Walker    /**
13307df3775SJim Walker     * Return the size of the buffer returned by getDocNameSpacedData()
13407df3775SJim Walker     */
13507df3775SJim Walker    size_t getDocNameSpacedSize() const {
13607df3775SJim Walker        return keydata.size();
13707df3775SJim Walker    }
13807df3775SJim Walker
13907df3775SJim Walker    bool operator==(const StoredDocKey& rhs) const {
14007df3775SJim Walker        return keydata == rhs.keydata;
14107df3775SJim Walker    }
14207df3775SJim Walker
14307df3775SJim Walker    bool operator!=(const StoredDocKey& rhs) const {
14407df3775SJim Walker        return !(*this == rhs);
14507df3775SJim Walker    }
14607df3775SJim Walker
14707df3775SJim Walker    bool operator<(const StoredDocKey& rhs) const {
14807df3775SJim Walker        return keydata < rhs.keydata;
14907df3775SJim Walker    }
15007df3775SJim Walker
15107df3775SJim Walkerprotected:
15207df3775SJim Walker    static const size_t namespaceBytes = sizeof(DocNamespace);
15307df3775SJim Walker    std::string keydata;
15407df3775SJim Walker};
15507df3775SJim Walker
15607df3775SJim Walkerstd::ostream& operator<<(std::ostream& os, const StoredDocKey& key);
15707df3775SJim Walker
15807df3775SJim Walkerstatic_assert(sizeof(DocNamespace) == sizeof(uint8_t),
15907df3775SJim Walker              "StoredDocKey: DocNamespace is too large");
16007df3775SJim Walker
16107df3775SJim Walker/**
16207df3775SJim Walker * A hash function for StoredDocKey so they can be used in std::map and friends.
16307df3775SJim Walker */
16407df3775SJim Walkernamespace std {
16507df3775SJim Walkertemplate <>
16607df3775SJim Walkerstruct hash<StoredDocKey> {
16707df3775SJim Walker    std::size_t operator()(const StoredDocKey& key) const {
16807df3775SJim Walker        return key.hash();
16907df3775SJim Walker    }
17007df3775SJim Walker};
17107df3775SJim Walker}
1728ca2bbe4SJim Walker
173324c43fcSJim Walkerclass MutationLogEntryV2;
1748ca2bbe4SJim Walkerclass StoredValue;
1758ca2bbe4SJim Walker
1768ca2bbe4SJim Walker/**
1778ca2bbe4SJim Walker * SerialisedDocKey maintains the key data in an allocation that is not owned by
1788ca2bbe4SJim Walker * the class. The class is essentially immutable, providing a "view" onto the
1798ca2bbe4SJim Walker * larger block.
1808ca2bbe4SJim Walker *
1818ca2bbe4SJim Walker * For example where a StoredDocKey needs to exist as part of a bigger block of
1828ca2bbe4SJim Walker * data, SerialisedDocKey is the class to use.
1838ca2bbe4SJim Walker *
1848ca2bbe4SJim Walker * A limited number of classes are friends and only those classes can construct
1858ca2bbe4SJim Walker * a SerialisedDocKey.
1868ca2bbe4SJim Walker */
1878ca2bbe4SJim Walkerclass SerialisedDocKey : public DocKeyInterface<SerialisedDocKey> {
1888ca2bbe4SJim Walkerpublic:
1898ca2bbe4SJim Walker    /**
1908ca2bbe4SJim Walker     * The copy constructor is deleted due to the bytes living outside of the
1918ca2bbe4SJim Walker     * object.
1928ca2bbe4SJim Walker     */
1938ca2bbe4SJim Walker    SerialisedDocKey(const SerialisedDocKey& obj) = delete;
1948ca2bbe4SJim Walker
1958ca2bbe4SJim Walker    const uint8_t* data() const {
1968ca2bbe4SJim Walker        return bytes;
1978ca2bbe4SJim Walker    }
1988ca2bbe4SJim Walker
1998ca2bbe4SJim Walker    size_t size() const {
2008ca2bbe4SJim Walker        return length;
2018ca2bbe4SJim Walker    }
2028ca2bbe4SJim Walker
2038ca2bbe4SJim Walker    DocNamespace getDocNamespace() const {
2048ca2bbe4SJim Walker        return docNamespace;
2058ca2bbe4SJim Walker    }
2068ca2bbe4SJim Walker
2078ca2bbe4SJim Walker    bool operator==(const DocKey rhs) const {
2088ca2bbe4SJim Walker        return size() == rhs.size() &&
2098ca2bbe4SJim Walker               getDocNamespace() == rhs.getDocNamespace() &&
2108ca2bbe4SJim Walker               std::memcmp(data(), rhs.data(), size()) == 0;
2118ca2bbe4SJim Walker    }
2128ca2bbe4SJim Walker
2138ca2bbe4SJim Walker    /**
2148ca2bbe4SJim Walker     * Return how many bytes are (or need to be) allocated to this object
2158ca2bbe4SJim Walker     */
2168ca2bbe4SJim Walker    size_t getObjectSize() const {
2178ca2bbe4SJim Walker        return getObjectSize(length);
2188ca2bbe4SJim Walker    }
2198ca2bbe4SJim Walker
2208ca2bbe4SJim Walker    /**
2218ca2bbe4SJim Walker     * Return how many bytes are needed to store the DocKey
2228ca2bbe4SJim Walker     * @param key a DocKey that needs to be stored in a SerialisedDocKey
2238ca2bbe4SJim Walker     */
2248ca2bbe4SJim Walker    static size_t getObjectSize(const DocKey key) {
2258ca2bbe4SJim Walker        return getObjectSize(key.size());
2268ca2bbe4SJim Walker    }
2278ca2bbe4SJim Walker
2288ca2bbe4SJim Walker    /**
2298ca2bbe4SJim Walker     * Create a SerialisedDocKey and return a unique_ptr to the object.
2308ca2bbe4SJim Walker     * Note that the allocation is bigger than sizeof(SerialisedDocKey)
2318ca2bbe4SJim Walker     * @param key a DocKey to be stored as a SerialisedDocKey
2328ca2bbe4SJim Walker     */
2338ca2bbe4SJim Walker    struct SerialisedDocKeyDelete {
2348ca2bbe4SJim Walker        void operator()(SerialisedDocKey* p) {
2358ca2bbe4SJim Walker            p->~SerialisedDocKey();
2368ca2bbe4SJim Walker            delete[] reinterpret_cast<uint8_t*>(p);
2378ca2bbe4SJim Walker        }
2388ca2bbe4SJim Walker    };
2398ca2bbe4SJim Walker
2408ca2bbe4SJim Walker    static std::unique_ptr<SerialisedDocKey, SerialisedDocKeyDelete> make(
2418ca2bbe4SJim Walker            const DocKey key) {
2428ca2bbe4SJim Walker        std::unique_ptr<SerialisedDocKey, SerialisedDocKeyDelete> rval(
2438ca2bbe4SJim Walker                reinterpret_cast<SerialisedDocKey*>(
2448ca2bbe4SJim Walker                        new uint8_t[getObjectSize(key)]));
2458ca2bbe4SJim Walker        new (rval.get()) SerialisedDocKey(key);
2468ca2bbe4SJim Walker        return rval;
2478ca2bbe4SJim Walker    }
2488ca2bbe4SJim Walker
2498ca2bbe4SJim Walkerprotected:
2508ca2bbe4SJim Walker    /**
2518ca2bbe4SJim Walker     * These following classes are "white-listed". They know how to allocate
2528ca2bbe4SJim Walker     * and construct this object so are allowed access to the constructor.
2538ca2bbe4SJim Walker     */
254324c43fcSJim Walker    friend class MutationLogEntryV2;
2558ca2bbe4SJim Walker    friend class StoredValue;
2568ca2bbe4SJim Walker
2579b1d3292SJim Walker    SerialisedDocKey() : length(0), docNamespace(), bytes() {
2588ca2bbe4SJim Walker    }
2598ca2bbe4SJim Walker
2608ca2bbe4SJim Walker    /**
2618ca2bbe4SJim Walker     * Create a SerialisedDocKey from a DocKey. Protected constructor as this
2628ca2bbe4SJim Walker     * must be used by friends who know how to pre-allocate the object storage.
2638ca2bbe4SJim Walker     * throws length_error if DocKey::len exceeds 255
2648ca2bbe4SJim Walker     * @param key a DocKey to be stored
2658ca2bbe4SJim Walker     */
2668ca2bbe4SJim Walker    SerialisedDocKey(const DocKey key)
267ab711953STim Bradgate        : length(0), docNamespace(key.getDocNamespace()) {
268ab711953STim Bradgate        if (key.size() > std::numeric_limits<uint8_t>::max()) {
2698ca2bbe4SJim Walker            throw std::length_error(
2708ca2bbe4SJim Walker                    "SerialisedDocKey(const DocKey key) " +
27137cf721aSJim Walker                    std::to_string(key.size()) +
27237cf721aSJim Walker                    " exceeds std::numeric_limits<uint8_t>::max()");
2738ca2bbe4SJim Walker        }
274ab711953STim Bradgate        length = gsl::narrow_cast<uint8_t>(key.size());
2758ca2bbe4SJim Walker        // Copy the data into bytes, which should be allocated into a larger
2768ca2bbe4SJim Walker        // buffer.
2772d4b51c0SDave Rigby        std::copy(key.data(), key.data() + key.size(), bytes);
2788ca2bbe4SJim Walker    }
2798ca2bbe4SJim Walker
2809c1a803fSDave Rigby    /**
2819c1a803fSDave Rigby     * Returns the size in bytes of this object - fixed size plus the variable
2829c1a803fSDave Rigby     * length for the bytes making up the key.
2839c1a803fSDave Rigby     */
2848ca2bbe4SJim Walker    static size_t getObjectSize(size_t len) {
2858ca2bbe4SJim Walker        return sizeof(SerialisedDocKey) + len - sizeof(SerialisedDocKey().bytes);
2868ca2bbe4SJim Walker    }
2878ca2bbe4SJim Walker
2888ca2bbe4SJim Walker    uint8_t length;
2898ca2bbe4SJim Walker    DocNamespace docNamespace;
2908ca2bbe4SJim Walker    uint8_t bytes[1];
2918ca2bbe4SJim Walker};
2928ca2bbe4SJim Walker
293b762539bSDave Rigbystd::ostream& operator<<(std::ostream& os, const SerialisedDocKey& key);
294b762539bSDave Rigby
2958ca2bbe4SJim Walkerstatic_assert(std::is_standard_layout<SerialisedDocKey>::value,
2968ca2bbe4SJim Walker              "SeralisedDocKey: must satisfy is_standard_layout");