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