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 &sector_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_);