pw_blob_store: Add size report

Add size reports for both standard buffered blob and deferred writer
blob store

Change-Id: If8688195f8865c5accc1ec86a9ec57c7b1faecbb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/31840
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: David Rogers <davidrogers@google.com>
diff --git a/pw_blob_store/BUILD.gn b/pw_blob_store/BUILD.gn
index e6c9311..236c8d1 100644
--- a/pw_blob_store/BUILD.gn
+++ b/pw_blob_store/BUILD.gn
@@ -14,6 +14,7 @@
 
 import("//build_overrides/pigweed.gni")
 
+import("$dir_pw_bloat/bloat.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_unit_test/test.gni")
@@ -84,4 +85,29 @@
 
 pw_doc_group("docs") {
   sources = [ "docs.rst" ]
+  report_deps = [ ":blob_size" ]
+}
+
+pw_size_report("blob_size") {
+  title = "Pigweed BlobStore size report"
+
+  # To see all the symbols, uncomment the following:
+  # Note: The size report RST table won't be generated when full_report = true.
+  # full_report = true
+
+  binaries = [
+    {
+      target = "size_report:basic_blob"
+      base = "size_report:base"
+      label = "BlobStore"
+    },
+  ]
+
+  binaries += [
+    {
+      target = "size_report:deferred_write_blob"
+      base = "size_report:base"
+      label = "BlobStore with deferred write"
+    },
+  ]
 }
diff --git a/pw_blob_store/docs.rst b/pw_blob_store/docs.rst
index a84a22e..906d942 100644
--- a/pw_blob_store/docs.rst
+++ b/pw_blob_store/docs.rst
@@ -10,7 +10,7 @@
 
 Write and read are only done using the BlobWriter and BlobReader classes.
 
-Once a blob write is closed, reopening followed by a Discard(), Write(), or
+Once a blob write is closed, reopening for write followed by a Discard(), Write(), or
 Erase() will discard the previous blob.
 
 Write blob:
@@ -26,5 +26,12 @@
      BlobReader::GetMemoryMappedBlob().
   3) BlobReader::Close().
 
+Size report
+-----------
+The following size report showcases the memory usage of the blob store.
+
+.. include:: blob_size
+
+
 .. note::
   The documentation for this module is currently incomplete.
