From 43b85e93c7acd865be2de009651e146e9faece96 Mon Sep 17 00:00:00 2001
From: Joanna Hulboj <joanna.hulboj@isode.com>
Date: Wed, 5 Apr 2017 11:33:36 +0100
Subject: Simplify algorithm for concatenating using modern c++

Test-Information:
Unit tests pass OK.

Change-Id: I6328dd55482ed8058ec8510a873fdd64ff978c72

diff --git a/Swiften/Base/Concat.h b/Swiften/Base/Concat.h
index df5d77b..8923a8f 100644
--- a/Swiften/Base/Concat.h
+++ b/Swiften/Base/Concat.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2015 Isode Limited.
+ * Copyright (c) 2011-2017 Isode Limited.
  * All rights reserved.
  * See the COPYING file for more information.
  */
@@ -7,69 +7,35 @@
 #pragma once
 
 #include <algorithm>
+#include <type_traits>
 
 namespace Swift {
-    template<typename C>
-    C concat(const C& c1, const C& c2) {
-        C result;
-        result.resize(c1.size() + c2.size());
-        std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size());
-        std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size());
-        std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size());
-        std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size());
-        std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))))));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size());
-        std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))))));
-        return result;
-    }
-
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7, const C& c8) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size() + c8.size());
-        std::copy(c8.begin(), c8.end(), std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))))))));
-        return result;
-    }
+    template<typename V1, typename V2, typename... Rest>
+    std::size_t getReserveAmount(V1 const& v1, V2 const& v2, Rest const&... rest) SWIFTEN_NOEXCEPT
+    {
+        // Total size of the concatenated output is equal to sum of sizes of all the containers that were passed in.
+        // Two first can be accessed directly:
+        std::size_t sizeSum = v1.size() + v2.size();
+        // While the rest needs to be calculated in parameter pack expansion:
+        // http://stackoverflow.com/a/25683817/61574
+        // Pack expansion can occur inside a braced-init-list - dummy array is used for that (with side effect of accumulating the size)
+        int dummy[] = {0, ((sizeSum += rest.size()), void(), 0)...};
+
+        (void)dummy; // dummy is unused - suppress compiler warning
+        return sizeSum;
+}
 
-    template<typename C>
-    C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7, const C& c8, const C& c9) {
-        C result;
-        result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size() + c8.size() + c9.size());
-        std::copy(c9.begin(), c9.end(), std::copy(c8.begin(), c8.end(), std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))))))));
-        return result;
+    template<typename V1, typename V2, typename... Rest>
+    typename std::decay<V1>::type concat(V1&& v1, V2&& v2, Rest&&... rest) {
+        // Create the output container and reserve the required amount of space
+        typename std::decay<V1>::type v;
+        v.reserve(getReserveAmount(v1, v2, rest...));
+        // Insert the elements from input containers to output. Directly for the first two:
+        v.insert(v.cend(), v1.cbegin(), v1.cend());
+        v.insert(v.cend(), v2.cbegin(), v2.cend());
+        // Use pack expansion for the others:
+        int dummy[] = {0, ((v.insert(v.cend(), rest.cbegin(), rest.cend())), void(), 0 )...};
+        (void)dummy;
+        return v;
     }
 }
-- 
cgit v0.10.2-6-g49f6