blob: 7c83ecadbdc424b276d65cf78a65c31290e889a4 [file] [log] [blame]
Wyatt Heplerb7609542020-01-24 10:29:54 -08001// 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
Wyatt Heplerb7609542020-01-24 10:29:54 -080015#include "pw_kvs/key_value_store.h"
16
17#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080018#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080019
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080020#include "pw_kvs_private/format.h"
21#include "pw_kvs_private/macros.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080022
Wyatt Hepler2ad60672020-01-21 08:00:16 -080023namespace pw::kvs {
Wyatt Heplerb7609542020-01-24 10:29:54 -080024
Wyatt Hepleracaacf92020-01-24 10:58:30 -080025using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080026using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080027
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080028namespace {
29
30// constexpr uint32_t SixFiveFiveNineNine(std::string_view string) {
31constexpr uint32_t HashKey(std::string_view string) {
32 uint32_t hash = 0;
33 uint32_t coefficient = 65599u;
34
35 for (char ch : string) {
36 hash += coefficient * unsigned(ch);
37 coefficient *= 65599u;
Wyatt Heplerb7609542020-01-24 10:29:54 -080038 }
39
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080040 return hash;
Wyatt Heplerb7609542020-01-24 10:29:54 -080041}
42
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080043constexpr size_t EntrySize(string_view key, span<const byte> value) {
44 return sizeof(EntryHeader) + key.size() + value.size();
45}
46
47} // namespace
48
49Status KeyValueStore::Init() {
50 // enabled_ = true;
51
52 return Status::UNIMPLEMENTED;
53}
54
55StatusWithSize KeyValueStore::Get(string_view key,
56 span<byte> value_buffer) const {
57 TRY(InvalidOperation(key));
58
59 const KeyMapEntry* key_map_entry;
60 TRY(FindKeyMapEntry(key, &key_map_entry));
61
62 EntryHeader header;
63 TRY(ReadEntryHeader(*key_map_entry, &header));
64 StatusWithSize result = ReadEntryValue(*key_map_entry, header, value_buffer);
65
66 if (result.ok() && options_.verify_on_read) {
67 return ValidateEntryChecksum(header, key, value_buffer);
68 }
69 return result;
70}
71
72Status KeyValueStore::Put(string_view key, span<const byte> value) {
73 TRY(InvalidOperation(key));
74
75 if (value.size() > (1 << 24)) {
76 // TODO: Reject sizes that are larger than the maximum?
77 }
78
79 if (KeyMapEntry * entry; FindKeyMapEntry(key, &entry).ok()) {
80 return WriteEntryForExistingKey(entry, key, value);
81 }
82 return WriteEntryForNewKey(key, value);
83}
84
85Status KeyValueStore::Delete(string_view key) {
86 TRY(InvalidOperation(key));
87
88 return Status::UNIMPLEMENTED;
89}
90
91const KeyValueStore::Entry& KeyValueStore::Iterator::operator*() {
92 const KeyMapEntry& map_entry = entry_.kvs_.key_map_[index_];
93
94 EntryHeader header;
95 if (entry_.kvs_.ReadEntryHeader(map_entry, &header).ok()) {
96 entry_.kvs_.ReadEntryKey(
97 map_entry, header.key_length(), entry_.key_buffer_.data());
98 }
99
100 return entry_;
101}
102
103Status KeyValueStore::InvalidOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800104 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800105 return Status::INVALID_ARGUMENT;
106 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800107 if (!/*enabled_*/ 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800108 return Status::FAILED_PRECONDITION;
109 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800110 return Status::OK;
111}
112
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800113Status KeyValueStore::FindKeyMapEntry(string_view key,
114 const KeyMapEntry** result) const {
115 char key_buffer[kMaxKeyLength];
116 const uint32_t hash = HashKey(key);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800117
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800118 for (auto& entry : entries()) {
119 if (entry.key_hash == hash) {
120 TRY(ReadEntryKey(entry, key.size(), key_buffer));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800121
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800122 if (key == string_view(key_buffer, key.size())) {
123 *result = &entry;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800124 return Status::OK;
125 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800126 }
127 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800128 return Status::NOT_FOUND;
129}
130
131Status KeyValueStore::ReadEntryHeader(const KeyMapEntry& entry,
132 EntryHeader* header) const {
133 return partition_.Read(entry.address, sizeof(*header), header).status();
134}
135
136Status KeyValueStore::ReadEntryKey(const KeyMapEntry& entry,
137 size_t key_length,
138 char* key) const {
139 // TODO: This check probably shouldn't be here; this is like
140 // checking that the Cortex M's RAM isn't corrupt. This should be
141 // done at boot time.
142 // ^^ This argument sometimes comes from EntryHeader::key_value_len,
143 // which is read directly from flash. If it's corrupted, we shouldn't try
144 // to read a bunch of extra data.
145 if (key_length == 0u || key_length > kMaxKeyLength) {
146 return Status::DATA_LOSS;
147 }
148 // The key is immediately after the entry header.
149 return partition_.Read(entry.address + sizeof(EntryHeader), key_length, key)
150 .status();
151}
152
153StatusWithSize KeyValueStore::ReadEntryValue(const KeyMapEntry& entry,
154 const EntryHeader& header,
155 span<byte> value) const {
156 const size_t read_size = std::min(header.value_length(), value.size());
157 StatusWithSize result =
158 partition_.Read(entry.address + sizeof(header) + header.key_length(),
159 value.subspan(read_size));
160 TRY(result);
161 if (read_size != header.value_length()) {
162 return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
163 }
164 return StatusWithSize(read_size);
165}
166
167Status KeyValueStore::ValidateEntryChecksum(const EntryHeader& header,
168 string_view key,
169 span<const byte> value) const {
170 CalculateEntryChecksum(header, key, value);
171 return entry_header_format_.checksum->Verify(header.checksum());
172}
173
174Status KeyValueStore::WriteEntryForExistingKey(KeyMapEntry* key_map_entry,
175 string_view key,
176 span<const byte> value) {
177 SectorMapEntry* sector;
178 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
179 return AppendEntry(sector, key_map_entry, key, value);
180}
181
182Status KeyValueStore::WriteEntryForNewKey(string_view key,
183 span<const byte> value) {
184 if (EntryMapFull()) {
185 // TODO: Log, and also expose "in memory keymap full" in stats dump.
186 return Status::RESOURCE_EXHAUSTED;
187 }
188
189 // Modify the entry at the end of the array, without bumping the map size
190 // so the entry is prepared and written without committing first.
191 KeyMapEntry& entry = key_map_[key_map_size_];
192 entry.key_hash = HashKey(key);
193 entry.key_version = 0; // will be incremented by AppendEntry()
194
195 SectorMapEntry* sector;
196 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
197 TRY(AppendEntry(sector, &entry, key, value));
198
199 // Only increment key_map_size_ when we are certain the write
200 // succeeded.
201 key_map_size_ += 1;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800202 return Status::OK;
203}
204
David Rogersa12786b2020-01-31 16:02:33 -0800205Status KeyValueStore::RelocateEntry(KeyMapEntry& entry) {
206 // TODO: implement me
207 (void)entry;
208 return Status::UNIMPLEMENTED;
209}
210
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800211// Find either an existing sector with enough space, or an empty sector.
212// Maintains the invariant that there is always at least 1 empty sector.
213KeyValueStore::SectorMapEntry* KeyValueStore::FindSectorWithSpace(size_t size) {
214 int start = (last_written_sector_ + 1) % sector_map_.size();
215 SectorMapEntry* first_empty_sector = nullptr;
216 bool at_least_two_empty_sectors = false;
217
218 for (size_t i = start; i != last_written_sector_;
219 i = (i + 1) % sector_map_.size()) {
220 SectorMapEntry& sector = sector_map_[i];
221 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
222 return &sector;
223 }
224
225 if (SectorEmpty(sector)) {
226 if (first_empty_sector == nullptr) {
227 first_empty_sector = &sector;
228 } else {
229 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800230 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800231 }
232 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800233
234 if (at_least_two_empty_sectors) {
235 return first_empty_sector;
236 }
237 return nullptr;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800238}
239
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800240Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorMapEntry** sector,
241 size_t size) {
242 *sector = FindSectorWithSpace(size);
243 if (*sector != nullptr) {
244 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800245 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800246 if (options_.partial_gc_on_write) {
247 return GarbageCollectOneSector(sector);
248 }
249 return Status::RESOURCE_EXHAUSTED;
250}
251
David Rogersa12786b2020-01-31 16:02:33 -0800252KeyValueStore::SectorMapEntry* KeyValueStore::FindSectorToGarbageCollect() {
253 SectorMapEntry* sector_candidate = nullptr;
254 size_t candidate_bytes = 0;
255
256 // Step 1: Try to find a sectors with stale keys and no valid keys (no
257 // relocation needed). If any such sectors are found, use the sector with the
258 // most reclaimable bytes.
259 for (auto& sector : sector_map_) {
260 if ((sector.valid_bytes == 0) &&
261 (RecoverableBytes(sector) > candidate_bytes)) {
262 sector_candidate = &sector;
263 candidate_bytes = RecoverableBytes(sector);
264 }
265 }
266
267 // Step 2: If step 1 yields no sectors, just find the sector with the most
268 // reclaimable bytes.
269 if (sector_candidate == nullptr) {
270 for (auto& sector : sector_map_) {
271 if (RecoverableBytes(sector) > candidate_bytes) {
272 sector_candidate = &sector;
273 candidate_bytes = RecoverableBytes(sector);
274 }
275 }
276 }
277
278 return sector_candidate;
279}
280
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800281Status KeyValueStore::GarbageCollectOneSector(SectorMapEntry** sector) {
David Rogersa12786b2020-01-31 16:02:33 -0800282 // Step 1: Find the sector to garbage collect
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800283 SectorMapEntry* sector_to_gc = FindSectorToGarbageCollect();
284
David Rogersa12786b2020-01-31 16:02:33 -0800285 if (sector_to_gc == nullptr) {
286 return Status::RESOURCE_EXHAUSTED;
287 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800288
David Rogersa12786b2020-01-31 16:02:33 -0800289 // Step 2: Move any valid entries in the GC sector to other sectors
290 if (sector_to_gc->valid_bytes != 0) {
291 for (auto& entry : entries()) {
292 if (AddressInSector(*sector_to_gc, entry.address)) {
293 TRY(RelocateEntry(entry));
294 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800295 }
296 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800297
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800298 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800299 return Status::INTERNAL;
300 }
301
David Rogersa12786b2020-01-31 16:02:33 -0800302 // Step 3: Reinitialize the sector
303 sector_to_gc->tail_free_bytes = 0;
304 TRY(partition_.Erase(SectorBaseAddress(sector_to_gc), 1));
305 sector_to_gc->tail_free_bytes = partition_.sector_size_bytes();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800306
David Rogersa12786b2020-01-31 16:02:33 -0800307 *sector = sector_to_gc;
308 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800309}
310
311Status KeyValueStore::AppendEntry(SectorMapEntry* sector,
312 KeyMapEntry* entry,
313 const string_view key,
314 span<const byte> value) {
315 // write header, key, and value
316 const EntryHeader header(entry_header_format_.magic,
317 CalculateEntryChecksum(header, key, value),
318 key.size(),
319 value.size(),
320 entry->key_version + 1);
321
322 // Handles writing multiple concatenated buffers, while breaking up the writes
323 // into alignment-sized blocks.
324 Address address = NextWritableAddress(sector);
325 TRY_ASSIGN(
326 size_t written,
327 partition_.Write(
328 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
329
330 if (options_.verify_on_write) {
331 TRY(VerifyEntry(sector, entry));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800332 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800333
334 // TODO: UPDATE last_written_sector_ appropriately
335
336 entry->address = address;
337 entry->key_version = header.key_version();
338 sector->valid_bytes += written;
339 sector->tail_free_bytes -= written;
340 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800341}
342
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800343Status KeyValueStore::VerifyEntry(SectorMapEntry* sector, KeyMapEntry* entry) {
344 // TODO: Implement me!
345 (void)sector;
346 (void)entry;
347 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800348}
349
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800350span<const byte> KeyValueStore::CalculateEntryChecksum(
351 const EntryHeader& header,
352 const string_view key,
353 span<const byte> value) const {
354 auto& checksum = *entry_header_format_.checksum;
355
356 checksum.Reset();
357 checksum.Update(header.DataForChecksum());
358 checksum.Update(as_bytes(span(key)));
359 checksum.Update(value.data(), value.size_bytes());
360 return checksum.state();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800361}
362
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800363} // namespace pw::kvs