[llcpp] fidl::LinearizeAndEncode + passing conformance tests

Will switch over to LinearizeAndEncode in next CL.
Change Ia83987d7995066d9a82c8d347cd0f34cc6803b75 is needed for
this to work with empty requests / responses.

Change-Id: Ibe04b261d169039495ef28e2143c1e83b4282757
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/396713
Commit-Queue: Benjamin Prosnitz <bprosnitz@google.com>
Reviewed-by: Yifei Teng <yifeit@google.com>
Testability-Review: Yifei Teng <yifeit@google.com>
diff --git a/src/lib/fidl/llcpp/tests/test_utils.h b/src/lib/fidl/llcpp/tests/test_utils.h
index 9efd3ce..29a6778 100644
--- a/src/lib/fidl/llcpp/tests/test_utils.h
+++ b/src/lib/fidl/llcpp/tests/test_utils.h
@@ -5,7 +5,9 @@
 #ifndef SRC_LIB_FIDL_LLCPP_TESTS_TEST_UTILS_H_
 #define SRC_LIB_FIDL_LLCPP_TESTS_TEST_UTILS_H_
 
+#include <lib/fidl/llcpp/aligned.h>
 #include <lib/fidl/llcpp/coding.h>
+#include <lib/fidl/llcpp/linearized.h>
 #include <zircon/status.h>
 #include <zircon/types.h>
 
@@ -14,56 +16,21 @@
 #include <iostream>
 #include <vector>
 
