pw_kvs: Add implementation for RelocateEntry()
Add implementation for RelocateEntry().
Add 4K buffer member to the KeyValueStore class for use with relocation
and general use.
Change-Id: I29d30fe539959464be8ac624454ff4c94c3cc54f
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index f106f9f..86b8bf4 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -49,6 +49,10 @@
return sizeof(EntryHeader) + key.size() + value.size();
}
+constexpr size_t EntrySize(const EntryHeader& header) {
+ return sizeof(EntryHeader) + header.key_length() + header.value_length();
+}
+
} // namespace
Status KeyValueStore::Init() {
@@ -58,6 +62,14 @@
const size_t sector_size_bytes = partition_.sector_size_bytes();
const size_t sector_count = partition_.sector_count();
+ if (working_buffer_.size() < sector_size_bytes) {
+ CRT("ERROR: working_buffer_ (%zu bytes) is smaller than sector "
+ "size (%zu bytes)",
+ working_buffer_.size(),
+ sector_size_bytes);
+ return Status::INVALID_ARGUMENT;
+ }
+
DBG("First pass: Read all entries from all sectors");
for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
// Track writable bytes in this sector. Updated after reading each entry.
@@ -432,10 +444,42 @@
}
Status KeyValueStore::RelocateEntry(KeyDescriptor& key_descriptor) {
- // TODO: Implement this function!
- (void)key_descriptor;
- return Status::OK;
- return Status::UNIMPLEMENTED;
+ struct TempEntry {
+ std::array<char, kMaxKeyLength + 1> key;
+ std::array<char, sizeof(working_buffer_) - sizeof(key)> value;
+ };
+ TempEntry* entry = reinterpret_cast<TempEntry*>(working_buffer_.data());
+
+ // Read the entry to be relocated. Store the header in a local variable and
+ // store the key and value in the TempEntry stored in the static allocated
+ // working_buffer_.
+ EntryHeader header;
+ TRY(ReadEntryHeader(key_descriptor, &header));
+ TRY(ReadEntryKey(key_descriptor, header.key_length(), entry->key.data()));
+ string_view key = string_view(entry->key.data(), header.key_length());
+ StatusWithSize result = ReadEntryValue(
+ key_descriptor, header, as_writable_bytes(span(entry->value)));
+ if (!result.status().ok()) {
+ return Status::INTERNAL;
+ }
+
+ auto value = span(entry->value.data(), result.size());
+
+ TRY(header.VerifyChecksum(
+ entry_header_format_.checksum, key, as_bytes(value)));
+
+ SectorDescriptor* old_sector = SectorFromAddress(key_descriptor.address);
+ if (old_sector == nullptr) {
+ return Status::INTERNAL;
+ }
+
+ // Find a new sector for the entry and write it to the new location.
+ SectorDescriptor* new_sector =
+ FindSectorWithSpace(EntrySize(header), old_sector, true);
+ if (new_sector == nullptr) {
+ return Status::RESOURCE_EXHAUSTED;
+ }
+ return AppendEntry(new_sector, &key_descriptor, key, as_bytes(value));
}
// Find either an existing sector with enough space that is not the sector to
@@ -479,7 +523,7 @@
}
if (at_least_two_empty_sectors) {
- DBG("Found a empty sector and a spare one; returning the first found (%d)",
+ DBG("Found a usable empty sector; returning the first found (%d)",
static_cast<int>(first_empty_sector - sector_map_.data()));
return first_empty_sector;
}
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index 0c974f2..ff851f8 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -69,6 +69,7 @@
static constexpr size_t kMaxKeyLength = 64;
static constexpr size_t kMaxEntries = 64;
static constexpr size_t kUsableSectors = 64;
+ static constexpr size_t kWorkingBufferSizeBytes = (4 * 1024);
// +1 for null-terminator.
using KeyBuffer = std::array<char, kMaxKeyLength + 1>;
@@ -84,7 +85,8 @@
key_descriptor_list_{},
key_descriptor_list_size_(0),
sector_map_{},
- last_written_sector_(0) {}
+ last_written_sector_(0),
+ working_buffer_{} {}
Status Init();
bool initialized() const { return false; } // TODO: Implement this
@@ -304,6 +306,10 @@
return (sector - sector_map_.data()) * partition_.sector_size_bytes();
}
+ SectorDescriptor* SectorFromAddress(Address address) {
+ return §or_map_[address / partition_.sector_size_bytes()];
+ }
+
Address NextWritableAddress(SectorDescriptor* sector) const {
return SectorBaseAddress(sector) + partition_.sector_size_bytes() -
sector->tail_free_bytes;
@@ -334,6 +340,11 @@
size_t last_written_sector_;
bool enabled_ = false;
+
+ // Working buffer is a general purpose buffer for operations (such as init or
+ // relcate) to use for working space to remove the need to allocate temporary
+ // space.
+ std::array<char, kWorkingBufferSizeBytes> working_buffer_;
};
} // namespace pw::kvs
diff --git a/pw_kvs/pw_kvs_private/format.h b/pw_kvs/pw_kvs_private/format.h
index 2fefd6e..d89fc9e 100644
--- a/pw_kvs/pw_kvs_private/format.h
+++ b/pw_kvs/pw_kvs_private/format.h
@@ -53,12 +53,16 @@
uint32_t checksum() const { return checksum_; }
- size_t key_length() const { return key_value_length_ & kKeyLengthMask; }
+ constexpr size_t key_length() const {
+ return key_value_length_ & kKeyLengthMask;
+ }
void set_key_length(uint32_t key_length) {
key_value_length_ = key_length | (~kKeyLengthMask & key_value_length_);
}
- size_t value_length() const { return key_value_length_ >> kValueLengthShift; }
+ constexpr size_t value_length() const {
+ return key_value_length_ >> kValueLengthShift;
+ }
void set_value_length(uint32_t value_length) {
key_value_length_ = (value_length << kValueLengthShift) |
(kKeyLengthMask & key_value_length_);