1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <cstdint> 18 #include <span> 19 #include <type_traits> 20 21 #include "pw_containers/vector.h" 22 #include "pw_kvs/flash_memory.h" 23 #include "pw_kvs/format.h" 24 #include "pw_kvs/internal/key_descriptor.h" 25 #include "pw_kvs/internal/sectors.h" 26 #include "pw_kvs/key.h" 27 28 namespace pw { 29 namespace kvs { 30 namespace internal { 31 32 // Caches information about a key-value entry. Facilitates quickly finding 33 // entries without having to read flash. 34 class EntryMetadata { 35 public: 36 using Address = FlashPartition::Address; 37 38 EntryMetadata() = default; 39 hash()40 uint32_t hash() const { return descriptor_->key_hash; } 41 transaction_id()42 uint32_t transaction_id() const { return descriptor_->transaction_id; } 43 state()44 EntryState state() const { return descriptor_->state; } 45 46 // The first known address of this entry. first_address()47 uint32_t first_address() const { return addresses_[0]; } 48 49 // All addresses for this entry, including redundant entries, if any. addresses()50 const std::span<Address>& addresses() const { return addresses_; } 51 52 // True if the KeyDesctiptor's transaction ID is newer than the specified ID. IsNewerThan(uint32_t other_transaction_id)53 bool IsNewerThan(uint32_t other_transaction_id) const { 54 // TODO: Consider handling rollover. 55 return transaction_id() > other_transaction_id; 56 } 57 58 // Adds a new address to the entry metadata. MUST NOT be called more times 59 // than allowed by the redundancy. AddNewAddress(Address address)60 void AddNewAddress(Address address) { 61 addresses_[addresses_.size()] = address; 62 addresses_ = std::span<Address>(addresses_.begin(), addresses_.size() + 1); 63 } 64 65 // Remove an address from the entry metadata. 66 void RemoveAddress(Address address_to_remove); 67 68 // Resets the KeyDescrtiptor and addresses to refer to the provided 69 // KeyDescriptor and address. 70 void Reset(const KeyDescriptor& descriptor, Address address); 71 72 private: 73 friend class EntryCache; 74 EntryMetadata(KeyDescriptor & descriptor,std::span<Address> addresses)75 constexpr EntryMetadata(KeyDescriptor& descriptor, 76 std::span<Address> addresses) 77 : descriptor_(&descriptor), addresses_(addresses) {} 78 79 KeyDescriptor* descriptor_; 80 std::span<Address> addresses_; 81 }; 82 83 // Tracks entry metadata. Combines KeyDescriptors and with their associated 84 // addresses. 85 class EntryCache { 86 private: 87 enum Constness : bool { kMutable = false, kConst = true }; 88 89 // Iterates over the EntryCache as EntryMetadata objects. 90 template <Constness kIsConst> 91 class Iterator { 92 public: 93 using value_type = 94 std::conditional_t<kIsConst, const EntryMetadata, EntryMetadata>; 95 96 Iterator& operator++() { 97 ++metadata_.descriptor_; 98 return *this; 99 } 100 Iterator& operator++(int) { return operator++(); } 101 102 // Updates the internal EntryMetadata object. 103 value_type& operator*() const { 104 metadata_.addresses_ = entry_cache_->addresses( 105 metadata_.descriptor_ - entry_cache_->descriptors_.begin()); 106 return metadata_; 107 } 108 value_type* operator->() const { return &operator*(); } 109 110 constexpr bool operator==(const Iterator& rhs) const { 111 return metadata_.descriptor_ == rhs.metadata_.descriptor_; 112 } 113 constexpr bool operator!=(const Iterator& rhs) const { 114 return metadata_.descriptor_ != rhs.metadata_.descriptor_; 115 } 116 117 // Allow non-const to convert to const. 118 operator Iterator<kConst>() const { 119 return {entry_cache_, metadata_.descriptor_}; 120 } 121 122 private: 123 friend class EntryCache; 124 Iterator(const EntryCache * entry_cache,KeyDescriptor * descriptor)125 constexpr Iterator(const EntryCache* entry_cache, KeyDescriptor* descriptor) 126 : entry_cache_(entry_cache), metadata_(*descriptor, {}) {} 127 128 const EntryCache* entry_cache_; 129 130 // Mark this mutable so it can be updated in the const operator*() method. 131 // This allows lazy updating of the EntryMetadata. 132 mutable EntryMetadata metadata_; 133 }; 134 135 public: 136 using iterator = Iterator<kMutable>; 137 using const_iterator = Iterator<kConst>; 138 139 using Address = FlashPartition::Address; 140 141 // The type to use for an address list with the specified number of entries 142 // and redundancy. kRedundancy extra entries are added to make room for a 143 // temporary list of entry addresses. 144 template <size_t kMaxEntries, size_t kRedundancy> 145 using AddressList = Address[kMaxEntries * kRedundancy + kRedundancy]; 146 EntryCache(Vector<KeyDescriptor> & descriptors,Address * addresses,size_t redundancy)147 constexpr EntryCache(Vector<KeyDescriptor>& descriptors, 148 Address* addresses, 149 size_t redundancy) 150 : descriptors_(descriptors), 151 addresses_(addresses), 152 redundancy_(redundancy) {} 153 154 // Clears all KeyDescriptors. Reset()155 void Reset() const { descriptors_.clear(); } 156 157 // Finds the metadata for an entry matching a particular key. Searches for a 158 // KeyDescriptor that matches this key and sets *metadata to point to it if 159 // one is found. 160 // 161 // OK: there is a matching descriptor and *metadata is set 162 // NOT_FOUND: there is no descriptor that matches this key, but this key 163 // has a unique hash (and could potentially be added to the 164 // KVS) 165 // ALREADY_EXISTS: there is no descriptor that matches this key, but the 166 // key's hash collides with the hash for an existing 167 // descriptor 168 // 169 StatusWithSize Find(FlashPartition& partition, 170 const Sectors& sectors, 171 const EntryFormats& formats, 172 Key key, 173 EntryMetadata* metadata) const; 174 175 // Adds a new descriptor to the descriptor list. The entry MUST be unique and 176 // the EntryCache must NOT be full! 177 EntryMetadata AddNew(const KeyDescriptor& entry, Address address) const; 178 179 // Adds a new descriptor, overwrites an existing one, or adds an additional 180 // redundant address to one. The sector size is included for checking that 181 // redundant entries are in different sectors. 182 Status AddNewOrUpdateExisting(const KeyDescriptor& descriptor, 183 Address address, 184 size_t sector_size_bytes) const; 185 186 // Returns a pointer to an array of redundancy() addresses for temporary use. 187 // This is used by the KeyValueStore to track reserved addresses when finding 188 // space for a new entry. TempReservedAddressesForWrite()189 Address* TempReservedAddressesForWrite() const { 190 return &addresses_[descriptors_.max_size() * redundancy_]; 191 } 192 193 // The number of copies of each entry. redundancy()194 size_t redundancy() const { return redundancy_; } 195 196 // True if no more entries can be added to the cache. full()197 bool full() const { return descriptors_.full(); } 198 199 // The total number of entries, including tombstone entries. total_entries()200 size_t total_entries() const { return descriptors_.size(); } 201 202 // The total number of present (non-tombstone) entries. 203 size_t present_entries() const; 204 205 // The maximum number of entries supported by this EntryCache. max_entries()206 size_t max_entries() const { return descriptors_.max_size(); } 207 begin()208 iterator begin() const { return {this, descriptors_.begin()}; } cbegin()209 const_iterator cbegin() const { return {this, descriptors_.begin()}; } 210 end()211 iterator end() const { return {this, descriptors_.end()}; } cend()212 const_iterator cend() const { return {this, descriptors_.end()}; } 213 214 private: 215 int FindIndex(uint32_t key_hash) const; 216 217 // Adds the address to the descriptor at the specified index if there is an 218 // address slot available. 219 void AddAddressIfRoom(size_t descriptor_index, Address address) const; 220 221 // Returns a std::span of the valid addresses for the descriptor. 222 std::span<Address> addresses(size_t descriptor_index) const; 223 first_address(size_t descriptor_index)224 Address* first_address(size_t descriptor_index) const { 225 return &addresses_[descriptor_index * redundancy_]; 226 } 227 228 Address* ResetAddresses(size_t descriptor_index, Address address) const; 229 230 Vector<KeyDescriptor>& descriptors_; 231 FlashPartition::Address* const addresses_; 232 const size_t redundancy_; 233 }; 234 235 } // namespace internal 236 } // namespace kvs 237 } // namespace pw 238