blob: d3d90013678bd27effba885d3783fe577c82f50b [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
David Rogers2761aeb2020-01-31 17:09:00 -080059 const KeyDescriptor* key_descriptor;
60 TRY(FindKeyDescriptor(key, &key_descriptor));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080061
62 EntryHeader header;
David Rogers2761aeb2020-01-31 17:09:00 -080063 TRY(ReadEntryHeader(*key_descriptor, &header));
64 StatusWithSize result = ReadEntryValue(*key_descriptor, header, value_buffer);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080065
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
David Rogers2761aeb2020-01-31 17:09:00 -080079 KeyDescriptor* key_descriptor;
80 if (FindKeyDescriptor(key, &key_descriptor).ok()) {
81 return WriteEntryForExistingKey(key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080082 }
David Rogers2761aeb2020-01-31 17:09:00 -080083
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080084 return WriteEntryForNewKey(key, value);
85}
86
87Status KeyValueStore::Delete(string_view key) {
88 TRY(InvalidOperation(key));
89
90 return Status::UNIMPLEMENTED;
91}
92
93const KeyValueStore::Entry& KeyValueStore::Iterator::operator*() {
David Rogers2761aeb2020-01-31 17:09:00 -080094 const KeyDescriptor& key_descriptor =
95 entry_.kvs_.key_descriptor_list_[index_];
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080096
97 EntryHeader header;
David Rogers2761aeb2020-01-31 17:09:00 -080098 if (entry_.kvs_.ReadEntryHeader(key_descriptor, &header).ok()) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080099 entry_.kvs_.ReadEntryKey(
David Rogers2761aeb2020-01-31 17:09:00 -0800100 key_descriptor, header.key_length(), entry_.key_buffer_.data());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800101 }
102
103 return entry_;
104}
105
106Status KeyValueStore::InvalidOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800107 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800108 return Status::INVALID_ARGUMENT;
109 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800110 if (!/*enabled_*/ 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800111 return Status::FAILED_PRECONDITION;
112 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800113 return Status::OK;
114}
115
David Rogers2761aeb2020-01-31 17:09:00 -0800116Status KeyValueStore::FindKeyDescriptor(string_view key,
117 const KeyDescriptor** result) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800118 char key_buffer[kMaxKeyLength];
119 const uint32_t hash = HashKey(key);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800120
David Rogers2761aeb2020-01-31 17:09:00 -0800121 for (auto& descriptor : key_descriptors()) {
122 if (descriptor.key_hash == hash) {
123 TRY(ReadEntryKey(descriptor, key.size(), key_buffer));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800124
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800125 if (key == string_view(key_buffer, key.size())) {
David Rogers2761aeb2020-01-31 17:09:00 -0800126 *result = &descriptor;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800127 return Status::OK;
128 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800129 }
130 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800131 return Status::NOT_FOUND;
132}
133
David Rogers2761aeb2020-01-31 17:09:00 -0800134Status KeyValueStore::ReadEntryHeader(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800135 EntryHeader* header) const {
David Rogers2761aeb2020-01-31 17:09:00 -0800136 return partition_.Read(descriptor.address, sizeof(*header), header).status();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800137}
138
David Rogers2761aeb2020-01-31 17:09:00 -0800139Status KeyValueStore::ReadEntryKey(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800140 size_t key_length,
141 char* key) const {
142 // TODO: This check probably shouldn't be here; this is like
143 // checking that the Cortex M's RAM isn't corrupt. This should be
144 // done at boot time.
145 // ^^ This argument sometimes comes from EntryHeader::key_value_len,
146 // which is read directly from flash. If it's corrupted, we shouldn't try
147 // to read a bunch of extra data.
148 if (key_length == 0u || key_length > kMaxKeyLength) {
149 return Status::DATA_LOSS;
150 }
151 // The key is immediately after the entry header.
David Rogers2761aeb2020-01-31 17:09:00 -0800152 return partition_
153 .Read(descriptor.address + sizeof(EntryHeader), key_length, key)
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800154 .status();
155}
156
David Rogers2761aeb2020-01-31 17:09:00 -0800157StatusWithSize KeyValueStore::ReadEntryValue(
158 const KeyDescriptor& key_descriptor,
159 const EntryHeader& header,
160 span<byte> value) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800161 const size_t read_size = std::min(header.value_length(), value.size());
David Rogers2761aeb2020-01-31 17:09:00 -0800162 StatusWithSize result = partition_.Read(
163 key_descriptor.address + sizeof(header) + header.key_length(),
164 value.subspan(read_size));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800165 TRY(result);
166 if (read_size != header.value_length()) {
167 return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
168 }
169 return StatusWithSize(read_size);
170}
171
172Status KeyValueStore::ValidateEntryChecksum(const EntryHeader& header,
173 string_view key,
174 span<const byte> value) const {
175 CalculateEntryChecksum(header, key, value);
176 return entry_header_format_.checksum->Verify(header.checksum());
177}
178
David Rogers2761aeb2020-01-31 17:09:00 -0800179Status KeyValueStore::WriteEntryForExistingKey(KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800180 string_view key,
181 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800182 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800183 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
David Rogers2761aeb2020-01-31 17:09:00 -0800184 return AppendEntry(sector, key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800185}
186
187Status KeyValueStore::WriteEntryForNewKey(string_view key,
188 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800189 if (KeyListFull()) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800190 // TODO: Log, and also expose "in memory keymap full" in stats dump.
191 return Status::RESOURCE_EXHAUSTED;
192 }
193
David Rogers2761aeb2020-01-31 17:09:00 -0800194 // Modify the key descriptor at the end of the array, without bumping the map
195 // size so the key descriptor is prepared and written without committing
196 // first.
197 KeyDescriptor& key_descriptor =
198 key_descriptor_list_[key_descriptor_list_size_];
199 key_descriptor.key_hash = HashKey(key);
200 key_descriptor.key_version = 0; // will be incremented by AppendEntry()
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800201
David Rogers2761aeb2020-01-31 17:09:00 -0800202 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800203 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
David Rogers2761aeb2020-01-31 17:09:00 -0800204 TRY(AppendEntry(sector, &key_descriptor, key, value));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800205
David Rogers2761aeb2020-01-31 17:09:00 -0800206 // Only increment key_descriptor_list_size_ when we are certain the write
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800207 // succeeded.
David Rogers2761aeb2020-01-31 17:09:00 -0800208 key_descriptor_list_size_ += 1;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800209 return Status::OK;
210}
211
David Rogers2761aeb2020-01-31 17:09:00 -0800212Status KeyValueStore::RelocateEntry(KeyDescriptor& key_descriptor) {
David Rogersa12786b2020-01-31 16:02:33 -0800213 // TODO: implement me
David Rogers2761aeb2020-01-31 17:09:00 -0800214 (void)key_descriptor;
David Rogersa12786b2020-01-31 16:02:33 -0800215 return Status::UNIMPLEMENTED;
216}
217
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800218// Find either an existing sector with enough space, or an empty sector.
219// Maintains the invariant that there is always at least 1 empty sector.
David Rogers2761aeb2020-01-31 17:09:00 -0800220KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorWithSpace(
221 size_t size) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800222 int start = (last_written_sector_ + 1) % sector_map_.size();
David Rogers2761aeb2020-01-31 17:09:00 -0800223 SectorDescriptor* first_empty_sector = nullptr;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800224 bool at_least_two_empty_sectors = false;
225
226 for (size_t i = start; i != last_written_sector_;
227 i = (i + 1) % sector_map_.size()) {
David Rogers2761aeb2020-01-31 17:09:00 -0800228 SectorDescriptor& sector = sector_map_[i];
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800229 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
230 return &sector;
231 }
232
233 if (SectorEmpty(sector)) {
234 if (first_empty_sector == nullptr) {
235 first_empty_sector = &sector;
236 } else {
237 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800238 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800239 }
240 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800241
242 if (at_least_two_empty_sectors) {
243 return first_empty_sector;
244 }
245 return nullptr;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800246}
247
David Rogers2761aeb2020-01-31 17:09:00 -0800248Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorDescriptor** sector,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800249 size_t size) {
250 *sector = FindSectorWithSpace(size);
251 if (*sector != nullptr) {
252 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800253 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800254 if (options_.partial_gc_on_write) {
255 return GarbageCollectOneSector(sector);
256 }
257 return Status::RESOURCE_EXHAUSTED;
258}
259
David Rogers2761aeb2020-01-31 17:09:00 -0800260KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorToGarbageCollect() {
261 SectorDescriptor* sector_candidate = nullptr;
David Rogersa12786b2020-01-31 16:02:33 -0800262 size_t candidate_bytes = 0;
263
264 // Step 1: Try to find a sectors with stale keys and no valid keys (no
265 // relocation needed). If any such sectors are found, use the sector with the
266 // most reclaimable bytes.
267 for (auto& sector : sector_map_) {
268 if ((sector.valid_bytes == 0) &&
269 (RecoverableBytes(sector) > candidate_bytes)) {
270 sector_candidate = &sector;
271 candidate_bytes = RecoverableBytes(sector);
272 }
273 }
274
275 // Step 2: If step 1 yields no sectors, just find the sector with the most
276 // reclaimable bytes.
277 if (sector_candidate == nullptr) {
278 for (auto& sector : sector_map_) {
279 if (RecoverableBytes(sector) > candidate_bytes) {
280 sector_candidate = &sector;
281 candidate_bytes = RecoverableBytes(sector);
282 }
283 }
284 }
285
286 return sector_candidate;
287}
288
David Rogers2761aeb2020-01-31 17:09:00 -0800289Status KeyValueStore::GarbageCollectOneSector(SectorDescriptor** sector) {
David Rogersa12786b2020-01-31 16:02:33 -0800290 // Step 1: Find the sector to garbage collect
David Rogers2761aeb2020-01-31 17:09:00 -0800291 SectorDescriptor* sector_to_gc = FindSectorToGarbageCollect();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800292
David Rogersa12786b2020-01-31 16:02:33 -0800293 if (sector_to_gc == nullptr) {
294 return Status::RESOURCE_EXHAUSTED;
295 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800296
David Rogersa12786b2020-01-31 16:02:33 -0800297 // Step 2: Move any valid entries in the GC sector to other sectors
298 if (sector_to_gc->valid_bytes != 0) {
David Rogers2761aeb2020-01-31 17:09:00 -0800299 for (auto& descriptor : key_descriptors()) {
300 if (AddressInSector(*sector_to_gc, descriptor.address)) {
301 TRY(RelocateEntry(descriptor));
David Rogersa12786b2020-01-31 16:02:33 -0800302 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800303 }
304 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800305
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800306 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800307 return Status::INTERNAL;
308 }
309
David Rogersa12786b2020-01-31 16:02:33 -0800310 // Step 3: Reinitialize the sector
311 sector_to_gc->tail_free_bytes = 0;
312 TRY(partition_.Erase(SectorBaseAddress(sector_to_gc), 1));
313 sector_to_gc->tail_free_bytes = partition_.sector_size_bytes();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800314
David Rogersa12786b2020-01-31 16:02:33 -0800315 *sector = sector_to_gc;
316 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800317}
318
David Rogers2761aeb2020-01-31 17:09:00 -0800319Status KeyValueStore::AppendEntry(SectorDescriptor* sector,
320 KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800321 const string_view key,
322 span<const byte> value) {
323 // write header, key, and value
324 const EntryHeader header(entry_header_format_.magic,
325 CalculateEntryChecksum(header, key, value),
326 key.size(),
327 value.size(),
David Rogers2761aeb2020-01-31 17:09:00 -0800328 key_descriptor->key_version + 1);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800329
330 // Handles writing multiple concatenated buffers, while breaking up the writes
331 // into alignment-sized blocks.
332 Address address = NextWritableAddress(sector);
333 TRY_ASSIGN(
334 size_t written,
335 partition_.Write(
336 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
337
338 if (options_.verify_on_write) {
David Rogers2761aeb2020-01-31 17:09:00 -0800339 TRY(VerifyEntry(sector, key_descriptor));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800340 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800341
342 // TODO: UPDATE last_written_sector_ appropriately
343
David Rogers2761aeb2020-01-31 17:09:00 -0800344 key_descriptor->address = address;
345 key_descriptor->key_version = header.key_version();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800346 sector->valid_bytes += written;
347 sector->tail_free_bytes -= written;
348 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800349}
350
David Rogers2761aeb2020-01-31 17:09:00 -0800351Status KeyValueStore::VerifyEntry(SectorDescriptor* sector,
352 KeyDescriptor* key_descriptor) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800353 // TODO: Implement me!
354 (void)sector;
David Rogers2761aeb2020-01-31 17:09:00 -0800355 (void)key_descriptor;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800356 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800357}
358
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800359span<const byte> KeyValueStore::CalculateEntryChecksum(
360 const EntryHeader& header,
361 const string_view key,
362 span<const byte> value) const {
363 auto& checksum = *entry_header_format_.checksum;
364
365 checksum.Reset();
366 checksum.Update(header.DataForChecksum());
367 checksum.Update(as_bytes(span(key)));
368 checksum.Update(value.data(), value.size_bytes());
369 return checksum.state();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800370}
371
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800372} // namespace pw::kvs