blob: 1801dc3cc5ac35fc946fb3efbdd5715d6b06cbe5 [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
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800205// Find either an existing sector with enough space, or an empty sector.
206// Maintains the invariant that there is always at least 1 empty sector.
207KeyValueStore::SectorMapEntry* KeyValueStore::FindSectorWithSpace(size_t size) {
208 int start = (last_written_sector_ + 1) % sector_map_.size();
209 SectorMapEntry* first_empty_sector = nullptr;
210 bool at_least_two_empty_sectors = false;
211
212 for (size_t i = start; i != last_written_sector_;
213 i = (i + 1) % sector_map_.size()) {
214 SectorMapEntry& sector = sector_map_[i];
215 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
216 return &sector;
217 }
218
219 if (SectorEmpty(sector)) {
220 if (first_empty_sector == nullptr) {
221 first_empty_sector = &sector;
222 } else {
223 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800224 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800225 }
226 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800227
228 if (at_least_two_empty_sectors) {
229 return first_empty_sector;
230 }
231 return nullptr;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800232}
233
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800234Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorMapEntry** sector,
235 size_t size) {
236 *sector = FindSectorWithSpace(size);
237 if (*sector != nullptr) {
238 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800239 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800240 if (options_.partial_gc_on_write) {
241 return GarbageCollectOneSector(sector);
242 }
243 return Status::RESOURCE_EXHAUSTED;
244}
245
246Status KeyValueStore::GarbageCollectOneSector(SectorMapEntry** sector) {
247 // TODO: THIS NEEDS WORK
248 (void)sector;
249 SectorMapEntry* sector_to_gc = FindSectorToGarbageCollect();
250
251 const Address sector_base = SectorBaseAddress(sector_to_gc);
252 const Address sector_end = sector_base + partition_.sector_size_bytes();
253
254 for (auto entry : entries()) {
255 if ((entry.address >= sector_base) && (entry.address < sector_end)) {
256 // RelocateEntry(entry);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800257 }
258 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800259
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800260 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800261 return Status::INTERNAL;
262 }
263
Wyatt Heplerb7609542020-01-24 10:29:54 -0800264 return Status::OK;
265}
266
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800267KeyValueStore::SectorMapEntry* KeyValueStore::FindSectorToGarbageCollect() {
268 // TODO: implement me
269 return nullptr;
270}
271
272Status KeyValueStore::AppendEntry(SectorMapEntry* sector,
273 KeyMapEntry* entry,
274 const string_view key,
275 span<const byte> value) {
276 // write header, key, and value
277 const EntryHeader header(entry_header_format_.magic,
278 CalculateEntryChecksum(header, key, value),
279 key.size(),
280 value.size(),
281 entry->key_version + 1);
282
283 // Handles writing multiple concatenated buffers, while breaking up the writes
284 // into alignment-sized blocks.
285 Address address = NextWritableAddress(sector);
286 TRY_ASSIGN(
287 size_t written,
288 partition_.Write(
289 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
290
291 if (options_.verify_on_write) {
292 TRY(VerifyEntry(sector, entry));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800293 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800294
295 // TODO: UPDATE last_written_sector_ appropriately
296
297 entry->address = address;
298 entry->key_version = header.key_version();
299 sector->valid_bytes += written;
300 sector->tail_free_bytes -= written;
301 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800302}
303
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800304Status KeyValueStore::VerifyEntry(SectorMapEntry* sector, KeyMapEntry* entry) {
305 // TODO: Implement me!
306 (void)sector;
307 (void)entry;
308 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800309}
310
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800311span<const byte> KeyValueStore::CalculateEntryChecksum(
312 const EntryHeader& header,
313 const string_view key,
314 span<const byte> value) const {
315 auto& checksum = *entry_header_format_.checksum;
316
317 checksum.Reset();
318 checksum.Update(header.DataForChecksum());
319 checksum.Update(as_bytes(span(key)));
320 checksum.Update(value.data(), value.size_bytes());
321 return checksum.state();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800322}
323
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800324Status KeyValueStore::RelocateEntry(KeyMapEntry* entry) {
325 // TODO: implement me
326 (void)entry;
327 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800328}
329
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800330} // namespace pw::kvs