+// Testing utilities indended for GIDL-generated conformance tests.
 namespace llcpp_conformance_utils {
 
 bool ComparePayload(const uint8_t* actual, size_t actual_size, const uint8_t* expected,
                     size_t expected_size);
 
-// A wrapper around fidl::LinearizeResult that owns the memory.
-template <typename FidlType>
-struct OwnedLinearizeResult {
-  // The result, whose message field points to either inline_buffer or
-  // linearized_buffer (or neither in error conditions).
-  fidl::LinearizeResult<FidlType> result;
-  // Used for types with no out-of-line parts, which do not need linearization.
-  FIDL_ALIGNDECL char inline_buffer[FidlAlign(sizeof(FidlType))];
-  // Used in the general case where linearization is needed.
-  std::vector<uint8_t> linearized_buffer;
-};
-
-// Linearizes |value|, producing an |OwnedLinearizeResult|.
-// Note: This is destructive to |value|.
-template <typename FidlType>
-OwnedLinearizeResult<FidlType> Linearize(FidlType* value) {
-  static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required");
-
-  OwnedLinearizeResult<FidlType> owned_result;
-  FidlType* aligned_value = new (owned_result.inline_buffer) FidlType(std::move(*value));
-
-  if constexpr (FidlType::HasPointer) {
-    owned_result.linearized_buffer.resize(ZX_CHANNEL_MAX_MSG_BYTES);
-    memset(owned_result.linearized_buffer.data(), 0xcd, owned_result.linearized_buffer.size());
-    owned_result.result = fidl::Linearize(
-        aligned_value,
-        fidl::BytePart(&owned_result.linearized_buffer[0], ZX_CHANNEL_MAX_MSG_BYTES));
-  } else {
-    owned_result.result =
-        fidl::LinearizeResult(ZX_OK, nullptr,
-                              fidl::DecodedMessage<FidlType>(fidl::BytePart(
-                                  reinterpret_cast<uint8_t*>(aligned_value),
-                                  FidlAlign(sizeof(FidlType)), FidlAlign(sizeof(FidlType)))));
-  }
-  return owned_result;
-}
-
 // Verifies that |value| encodes to |bytes|.
-// Note: This is destructive to |value|.
+// Note: This is destructive to |value| - a new value must be created with each call.
 template <typename FidlType>
-bool EncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) {
+bool LinearizeThenEncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) {
   static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required");
 
-  auto owned_linearize_result = Linearize(value);
-  auto& linearize_result = owned_linearize_result.result;
+  fidl::aligned<FidlType> aligned_value = std::move(*value);
+  auto linearized = fidl::internal::Linearized<FidlType>(&aligned_value.value);
+  auto& linearize_result = linearized.result();
   if (linearize_result.status != ZX_OK || linearize_result.error != nullptr) {
     std::cout << "Linearization failed (" << zx_status_get_string(linearize_result.status)
               << "): " << linearize_result.error << std::endl;
@@ -81,13 +48,14 @@
 }
 
 // Verifies that |value| fails to encode, with the expected error code.
-// Note: This is destructive to |value|.
+// Note: This is destructive to |value| - a new value must be created with each call.
 template <typename FidlType>
-bool EncodeFailure(FidlType* value, zx_status_t expected_error_code) {
+bool LinearizeThenEncodeFailure(FidlType* value, zx_status_t expected_error_code) {
   static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required");
 
-  auto owned_linearize_result = Linearize(value);
-  auto& linearize_result = owned_linearize_result.result;
+  fidl::aligned<FidlType> aligned_value = std::move(*value);
+  auto linearized = fidl::internal::Linearized<FidlType>(&aligned_value.value);
+  auto& linearize_result = linearized.result();
   if (linearize_result.status != ZX_OK && linearize_result.status != expected_error_code) {
     std::cout << "Linearization failed with error code "
               << zx_status_get_string(linearize_result.status) << " (" << linearize_result.error
@@ -110,6 +78,44 @@
   return true;
 }
 
+// Verifies that |value| encodes to |bytes|.
+// Note: This is destructive to |value| - a new value must be created with each call.
+template <typename FidlType>
+bool CombinedLinearizeAndEncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) {
+  static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required");
+
+  ::fidl::internal::LinearizeBuffer<FidlType> buffer;
+  auto encode_result = fidl::LinearizeAndEncode(value, buffer.buffer());
+  if (encode_result.status != ZX_OK || encode_result.error != nullptr) {
+    std::cout << "Encoding failed (" << zx_status_get_string(encode_result.status)
+              << "): " << encode_result.error << std::endl;
+    return false;
+  }
+  return ComparePayload(encode_result.message.bytes().data(),
+                        encode_result.message.bytes().actual(), &bytes[0], bytes.size());
+}
+
+// Verifies that |value| fails to encode, with the expected error code.
+// Note: This is destructive to |value| - a new value must be created with each call.
+template <typename FidlType>
+bool CombinedLinearizeAndEncodeFailure(FidlType* value, zx_status_t expected_error_code) {
+  static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required");
+
+  ::fidl::internal::LinearizeBuffer<FidlType> buffer;
+  auto encode_result = fidl::LinearizeAndEncode(value, buffer.buffer());
+  if (encode_result.status == ZX_OK) {
+    std::cout << "Encoding unexpectedly succeeded" << std::endl;
+    return false;
+  }
+  if (encode_result.status != expected_error_code) {
+    std::cout << "Encoding failed with error code " << zx_status_get_string(encode_result.status)
+              << " (" << encode_result.error << "), but expected error code "
+              << zx_status_get_string(expected_error_code) << std::endl;
+    return false;
+  }
+  return true;
+}
+
 // Verifies that |bytes| decodes to an object that is the same as |value|.
 template <typename FidlType>
 bool DecodeSuccess(FidlType* value, std::vector<uint8_t> bytes) {
diff --git a/tools/fidl/gidl/llcpp/conformance.go b/tools/fidl/gidl/llcpp/conformance.go
index 3636890..bcccca1 100644
--- a/tools/fidl/gidl/llcpp/conformance.go
+++ b/tools/fidl/gidl/llcpp/conformance.go
@@ -28,13 +28,21 @@
 #include "src/lib/fidl/llcpp/tests/test_utils.h"
 
 {{ range .EncodeSuccessCases }}
-TEST(Conformance, {{ .Name }}_Encode) {
+TEST(Conformance, {{ .Name }}_LinearizeThenEncode) {
 	fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator;
 	fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator;
 	{{ .ValueBuild }}
 	const auto expected = {{ .Bytes }};
 	auto obj = {{ .ValueVar }};
-	EXPECT_TRUE(llcpp_conformance_utils::EncodeSuccess(&obj, expected));
+	EXPECT_TRUE(llcpp_conformance_utils::LinearizeThenEncodeSuccess(&obj, expected));
+}
+TEST(Conformance, {{ .Name }}_CombinedLinearizeAndEncode) {
+	fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator;
+	fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator;
+	{{ .ValueBuild }}
+	const auto expected = {{ .Bytes }};
+	auto obj = {{ .ValueVar }};
+	EXPECT_TRUE(llcpp_conformance_utils::CombinedLinearizeAndEncodeSuccess(&obj, expected));
 }
 {{ end }}
 
@@ -50,12 +58,19 @@
 {{ end }}
 
 {{ range .EncodeFailureCases }}
-TEST(Conformance, {{ .Name }}_Encode_Failure) {
+TEST(Conformance, {{ .Name }}_LinearizeThenEncode_Failure) {
 	fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator;
 	fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator;
 	{{ .ValueBuild }}
 	auto obj = {{ .ValueVar }};
-	EXPECT_TRUE(llcpp_conformance_utils::EncodeFailure(&obj, {{ .ErrorCode }}));
+	EXPECT_TRUE(llcpp_conformance_utils::LinearizeThenEncodeFailure(&obj, {{ .ErrorCode }}));
+}
+TEST(Conformance, {{ .Name }}_CombinedLinearizeAndEncode_Failure) {
+	fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator;
+	fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator;
+	{{ .ValueBuild }}
+	auto obj = {{ .ValueVar }};
+	EXPECT_TRUE(llcpp_conformance_utils::CombinedLinearizeAndEncodeFailure(&obj, {{ .ErrorCode }}));
 }
 {{ end }}
 
diff --git a/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h b/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h
index 0106106..c831748 100644
--- a/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h
+++ b/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h
@@ -197,6 +197,26 @@
   return result;
 }
 
+template <typename FidlType>
+EncodeResult<FidlType> LinearizeAndEncode(FidlType* value, BytePart bytes) {
+  static_assert(IsFidlType<FidlType>::value, "FIDL type required");
+  static_assert(FidlType::Type != nullptr, "FidlType should have a coding table");
+  EncodeResult<FidlType> result;
+  uint32_t num_bytes_actual;
+  uint32_t num_handles_actual;
+  result.message.bytes() = std::move(bytes);
+  result.status = fidl_linearize_and_encode(
+      FidlType::Type, value, result.message.bytes().data(), result.message.bytes().capacity(),
+      result.message.handles().data(), result.message.handles().capacity(), &num_bytes_actual,
+      &num_handles_actual, &result.error);
+  if (result.status != ZX_OK) {
+    return result;
+  }
+  result.message.bytes().set_actual(num_bytes_actual);
+  result.message.handles().set_actual(num_handles_actual);
+  return result;
+}
+
 #ifdef __Fuchsia__
 
 namespace {