From b807bfd9ce0a83f31884a9b093d37a127cca7d5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=BDan=20Dober=C5=A1ek?= <zan@falconsigh.net>
Date: Thu, 13 Oct 2022 12:11:10 -0700
Subject: [PATCH] [WK2] constexpr-ify IPC encoding, decoding of std::tuple<>
 objects https://bugs.webkit.org/show_bug.cgi?id=246149

Reviewed by Kimmo Kinnunen.

Simplify the ArgumentCoder<std::tuple<>> specialization, removing helper structs
and leaning into constexpr for a finer implementation.

The encoding remains the same, just the helper struct's methods inlined into the
ArgumentCoder specialization.

The decoding methods are reworked to use constexpr to make compile-time codepaths.
For each desired element, decoding is done into std::optional<> values that are
then passed through tuples of rvalue references into recursive calls, with the
index sequence adjusted each time to address the next element. Once all elements
are successfully decoded, the final tuple is constructed, with the decoded objects
all moved into that tuple. If decoding was unsuccessful at any point, a nullopt
value is returned immediately, as before.

This avoids concatenating tuples that was occurring for each decoded element, and
was ending up moving each contained element into the concatenation result for each
such concatenation.

* Source/WebKit/Platform/IPC/ArgumentCoders.h:
(IPC::TupleEncoder::encode): Deleted.
(IPC::TupleDecoder<>::decode): Deleted.

Canonical link: https://commits.webkit.org/255496@main
---
 Source/WebKit/Platform/IPC/ArgumentCoders.h | 54 +++++++--------------
 1 file changed, 17 insertions(+), 37 deletions(-)

diff --git a/Source/WebKit/Platform/IPC/ArgumentCoders.h b/Source/WebKit/Platform/IPC/ArgumentCoders.h
index 7364cc62181c..45e9605be7fc 100644
--- a/Source/WebKit/Platform/IPC/ArgumentCoders.h
+++ b/Source/WebKit/Platform/IPC/ArgumentCoders.h
@@ -436,8 +436,7 @@ template<typename T> struct ArgumentCoder<Ref<T>> {
     }
 };
 
-template<typename... Elements>
-struct TupleEncoder {
+template<typename... Elements> struct ArgumentCoder<std::tuple<Elements...>> {
     template<typename Encoder>
     static void encode(Encoder& encoder, const std::tuple<Elements...>& tuple)
     {
@@ -450,48 +449,29 @@ struct TupleEncoder {
         if constexpr (sizeof...(Indices) > 0)
             (encoder << ... << std::get<Indices>(tuple));
     }
-};
 
-template<typename... Elements> struct TupleDecoder;
-
-template<typename Type, typename... Types>
-struct TupleDecoder<Type, Types...> {
     template<typename Decoder>
-    static std::optional<std::tuple<Type, Types...>> decode(Decoder& decoder)
-    {
-        std::optional<Type> optional;
-        decoder >> optional;
-        if (!optional)
-            return std::nullopt;
-
-        std::optional<std::tuple<Types...>> remainder = TupleDecoder<Types...>::decode(decoder);
-        if (!remainder)
-            return std::nullopt;
-
-        return std::tuple_cat(std::make_tuple(WTFMove(*optional)), WTFMove(*remainder));
-    }
-};
-
-template<>
-struct TupleDecoder<> {
-    template<typename Decoder>
-    static std::optional<std::tuple<>> decode(Decoder&)
+    static std::optional<std::tuple<Elements...>> decode(Decoder& decoder)
     {
-        return std::make_tuple();
+        return decode(decoder, std::tuple<> { }, std::index_sequence_for<> { });
     }
-};
 
-template<typename... Elements> struct ArgumentCoder<std::tuple<Elements...>> {
-    template<typename Encoder>
-    static void encode(Encoder& encoder, const std::tuple<Elements...>& tuple)
+    template<typename Decoder, typename OptionalTuple, size_t... Indices>
+    static std::optional<std::tuple<Elements...>> decode(Decoder& decoder, OptionalTuple&& optionalTuple, std::index_sequence<Indices...>)
     {
-        TupleEncoder<Elements...>::encode(encoder, tuple);
-    }
+        constexpr size_t Index = sizeof...(Indices);
+        static_assert(Index == std::tuple_size_v<OptionalTuple>);
 
-    template<typename Decoder>
-    static std::optional<std::tuple<Elements...>> decode(Decoder& decoder)
-    {
-        return TupleDecoder<Elements...>::decode(decoder);
+        if constexpr (Index < sizeof...(Elements)) {
+            std::optional<std::tuple_element_t<Index, std::tuple<Elements...>>> optional;
+            decoder >> optional;
+            if (!optional)
+                return std::nullopt;
+            return decode(decoder, std::forward_as_tuple(std::get<Indices>(WTFMove(optionalTuple))..., WTFMove(optional)), std::make_index_sequence<Index + 1> { });
+        } else {
+            static_assert(Index == sizeof...(Elements));
+            return std::make_tuple(*std::get<Indices>(WTFMove(optionalTuple))...);
+        }
     }
 };
 
