1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 // This file defines classes for managing the in-flash format for KVS entires.
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_kvs/key.h"
36 // Entry represents a key-value entry in a flash partition.
39 static constexpr size_t kMinAlignmentBytes = sizeof(EntryHeader);
40 static constexpr size_t kMaxKeyLength = 0b111111;
42 using Address = FlashPartition::Address;
44 // Buffer capable of holding any valid key (without a null terminator);
45 using KeyBuffer = std::array<char, kMaxKeyLength>;
47 // Returns flash partition Read error codes, or one of the following:
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
53 static Status Read(FlashPartition& partition,
55 const internal::EntryFormats& formats,
58 // Reads a key into a buffer, which must be at least key_length bytes.
59 static Status ReadKey(FlashPartition& partition,
64 // Creates a new Entry for a valid (non-deleted) entry.
65 static Entry Valid(FlashPartition& partition,
67 const EntryFormat& format,
69 std::span<const std::byte> value,
70 uint32_t transaction_id) {
72 partition, address, format, key, value, value.size(), transaction_id);
75 // Creates a new Entry for a tombstone entry, which marks a deleted key.
76 static Entry Tombstone(FlashPartition& partition,
78 const EntryFormat& format,
80 uint32_t transaction_id) {
81 return Entry(partition,
92 KeyDescriptor descriptor(Key key) const { return descriptor(Hash(key)); }
94 KeyDescriptor descriptor(uint32_t key_hash) const {
95 return KeyDescriptor{key_hash,
97 deleted() ? EntryState::kDeleted : EntryState::kValid};
100 StatusWithSize Write(Key key, std::span<const std::byte> value) const;
102 // Changes the format and transcation ID for this entry. In order to calculate
103 // the new checksum, the entire entry is read into a small stack-allocated
104 // buffer. The updated entry may be written to flash using the Copy function.
105 Status Update(const EntryFormat& new_format, uint32_t new_transaction_id);
107 // Writes this entry at a new address. The key and value are read from the
108 // entry's current address. The Entry object's header, which may be newer than
109 // what is in flash, is used.
110 StatusWithSize Copy(Address new_address) const;
112 // Reads a key into a buffer, which must be large enough for a max-length key.
113 // If successful, the size is returned in the StatusWithSize. The key is not
115 template <size_t kSize>
116 StatusWithSize ReadKey(std::array<char, kSize>& key) const {
117 static_assert(kSize >= kMaxKeyLength);
118 return StatusWithSize(
119 ReadKey(partition(), address_, key_length(), key.data()), key_length());
122 StatusWithSize ReadValue(std::span<std::byte> buffer,
123 size_t offset_bytes = 0) const;
125 Status ValueMatches(std::span<const std::byte> value) const;
127 Status VerifyChecksum(Key key, std::span<const std::byte> value) const;
129 Status VerifyChecksumInFlash() const;
131 // Calculates the total size of an entry, including padding.
132 static size_t size(const FlashPartition& partition,
134 std::span<const std::byte> value) {
135 return AlignUp(sizeof(EntryHeader) + key.size() + value.size(),
136 std::max(partition.alignment_bytes(), kMinAlignmentBytes));
139 // Byte size of overhead (not-key, not-value) in an entry. Does not include
140 // any paddding used to get proper size alignment.
141 static constexpr size_t entry_overhead() { return sizeof(EntryHeader); }
143 Address address() const { return address_; }
145 void set_address(Address address) { address_ = address; }
147 // The address at which the next possible entry could be located.
148 Address next_address() const { return address() + size(); }
150 // Total size of this entry, including padding.
151 size_t size() const { return AlignUp(content_size(), alignment_bytes()); }
153 // The length of the key in bytes. Keys are not null terminated.
154 size_t key_length() const { return header_.key_length_bytes; }
156 // The size of the value, without padding. The size is 0 if this is a
158 size_t value_size() const {
159 return deleted() ? 0u : header_.value_size_bytes;
162 uint32_t magic() const { return header_.magic; }
164 uint32_t transaction_id() const { return header_.transaction_id; }
166 // True if this is a tombstone entry.
167 bool deleted() const {
168 return header_.value_size_bytes == kDeletedValueLength;
171 void DebugLog() const;
174 static constexpr uint16_t kDeletedValueLength = 0xFFFF;
176 Entry(FlashPartition& partition,
178 const EntryFormat& format,
180 std::span<const std::byte> value,
181 uint16_t value_size_bytes,
182 uint32_t transaction_id);
184 constexpr Entry(FlashPartition* partition,
186 const EntryFormat& format,
188 : partition_(partition),
190 checksum_algo_(format.checksum),
193 FlashPartition& partition() const { return *partition_; }
195 size_t alignment_bytes() const { return (header_.alignment_units + 1) * 16; }
197 // The total size of the entry, excluding padding.
198 size_t content_size() const {
199 return sizeof(EntryHeader) + key_length() + value_size();
202 std::span<const std::byte> checksum_bytes() const {
203 return std::as_bytes(std::span<const uint32_t>(&header_.checksum, 1));
206 std::span<const std::byte> CalculateChecksum(
207 Key key, std::span<const std::byte> value) const;
209 Status CalculateChecksumFromFlash();
211 // Update the checksum with 0s to pad the entry to its alignment boundary.
212 void AddPaddingBytesToChecksum() const;
214 static constexpr uint8_t alignment_bytes_to_units(size_t alignment_bytes) {
215 return (alignment_bytes + 15) / 16 - 1; // An alignment of 0 is invalid.
218 FlashPartition* partition_;
220 ChecksumAlgorithm* checksum_algo_;
224 } // namespace internal