xref: /aosp_15_r20/external/pigweed/pw_kvs/public/pw_kvs/internal/entry.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 
15 // This file defines classes for managing the in-flash format for KVS entires.
16 #pragma once
17 
18 #include <algorithm>
19 #include <array>
20 #include <cstddef>
21 #include <cstdint>
22 #include <string_view>
23 
24 #include "pw_kvs/alignment.h"
25 #include "pw_kvs/checksum.h"
26 #include "pw_kvs/flash_memory.h"
27 #include "pw_kvs/format.h"
28 #include "pw_kvs/internal/hash.h"
29 #include "pw_kvs/internal/key_descriptor.h"
30 #include "pw_span/span.h"
31 
32 namespace pw {
33 namespace kvs {
34 namespace internal {
35 
36 // Entry represents a key-value entry in a flash partition.
37 class Entry {
38  public:
39   static constexpr size_t kMinAlignmentBytes = sizeof(EntryHeader);
40   static constexpr size_t kMaxKeyLength = 0b111111;
41 
42   using Address = FlashPartition::Address;
43 
44   // Buffer capable of holding any valid key (without a null terminator);
45   using KeyBuffer = std::array<char, kMaxKeyLength>;
46 
47   // Returns flash partition Read error codes, or one of the following:
48   //
49   //          OK: successfully read the header and initialized the Entry
50   //   NOT_FOUND: read the header, but the data appears to be erased
51   //   DATA_LOSS: read the header, but it contained invalid data
52   //
53   static Status Read(FlashPartition& partition,
54                      Address address,
55                      const internal::EntryFormats& formats,
56                      Entry* entry);
57 
58   // Reads a key into a buffer, which must be at least key_length bytes.
59   static Status ReadKey(FlashPartition& partition,
60                         Address address,
61                         size_t key_length,
62                         char* key);
63 
64   // Creates a new Entry for a valid (non-deleted) entry.
Valid(FlashPartition & partition,Address address,const EntryFormat & format,std::string_view key,span<const std::byte> value,uint32_t transaction_id)65   static Entry Valid(FlashPartition& partition,
66                      Address address,
67                      const EntryFormat& format,
68                      std::string_view key,
69                      span<const std::byte> value,
70                      uint32_t transaction_id) {
71     return Entry(
72         partition, address, format, key, value, value.size(), transaction_id);
73   }
74 
75   // Creates a new Entry for a tombstone entry, which marks a deleted key.
Tombstone(FlashPartition & partition,Address address,const EntryFormat & format,std::string_view key,uint32_t transaction_id)76   static Entry Tombstone(FlashPartition& partition,
77                          Address address,
78                          const EntryFormat& format,
79                          std::string_view key,
80                          uint32_t transaction_id) {
81     return Entry(partition,
82                  address,
83                  format,
84                  key,
85                  {},
86                  kDeletedValueLength,
87                  transaction_id);
88   }
89 
90   Entry() = default;
91 
descriptor(std::string_view key)92   KeyDescriptor descriptor(std::string_view key) const {
93     return descriptor(Hash(key));
94   }
95 
descriptor(uint32_t key_hash)96   KeyDescriptor descriptor(uint32_t key_hash) const {
97     return KeyDescriptor{key_hash,
98                          transaction_id(),
99                          deleted() ? EntryState::kDeleted : EntryState::kValid};
100   }
101 
102   StatusWithSize Write(std::string_view key, span<const std::byte> value) const;
103 
104   // Changes the format and transcation ID for this entry. In order to calculate
105   // the new checksum, the entire entry is read into a small stack-allocated
106   // buffer. The updated entry may be written to flash using the Copy function.
107   Status Update(const EntryFormat& new_format, uint32_t new_transaction_id);
108 
109   // Writes this entry at a new address. The key and value are read from the
110   // entry's current address. The Entry object's header, which may be newer than
111   // what is in flash, is used.
112   StatusWithSize Copy(Address new_address) const;
113 
114   // Reads a key into a buffer, which must be large enough for a max-length key.
115   // If successful, the size is returned in the StatusWithSize. The key is not
116   // null terminated.
117   template <size_t kSize>
ReadKey(std::array<char,kSize> & key)118   StatusWithSize ReadKey(std::array<char, kSize>& key) const {
119     static_assert(kSize >= kMaxKeyLength);
120     return StatusWithSize(
121         ReadKey(partition(), address_, key_length(), key.data()), key_length());
122   }
123 
124   StatusWithSize ReadValue(span<std::byte> buffer,
125                            size_t offset_bytes = 0) const;
126 
127   Status ValueMatches(span<const std::byte> value) const;
128 
129   Status VerifyChecksum(std::string_view key,
130                         span<const std::byte> value) const;
131 
132   Status VerifyChecksumInFlash() const;
133 
134   // Calculates the total size of an entry, including padding.
size(const FlashPartition & partition,std::string_view key,span<const std::byte> value)135   static size_t size(const FlashPartition& partition,
136                      std::string_view key,
137                      span<const std::byte> value) {
138     return AlignUp(sizeof(EntryHeader) + key.size() + value.size(),
139                    std::max(partition.alignment_bytes(), kMinAlignmentBytes));
140   }
141 
142   // Byte size of overhead (not-key, not-value) in an entry. Does not include
143   // any paddding used to get proper size alignment.
entry_overhead()144   static constexpr size_t entry_overhead() { return sizeof(EntryHeader); }
145 
address()146   Address address() const { return address_; }
147 
set_address(Address address)148   void set_address(Address address) { address_ = address; }
149 
150   // The address at which the next possible entry could be located.
next_address()151   Address next_address() const { return address() + size(); }
152 
153   // Total size of this entry, including padding.
size()154   size_t size() const { return AlignUp(content_size(), alignment_bytes()); }
155 
156   // The length of the key in bytes. Keys are not null terminated.
key_length()157   size_t key_length() const { return header_.key_length_bytes; }
158 
159   // The size of the value, without padding. The size is 0 if this is a
160   // tombstone entry.
value_size()161   size_t value_size() const {
162     return deleted() ? 0u : header_.value_size_bytes;
163   }
164 
magic()165   uint32_t magic() const { return header_.magic; }
166 
transaction_id()167   uint32_t transaction_id() const { return header_.transaction_id; }
168 
169   // True if this is a tombstone entry.
deleted()170   bool deleted() const {
171     return header_.value_size_bytes == kDeletedValueLength;
172   }
173 
174   void DebugLog() const;
175 
176  private:
177   static constexpr uint16_t kDeletedValueLength = 0xFFFF;
178 
179   Entry(FlashPartition& partition,
180         Address address,
181         const EntryFormat& format,
182         std::string_view key,
183         span<const std::byte> value,
184         uint16_t value_size_bytes,
185         uint32_t transaction_id);
186 
Entry(FlashPartition * partition,Address address,const EntryFormat & format,const EntryHeader & header)187   constexpr Entry(FlashPartition* partition,
188                   Address address,
189                   const EntryFormat& format,
190                   const EntryHeader& header)
191       : partition_(partition),
192         address_(address),
193         checksum_algo_(format.checksum),
194         header_(header) {}
195 
partition()196   FlashPartition& partition() const { return *partition_; }
197 
alignment_bytes()198   size_t alignment_bytes() const { return (header_.alignment_units + 1) * 16; }
199 
200   // The total size of the entry, excluding padding.
content_size()201   size_t content_size() const {
202     return sizeof(EntryHeader) + key_length() + value_size();
203   }
204 
checksum_bytes()205   span<const std::byte> checksum_bytes() const {
206     return as_bytes(span<const uint32_t>(&header_.checksum, 1));
207   }
208 
209   span<const std::byte> CalculateChecksum(std::string_view key,
210                                           span<const std::byte> value) const;
211 
212   Status CalculateChecksumFromFlash();
213 
214   // Update the checksum with 0s to pad the entry to its alignment boundary.
215   void AddPaddingBytesToChecksum() const;
216 
alignment_bytes_to_units(size_t alignment_bytes)217   static constexpr uint8_t alignment_bytes_to_units(size_t alignment_bytes) {
218     return (alignment_bytes + 15) / 16 - 1;  // An alignment of 0 is invalid.
219   }
220 
221   FlashPartition* partition_;
222   Address address_;
223   ChecksumAlgorithm* checksum_algo_;
224   EntryHeader header_;
225 };
226 
227 }  // namespace internal
228 }  // namespace kvs
229 }  // namespace pw
230