pw_kvs: Expand Entry tests; fix issues

- Expand the tests for the KVS entry class.
- Fix a few minor issues found by the tests.

Change-Id: I92b5dd614d2a726c912b7b1bf1f9ebd47a702be4
diff --git a/pw_kvs/BUILD.gn b/pw_kvs/BUILD.gn
index c2552c8..205496c 100644
--- a/pw_kvs/BUILD.gn
+++ b/pw_kvs/BUILD.gn
@@ -50,6 +50,7 @@
   friend = [
     ":entry_test",
     ":key_value_store_test",
+    ":key_value_store_map_test",
   ]
 }
 
diff --git a/pw_kvs/debug_cli.cc b/pw_kvs/debug_cli.cc
index 8f5baeb..58eeac8 100644
--- a/pw_kvs/debug_cli.cc
+++ b/pw_kvs/debug_cli.cc
@@ -68,11 +68,10 @@
         char value[64] = {};
         if (StatusWithSize result = entry.Get(as_writable_bytes(span(value)));
             result.ok()) {
-          printf("%2d: %s='%s'\n", ++i, entry.key().data(), value);
+          printf("%2d: %s='%s'\n", ++i, entry.key(), value);
         } else {
-          printf("FAILED to Get key %s: %s\n",
-                 entry.key().data(),
-                 result.status().str());
+          printf(
+              "FAILED to Get key %s: %s\n", entry.key(), result.status().str());
         }
       }
       printf("---------------------------------------------- END CONTENTS\n");
diff --git a/pw_kvs/entry.cc b/pw_kvs/entry.cc
index c0cc9e6..458a12c 100644
--- a/pw_kvs/entry.cc
+++ b/pw_kvs/entry.cc
@@ -28,23 +28,28 @@
 Status Entry::Read(FlashPartition& partition, Address address, Entry* entry) {
   EntryHeader header;
   TRY(partition.Read(address, sizeof(header), &header));
-  *entry = Entry(&partition, address, header);
 
   if (partition.AppearsErased(as_bytes(span(&header.magic, 1)))) {
     return Status::NOT_FOUND;
   }
+  if (header.key_length_bytes > kMaxKeyLength) {
+    return Status::DATA_LOSS;
+  }
+
+  *entry = Entry(&partition, address, header);
   return Status::OK;
 }
 
-StatusWithSize Entry::ReadKey(FlashPartition& partition,
-                              Address address,
-                              size_t key_length,
-                              KeyBuffer& key) {
+Status Entry::ReadKey(FlashPartition& partition,
+                      Address address,
+                      size_t key_length,
+                      char* key) {
   if (key_length == 0u || key_length > kMaxKeyLength) {
-    return StatusWithSize(Status::DATA_LOSS);
+    return Status::DATA_LOSS;
   }
 
-  return partition.Read(address + sizeof(EntryHeader), key_length, key.data());
+  return partition.Read(address + sizeof(EntryHeader), key_length, key)
+      .status();
 }
 
 Entry::Entry(FlashPartition& partition,
@@ -131,7 +136,7 @@
   }
 
   if (algorithm == nullptr) {
-    return Status::OK;
+    return checksum() == 0 ? Status::OK : Status::DATA_LOSS;
   }
 
   // The checksum is calculated as if the header's checksum field were 0.
diff --git a/pw_kvs/entry_test.cc b/pw_kvs/entry_test.cc
index 26c44a3..00a5bd9 100644
--- a/pw_kvs/entry_test.cc
+++ b/pw_kvs/entry_test.cc
@@ -14,45 +14,48 @@
 
 #include "pw_kvs_private/entry.h"
 
+#include <string_view>
+
 #include "gtest/gtest.h"
 #include "pw_kvs/alignment.h"
+#include "pw_kvs/crc16_checksum.h"
 #include "pw_kvs/flash_memory.h"
 #include "pw_kvs/in_memory_fake_flash.h"
+#include "pw_kvs_private/byte_utils.h"
+#include "pw_span/span.h"
 
 namespace pw::kvs {
 namespace {
 
-// TODO(hepler): expand these tests
-
-FakeFlashBuffer<128, 4> test_flash(16);
-FlashPartition test_partition(&test_flash, 0, test_flash.sector_count());
+using std::byte;
+using std::string_view;
 
 TEST(Entry, Size_RoundsUpToAlignment) {
+  FakeFlashBuffer<64, 2> flash(16);
+  FlashPartition partition(&flash, 0, flash.sector_count());
+
   for (size_t alignment_bytes = 1; alignment_bytes <= 4096; ++alignment_bytes) {
     const size_t align = AlignUp(alignment_bytes, Entry::kMinAlignmentBytes);
 
     for (size_t value : {size_t(0), align - 1, align, align + 1, 2 * align}) {
-      Entry entry = Entry::Valid(test_partition,
-                                 0,
-                                 9,
-                                 nullptr,
-                                 "k",
-                                 {nullptr, value},
-                                 alignment_bytes,
-                                 0);
+      Entry entry = Entry::Valid(
+          partition, 0, 9, nullptr, "k", {nullptr, value}, alignment_bytes, 0);
       ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */ + value, align),
                 entry.size());
     }
 
-    Entry entry = Entry::Tombstone(
-        test_partition, 0, 9, nullptr, "k", alignment_bytes, 0);
+    Entry entry =
+        Entry::Tombstone(partition, 0, 9, nullptr, "k", alignment_bytes, 0);
     ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */, align), entry.size());
   }
 }
 
 TEST(Entry, Construct_ValidEntry) {
+  FakeFlashBuffer<64, 2> flash(16);
+  FlashPartition partition(&flash, 0, flash.sector_count());
+
   auto entry = Entry::Valid(
-      test_partition, 1, 9, nullptr, "k", as_bytes(span("123")), 1, 9876);
+      partition, 1, 9, nullptr, "k", as_bytes(span("123")), 1, 9876);
 
   EXPECT_FALSE(entry.deleted());
   EXPECT_EQ(entry.magic(), 9u);
@@ -61,7 +64,10 @@
 }
 
 TEST(Entry, Construct_Tombstone) {
-  auto entry = Entry::Tombstone(test_partition, 1, 99, nullptr, "key", 1, 123);
+  FakeFlashBuffer<64, 2> flash(16);
+  FlashPartition partition(&flash, 0, flash.sector_count());
+
+  auto entry = Entry::Tombstone(partition, 1, 99, nullptr, "key", 1, 123);
 
   EXPECT_TRUE(entry.deleted());
   EXPECT_EQ(entry.magic(), 99u);
@@ -69,5 +75,192 @@
   EXPECT_EQ(entry.key_version(), 123u);
 }
 
+constexpr auto kHeader1 = ByteStr(
+    "\x0d\xf0\x0d\x60"  // magic
+    "\xC5\x65\x00\x00"  // checksum (CRC16)
+    "\x01"              // alignment
+    "\x05"              // key length
+    "\x06\x00"          // value size
+    "\x99\x98\x97\x96"  // version
+);
+
+constexpr auto kKey1 = ByteStr("key45");
+constexpr auto kValue1 = ByteStr("VALUE!");
+constexpr auto kPadding1 = ByteStr("\0\0\0\0\0");
+
+constexpr auto kEntry1 = AsBytes(kHeader1, kKey1, kValue1, kPadding1);
+
+class ValidEntryInFlash : public ::testing::Test {
+ protected:
+  ValidEntryInFlash() : flash_(kEntry1), partition_(&flash_) {
+    EXPECT_EQ(Status::OK, Entry::Read(partition_, 0, &entry_));
+  }
+
+  FakeFlashBuffer<1024, 4> flash_;
+  FlashPartition partition_;
+  Entry entry_;
+};
+
+TEST_F(ValidEntryInFlash, PassesChecksumVerification) {
+  ChecksumCrc16 checksum;
+  EXPECT_EQ(Status::OK, entry_.VerifyChecksumInFlash(&checksum));
+  EXPECT_EQ(Status::OK, entry_.VerifyChecksum(&checksum, "key45", kValue1));
+}
+
+TEST_F(ValidEntryInFlash, HeaderContents) {
+  EXPECT_EQ(entry_.magic(), 0x600DF00Du);
+  EXPECT_EQ(entry_.key_length(), 5u);
+  EXPECT_EQ(entry_.value_size(), 6u);
+  EXPECT_EQ(entry_.key_version(), 0x96979899u);
+  EXPECT_FALSE(entry_.deleted());
+}
+
+TEST_F(ValidEntryInFlash, ReadKey) {
+  Entry::KeyBuffer key = {};
+  auto result = entry_.ReadKey(key);
+
+  ASSERT_EQ(Status::OK, result.status());
+  EXPECT_EQ(result.size(), entry_.key_length());
+  EXPECT_STREQ(key.data(), "key45");
+}
+
+TEST_F(ValidEntryInFlash, ReadValue) {
+  char value[32] = {};
+  auto result = entry_.ReadValue(as_writable_bytes(span(value)));
+
+  ASSERT_EQ(Status::OK, result.status());
+  EXPECT_EQ(result.size(), entry_.value_size());
+  EXPECT_STREQ(value, "VALUE!");
+}
+
+TEST_F(ValidEntryInFlash, ReadValue_ResourceExhaustedIfBufferFills) {
+  char value[3] = {};
+  auto result = entry_.ReadValue(as_writable_bytes(span(value)));
+
+  ASSERT_EQ(Status::RESOURCE_EXHAUSTED, result.status());
+  EXPECT_EQ(3u, result.size());
+  EXPECT_EQ(value[0], 'V');
+  EXPECT_EQ(value[1], 'A');
+  EXPECT_EQ(value[2], 'L');
+}
+
+TEST(ValidEntry, Write) {
+  FakeFlashBuffer<1024, 4> flash;
+  FlashPartition partition(&flash);
+  ChecksumCrc16 checksum;
+
+  Entry entry = Entry::Valid(
+      partition, 53, 0x600DF00Du, &checksum, "key45", kValue1, 32, 0x96979899u);
+
+  auto result = entry.Write("key45", kValue1);
+  EXPECT_EQ(Status::OK, result.status());
+  EXPECT_EQ(32u, result.size());
+  EXPECT_EQ(std::memcmp(&flash.buffer()[53], kEntry1.data(), kEntry1.size()),
+            0);
+}
+
+constexpr auto kHeader2 = ByteStr(
+    "\x0d\xf0\x0d\x60"  // magic
+    "\xd5\xf5\x00\x00"  // checksum (CRC16)
+    "\x00"              // alignment
+    "\x01"              // key length
+    "\xff\xff"          // value size
+    "\x00\x01\x02\x03"  // key version
+);
+
+constexpr auto kKeyAndPadding2 = ByteStr("K\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+
+class TombstoneEntryInFlash : public ::testing::Test {
+ protected:
+  TombstoneEntryInFlash()
+      : flash_(AsBytes(kHeader2, kKeyAndPadding2)), partition_(&flash_) {
+    EXPECT_EQ(Status::OK, Entry::Read(partition_, 0, &entry_));
+  }
+
+  FakeFlashBuffer<1024, 4> flash_;
+  FlashPartition partition_;
+  Entry entry_;
+};
+
+TEST_F(TombstoneEntryInFlash, PassesChecksumVerification) {
+  ChecksumCrc16 checksum;
+  EXPECT_EQ(Status::OK, entry_.VerifyChecksumInFlash(&checksum));
+  EXPECT_EQ(Status::OK, entry_.VerifyChecksum(&checksum, "K", {}));
+}
+
+TEST_F(TombstoneEntryInFlash, HeaderContents) {
+  EXPECT_EQ(entry_.magic(), 0x600DF00Du);
+  EXPECT_EQ(entry_.key_length(), 1u);
+  EXPECT_EQ(entry_.value_size(), 0u);
+  EXPECT_EQ(entry_.key_version(), 0x03020100u);
+  EXPECT_TRUE(entry_.deleted());
+}
+
+TEST_F(TombstoneEntryInFlash, ReadKey) {
+  Entry::KeyBuffer key = {};
+  auto result = entry_.ReadKey(key);
+
+  ASSERT_EQ(Status::OK, result.status());
+  EXPECT_EQ(result.size(), entry_.key_length());
+  EXPECT_STREQ(key.data(), "K");
+}
+
+TEST_F(TombstoneEntryInFlash, ReadValue) {
+  char value[32] = {};
+  auto result = entry_.ReadValue(as_writable_bytes(span(value)));
+
+  ASSERT_EQ(Status::OK, result.status());
+  EXPECT_EQ(0u, result.size());
+}
+
+TEST(TombstoneEntry, Write) {
+  FakeFlashBuffer<1024, 4> flash;
+  FlashPartition partition(&flash);
+  ChecksumCrc16 checksum;
+
+  Entry entry = Entry::Tombstone(
+      partition, 16, 0x600DF00Du, &checksum, "K", 16, 0x03020100);
+
+  auto result = entry.Write("K", {});
+  EXPECT_EQ(Status::OK, result.status());
+  EXPECT_EQ(32u, result.size());
+  EXPECT_EQ(std::memcmp(&flash.buffer()[16],
+                        AsBytes(kHeader2, kKeyAndPadding2).data(),
+                        kEntry1.size()),
+            0);
+}
+
+TEST(Entry, Checksum_NoChecksumRequiresZero) {
+  FakeFlashBuffer<1024, 4> flash(kEntry1);
+  FlashPartition partition(&flash);
+  Entry entry;
+  ASSERT_EQ(Status::OK, Entry::Read(partition, 0, &entry));
+
+  EXPECT_EQ(Status::DATA_LOSS, entry.VerifyChecksumInFlash(nullptr));
+  EXPECT_EQ(Status::DATA_LOSS, entry.VerifyChecksum(nullptr, {}, {}));
+
+  std::memset(&flash.buffer()[4], 0, 4);  // set the checksum field to 0
+  ASSERT_EQ(Status::OK, Entry::Read(partition, 0, &entry));
+  EXPECT_EQ(Status::OK, entry.VerifyChecksumInFlash(nullptr));
+  EXPECT_EQ(Status::OK, entry.VerifyChecksum(nullptr, {}, {}));
+}
+
+TEST(Entry, Checksum_ChecksPadding) {
+  FakeFlashBuffer<1024, 4> flash(
+      AsBytes(kHeader1, kKey1, kValue1, ByteStr("\0\0\0\0\1")));
+  FlashPartition partition(&flash);
+  Entry entry;
+  ASSERT_EQ(Status::OK, Entry::Read(partition, 0, &entry));
+
+  // Last byte in padding is a 1; should fail.
+  ChecksumCrc16 checksum;
+  EXPECT_EQ(Status::DATA_LOSS, entry.VerifyChecksumInFlash(&checksum));
+
+  // The in-memory verification fills in 0s for the padding.
+  EXPECT_EQ(Status::OK, entry.VerifyChecksum(&checksum, "key45", kValue1));
+
+  flash.buffer()[kEntry1.size() - 1] = byte{0};
+  EXPECT_EQ(Status::OK, entry.VerifyChecksumInFlash(&checksum));
+}
 }  // namespace
 }  // namespace pw::kvs
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 29fa778..b18d4f2 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -25,10 +25,17 @@
 #include "pw_log/log.h"
 
 namespace pw::kvs {
+namespace {
 
 using std::byte;
 using std::string_view;
 
+constexpr bool InvalidKey(std::string_view key) {
+  return key.empty() || (key.size() > Entry::kMaxKeyLength);
+}
+
+}  // namespace
+
 KeyValueStore::KeyValueStore(FlashPartition* partition,
                              const EntryHeaderFormat& format,
                              const Options& options)
@@ -148,7 +155,7 @@
   }
 
   // Read the key from flash & validate the entry (which reads the value).
