pw_kvs: Allow specifying an offset in Get
Change-Id: Id0b27a3b7cf3d7fe53e934e2a4822f00f21cb517
diff --git a/pw_kvs/entry.cc b/pw_kvs/entry.cc
index 458a12c..33e86a8 100644
--- a/pw_kvs/entry.cc
+++ b/pw_kvs/entry.cc
@@ -89,14 +89,20 @@
{as_bytes(span(&header_, 1)), as_bytes(span(key)), value});
}
-StatusWithSize Entry::ReadValue(span<byte> value) const {
- const size_t read_size = std::min(value_size(), value.size());
- StatusWithSize result =
- partition().Read(address_ + sizeof(EntryHeader) + key_length(),
- value.subspan(0, read_size));
+StatusWithSize Entry::ReadValue(span<byte> buffer, size_t offset_bytes) const {
+ if (offset_bytes > value_size()) {
+ return StatusWithSize(Status::OUT_OF_RANGE);
+ }
+
+ const size_t remaining_bytes = value_size() - offset_bytes;
+ const size_t read_size = std::min(buffer.size(), remaining_bytes);
+
+ StatusWithSize result = partition().Read(
+ address_ + sizeof(EntryHeader) + key_length() + offset_bytes,
+ buffer.subspan(0, read_size));
TRY_WITH_SIZE(result);
- if (read_size != value_size()) {
+ if (read_size != remaining_bytes) {
return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
}
return StatusWithSize(read_size);
diff --git a/pw_kvs/entry_test.cc b/pw_kvs/entry_test.cc
index 00a5bd9..5022034 100644
--- a/pw_kvs/entry_test.cc
+++ b/pw_kvs/entry_test.cc
@@ -133,7 +133,7 @@
EXPECT_STREQ(value, "VALUE!");
}
-TEST_F(ValidEntryInFlash, ReadValue_ResourceExhaustedIfBufferFills) {
+TEST_F(ValidEntryInFlash, ReadValue_BufferTooSmall) {
char value[3] = {};
auto result = entry_.ReadValue(as_writable_bytes(span(value)));
@@ -144,6 +144,43 @@
EXPECT_EQ(value[2], 'L');
}
+TEST_F(ValidEntryInFlash, ReadValue_WithOffset) {
+ char value[3] = {};
+ auto result = entry_.ReadValue(as_writable_bytes(span(value)), 3);
+
+ ASSERT_EQ(Status::OK, result.status());
+ EXPECT_EQ(3u, result.size());
+ EXPECT_EQ(value[0], 'U');
+ EXPECT_EQ(value[1], 'E');
+ EXPECT_EQ(value[2], '!');
+}
+
+TEST_F(ValidEntryInFlash, ReadValue_WithOffset_BufferTooSmall) {
+ char value[1] = {};
+ auto result = entry_.ReadValue(as_writable_bytes(span(value)), 4);
+
+ ASSERT_EQ(Status::RESOURCE_EXHAUSTED, result.status());
+ EXPECT_EQ(1u, result.size());
+ EXPECT_EQ(value[0], 'E');
+}
+
+TEST_F(ValidEntryInFlash, ReadValue_WithOffset_EmptyRead) {
+ char value[16] = {'?'};
+ auto result = entry_.ReadValue(as_writable_bytes(span(value)), 6);
+
+ ASSERT_EQ(Status::OK, result.status());
+ EXPECT_EQ(0u, result.size());
+ EXPECT_EQ(value[0], '?');
+}
+
+TEST_F(ValidEntryInFlash, ReadValue_WithOffset_PastEnd) {
+ char value[16] = {};
+ auto result = entry_.ReadValue(as_writable_bytes(span(value)), 7);
+
+ EXPECT_EQ(Status::OUT_OF_RANGE, result.status());
+ EXPECT_EQ(0u, result.size());
+}
+
TEST(ValidEntry, Write) {
FakeFlashBuffer<1024, 4> flash;
FlashPartition partition(&flash);
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 72fc21f..32fbfc2 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -233,7 +233,8 @@
}
StatusWithSize KeyValueStore::Get(string_view key,
- span<byte> value_buffer) const {
+ span<byte> value_buffer,
+ size_t offset_bytes) const {
TRY_WITH_SIZE(CheckOperation(key));
const KeyDescriptor* key_descriptor;
@@ -242,8 +243,8 @@
Entry entry;
TRY_WITH_SIZE(Entry::Read(partition_, key_descriptor->address, &entry));
- StatusWithSize result = entry.ReadValue(value_buffer);
- if (result.ok() && options_.verify_on_read) {
+ StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
+ if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Status verify_result =
entry.VerifyChecksum(entry_header_format_.checksum,
key,
diff --git a/pw_kvs/key_value_store_test.cc b/pw_kvs/key_value_store_test.cc
index 0194b81..8a4a6d8 100644
--- a/pw_kvs/key_value_store_test.cc
+++ b/pw_kvs/key_value_store_test.cc
@@ -298,6 +298,46 @@
EXPECT_EQ(Status::INVALID_ARGUMENT, kvs_.Put("K", big_data));
}
+TEST_F(EmptyInitializedKvs, Get_Simple) {
+ ASSERT_EQ(Status::OK, kvs_.Put("Charles", as_bytes(span("Mingus"))));
+
+ char value[16];
+ auto result = kvs_.Get("Charles", as_writable_bytes(span(value)));
+ EXPECT_EQ(Status::OK, result.status());
+ EXPECT_EQ(sizeof("Mingus"), result.size());
+ EXPECT_STREQ("Mingus", value);
+}
+
+TEST_F(EmptyInitializedKvs, Get_WithOffset) {
+ ASSERT_EQ(Status::OK, kvs_.Put("Charles", as_bytes(span("Mingus"))));
+
+ char value[16];
+ auto result = kvs_.Get("Charles", as_writable_bytes(span(value)), 4);
+ EXPECT_EQ(Status::OK, result.status());
+ EXPECT_EQ(sizeof("Mingus") - 4, result.size());
+ EXPECT_STREQ("us", value);
+}
+
+TEST_F(EmptyInitializedKvs, Get_WithOffset_FillBuffer) {
+ ASSERT_EQ(Status::OK, kvs_.Put("Charles", as_bytes(span("Mingus"))));
+
+ char value[4] = {};
+ auto result = kvs_.Get("Charles", as_writable_bytes(span(value, 3)), 1);
+ EXPECT_EQ(Status::RESOURCE_EXHAUSTED, result.status());
+ EXPECT_EQ(3u, result.size());
+ EXPECT_STREQ("ing", value);
+}
+
+TEST_F(EmptyInitializedKvs, Get_WithOffset_PastEnd) {
+ ASSERT_EQ(Status::OK, kvs_.Put("Charles", as_bytes(span("Mingus"))));
+
+ char value[16];
+ auto result =
+ kvs_.Get("Charles", as_writable_bytes(span(value)), sizeof("Mingus") + 1);
+ EXPECT_EQ(Status::OUT_OF_RANGE, result.status());
+ EXPECT_EQ(0u, result.size());
+}
+
TEST_F(EmptyInitializedKvs, Delete_GetDeletedKey_ReturnsNotFound) {
ASSERT_EQ(Status::OK, kvs_.Put("kEy", as_bytes(span("123"))));
ASSERT_EQ(Status::OK, kvs_.Delete("kEy"));
@@ -385,16 +425,26 @@
}
TEST_F(EmptyInitializedKvs, Iteration_OneItem) {
- char value[] = "123";
-
- ASSERT_EQ(Status::OK, kvs_.Put("kEy", as_bytes(span(value))));
+ ASSERT_EQ(Status::OK, kvs_.Put("kEy", as_bytes(span("123"))));
for (KeyValueStore::Item entry : kvs_) {
EXPECT_STREQ(entry.key(), "kEy"); // Make sure null-terminated.
- char buffer[sizeof(value)] = {};
+ char buffer[sizeof("123")] = {};
EXPECT_EQ(Status::OK, entry.Get(&buffer));
- EXPECT_STREQ(value, buffer);
+ EXPECT_STREQ("123", buffer);
+ }
+}
+
+TEST_F(EmptyInitializedKvs, Iteration_GetWithOffset) {
+ ASSERT_EQ(Status::OK, kvs_.Put("key", as_bytes(span("not bad!"))));
+
+ for (KeyValueStore::Item entry : kvs_) {
+ char buffer[5];
+ auto result = entry.Get(as_writable_bytes(span(buffer)), 4);
+ EXPECT_EQ(Status::OK, result.status());
+ EXPECT_EQ(5u, result.size());
+ EXPECT_STREQ("bad!", buffer);
}
}
@@ -843,7 +893,7 @@
// The value we read back should be the last value we set
std::memset(get_buf, 0, sizeof(get_buf));
result = kvs_.Get("const_entry", span(get_buf));
- ASSERT_TRUE(result.ok());
+ ASSERT_EQ(Status::OK, result.status());
ASSERT_EQ(result.size(), test_iteration);
for (size_t j = 0; j < test_iteration; j++) {
EXPECT_EQ(set_buf[j], get_buf[j]);
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index 72dd1a4..5a43bca 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -100,7 +100,9 @@
bool initialized() const { return initialized_; }
- StatusWithSize Get(std::string_view key, span<std::byte> value) const;
+ StatusWithSize Get(std::string_view key,
+ span<std::byte> value,
+ size_t offset_bytes = 0) const;
// This overload of Get accepts a pointer to a trivially copyable object.
// const T& is used instead of T* to prevent arrays from satisfying this
@@ -155,8 +157,9 @@
// 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);
+ StatusWithSize Get(span<std::byte> value_buffer,
+ size_t offset_bytes = 0) const {
+ return kvs_.Get(key(), value_buffer, offset_bytes);
}
template <typename Pointer,
diff --git a/pw_kvs/pw_kvs_private/entry.h b/pw_kvs/pw_kvs_private/entry.h
index 3741ea1..f42df7c 100644
--- a/pw_kvs/pw_kvs_private/entry.h
+++ b/pw_kvs/pw_kvs_private/entry.h
@@ -135,7 +135,8 @@
ReadKey(partition(), address_, key_length(), key.data()), key_length());
}
- StatusWithSize ReadValue(span<std::byte> value) const;
+ StatusWithSize ReadValue(span<std::byte> buffer,
+ size_t offset_bytes = 0) const;
Status VerifyChecksum(ChecksumAlgorithm* algorithm,
std::string_view key,