pw_rpc: Add config header; Nanopb struct options

- Add a configuration header for pw_rpc.
- Expose options for configuring how to allocate the buffers for Nanopb
  request/response structs and how large to make them by default.

Change-Id: I741fdf852468d6e4200e5338a9dd939751ba19e4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/26141
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_rpc/BUILD b/pw_rpc/BUILD
index cdd1084..244ce00 100644
--- a/pw_rpc/BUILD
+++ b/pw_rpc/BUILD
@@ -68,6 +68,7 @@
         "channel.cc",
         "packet.cc",
         "public/pw_rpc/internal/channel.h",
+        "public/pw_rpc/internal/config.h",
         "public/pw_rpc/internal/method_type.h",
         "public/pw_rpc/internal/packet.h",
     ],
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index 4b6d0ea..939f074 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -15,19 +15,35 @@
 import("//build_overrides/pigweed.gni")
 
 import("$dir_pw_bloat/bloat.gni")
+import("$dir_pw_build/module_config.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_protobuf_compiler/proto.gni")
 import("$dir_pw_third_party/nanopb/nanopb.gni")
 import("$dir_pw_unit_test/test.gni")
 
-config("default_config") {
+declare_args() {
+  # The build target that overrides the default configuration options for this
+  # module. This should point to a source set that provides defines through a
+  # public config (which may -include a file or add defines directly).
+  pw_rpc_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
+}
+
+config("public_include_path") {
   include_dirs = [ "public" ]
   visibility = [ ":*" ]
 }
 
+pw_source_set("config") {
+  sources = [ "public/pw_rpc/internal/config.h" ]
+  public_configs = [ ":public_include_path" ]
+  public_deps = [ pw_rpc_CONFIG ]
+  visibility = [ "./*" ]
+  friend = [ "./*" ]
+}
+
 pw_source_set("server") {
-  public_configs = [ ":default_config" ]
+  public_configs = [ ":public_include_path" ]
   public_deps = [ ":common" ]
   deps = [ dir_pw_log ]
   public = [
@@ -51,7 +67,7 @@
 }
 
 pw_source_set("client") {
-  public_configs = [ ":default_config" ]
+  public_configs = [ ":public_include_path" ]
   public_deps = [ ":common" ]
   deps = [ dir_pw_log ]
   public = [
@@ -66,7 +82,7 @@
 
 # Classes shared by the server and client.
 pw_source_set("common") {
-  public_configs = [ ":default_config" ]
+  public_configs = [ ":public_include_path" ]
   public_deps = [
     ":protos.pwpb",
     "$dir_pw_containers:intrusive_list",
diff --git a/pw_rpc/nanopb/BUILD.gn b/pw_rpc/nanopb/BUILD.gn
index baf2805..97b97a1 100644
--- a/pw_rpc/nanopb/BUILD.gn
+++ b/pw_rpc/nanopb/BUILD.gn
@@ -30,6 +30,7 @@
   sources = [ "nanopb_method.cc" ]
   public_deps = [
     ":common",
+    "..:config",
     "..:server",
   ]
   deps = [ dir_pw_log ]
diff --git a/pw_rpc/nanopb/public/pw_rpc/internal/nanopb_method.h b/pw_rpc/nanopb/public/pw_rpc/internal/nanopb_method.h
index 33cbaf7..2a20600 100644
--- a/pw_rpc/nanopb/public/pw_rpc/internal/nanopb_method.h
+++ b/pw_rpc/nanopb/public/pw_rpc/internal/nanopb_method.h
@@ -20,6 +20,7 @@
 #include <type_traits>
 
 #include "pw_rpc/internal/base_server_writer.h"
+#include "pw_rpc/internal/config.h"
 #include "pw_rpc/internal/method.h"
 #include "pw_rpc/internal/method_type.h"
 #include "pw_rpc/internal/nanopb_common.h"
@@ -222,7 +223,7 @@
   // avoid generating unnecessary copies of the invoker functions.
   template <typename T>
   static constexpr size_t AllocateSpaceFor() {
-    return std::max(sizeof(T), size_t(64));
+    return std::max(sizeof(T), cfg::kNanopbStructMinBufferSize);
   }
 
   constexpr NanopbMethod(uint32_t id,
@@ -250,8 +251,10 @@
   static void UnaryInvoker(const Method& method,
                            ServerCall& call,
                            const Packet& request) {
+    _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS
     std::aligned_storage_t<request_size, alignof(std::max_align_t)>
         request_struct{};
+    _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS
     std::aligned_storage_t<response_size, alignof(std::max_align_t)>
         response_struct{};
 
@@ -266,6 +269,7 @@
   static void ServerStreamingInvoker(const Method& method,
                                      ServerCall& call,
                                      const Packet& request) {
+    _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS
     std::aligned_storage_t<request_size, alignof(std::max_align_t)>
         request_struct{};
 
diff --git a/pw_rpc/public/pw_rpc/internal/config.h b/pw_rpc/public/pw_rpc/internal/config.h
new file mode 100644
index 0000000..aa9d9ee
--- /dev/null
+++ b/pw_rpc/public/pw_rpc/internal/config.h
@@ -0,0 +1,57 @@
+// Copyright 2020 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.
+
+// Configuration macros for the pw_rpc module.
+#pragma once
+
+#include <cstddef>
+
+// The Nanopb-based pw_rpc implementation allocates memory to use for Nanopb
+// structs for the request and response protobufs. The template function that
+// allocates these structs rounds struct sizes up to this value so that
+// different structs can be allocated with the same function. Structs with sizes
+// larger than this value cause an extra function to be created, which slightly
+// increases code size.
+//
+// Ideally, this value will be set to the size of the largest Nanopb struct used
+// as an RPC request or response. The buffer can be stack or globally allocated
+// (see PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE).
+#ifndef PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE
+#define PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE 64
+#endif  // PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE
+
+namespace pw::rpc::cfg {
+
+inline constexpr size_t kNanopbStructMinBufferSize =
+    PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE;
+
+}  // namespace pw::rpc::cfg
+
+#undef PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE
+
+// This option determines whether to allocate the Nanopb structs on the stack or
+// in a global variable. Globally allocated structs are NOT thread safe, but
+// work fine when the RPC server's ProcessPacket function is only called from
+// one thread.
+#ifndef PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE
+#define PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE 1
+#endif  // PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE
+
+#if PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE
+#define _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS
+#else
+#define _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS static
+#endif  // PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE
+
+#undef PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE