pw_kvs: Rename EntryHeader to Entry
- Rename EntryHeader to Entry, since it is more than just the
EntryHeader.
- Introduce POD EntryHeader struct that represents the on-disk entry
format.
- Remove unused set functions.
Change-Id: I407d615e559ad77824fbd6c6db66aa2c666c1c1e
diff --git a/pw_kvs/entry_test.cc b/pw_kvs/entry_test.cc
index 4ec9425..069f557 100644
--- a/pw_kvs/entry_test.cc
+++ b/pw_kvs/entry_test.cc
@@ -19,20 +19,19 @@
namespace pw::kvs {
namespace {
-TEST(EntryHeader, Alignment) {
+TEST(Entry, Alignment) {
for (size_t alignment_bytes = 1; alignment_bytes <= 4096; ++alignment_bytes) {
- ASSERT_EQ(AlignUp(alignment_bytes, EntryHeader::kMinAlignmentBytes),
- EntryHeader::Valid(9, nullptr, "k", {}, alignment_bytes, 0)
+ ASSERT_EQ(AlignUp(alignment_bytes, Entry::kMinAlignmentBytes),
+ Entry::Valid(9, nullptr, "k", {}, alignment_bytes, 0)
.alignment_bytes());
- ASSERT_EQ(AlignUp(alignment_bytes, EntryHeader::kMinAlignmentBytes),
- EntryHeader::Tombstone(9, nullptr, "k", alignment_bytes, 0)
+ ASSERT_EQ(AlignUp(alignment_bytes, Entry::kMinAlignmentBytes),
+ Entry::Tombstone(9, nullptr, "k", alignment_bytes, 0)
.alignment_bytes());
}
}
-TEST(EntryHeader, ValidEntry) {
- EntryHeader entry =
- EntryHeader::Valid(9, nullptr, "k", as_bytes(span("123")), 1, 9876);
+TEST(Entry, ValidEntry) {
+ Entry entry = Entry::Valid(9, nullptr, "k", as_bytes(span("123")), 1, 9876);
EXPECT_FALSE(entry.deleted());
EXPECT_EQ(entry.magic(), 9u);
@@ -43,8 +42,8 @@
EXPECT_EQ(entry.key_version(), 9876u);
}
-TEST(EntryHeader, Tombstone) {
- EntryHeader entry = EntryHeader::Tombstone(99, nullptr, "key", 1, 123);
+TEST(Entry, Tombstone) {
+ Entry entry = Entry::Tombstone(99, nullptr, "key", 1, 123);
EXPECT_TRUE(entry.deleted());
EXPECT_EQ(entry.magic(), 99u);
diff --git a/pw_kvs/format.cc b/pw_kvs/format.cc
index 2c09250..04c69de 100644
--- a/pw_kvs/format.cc
+++ b/pw_kvs/format.cc
@@ -22,33 +22,33 @@
using std::byte;
using std::string_view;
-EntryHeader::EntryHeader(uint32_t magic,
- ChecksumAlgorithm* algorithm,
- string_view key,
- span<const byte> value,
- uint16_t value_length_bytes,
- size_t alignment_bytes,
- uint32_t key_version)
- : magic_(magic),
- checksum_(kNoChecksum),
- alignment_units_(alignment_bytes_to_units(alignment_bytes)),
- key_length_bytes_(key.size()),
- value_length_bytes_(value_length_bytes),
- key_version_(key_version) {
+Entry::Entry(uint32_t magic,
+ ChecksumAlgorithm* algorithm,
+ string_view key,
+ span<const byte> value,
+ uint16_t value_length_bytes,
+ size_t alignment_bytes,
+ uint32_t key_version)
+ : header_{.magic = magic,
+ .checksum = kNoChecksum,
+ .alignment_units = alignment_bytes_to_units(alignment_bytes),
+ .key_length_bytes = static_cast<uint8_t>(key.size()),
+ .value_length_bytes = value_length_bytes,
+ .key_version = key_version} {
if (algorithm != nullptr) {
span<const byte> checksum = CalculateChecksum(algorithm, key, value);
- std::memcpy(&checksum_,
+ std::memcpy(&header_.checksum,
checksum.data(),
- std::min(checksum.size(), sizeof(checksum_)));
+ std::min(checksum.size(), sizeof(header_.checksum)));
}
// TODO: 0 is an invalid alignment value. There should be an assert for this.
// DCHECK_NE(alignment_bytes, 0);
}
-Status EntryHeader::VerifyChecksum(ChecksumAlgorithm* algorithm,
- string_view key,
- span<const byte> value) const {
+Status Entry::VerifyChecksum(ChecksumAlgorithm* algorithm,
+ string_view key,
+ span<const byte> value) const {
if (algorithm == nullptr) {
return checksum() == kNoChecksum ? Status::OK : Status::DATA_LOSS;
}
@@ -56,9 +56,9 @@
return algorithm->Verify(checksum_bytes());
}
-Status EntryHeader::VerifyChecksumInFlash(FlashPartition* partition,
- FlashPartition::Address address,
- ChecksumAlgorithm* algorithm) const {
+Status Entry::VerifyChecksumInFlash(FlashPartition* partition,
+ FlashPartition::Address address,
+ ChecksumAlgorithm* algorithm) const {
// Read the entire entry piece-by-piece into a small buffer.
// TODO: This read may be unaligned. The partition can handle this, but
// consider creating a API that skips the intermediate buffering.
@@ -108,9 +108,9 @@
return algorithm->Verify(checksum_bytes());
}
-span<const byte> EntryHeader::CalculateChecksum(ChecksumAlgorithm* algorithm,
- const string_view key,
- span<const byte> value) const {
+span<const byte> Entry::CalculateChecksum(ChecksumAlgorithm* algorithm,
+ const string_view key,
+ span<const byte> value) const {
algorithm->Reset();
algorithm->Update(reinterpret_cast<const byte*>(this) + checked_data_offset(),
sizeof(*this) - checked_data_offset());
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index a30bc39..7ee058e 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -125,7 +125,7 @@
// For every valid key, increment the valid bytes for that sector.
for (KeyDescriptor& key_descriptor : key_descriptors_) {
uint32_t sector_id = key_descriptor.address / sector_size_bytes;
- EntryHeader header;
+ Entry header;
TRY(ReadEntryHeader(key_descriptor.address, &header));
sectors_[sector_id].valid_bytes += header.size();
}
@@ -135,7 +135,7 @@
Status KeyValueStore::LoadEntry(Address entry_address,
Address* next_entry_address) {
- EntryHeader header;
+ Entry header;
TRY(ReadEntryHeader(entry_address, &header));
// TODO: Should likely add a "LogHeader" method or similar.
DBG("Header: ");
@@ -231,8 +231,7 @@
}
// TODO: Finish.
-bool KeyValueStore::HeaderLooksLikeUnwrittenData(
- const EntryHeader& header) const {
+bool KeyValueStore::HeaderLooksLikeUnwrittenData(const Entry& header) const {
// TODO: This is not correct; it should call through to flash memory.
return header.magic() == 0xffffffff;
}
@@ -257,7 +256,7 @@
return Status::NOT_FOUND;
}
- EntryHeader header;
+ Entry header;
TRY(ReadEntryHeader(key_descriptor->address, &header));
StatusWithSize result = ReadEntryValue(*key_descriptor, header, value_buffer);
@@ -320,7 +319,7 @@
const KeyValueStore::Item& KeyValueStore::iterator::operator*() {
std::memset(item_.key_buffer_.data(), 0, item_.key_buffer_.size());
- EntryHeader header;
+ Entry header;
if (item_.kvs_.ReadEntryHeader(descriptor().address, &header).ok()) {
item_.kvs_.ReadEntryKey(
descriptor().address, header.key_length(), item_.key_buffer_.data());
@@ -362,7 +361,7 @@
return Status::NOT_FOUND;
}
- EntryHeader header;
+ Entry header;
TRY(ReadEntryHeader(key_descriptor->address, &header));
return StatusWithSize(header.value_length());
@@ -425,8 +424,7 @@
return Status::NOT_FOUND;
}
-Status KeyValueStore::ReadEntryHeader(Address address,
- EntryHeader* header) const {
+Status KeyValueStore::ReadEntryHeader(Address address, Entry* header) const {
return partition_.Read(address, sizeof(*header), header).status();
}
@@ -436,7 +434,7 @@
// TODO: This check probably shouldn't be here; this is like
// checking that the Cortex M's RAM isn't corrupt. This should be
// done at boot time.
- // ^^ This argument sometimes comes from EntryHeader::key_value_len,
+ // ^^ This argument sometimes comes from Entry::key_value_len,
// which is read directly from flash. If it's corrupted, we shouldn't try
// to read a bunch of extra data.
if (key_length == 0u || key_length > kMaxKeyLength) {
@@ -449,7 +447,7 @@
StatusWithSize KeyValueStore::ReadEntryValue(
const KeyDescriptor& key_descriptor,
- const EntryHeader& header,
+ const Entry& header,
span<byte> value) const {
const size_t read_size = std::min(header.value_length(), value.size());
StatusWithSize result = partition_.Read(
@@ -467,13 +465,13 @@
string_view key,
span<const byte> value) {
// Find the original entry and sector to update the sector's valid_bytes.
- EntryHeader original_entry;
+ Entry original_entry;
TRY(ReadEntryHeader(key_descriptor->address, &original_entry));
SectorDescriptor* old_sector = SectorFromAddress(key_descriptor->address);
SectorDescriptor* sector;
TRY(FindOrRecoverSectorWithSpace(
- §or, EntryHeader::size(partition_.alignment_bytes(), key, value)));
+ §or, Entry::size(partition_.alignment_bytes(), key, value)));
DBG("Writing existing entry; found sector: %zu", SectorIndex(sector));
if (old_sector != SectorFromAddress(key_descriptor->address)) {
@@ -505,7 +503,7 @@
SectorDescriptor* sector;
TRY(FindOrRecoverSectorWithSpace(
- §or, EntryHeader::size(partition_.alignment_bytes(), key, value)));
+ §or, Entry::size(partition_.alignment_bytes(), key, value)));
DBG("Writing new entry; found sector: %zu", SectorIndex(sector));
TRY(AppendEntry(sector, &key_descriptor, key, value));
@@ -526,7 +524,7 @@
// 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;
+ Entry header;
TRY(ReadEntryHeader(key_descriptor.address, &header));
TRY(ReadEntryKey(
key_descriptor.address, header.key_length(), entry->key.data()));
@@ -726,21 +724,21 @@
span<const byte> value,
KeyDescriptor::State new_state) {
// write header, key, and value
- EntryHeader header;
+ Entry header;
if (new_state == KeyDescriptor::kDeleted) {
- header = EntryHeader::Tombstone(entry_header_format_.magic,
- entry_header_format_.checksum,
- key,
- partition_.alignment_bytes(),
- key_descriptor->key_version + 1);
+ header = Entry::Tombstone(entry_header_format_.magic,
+ entry_header_format_.checksum,
+ key,
+ partition_.alignment_bytes(),
+ key_descriptor->key_version + 1);
} else {
- header = EntryHeader::Valid(entry_header_format_.magic,
- entry_header_format_.checksum,
- key,
- value,
- partition_.alignment_bytes(),
- key_descriptor->key_version + 1);
+ header = Entry::Valid(entry_header_format_.magic,
+ entry_header_format_.checksum,
+ key,
+ value,
+ partition_.alignment_bytes(),
+ key_descriptor->key_version + 1);
}
DBG("Appending %zu B entry with key version: %x",
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index 0625d83..bc6ff37 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -48,7 +48,7 @@
std::bool_constant<internal::ConvertsToSpan<std::remove_reference_t<T>>(0)>;
// Internal-only persistent storage header format.
-class EntryHeader;
+class Entry;
struct EntryHeaderFormat {
// Magic is a unique constant identifier for entries.
@@ -277,11 +277,11 @@
key, const_cast<const KeyDescriptor**>(result));
}
- Status ReadEntryHeader(Address address, EntryHeader* header) const;
+ Status ReadEntryHeader(Address address, Entry* header) const;
Status ReadEntryKey(Address address, size_t key_length, char* key) const;
StatusWithSize ReadEntryValue(const KeyDescriptor& key_descriptor,
- const EntryHeader& header,
+ const Entry& header,
span<std::byte> value) const;
Status LoadEntry(Address entry_address, Address* next_entry_address);
@@ -289,7 +289,7 @@
const KeyDescriptor& key_descriptor);
Status AppendEmptyDescriptor(KeyDescriptor** new_descriptor);
- Status ValidateEntryChecksumInFlash(const EntryHeader& header,
+ Status ValidateEntryChecksumInFlash(const Entry& header,
std::string_view key,
const KeyDescriptor& entry) const;
@@ -313,7 +313,7 @@
SectorDescriptor* FindSectorToGarbageCollect();
- bool HeaderLooksLikeUnwrittenData(const EntryHeader& header) const;
+ bool HeaderLooksLikeUnwrittenData(const Entry& header) const;
KeyDescriptor* FindDescriptor(uint32_t hash);
diff --git a/pw_kvs/pw_kvs_private/format.h b/pw_kvs/pw_kvs_private/format.h
index e837238..bb68e78 100644
--- a/pw_kvs/pw_kvs_private/format.h
+++ b/pw_kvs/pw_kvs_private/format.h
@@ -27,42 +27,70 @@
namespace pw::kvs {
-// EntryHeader represents a key-value entry as stored in flash.
-class EntryHeader {
+// Disk format of the header used for each key-value entry.
+struct EntryHeader {
+ uint32_t magic;
+
+ // The checksum of the entire entry, including the header, key, value, and
+ // zero-value padding bytes. The checksum is calculated as if the checksum
+ // field value was zero.
+ uint32_t checksum;
+
+ // Stores the alignment in 16-byte units, starting from 16. To calculate the
+ // number of bytes, add one to this number and multiply by 16.
+ uint8_t alignment_units;
+
+ // The length of the key in bytes. The key is not null terminated.
+ // 6 bits, 0:5 - key length - maximum 64 characters
+ // 2 bits, 6:7 - reserved
+ uint8_t key_length_bytes;
+
+ // Byte length of the value; maximum of 65534. The max uint16_t value (65535
+ // or 0xFFFF) is reserved to indicate this is a tombstone (deleted) entry.
+ uint16_t value_length_bytes;
+
+ // The version of the key. Monotonically increasing.
+ uint32_t key_version;
+};
+
+static_assert(sizeof(EntryHeader) == 16, "EntryHeader must not have padding");
+
+// Entry represents a key-value entry.
+class Entry {
public:
- static constexpr size_t kMinAlignmentBytes = 16;
+ static constexpr size_t kMinAlignmentBytes = sizeof(EntryHeader);
- EntryHeader() = default;
+ Entry() = default;
- // Creates a new EntryHeader for a valid (non-deleted) entry.
- static EntryHeader Valid(uint32_t magic,
- ChecksumAlgorithm* algorithm,
- std::string_view key,
- span<const std::byte> value,
- size_t alignment_bytes,
- uint32_t key_version) {
- return EntryHeader(magic,
- algorithm,
- key,
- value,
- value.size(),
- alignment_bytes,
- key_version);
+ // Creates a new Entry for a valid (non-deleted) entry.
+ static Entry Valid(uint32_t magic,
+ ChecksumAlgorithm* algorithm,
+ std::string_view key,
+ span<const std::byte> value,
+ size_t alignment_bytes,
+ uint32_t key_version) {
+ return Entry(magic,
+ algorithm,
+ key,
+ value,
+ value.size(),
+ alignment_bytes,
+ key_version);
}
- // Creates a new EntryHeader for a tombstone entry, which marks a deleted key.
- static EntryHeader Tombstone(uint32_t magic,
- ChecksumAlgorithm* algorithm,
- std::string_view key,
- size_t alignment_bytes,
- uint32_t key_version) {
- return EntryHeader(magic,
- algorithm,
- key,
- {},
- kDeletedValueLength,
- alignment_bytes,
- key_version);
+ // Creates a new Entry for a tombstone entry, which marks a deleted key.
+ static Entry Tombstone(uint32_t magic,
+ ChecksumAlgorithm* algorithm,
+ std::string_view key,
+ size_t alignment_bytes,
+ uint32_t key_version) {
+ return Entry(magic,
+ algorithm,
+ key,
+ {},
+ kDeletedValueLength,
+ alignment_bytes,
+ key_version);
}
Status VerifyChecksum(ChecksumAlgorithm* algorithm,
@@ -84,32 +112,30 @@
// Total size of this entry, including padding.
size_t size() const { return AlignUp(content_size(), alignment_bytes()); }
- uint32_t magic() const { return magic_; }
+ uint32_t magic() const { return header_.magic; }
- uint32_t checksum() const { return checksum_; }
+ 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 key_length_bytes_; }
+ size_t key_length() const { return header_.key_length_bytes; }
static constexpr size_t max_key_length() { return kKeyLengthMask; }
- void set_key_length(uint32_t key_length) { key_length_bytes_ = key_length; }
-
// The length of the value, which is 0 if this is a tombstone entry.
- size_t value_length() const { return deleted() ? 0u : value_length_bytes_; }
+ size_t value_length() const {
+ return deleted() ? 0u : header_.value_length_bytes;
+ }
static constexpr size_t max_value_length() { return 0xFFFE; }
- void set_value_length(uint16_t value_length) {
- value_length_bytes_ = value_length;
- }
+ size_t alignment_bytes() const { return (header_.alignment_units + 1) * 16; }
- size_t alignment_bytes() const { return (alignment_units_ + 1) * 16; }
-
- uint32_t key_version() const { return key_version_; }
+ uint32_t key_version() const { return header_.key_version; }
// True if this is a tombstone entry.
- bool deleted() const { return value_length_bytes_ == kDeletedValueLength; }
+ bool deleted() const {
+ return header_.value_length_bytes == kDeletedValueLength;
+ }
private:
// The total size of the entry, excluding padding.
@@ -121,20 +147,20 @@
static constexpr uint32_t kKeyLengthMask = 0b111111;
static constexpr uint16_t kDeletedValueLength = 0xFFFF;
- EntryHeader(uint32_t magic,
- ChecksumAlgorithm* algorithm,
- std::string_view key,
- span<const std::byte> value,
- uint16_t value_length_bytes,
- size_t alignment_bytes,
- uint32_t key_version);
+ Entry(uint32_t magic,
+ ChecksumAlgorithm* algorithm,
+ std::string_view key,
+ span<const std::byte> value,
+ uint16_t value_length_bytes,
+ size_t alignment_bytes,
+ uint32_t key_version);
static constexpr size_t checked_data_offset() {
- return offsetof(EntryHeader, alignment_units_);
+ return offsetof(EntryHeader, alignment_units);
}
span<const std::byte> checksum_bytes() const {
- return as_bytes(span(&checksum_, 1));
+ return as_bytes(span(&header_.checksum, 1));
}
span<const std::byte> CalculateChecksum(ChecksumAlgorithm* algorithm,
@@ -145,27 +171,7 @@
return (alignment_bytes + 15) / 16 - 1; // An alignment of 0 is invalid.
}
- uint32_t magic_;
- uint32_t checksum_;
-
- // Stores the alignment in 16-byte units, starting from 16. To calculate the
- // number of bytes, add one to this number and multiply by 16.
- uint8_t alignment_units_;
-
- // The length of the key in bytes.
- // 6 bits, 0:5 - key - maximum 64 characters
- // 2 bits, 6:7 - reserved
- uint8_t key_length_bytes_;
-
- // Byte length of the value; maximum of 65534. The max uint16_t value (65535
- // or 0xFFFF) is reserved to indicate this is a tombstone (deleted) entry.
- uint16_t value_length_bytes_;
-
- // The version of the key. Monotonically increasing.
- uint32_t key_version_;
+ EntryHeader header_;
};
-static_assert(sizeof(EntryHeader) == 16, "EntryHeader should have no padding");
-static_assert(sizeof(EntryHeader) == EntryHeader::kMinAlignmentBytes);
-
} // namespace pw::kvs