diff --git a/pw_blob_store/size_report/BUILD b/pw_blob_store/size_report/BUILD
new file mode 100644
index 0000000..4811b4f
--- /dev/null
+++ b/pw_blob_store/size_report/BUILD
@@ -0,0 +1,63 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_binary",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_binary(
+    name = "base",
+    srcs = ["base.cc"],
+    deps = [
+        "//pw_assert",
+        "//pw_bloat:bloat_this_binary",
+        "//pw_kvs",
+        "//pw_kvs:flash_test_partition",
+        "//pw_kvs:fake_flash_12_byte_partition",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "basic_blob",
+    srcs = ["basic_blob.cc"],
+    deps = [
+        "//pw_assert",
+        "//pw_bloat:bloat_this_binary",
+        "//pw_blob_store",
+        "//pw_kvs",
+        "//pw_kvs:flash_test_partition",
+        "//pw_kvs:fake_flash_12_byte_partition",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "deferred_write_blob",
+    srcs = ["deferred_write_blob.cc"],
+    deps = [
+        "//pw_assert",
+        "//pw_bloat:bloat_this_binary",
+        "//pw_blob_store",
+        "//pw_kvs",
+        "//pw_kvs:flash_test_partition",
+        "//pw_kvs:fake_flash_12_byte_partition",
+        "//pw_log",
+    ],
+)
diff --git a/pw_blob_store/size_report/BUILD.gn b/pw_blob_store/size_report/BUILD.gn
new file mode 100644
index 0000000..aca6b84
--- /dev/null
+++ b/pw_blob_store/size_report/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+_deps = [
+  "$dir_pw_bloat:bloat_this_binary",
+  "$dir_pw_kvs:fake_flash_12_byte_partition",
+  "$dir_pw_kvs:flash_test_partition",
+  dir_pw_assert,
+  dir_pw_kvs,
+  dir_pw_log,
+]
+
+pw_executable("base") {
+  sources = [ "base.cc" ]
+  deps = _deps
+}
+
+pw_executable("basic_blob") {
+  sources = [ "basic_blob.cc" ]
+  deps = _deps + [ dir_pw_blob_store ]
+}
+
+pw_executable("deferred_write_blob") {
+  sources = [ "deferred_write_blob.cc" ]
+  deps = _deps + [ dir_pw_blob_store ]
+}
diff --git a/pw_blob_store/size_report/base.cc b/pw_blob_store/size_report/base.cc
new file mode 100644
index 0000000..8f0cc65
--- /dev/null
+++ b/pw_blob_store/size_report/base.cc
@@ -0,0 +1,78 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstring>
+
+#include "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_kvs/flash_test_partition.h"
+#include "pw_kvs/key_value_store.h"
+#include "pw_log/log.h"
+
+char working_buffer[256];
+volatile bool is_set;
+
+constexpr size_t kMaxSectorCount = 64;
+constexpr size_t kKvsMaxEntries = 32;
+
+// For KVS magic value always use a random 32 bit integer rather than a human
+// readable 4 bytes. See pw_kvs/format.h for more information.
+static constexpr pw::kvs::EntryFormat kvs_format = {.magic = 0x22d3f8a0,
+                                                    .checksum = nullptr};
+
+volatile size_t kvs_entry_count;
+
+pw::kvs::KeyValueStoreBuffer<kKvsMaxEntries, kMaxSectorCount> test_kvs(
+    &pw::kvs::FlashTestPartition(), kvs_format);
+
+int volatile* unoptimizable;
+
+int main() {
+  pw::bloat::BloatThisBinary();
+
+  // Start of base **********************
+  // Ensure we are paying the cost for log and assert.
+  PW_CHECK_INT_GE(*unoptimizable, 0, "Ensure this CHECK logic stays");
+  PW_LOG_INFO("We care about optimizing: %d", *unoptimizable);
+
+  void* result =
+      std::memset((void*)working_buffer, sizeof(working_buffer), 0x55);
+  is_set = (result != nullptr);
+
+  test_kvs.Init();
+
+  unsigned kvs_value = 42;
+  test_kvs.Put("example_key", kvs_value);
+
+  kvs_entry_count = test_kvs.size();
+
+  unsigned read_value = 0;
+  test_kvs.Get("example_key", &read_value);
+  test_kvs.Delete("example_key");
+
+  auto val = pw::kvs::FlashTestPartition().PartitionAddressToMcuAddress(0);
+  PW_LOG_INFO("Use the variable. %u", unsigned(*val));
+
+  std::array<std::byte, 32> blob_source_buffer;
+  pw::ConstByteSpan write_data = std::span(blob_source_buffer);
+  char name[16] = "BLOB";
+  std::array<std::byte, 32> read_buffer;
+  pw::ByteSpan read_span = read_buffer;
+  PW_LOG_INFO("Do something so variables are used. %u, %c, %u",
+              unsigned(write_data.size()),
+              name[0],
+              unsigned(read_span.size()));
+  // End of base **********************
+  return 0;
+}
diff --git a/pw_blob_store/size_report/basic_blob.cc b/pw_blob_store/size_report/basic_blob.cc
new file mode 100644
index 0000000..667bc01
--- /dev/null
+++ b/pw_blob_store/size_report/basic_blob.cc
@@ -0,0 +1,104 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstring>
+
+#include "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_blob_store/blob_store.h"
+#include "pw_kvs/flash_test_partition.h"
+#include "pw_kvs/key_value_store.h"
+#include "pw_log/log.h"
+
+char working_buffer[256];
+volatile bool is_set;
+
+constexpr size_t kMaxSectorCount = 64;
+constexpr size_t kKvsMaxEntries = 32;
+
+// For KVS magic value always use a random 32 bit integer rather than a human
+// readable 4 bytes. See pw_kvs/format.h for more information.
+static constexpr pw::kvs::EntryFormat kvs_format = {.magic = 0x22d3f8a0,
+                                                    .checksum = nullptr};
+
+volatile size_t kvs_entry_count;
+
+pw::kvs::KeyValueStoreBuffer<kKvsMaxEntries, kMaxSectorCount> test_kvs(
+    &pw::kvs::FlashTestPartition(), kvs_format);
+
+int volatile* unoptimizable;
+
+int main() {
+  pw::bloat::BloatThisBinary();
+
+  // Start of base **********************
+  // Ensure we are paying the cost for log and assert.
+  PW_CHECK_INT_GE(*unoptimizable, 0, "Ensure this CHECK logic stays");
+  PW_LOG_INFO("We care about optimizing: %d", *unoptimizable);
+
+  void* result =
+      std::memset((void*)working_buffer, sizeof(working_buffer), 0x55);
+  is_set = (result != nullptr);
+
+  test_kvs.Init();
+
+  unsigned kvs_value = 42;
+  test_kvs.Put("example_key", kvs_value);
+
+  kvs_entry_count = test_kvs.size();
+
+  unsigned read_value = 0;
+  test_kvs.Get("example_key", &read_value);
+  test_kvs.Delete("example_key");
+
+  auto val = pw::kvs::FlashTestPartition().PartitionAddressToMcuAddress(0);
+  PW_LOG_INFO("Use the variable. %u", unsigned(*val));
+
+  std::array<std::byte, 32> blob_source_buffer;
+  pw::ConstByteSpan write_data = std::span(blob_source_buffer);
+  char name[16] = "BLOB";
+  std::array<std::byte, 32> read_buffer;
+  pw::ByteSpan read_span = read_buffer;
+  PW_LOG_INFO("Do something so variables are used. %u, %c, %u",
+              unsigned(write_data.size()),
+              name[0],
+              unsigned(read_span.size()));
+  // End of base **********************
+
+  // Start of basic blob **********************
+  constexpr size_t kBufferSize = 1;
+
+  pw::blob_store::BlobStoreBuffer<kBufferSize> blob(
+      name, pw::kvs::FlashTestPartition(), nullptr, test_kvs, kBufferSize);
+  blob.Init();
+
+  // Use writer.
+  pw::blob_store::BlobStore::BlobWriter writer(blob);
+  writer.Open();
+  writer.Write(write_data);
+  writer.Close();
+
+  // Use reader.
+  pw::blob_store::BlobStore::BlobReader reader(blob);
+  reader.Open();
+  pw::Result<pw::ConstByteSpan> get_result = reader.GetMemoryMappedBlob();
+  PW_LOG_INFO("%d", get_result.ok());
+  auto reader_result = reader.Read(read_span);
+  reader.Close();
+  PW_LOG_INFO("%d", reader_result.ok());
+
+  // End of basic blob **********************
+
+  return 0;
+}
diff --git a/pw_blob_store/size_report/deferred_write_blob.cc b/pw_blob_store/size_report/deferred_write_blob.cc
new file mode 100644
index 0000000..db2f18a
--- /dev/null
+++ b/pw_blob_store/size_report/deferred_write_blob.cc
@@ -0,0 +1,105 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstring>
+
+#include "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_blob_store/blob_store.h"
+#include "pw_kvs/flash_test_partition.h"
+#include "pw_kvs/key_value_store.h"
+#include "pw_log/log.h"
+
+char working_buffer[256];
+volatile bool is_set;
+
+constexpr size_t kMaxSectorCount = 64;
+constexpr size_t kKvsMaxEntries = 32;
+
+// For KVS magic value always use a random 32 bit integer rather than a human
+// readable 4 bytes. See pw_kvs/format.h for more information.
+static constexpr pw::kvs::EntryFormat kvs_format = {.magic = 0x22d3f8a0,
+                                                    .checksum = nullptr};
+
+volatile size_t kvs_entry_count;
+
+pw::kvs::KeyValueStoreBuffer<kKvsMaxEntries, kMaxSectorCount> test_kvs(
+    &pw::kvs::FlashTestPartition(), kvs_format);
+
+int volatile* unoptimizable;
+
+int main() {
+  pw::bloat::BloatThisBinary();
+
+  // Start of base **********************
+  // Ensure we are paying the cost for log and assert.
+  PW_CHECK_INT_GE(*unoptimizable, 0, "Ensure this CHECK logic stays");
+  PW_LOG_INFO("We care about optimizing: %d", *unoptimizable);
+
+  void* result =
+      std::memset((void*)working_buffer, sizeof(working_buffer), 0x55);
+  is_set = (result != nullptr);
+
+  test_kvs.Init();
+
+  unsigned kvs_value = 42;
+  test_kvs.Put("example_key", kvs_value);
+
+  kvs_entry_count = test_kvs.size();
+
+  unsigned read_value = 0;
+  test_kvs.Get("example_key", &read_value);
+  test_kvs.Delete("example_key");
+
+  auto val = pw::kvs::FlashTestPartition().PartitionAddressToMcuAddress(0);
+  PW_LOG_INFO("Use the variable. %u", unsigned(*val));
+
+  std::array<std::byte, 32> blob_source_buffer;
+  pw::ConstByteSpan write_data = std::span(blob_source_buffer);
+  char name[16] = "BLOB";
+  std::array<std::byte, 32> read_buffer;
+  pw::ByteSpan read_span = read_buffer;
+  PW_LOG_INFO("Do something so variables are used. %u, %c, %u",
+              unsigned(write_data.size()),
+              name[0],
+              unsigned(read_span.size()));
+  // End of base **********************
+
+  // Start of deferred blob **********************
+  constexpr size_t kBufferSize = 1;
+
+  pw::blob_store::BlobStoreBuffer<kBufferSize> blob(
+      name, pw::kvs::FlashTestPartition(), nullptr, test_kvs, kBufferSize);
+  blob.Init();
+
+  // Use writer.
+  pw::blob_store::BlobStore::DeferredWriter writer(blob);
+  writer.Open();
+  writer.Write(write_data);
+  writer.Flush();
+  writer.Close();
+
+  // Use reader.
+  pw::blob_store::BlobStore::BlobReader reader(blob);
+  reader.Open();
+  pw::Result<pw::ConstByteSpan> get_result = reader.GetMemoryMappedBlob();
+  PW_LOG_INFO("%d", get_result.ok());
+  auto reader_result = reader.Read(read_span);
+  reader.Close();
+  PW_LOG_INFO("%d", reader_result.ok());
+
+  // End of deferred blob **********************
+
+  return 0;
+}