From 20af36c8a48db680af3ba03e7f4752ef772b0aaf Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Mon, 6 Feb 2023 09:39:24 -0800 Subject: [PATCH] Improvements to C++ MakePath/MakePathD * Accept a vector argument to MakePath/MakePathD * Force a compiler error on an unpaired initializer_list * Fix OOB read in MakePath/MakePathD --- CPP/Clipper2Lib/include/clipper2/clipper.h | 83 +++++++++++++++------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index 3471b789..f5d17bb1 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -503,6 +503,37 @@ namespace Clipper2Lib { return true; } + namespace details { + + template + inline constexpr void MakePathGeneric(const T list, size_t size, + std::vector& result) + { + for (size_t i = 0; i < size; ++i) +#ifdef USINGZ + result[i / 2] = U{list[i], list[++i], 0}; +#else + result[i / 2] = U{list[i], list[++i]}; +#endif + } + + } // end details namespace + + template::value && + !std::is_same::value, bool + >::type = true> + inline Path64 MakePath(const std::vector& list) + { + const auto size = list.size() - list.size() % 2; + if (list.size() != size) + DoError(non_pair_error_i); // non-fatal without exception handling + Path64 result(size / 2); // else ignores unpaired value + details::MakePathGeneric(list, size, result); + return result; + } + template::value && @@ -510,37 +541,39 @@ namespace Clipper2Lib { >::type = true> inline Path64 MakePath(const T(&list)[N]) { - if (N % 2 != 0) + // Make the compiler error on unpaired value (i.e. no runtime effects). + static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); + Path64 result(N / 2); + details::MakePathGeneric(list, N, result); + return result; + } + + template::value && + !std::is_same::value, bool + >::type = true> + inline PathD MakePathD(const std::vector& list) + { + const auto size = list.size() - list.size() % 2; + if (list.size() != size) DoError(non_pair_error_i); // non-fatal without exception handling - Path64 result; // else ignores unpaired value - result.reserve(N / 2); -#ifdef USINGZ - for (size_t i = 0; i < N; ++i) - result.emplace_back(Point64{ list[i], list[++i], 0 }); -#else - for (size_t i = 0; i < N; ++i) - result.emplace_back(Point64{ list[i], list[++i] }); -#endif + PathD result(size / 2); // else ignores unpaired value + details::MakePathGeneric(list, size, result); return result; } - + template::value && - !std::is_same::value, bool - >::type = true> + typename std::enable_if< + std::is_arithmetic::value && + !std::is_same::value, bool + >::type = true> inline PathD MakePathD(const T(&list)[N]) { - if (N % 2 != 0) - DoError(non_pair_error_i); // non-fatal without exception handling - PathD result; // else ignores unpaired value - result.reserve(N / 2); -#ifdef USINGZ - for (size_t i = 0; i < N; ++i) - result.emplace_back(PointD{ list[i], list[++i], 0 }); -#else - for (size_t i = 0; i < N; ++i) - result.emplace_back(PointD{ list[i], list[++i] }); -#endif + // Make the compiler error on unpaired value (i.e. no runtime effects). + static_assert(N % 2 == 0, "MakePath requires an even number of arguments"); + PathD result(N / 2); + details::MakePathGeneric(list, N, result); return result; }