pw_blob_store: Add BlobWriterWithBuffer

Adds a helper BlobWriterWithBuffer so users don't need to manually call
RequiredMetadataBufferSize() and allocate an array with the result.

Change-Id: I909c3290993492fc4613fdc1ad1baee9bdbb5e95
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/63164
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_blob_store/blob_store_deferred_write_test.cc b/pw_blob_store/blob_store_deferred_write_test.cc
index 161ea9f..11b335e 100644
--- a/pw_blob_store/blob_store_deferred_write_test.cc
+++ b/pw_blob_store/blob_store_deferred_write_test.cc
@@ -67,7 +67,7 @@
         name, partition_, &checksum, kvs::TestKvs(), kWriteSize);
     EXPECT_EQ(OkStatus(), blob.Init());
 
-    BlobStore::DeferredWriter writer(blob, metadata_buffer_);
+    BlobStore::DeferredWriterWithBuffer writer(blob);
     EXPECT_EQ(OkStatus(), writer.Open());
 
     ByteSpan source = buffer_;
@@ -117,12 +117,9 @@
   static constexpr size_t kSectorSize = 1024;
   static constexpr size_t kSectorCount = 4;
   static constexpr size_t kBufferSize = 2 * kSectorSize;
-  static constexpr size_t kMetadataBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(0);
 
   kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
   kvs::FlashPartition partition_;
-  std::array<std::byte, kMetadataBufferSize> metadata_buffer_;
   std::array<std::byte, kSectorCount * kSectorSize> buffer_;
 };
 
diff --git a/pw_blob_store/blob_store_test.cc b/pw_blob_store/blob_store_test.cc
index 38f34e2..6e28c4d 100644
--- a/pw_blob_store/blob_store_test.cc
+++ b/pw_blob_store/blob_store_test.cc
@@ -77,7 +77,7 @@
         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
     EXPECT_EQ(OkStatus(), blob.Init());
 
-    BlobStore::BlobWriter writer(blob, metadata_buffer_);
+    BlobStore::BlobWriterWithBuffer writer(blob);
     EXPECT_EQ(OkStatus(), writer.Open());
     ASSERT_EQ(OkStatus(), writer.Write(write_data));
     EXPECT_EQ(OkStatus(), writer.Close());
@@ -151,12 +151,9 @@
   static constexpr size_t kSectorSize = 2048;
   static constexpr size_t kSectorCount = 2;
   static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
-  static constexpr size_t kMetadataBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(0);
 
   kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
   kvs::FlashPartition partition_;
-  std::array<std::byte, kMetadataBufferSize> metadata_buffer_;
   std::array<std::byte, kBlobDataSize> source_buffer_;
 };
 
@@ -175,13 +172,13 @@
       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
   ASSERT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer_);
+  BlobStore::BlobWriterWithBuffer writer(blob);
   ASSERT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(writer.ConservativeReadLimit(), 0u);
   EXPECT_EQ(writer.ConservativeWriteLimit(), kSectorSize * kSectorCount);
   ASSERT_EQ(OkStatus(), writer.Close());
 
-  BlobStore::DeferredWriter deferred_writer(blob, metadata_buffer_);
+  BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
   ASSERT_EQ(OkStatus(), deferred_writer.Open());
   EXPECT_EQ(deferred_writer.ConservativeReadLimit(), 0u);
   EXPECT_EQ(deferred_writer.ConservativeWriteLimit(), kBufferSize);
@@ -209,14 +206,14 @@
       "Blob_open", partition_, nullptr, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::DeferredWriter deferred_writer(blob, metadata_buffer_);
+  BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
   EXPECT_EQ(false, deferred_writer.IsOpen());
   EXPECT_EQ(OkStatus(), deferred_writer.Open());
   EXPECT_EQ(true, deferred_writer.IsOpen());
   EXPECT_EQ(OkStatus(), deferred_writer.Close());
   EXPECT_EQ(false, deferred_writer.IsOpen());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer_);
+  BlobStore::BlobWriterWithBuffer writer(blob);
   EXPECT_EQ(false, writer.IsOpen());
   EXPECT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(true, writer.IsOpen());
@@ -239,9 +236,6 @@
   InitSourceBufferToRandom(0x8675309);
   WriteTestBlock();
   constexpr std::string_view kFileName("my_file_1.bin");
-  constexpr size_t kEncodeBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(kFileName.size());
-  std::array<std::byte, kEncodeBufferSize> metadata_buffer = {};
   std::array<std::byte, 64> tmp_buffer = {};
   static_assert(kFileName.size() <= tmp_buffer.size());
   kvs::ChecksumCrc16 checksum;
@@ -253,7 +247,7 @@
         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
     EXPECT_EQ(OkStatus(), blob.Init());
 
-    BlobStore::BlobWriter writer(blob, metadata_buffer);
+    BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
 
     EXPECT_EQ(OkStatus(), writer.Open());
     EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
@@ -283,9 +277,6 @@
   InitSourceBufferToRandom(0x8675309);
   WriteTestBlock();
   constexpr std::string_view kFileName("my_file_1.bin");
-  constexpr size_t kEncodeBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(kFileName.size());
-  std::array<std::byte, kEncodeBufferSize> metadata_buffer = {};
   std::array<std::byte, 4> tmp_buffer = {};
   static_assert(kFileName.size() > tmp_buffer.size());
 
@@ -295,7 +286,7 @@
       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer);
+  BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
 
   EXPECT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
