diff --git a/include/duckhandy/implem/int_conv.hpp b/include/duckhandy/implem/int_conv.hpp index 11cceb0..3396003 100644 --- a/include/duckhandy/implem/int_conv.hpp +++ b/include/duckhandy/implem/int_conv.hpp @@ -18,7 +18,8 @@ #ifndef id4754A95F12BE4ADEA65642A056A51907 #define id4754A95F12BE4ADEA65642A056A51907 -#include "duckhandy/implem/reversed_sized_array_bt.hpp" +#include "reversed_sized_array_bt.hpp" +#include "../has_method.hpp" #include #include #include @@ -51,6 +52,14 @@ namespace dhandy { } } //unnamed namespace + //Used for checking ASCIITranslator::AltLetter + define_has_enum(AltLetter, AltLetter); + + template ::value != 0> + inline auto g_AltLetterOrZero = Tr::AltLetter; + template + inline auto g_AltLetterOrZero = Tr::NullChar; + template T to_integer_sse (const C* s, std::size_t l); @@ -202,7 +211,7 @@ namespace dhandy { }; template - struct AryConversion::value and not std::is_same::value and sizeof(I) <= sizeof(std::uint32_t)>::type> { + struct AryConversion::value and not std::is_same::value and sizeof(I) <= sizeof(std::uint32_t)>::type> { constexpr static const bool is_sse = true; template static I from_ary (C* beg, C* end) { return to_integer_sse(beg, end - beg); } }; @@ -246,13 +255,24 @@ namespace dhandy { __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); + const __m128i char_befo_a = _mm_set1_epi32(Tr::FirstLetter - 1); + const __m128i char_past_f = _mm_set1_epi32(Tr::FirstLetter + Base - 10); 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)); + __m128i mask = _mm_and_si128(_mm_cmpgt_epi32(digits, char_befo_a), _mm_cmplt_epi32(digits, char_past_f)); + __m128i offs = _mm_and_si128(mask, _mm_set1_epi32(Tr::FirstLetter - 10)); + + if constexpr (HasAltLetterEnum::value) { + const __m128i char_befo_A = _mm_set1_epi32(g_AltLetterOrZero - 1); + const __m128i char_past_F = _mm_set1_epi32(g_AltLetterOrZero + Base - 10); + const __m128i alt_mask = _mm_and_si128(_mm_cmpgt_epi32(digits, char_befo_A), _mm_cmplt_epi32(digits, char_past_F)); + const __m128i alt_offs = _mm_and_si128(alt_mask, _mm_set1_epi32(g_AltLetterOrZero - 10)); + offs = _mm_add_epi32(alt_offs, offs); + mask = _mm_or_si128(mask, alt_mask); + } + const __m128i addend = _mm_add_epi32(offs, _mm_andnot_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; @@ -261,7 +281,7 @@ namespace dhandy { { 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}; + constexpr 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); } } @@ -272,9 +292,9 @@ namespace dhandy { template struct ASCIITranslator { typedef C char_type; - static const constexpr bool BehavesLikeASCII = true; static const constexpr C FirstDigit = FDigit; static const constexpr C FirstLetter = FLetter; + //static const constexpr C AltLetter = FAltLetter; static const constexpr C Plus = CPlus; static const constexpr C Minus = CMinus; static const constexpr C NullChar = CNull; diff --git a/test/unit/int_conv_test.cpp b/test/unit/int_conv_test.cpp index cfe9276..f5eef34 100644 --- a/test/unit/int_conv_test.cpp +++ b/test/unit/int_conv_test.cpp @@ -29,6 +29,17 @@ template using int_info_16 = dhandy::implem::int_info; template using int_info_2 = dhandy::implem::int_info; namespace { + template + struct ASCIITranslatorIns : public dhandy::ASCIITranslator { + static const constexpr char AltLetter = 'A'; + static constexpr int from_digit (char dig) { + if (dig >= FirstLetter and dig <= FirstLetter + Base - 10 - 1) + return dig - FirstLetter + 10; + else + return dig - AltLetter + 10; + } + }; + template void AryConversionTestHelper (const std::string_view& s, T expected, bool expect_sse) { using AryConversion = dhandy::implem::AryConversion, false>; @@ -42,6 +53,13 @@ namespace { CHECK(AryConversion::is_sse == expect_sse); CHECK(AryConversion::from_ary(s.data(), s.data() + s.size()) == expected); } + + template + void AryConversionTestHelperIns (const std::string_view& s, T expected, bool expect_sse) { + using AryConversion = dhandy::implem::AryConversion, false>; + CHECK(AryConversion::is_sse == expect_sse); + CHECK(AryConversion::from_ary(s.data(), s.data() + s.size()) == expected); + } } //unnamed namespace TEST_CASE ("Check int to char array conversions", "[s2i][int_conv]") { @@ -170,6 +188,15 @@ TEST_CASE ("Check char array to int conversions", "[i2s][int_conv]") { AryConversionTestHelper("-50000", -50000, sizeof(signed int) <= sizeof(std::uint32_t)); AryConversionTestHelper("-1", -1, false); AryConversionTestHelper("-510123123123", -510123123123, false); + + //case insensitive SSE conversions + AryConversionTestHelperIns("7FfFfFfF", 0x7fffffff, true); + AryConversionTestHelperIns("AbCdEf01", 0xabcdef01, true); + AryConversionTestHelperIns("aBcDeF01", 0xabcdef01, true); + AryConversionTestHelperIns("Ff", 0xff, true); + AryConversionTestHelperIns("AfBe", 0xafbe, true); + AryConversionTestHelperIns("aAb", 0xaab, true); + AryConversionTestHelperIns("aAbBc", 0xaabbc, true); } TEST_CASE ("Check upcase/downcase int to array conversions", "[i2s][int_conv]") { @@ -195,3 +222,12 @@ TEST_CASE ("Check upcase/downcase int to array conversions", "[i2s][int_conv]") constexpr auto int4 = int_conv(integral_constant{}); CHECK("-256" == int4); } + +TEST_CASE ("Try int conv with non-char", "[i2s][int_conv]") { + using dhandy::int_to_ary; + using dhandy::ASCIITranslator; + + std::wstring_view exp1(L"235713"); + auto val1 = int_to_ary>(235713).to_string_view(); + CHECK(exp1 == val1); +}