From e06b87e8f8820afa13440ccc885117b029c639b3 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 21 Jul 2018 23:32:27 +0100 Subject: [PATCH] Implement constexpr int_to_ary. --- .gitignore | 1 + .../implem/reversed_sized_array_bt.hpp | 50 ++++++++ include/duckhandy/int_conv.hpp | 113 ++++++++++++++++++ test/unit/CMakeLists.txt | 3 +- test/unit/int_conv_test.cpp | 92 ++++++++++++++ 5 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 include/duckhandy/implem/reversed_sized_array_bt.hpp create mode 100644 include/duckhandy/int_conv.hpp create mode 100644 test/unit/int_conv_test.cpp diff --git a/.gitignore b/.gitignore index 6e92f57..f8f1c27 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ tags +compile_commands.json diff --git a/include/duckhandy/implem/reversed_sized_array_bt.hpp b/include/duckhandy/implem/reversed_sized_array_bt.hpp new file mode 100644 index 0000000..65faa5d --- /dev/null +++ b/include/duckhandy/implem/reversed_sized_array_bt.hpp @@ -0,0 +1,50 @@ +/* Copyright 2016, Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#ifndef idFC25566D624140559C54B39FFFE52F04 +#define idFC25566D624140559C54B39FFFE52F04 + +#include +#include +#include + +namespace dhandy { + template + class ReversedSizedArray { + static_assert(S > 0, "This container requires size to be at least 1"); + static_assert(std::is_trivial::value, "Only use this container with trivial types"); + public: + using iterator = typename std::array::iterator; + constexpr ReversedSizedArray() = default; + ~ReversedSizedArray() = default; + + constexpr std::size_t size() const { return S - (m_curr + 1); } + constexpr bool empty() const { return m_curr + 1 == S; } + constexpr const T operator[] (std::size_t idx) const { if (idx >= size()) throw std::out_of_range("Out of bound array access"); return m_data[idx + m_curr + 1]; } + constexpr T& operator[] (std::size_t idx) { if (idx >= size()) throw std::out_of_range("Out of bound array access"); return m_data[idx + m_curr + 1]; } + constexpr void push_front (const T& itm) { if (size() == S) throw std::length_error("ReversedSizedArray is full"); m_data[m_curr--] = itm; } + constexpr const T* data() const { return m_data.data() + m_curr + 1; } + constexpr iterator begin() { return m_data.begin() + m_curr + 1; } + constexpr iterator end() { return m_data.end(); } + + private: + std::array m_data {}; + std::size_t m_curr {S - 1}; + }; +} //namespace dhandy + +#endif diff --git a/include/duckhandy/int_conv.hpp b/include/duckhandy/int_conv.hpp new file mode 100644 index 0000000..ea57ef6 --- /dev/null +++ b/include/duckhandy/int_conv.hpp @@ -0,0 +1,113 @@ +/* Copyright 2016, 2017 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#ifndef id4754A95F12BE4ADEA65642A056A51907 +#define id4754A95F12BE4ADEA65642A056A51907 + +#include "implem/reversed_sized_array_bt.hpp" +#include "sprout/math/log10.hpp" +#include "sprout/math/abs.hpp" +#include "sprout/math/ceil.hpp" +#include +#include +#include +#if !defined(INT_CONV_WITHOUT_HELPERS) +# include +#endif + +namespace dhandy { + namespace implem { + template + constexpr std::size_t max_digit_count = static_cast( + sprout::ceil( + sprout::log10(sprout::abs(static_cast(std::numeric_limits::max()))) / + sprout::log10(static_cast(Base)) + ) + ); + + template + struct int_info { + static_assert(Base > 1, "Invalid base"); + static const constexpr bool always_unsigned = (Base == 16 or Base == 2); + static const constexpr bool is_signed = std::numeric_limits::is_signed and not always_unsigned; + static const constexpr std::size_t max_len = max_digit_count, I>::type, Base> + is_signed; + }; + + template ::is_signed> + struct IsNegative { static constexpr bool check (I) { return false; } }; + template + struct IsNegative { static constexpr bool check (I in) { return in < I(0); } }; + + template + constexpr bool is_negative (I in) { + return IsNegative::check(in); + } + + template < + typename I, + std::size_t Base, + bool IsSigned=std::numeric_limits::is_signed, + bool ForceUnsigned=int_info::always_unsigned + > struct NumberAdaptation { + static const constexpr bool BecomesUnsigned = IsSigned and ForceUnsigned; + using UnsignedType = typename std::make_unsigned::type; + using CastedType = typename std::conditional::type; + + template + static constexpr L abs(L in) { return (not BecomesUnsigned and std::numeric_limits::is_signed and in < L(0) ? -in : in); } + static constexpr CastedType cast (I in) { return static_cast(in); } + }; + } //namespace implem + + template + struct DefaultTranslator { + static constexpr C to_digit (unsigned int num) { + return (num <= 9 ? + static_cast(num + '0') : + static_cast(num + FirstLetter - 10) + ); + } + static constexpr C minus() { return '-'; } + }; + + template > + constexpr inline ReversedSizedArray().to_digit(1))>, implem::int_info::max_len + 1> int_to_ary (I in) { + using RetType = ReversedSizedArray().to_digit(1))>, implem::int_info::max_len + 1>; + using Num = implem::NumberAdaptation, Base>; + + const bool was_negative = implem::is_negative(in); + + RetType arr; + arr.push_front('\0'); + do { + arr.push_front(Tr::to_digit(static_cast(Num::abs(Num::cast(in)) % Base))); + in = static_cast(Num::cast(in) / static_cast(Base)); + } while (in); + if (was_negative) + arr.push_front(Tr::minus()); + return arr; + } + +#if !defined(INT_CONV_WITHOUT_HELPERS) + template + std::basic_string_view to_string_view (const ReversedSizedArray& ary) { + return std::basic_string_view(ary.data(), ary.size() - 1); + } +#endif +} //namespace dhandy + +#endif diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 4798277..222e57d 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -4,8 +4,9 @@ add_executable(${PROJECT_NAME} main.cpp lexical_cast_test.cpp endianness_test.cpp + int_conv_test.cpp ) -set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11) +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) target_include_directories(${PROJECT_NAME} diff --git a/test/unit/int_conv_test.cpp b/test/unit/int_conv_test.cpp new file mode 100644 index 0000000..795c358 --- /dev/null +++ b/test/unit/int_conv_test.cpp @@ -0,0 +1,92 @@ +/* Copyright 2017, Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#include "catch.hpp" +#include "duckhandy/int_conv.hpp" +#include "duckhandy/string_bt.hpp" +#include "sprout/cstring/strlen.hpp" +#include + +template using int_info_10 = dhandy::implem::int_info; +template using int_info_16 = dhandy::implem::int_info; +template using int_info_2 = dhandy::implem::int_info; + +TEST_CASE ("Check int to char array conversions", "[s2i][int_conv]") { + using dhandy::int_to_ary; + using dhandy::bt::string; + using dhandy::bt::make_string; + using sprout::strlen; + + CHECK(int_info_10::max_len == 3); + CHECK(int_info_10::max_len == 5); + CHECK(int_info_10::max_len == 10); + CHECK(int_info_10::max_len == 4); + CHECK(int_info_10::max_len == 6); + CHECK(int_info_10::max_len == 11); + + CHECK(int_info_16::max_len == 2); + CHECK(int_info_16::max_len == 4); + CHECK(int_info_16::max_len == 8); + CHECK(int_info_16::max_len == 2); + CHECK(int_info_16::max_len == 4); + CHECK(int_info_16::max_len == 8); + + CHECK(int_info_2::max_len == 8); + CHECK(int_info_2::max_len == 16); + CHECK(int_info_2::max_len == 32); + CHECK(int_info_2::max_len == 8); + CHECK(int_info_2::max_len == 16); + CHECK(int_info_2::max_len == 32); + + static_assert(int_to_ary(5)[0] == '5', "Algorithm error"); + static_assert(string(int_to_ary(10).data()) == make_string("10"), "Algorithm error"); + static_assert(string(int_to_ary(101).data()) == make_string("101"), "Algorithm error"); + static_assert(string(0xAB12).data()) + 1>(int_to_ary(0xAB12).data()) == make_string("43794"), "Algorithm error"); + static_assert(int_info_10::is_signed == true, "Wrong sign detection"); + static_assert(string(0xAB12).data()) + 1>(int_to_ary(0xAB12).data()) == make_string("-21742"), "Algorithm error"); + + CHECK(to_string_view(int_to_ary(0x123456789A)) == "78187493530"); + CHECK(to_string_view(int_to_ary(-1)) == "-1"); + CHECK(to_string_view(int_to_ary(0x1000000000000000)) == "1152921504606846976"); + CHECK(to_string_view(int_to_ary(0xF000000000000000)) == "-1152921504606846976"); + + CHECK(to_string_view(int_to_ary(0xFFFF)) == "ffff"); + CHECK(to_string_view(int_to_ary(0xCACA)) == "caca"); + CHECK(to_string_view(int_to_ary(0x10)) == "10"); + CHECK(to_string_view(int_to_ary(0x10)) == "10"); + CHECK(to_string_view(int_to_ary(0xF000)) == "f000"); + CHECK(to_string_view(int_to_ary(0xFEFE)) == "fefe"); + CHECK(to_string_view(int_to_ary(0xFEFE)) == "fefe"); + CHECK(to_string_view(int_to_ary(0423)) == "423"); + CHECK(to_string_view(int_to_ary(0777)) == "777"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + CHECK(to_string_view(int_to_ary(0)) == "0"); + //CHECK(to_string_view(int_to_ary(false)) == "0"); + //CHECK(to_string_view(int_to_ary(true)) == "0"); + CHECK(to_string_view(int_to_ary(0b10101010)) == "10101010"); + CHECK(to_string_view(int_to_ary(0b10101010)) == "10101010"); + CHECK(to_string_view(int_to_ary(0b11111111)) == "11111111"); + CHECK(to_string_view(int_to_ary(0b11111111)) == "11111111"); + CHECK(to_string_view(int_to_ary(0b111100001111)) == "111100001111"); + CHECK(to_string_view(int_to_ary(0b111100001111)) == "111100001111"); +}