-  KeyBuffer key_buffer;
+  Entry::KeyBuffer key_buffer;
   TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
   const string_view key(key_buffer.data(), key_length);
 
@@ -393,13 +400,13 @@
 //
 Status KeyValueStore::FindKeyDescriptor(string_view key,
                                         const KeyDescriptor** result) const {
-  Entry::KeyBuffer key_buffer;
   const uint32_t hash = HashKey(key);
+  Entry::KeyBuffer key_buffer;
 
   for (auto& descriptor : key_descriptors_) {
     if (descriptor.key_hash == hash) {
       TRY(Entry::ReadKey(
-          partition_, descriptor.address, key.size(), key_buffer));
+          partition_, descriptor.address, key.size(), key_buffer.data()));
 
       if (key == string_view(key_buffer.data(), key.size())) {
         DBG("Found match for key hash 0x%08" PRIx32, hash);
diff --git a/pw_kvs/key_value_store_map_test.cc b/pw_kvs/key_value_store_map_test.cc
index 542493d..43da122 100644
--- a/pw_kvs/key_value_store_map_test.cc
+++ b/pw_kvs/key_value_store_map_test.cc
@@ -30,6 +30,7 @@
 #include "pw_kvs/crc16_checksum.h"
 #include "pw_kvs/in_memory_fake_flash.h"
 #include "pw_kvs/key_value_store.h"
+#include "pw_kvs_private/entry.h"
 #include "pw_log/log.h"
 #include "pw_span/span.h"
 
@@ -114,8 +115,7 @@
 
         // Either add a new key or replace an existing one.
         if (empty() || random_int() % 2 == 0) {
-          key =
-              random_string(random_int() % (KeyValueStore::kMaxKeyLength + 1));
+          key = random_string(random_int() % (Entry::kMaxKeyLength + 1));
         } else {
           key = RandomPresentKey();
         }
@@ -197,7 +197,7 @@
 
       auto map_entry = map_.find(std::string(item.key()));
       if (map_entry == map_.end()) {
-        PW_LOG_CRITICAL("Entry %s missing from map", item.key().data());
+        PW_LOG_CRITICAL("Entry %s missing from map", item.key());
       } else if (map_entry != map_.end()) {
         EXPECT_EQ(map_entry->first, item.key());
 
@@ -218,7 +218,7 @@
 
     Status result = kvs_.Put(key, as_bytes(span(value)));
 
-    if (key.empty() || key.size() > KeyValueStore::kMaxKeyLength) {
+    if (key.empty() || key.size() > Entry::kMaxKeyLength) {
       EXPECT_EQ(Status::INVALID_ARGUMENT, result);
     } else if (map_.size() == KeyValueStore::kMaxEntries) {
       EXPECT_EQ(Status::RESOURCE_EXHAUSTED, result);
@@ -248,7 +248,7 @@
 
     Status result = kvs_.Delete(key);
 
-    if (key.empty() || key.size() > KeyValueStore::kMaxKeyLength) {
+    if (key.empty() || key.size() > Entry::kMaxKeyLength) {
       EXPECT_EQ(Status::INVALID_ARGUMENT, result);
     } else if (map_.count(key) == 0) {
       EXPECT_EQ(Status::NOT_FOUND, result);
@@ -270,18 +270,18 @@
 
   void StartOperation(const std::string& operation, const std::string& key) {
     count_ += 1;
-    PW_LOG_CRITICAL(
+    PW_LOG_DEBUG(
         "[%3u] START %s for '%s'", count_, operation.c_str(), key.c_str());
   }
 
   void FinishOperation(const std::string& operation,
                        Status result,
                        const std::string& key) {
-    PW_LOG_CRITICAL("[%3u] FINISH %s <%s> for '%s'",
-                    count_,
-                    operation.c_str(),
-                    result.str(),
-                    key.c_str());
+    PW_LOG_DEBUG("[%3u] FINISH %s <%s> for '%s'",
+                 count_,
+                 operation.c_str(),
+                 result.str(),
+                 key.c_str());
   }
 
   bool empty() const { return map_.empty(); }
diff --git a/pw_kvs/key_value_store_test.cc b/pw_kvs/key_value_store_test.cc
index 4b00ed5..1644e7c 100644
--- a/pw_kvs/key_value_store_test.cc
+++ b/pw_kvs/key_value_store_test.cc
@@ -374,7 +374,7 @@
   ASSERT_EQ(Status::OK, kvs_.Put("kEy", as_bytes(span(value))));
 
   for (KeyValueStore::Item entry : kvs_) {
-    EXPECT_STREQ(entry.key().data(), "kEy");  // Make sure null-terminated.
+    EXPECT_STREQ(entry.key(), "kEy");  // Make sure null-terminated.
 
     char buffer[sizeof(value)] = {};
     EXPECT_EQ(Status::OK, entry.Get(&buffer));
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index b29058d..b47041e 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -90,11 +90,6 @@
   static constexpr size_t kMaxUsableSectors = 256;
   static constexpr size_t kWorkingBufferSizeBytes = (4 * 1024);
 
-  // TODO: Remove the duplicate kMaxKeyLength and KeyBuffer definitions. These
-  // should be provided by the Entry class.
-  static constexpr size_t kMaxKeyLength = 0b111111;
-  using KeyBuffer = std::array<char, kMaxKeyLength + 1>;
-
   // In the future, will be able to provide additional EntryHeaderFormats for
   // backwards compatibility.
   KeyValueStore(FlashPartition* partition,
@@ -157,8 +152,8 @@
 
   class Item {
    public:
-    // Guaranteed to be null-terminated
-    std::string_view key() const { return key_buffer_.data(); }
+    // The key as a null-terminated string.
+    const char* key() const { return key_buffer_.data(); }
 
     StatusWithSize Get(span<std::byte> value_buffer) const {
       return kvs_.Get(key(), value_buffer);
@@ -178,7 +173,13 @@
     constexpr Item(const KeyValueStore& kvs) : kvs_(kvs), key_buffer_{} {}
 
     const KeyValueStore& kvs_;
-    KeyBuffer key_buffer_;
+
+    // TODO: Remove the duplicate kMaxKeyLength definition. This should be
+    // provided by the Entry class.
+    static constexpr size_t kMaxKeyLength = 0b111111;
+
+    // Buffer large enough for a null-terminated version of any valid key.
+    std::array<char, kMaxKeyLength + 1> key_buffer_;
   };
 
   class iterator {
@@ -280,10 +281,6 @@
 
   Status CheckOperation(std::string_view key) const;
 
-  static constexpr bool InvalidKey(std::string_view key) {
-    return key.empty() || (key.size() > kMaxKeyLength);
-  }
-
   Status FindKeyDescriptor(std::string_view key,
                            const KeyDescriptor** result) const;
 
diff --git a/pw_kvs/pw_kvs_private/entry.h b/pw_kvs/pw_kvs_private/entry.h
index c272886..4cae4bb 100644
--- a/pw_kvs/pw_kvs_private/entry.h
+++ b/pw_kvs/pw_kvs_private/entry.h
@@ -64,20 +64,22 @@
 
   using Address = FlashPartition::Address;
 
-  // Buffer capable of holding a null-terminated version of any valid key.
-  using KeyBuffer = std::array<char, kMaxKeyLength + 1>;
+  // Buffer capable of holding any valid key (without a null terminator);
+  using KeyBuffer = std::array<char, kMaxKeyLength>;
 
   // Returns flash partition Read error codes, or one of the following:
   //
   //          OK: successfully read the header and initialized the Entry
   //   NOT_FOUND: read the header, but the data appears to be erased
+  //   DATA_LOSS: read the header, but it contained invalid data
   //
   static Status Read(FlashPartition& partition, Address address, Entry* entry);
 
-  static StatusWithSize ReadKey(FlashPartition& partition,
-                                Address address,
-                                size_t key_length,
-                                KeyBuffer& key);
+  // Reads a key into a buffer, which must be at least key_length bytes.
+  static Status ReadKey(FlashPartition& partition,
+                        Address address,
+                        size_t key_length,
+                        char* key);
 
   // Creates a new Entry for a valid (non-deleted) entry.
   static Entry Valid(FlashPartition& partition,
@@ -123,8 +125,14 @@
 
   StatusWithSize Write(std::string_view key, span<const std::byte> value) const;
 
-  StatusWithSize ReadKey(KeyBuffer& key) const {
-    return ReadKey(partition(), address_, key_length(), key);
+  // Reads a key into a buffer, which must be large enough for a max-length key.
+  // If successful, the size is returned in the StatusWithSize. The key is not
+  // null terminated.
+  template <size_t kSize>
+  StatusWithSize ReadKey(std::array<char, kSize>& key) const {
+    static_assert(kSize >= kMaxKeyLength);
+    return StatusWithSize(
+        ReadKey(partition(), address_, key_length(), key.data()), key_length());
   }
 
   StatusWithSize ReadValue(span<std::byte> value) const;
@@ -149,6 +157,9 @@
   // Total size of this entry, including padding.
   size_t size() const { return AlignUp(content_size(), alignment_bytes()); }
 
+  // The length of the key in bytes. Keys are not null terminated.
+  size_t key_length() const { return header_.key_length_bytes; }
+
   // The size of the value, without padding. The size is 0 if this is a
   // tombstone entry.
   size_t value_size() const {
@@ -173,9 +184,6 @@
 
   uint32_t checksum() const { return header_.checksum; }
 
-  // The length of the key in bytes. Keys are not null terminated.
-  size_t key_length() const { return header_.key_length_bytes; }
-
   size_t alignment_bytes() const { return (header_.alignment_units + 1) * 16; }
 
   // The total size of the entry, excluding padding.