@@ -318,9 +309,6 @@
   InitSourceBufferToRandom(0x8675309);
   WriteTestBlock();
   constexpr std::string_view kFileName("my_file_1.bin");
-  constexpr size_t kEncodeBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(2);
-  std::array<std::byte, kEncodeBufferSize> metadata_buffer = {};
 
   kvs::ChecksumCrc16 checksum;
   constexpr size_t kBufferSize = 256;
@@ -328,7 +316,7 @@
       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer);
+  BlobStore::BlobWriterWithBuffer<2> writer(blob);
 
   EXPECT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(Status::ResourceExhausted(), writer.SetFileName(kFileName));
@@ -340,9 +328,6 @@
   WriteTestBlock();
 
   constexpr std::string_view kFileName("sliced_cheese.png");
-  constexpr size_t kEncodeBufferSize =
-      BlobStore::BlobWriter::RequiredMetadataBufferSize(kFileName.size());
-  std::array<std::byte, kEncodeBufferSize> metadata_buffer = {};
   std::array<std::byte, 64> tmp_buffer = {};
   static_assert(kFileName.size() <= tmp_buffer.size());
 
@@ -352,7 +337,7 @@
       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer);
+  BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
 
   EXPECT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
@@ -434,7 +419,7 @@
       blob_title, partition_, &checksum, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer_);
+  BlobStore::BlobWriterWithBuffer writer(blob);
 
   EXPECT_EQ(OkStatus(), writer.Open());
   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
@@ -460,7 +445,7 @@
       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
   EXPECT_EQ(OkStatus(), blob.Init());
 
-  BlobStore::BlobWriter writer(blob, metadata_buffer_);
+  BlobStore::BlobWriterWithBuffer writer(blob);
   EXPECT_EQ(OkStatus(), writer.Open());
 
   EXPECT_EQ(OkStatus(), writer.Erase());
diff --git a/pw_blob_store/docs.rst b/pw_blob_store/docs.rst
index f83d4e7..4421d58 100644
--- a/pw_blob_store/docs.rst
+++ b/pw_blob_store/docs.rst
@@ -27,7 +27,7 @@
 
 .. code-block:: cpp
 
-  BlobStore::BlobWriter writer(my_blob_store);
+  BlobStore::BlobWriterWithBuffer writer(my_blob_store);
   writer.Open();
   writer.Write(my_data);
 
@@ -63,8 +63,8 @@
 
 .. code-block:: cpp
 
-  std::array<std::byte, 48> metadata_encode_buffer;
-  BlobStore::BlobWriter writer(my_blob_store, metadata_encode_buffer);
+  constexpr size_t kMaxFileNameLength = 48;
+  BlobStore::BlobWriterWithBuffer<kMaxFileNameLength> writer(my_blob_store);
   writer.Open();
   writer.SetFileName("stonks.jpg");
   writer.Write(my_data);
diff --git a/pw_blob_store/public/pw_blob_store/blob_store.h b/pw_blob_store/public/pw_blob_store/blob_store.h
index b3b8495..f519f07 100644
--- a/pw_blob_store/public/pw_blob_store/blob_store.h
+++ b/pw_blob_store/public/pw_blob_store/blob_store.h
@@ -183,12 +183,22 @@
     }
   };
 
+  template <size_t kMaxFileNameSize = 0>
+  class BlobWriterWithBuffer final : public BlobWriter {
+   public:
+    constexpr BlobWriterWithBuffer(BlobStore& store)
+        : BlobWriter(store, buffer_), buffer_() {}
+
+   private:
+    std::array<std::byte, RequiredMetadataBufferSize(kMaxFileNameSize)> buffer_;
+  };
+
   // Implement the stream::Writer and erase interface with deferred action for a
   // BlobStore. If not already erased, the Flush will do any needed erase.
   //
   // Only one writter (of either type) is allowed to be open at a time.
   // Additionally, writters are unable to open if a reader is already open.
-  class DeferredWriter final : public BlobWriter {
+  class DeferredWriter : public BlobWriter {
    public:
     constexpr DeferredWriter(BlobStore& store, ByteSpan metadata_buffer)
         : BlobWriter(store, metadata_buffer) {}
@@ -208,7 +218,7 @@
     // be written. This is not necessarily the full number of bytes remaining in
     // the blob. Returns zero if, in the current state, Write would return
     // status other than OK. See stream.h for additional details.
-    size_t ConservativeLimit(LimitType limit) const override {
+    size_t ConservativeLimit(LimitType limit) const final {
       if (limit == LimitType::kWrite) {
         PW_DASSERT(open_);
         // Deferred writes need to fit in the write buffer.
@@ -218,12 +228,22 @@
     }
 
    private:
-    Status DoWrite(ConstByteSpan data) override {
+    Status DoWrite(ConstByteSpan data) final {
       PW_DASSERT(open_);
       return store_.AddToWriteBuffer(data);
     }
   };
 
+  template <size_t kMaxFileNameSize = 0>
+  class DeferredWriterWithBuffer final : public DeferredWriter {
+   public:
+    constexpr DeferredWriterWithBuffer(BlobStore& store)
+        : DeferredWriter(store, buffer_), buffer_() {}
+
+   private:
+    std::array<std::byte, RequiredMetadataBufferSize(kMaxFileNameSize)> buffer_;
+  };
+
   // Implement stream::Reader interface for BlobStore. Multiple readers may be
   // open at the same time, but readers may not be open with a writer open.
   class BlobReader final : public stream::SeekableReader {