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 #include <gsl/gsl> 20 21 #include "collections/collections_types.h" 22 23 #pragma once 24 25 namespace Collections { 26 27 /** 28 * Collections::DocKey extends a DocKey so that the length of the collection 29 * can be recorded (the collection is a prefix on the key). 30 * The length of the collection only describes how many bytes are before 31 * the collection separator, whether the collection is valid or not is not 32 * something this class determines. 33 */ 34 class DocKey : public ::DocKey { 35 public: 36 hash() const37 uint32_t hash() const { 38 return ::DocKey::hash(collectionLen); 39 } 40 41 /** 42 * Factory method to create a Collections::DocKey from a DocKey 43 */ make(const ::DocKey& key, const std::string& separator)44 static DocKey make(const ::DocKey& key, const std::string& separator) { 45 switch (key.getDocNamespace()) { 46 case DocNamespace::DefaultCollection: 47 return DocKey(key, 0, 0); 48 case DocNamespace::Collections: { 49 const uint8_t* collection = findCollection(key, separator); 50 if (collection) { 51 return DocKey(key, 52 gsl::narrow<uint8_t>(collection - key.data()), 53 gsl::narrow<uint8_t>(separator.size())); 54 } 55 // No collection found in this key, so return empty (len 0) 56 return DocKey(key, 0, 0); 57 } 58 case DocNamespace::System: 59 throw std::invalid_argument( 60 "DocKey::make incorrect use of SystemKey"); 61 } 62 throw std::invalid_argument("DocKey::make invalid key.namespace: " + 63 std::to_string(int(key.getDocNamespace()))); 64 } 65 66 /** 67 * Factory method to create a Collections::DocKey from a DocKey 68 */ 69 static Collections::DocKey make(const ::DocKey& key); 70 71 /** 72 * @return how many bytes of the key are a collection 73 */ getCollectionLen() const74 size_t getCollectionLen() const { 75 return collectionLen; 76 } 77 78 /** 79 * @return how the length of the separator used in creating this 80 */ getSeparatorLen() const81 size_t getSeparatorLen() const { 82 return separatorLen; 83 } 84 85 /** 86 * @return const_char_buffer representing the collection part of the DocKey 87 */ getCollection() const88 cb::const_char_buffer getCollection() const { 89 return {reinterpret_cast<const char*>(data()), getCollectionLen()}; 90 } 91 92 /** 93 * @return const_char_buffer representing the 'key' part of the DocKey 94 */ getKey() const95 cb::const_char_buffer getKey() const { 96 return {reinterpret_cast<const char*>(data() + getCollectionLen() + 97 getSeparatorLen()), 98 size() - getCollectionLen() - getSeparatorLen()}; 99 } 100 101 private: DocKey(const ::DocKey& key, uint8_t _collectionLen, uint8_t _separatorLen)102 DocKey(const ::DocKey& key, uint8_t _collectionLen, uint8_t _separatorLen) 103 : ::DocKey(key), 104 collectionLen(_collectionLen), 105 separatorLen(_separatorLen) { 106 if (_separatorLen > std::numeric_limits<uint8_t>::max()) { 107 throw std::invalid_argument( 108 "Collections::DocKey invalid separatorLen:" + 109 std::to_string(_separatorLen)); 110 } 111 } 112 113 /** 114 * Perform a search on the DocKey looking for the separator. 115 * 116 * @returns pointer to the start of the separator or nullptr if none found. 117 */ findCollection( const ::DocKey& key, const std::string& separator)118 static const cb::const_byte_buffer::iterator findCollection( 119 const ::DocKey& key, const std::string& separator) { 120 if (key.size() == 0 || separator.size() == 0 || 121 separator.size() > key.size()) { 122 return nullptr; 123 } 124 125 // SystemEvent Doc keys all start with $ (SystemEventFactory::make does) 126 // this. The collections separator could legally be $, so we need to 127 // ensure we skip character 0 to correctly split the key. 128 const int start = key.getDocNamespace() == DocNamespace::System ? 1 : 0; 129 130 auto rv = std::search(key.data() + start, 131 key.data() + key.size(), 132 separator.begin(), 133 separator.end()); 134 if (rv != (key.data() + key.size())) { 135 return rv; 136 } 137 return nullptr; 138 } 139 140 uint8_t collectionLen; 141 uint8_t separatorLen; 142 }; 143 } // end namespace Collections 144 145 namespace std { 146 template <> 147 struct hash<Collections::DocKey> { operator ()std::hash148 std::size_t operator()(const Collections::DocKey& key) const { 149 return key.hash(); 150 } 151 }; 152 } 153