From b935e7e72e003026161f5158d7ff12b3bb2cfdd0 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Wed, 25 Jul 2018 00:10:33 +0100 Subject: [PATCH] Add array to int function implementation. --- include/duckhandy/int_conv.hpp | 143 +++++++++++++++++++++++++++++++-- test/unit/int_conv_test.cpp | 36 +++++++++ 2 files changed, 174 insertions(+), 5 deletions(-) diff --git a/include/duckhandy/int_conv.hpp b/include/duckhandy/int_conv.hpp index 61b085d..8e08f4f 100644 --- a/include/duckhandy/int_conv.hpp +++ b/include/duckhandy/int_conv.hpp @@ -28,9 +28,29 @@ #if !defined(INT_CONV_WITHOUT_HELPERS) # include #endif +#include +#if defined(__SSE4_1__) +# include +#endif namespace dhandy { namespace implem { + namespace { + [[gnu::always_inline]] + inline __m128i muly(const __m128i &a, const __m128i &b) { +#if defined(__SSE4_1__) // modern CPU - use SSE 4.1 + return _mm_mullo_epi32(a, b); +#else // old CPU - use SSE 2 + __m128i tmp1 = _mm_mul_epu32(a,b); /* mul 2,0*/ + __m128i tmp2 = _mm_mul_epu32( _mm_srli_si128(a,4), _mm_srli_si128(b,4)); /* mul 3,1 */ + return _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE (0,0,2,0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE (0,0,2,0))); /* shuffle results to [63..0] and pack */ +#endif + } + } //unnamed namespace + + template + T to_integer_sse (const C* s, std::size_t l); + template constexpr std::size_t max_digit_count = static_cast( sprout::ceil( @@ -89,7 +109,7 @@ namespace dhandy { in = static_cast(Num::cast(in) / static_cast(Base)); } while (in); if (was_negative) - arr.push_front(Tr::minus()); + arr.push_front(Tr::Minus); return arr; } }; @@ -133,21 +153,129 @@ namespace dhandy { arr.push_front(Tr::to_digit(static_cast(lookup[Num::abs(in) * 2 + 0] - '0'))); } if (was_negative) - arr.push_front(Tr::minus()); + arr.push_front(Tr::Minus); return arr; } }; + + template + struct AryConversion { + template + static I from_ary (C* beg, C* end) { + I retval = 0; + I factor = 1; + std::size_t i = end - beg; + const bool was_negative = (i and *beg == Tr::Minus); + if (i and (*beg == Tr::Minus or *beg == Tr::Plus)) { + i--; + beg++; + } + + while (i--) { + retval += Tr::from_digit(beg[i]) * factor; + factor *= Base; + } + return retval; + } + }; + + template ::value and not std::is_same::value and sizeof(I) <= sizeof(uint32_t)>::type> + using SelectIForSSEToInt = I; + + template + struct AryConversion, Base, Tr> { + template static I from_ary (C* beg, C* end) { return to_integer_sse(beg, end - beg); } + }; + + template + struct AryConversion { + template static bool from_ary (C* beg, C* end) { + if (end == beg) + return false; + return (Tr::from_digit(*beg) ? true : false); + } + }; + + template + [[gnu::always_inline,gnu::pure]] + inline T negated_ifn (T n, bool negate) { + //return static_cast(((static_cast(n) - (mask bitand 1)) xor mask) bitor ((mask bitand 1) << 31)); + return (negate ? -n : n); + } + + template + [[gnu::pure]] + T to_integer_sse (const C* s, std::size_t l) { + static const constexpr int base1 = static_cast(Base); + static const constexpr int base2 = base1 * base1; + static const constexpr int base3 = base1 * base1 * base1; + static const constexpr int base4 = base2 * base2; + __builtin_prefetch(s, 0); + + const bool was_negative = (l and *s == Tr::Minus); + if (l and (*s == Tr::Minus or *s == Tr::Plus)) { + l--; + s++; + } + + switch (l) { + case 0: + return 0; + case 1: + return negated_ifn(Tr::from_digit(*s), was_negative); + case 2: + return negated_ifn(Tr::from_digit(s[0]) * base1 + Tr::from_digit(s[1]), was_negative); + case 3: + return negated_ifn(Tr::from_digit(s[0]) * base2 + Tr::from_digit(s[1]) * base1 + Tr::from_digit(s[2]), was_negative); + default: + { + __m128i factor = _mm_set_epi32(base3, base2, base1, 1); + __m128i res = _mm_set1_epi32(0); + const __m128i char_0 = _mm_set1_epi32(Tr::FirstDigit); + const __m128i char_a = _mm_set1_epi32(Tr::FirstLetter); + std::size_t idx = 0; + const std::size_t cap = l bitand -4; + do { + const __m128i digits = _mm_set_epi32(s[cap - idx - 3 - 1], s[cap - idx - 2 - 1], s[cap - idx - 1 - 1], s[cap - idx - 0 - 1]); + const __m128i mask = _mm_cmplt_epi32(digits, char_a); + const __m128i addend = _mm_add_epi32(_mm_andnot_si128(mask, _mm_sub_epi32(char_a, _mm_set1_epi32(10))), _mm_and_si128(mask, char_0)); + res = _mm_add_epi32(res, muly(_mm_sub_epi32(digits, addend), factor)); + factor = muly(factor, _mm_set1_epi32(base4)); + idx += 4; + } while (l - idx > 3); + + { + res = _mm_add_epi32(res, _mm_srli_si128(res, 8)); + res = _mm_add_epi32(res, _mm_srli_si128(res, 4)); + const std::array scale {1, base1, base2, base3}; + return negated_ifn(to_integer_sse(s + idx, l - idx) + _mm_cvtsi128_si32(res) * scale[l - idx], was_negative); + } + } + } + } } //namespace implem - template + template struct ASCIITranslator { + static const constexpr bool BehavesLikeASCII = true; + static const constexpr C FirstDigit = FDigit; + static const constexpr C FirstLetter = FLetter; + static const constexpr C Plus = CPlus; + static const constexpr C Minus = CMinus; + static constexpr C to_digit (unsigned int num) { return (num <= 9 ? - static_cast(num + '0') : + static_cast(num + FirstDigit) : static_cast(num + FirstLetter - 10) ); } - static constexpr C minus() { return '-'; } + + static constexpr int from_digit (C dig) { + return (dig < FirstLetter ? + dig - FirstDigit : + dig - FirstLetter + 10 + ); + } }; template using ASCIITranslatorUpcase = ASCIITranslator; @@ -157,6 +285,11 @@ namespace dhandy { return implem::IntConversion, Base, Tr>::to_ary(in); } + template > + inline R ary_to_int (C* beg, C* end) { + return implem::AryConversion::from_ary(beg, end); + } + #if !defined(INT_CONV_WITHOUT_HELPERS) template std::basic_string_view to_string_view (const ReversedSizedArray& ary) { diff --git a/test/unit/int_conv_test.cpp b/test/unit/int_conv_test.cpp index ff3c5dd..9874010 100644 --- a/test/unit/int_conv_test.cpp +++ b/test/unit/int_conv_test.cpp @@ -25,6 +25,14 @@ 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; +namespace { + template + void AryConversionTestHelper (const std::string_view& s, T expected) { + using AryConversion = dhandy::implem::AryConversion>; + CHECK(AryConversion::from_ary(s.data(), s.data() + s.size()) == expected); + } +} //unnamed namespace + TEST_CASE ("Check int to char array conversions", "[s2i][int_conv]") { using dhandy::int_to_ary; using dhandy::bt::string; @@ -101,3 +109,31 @@ TEST_CASE ("Check int to char array conversions", "[s2i][int_conv]") { CHECK(to_string_view(int_to_ary<__int128_t, 16>(num * 0x10000 + 0xffff)) == "ffffffffffffffffffff"); #endif } + +TEST_CASE ("Check char array to int conversions", "[i2s][int_conv]") { + AryConversionTestHelper("0", 0); + AryConversionTestHelper("0", 0); + AryConversionTestHelper("0", 0); + AryConversionTestHelper("ff", 0xff); + AryConversionTestHelper("ff", 0xff); + AryConversionTestHelper("rs", 1000); + AryConversionTestHelper("20", 16); + AryConversionTestHelper("1", 1); + AryConversionTestHelper("10", 10); + AryConversionTestHelper("100", 100); + AryConversionTestHelper("999", 999); + AryConversionTestHelper("1000", 1000); + AryConversionTestHelper("1001", 1001); + AryConversionTestHelper("12345", 12345); + AryConversionTestHelper("123456", 123456); + AryConversionTestHelper("1234567", 1234567); + AryConversionTestHelper("12345678", 12345678); + AryConversionTestHelper("123456789", 123456789); + AryConversionTestHelper("2147483647", 2147483647); + AryConversionTestHelper("ffffffffffffffff", 0xffffffffffffffff); + AryConversionTestHelper("ffffffffffffffff", 0xffffffffffffffff); + AryConversionTestHelper("7fffffffffffffff", 0x7fffffffffffffff); + AryConversionTestHelper("7fffffff", 0x7fffffff); + AryConversionTestHelper("1", true); + AryConversionTestHelper("0", false); +}