From 8b14b7cddc0c67cdb9d5564b37f4836deb53ca14 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 8 Dec 2022 12:25:27 -0800 Subject: [PATCH 1/2] Add tests for narrow_cast --- test/Jamfile.v2 | 1 + test/unordered/narrow_cast_tests.cpp | 107 +++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/unordered/narrow_cast_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c3284098..9a702bc6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -95,6 +95,7 @@ run exception/rehash_exception_tests.cpp ; run exception/swap_exception_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=2 ; run exception/merge_exception_tests.cpp ; +run unordered/narrow_cast_tests.cpp ; run quick.cpp ; import ../../config/checks/config : requires ; diff --git a/test/unordered/narrow_cast_tests.cpp b/test/unordered/narrow_cast_tests.cpp new file mode 100644 index 00000000..81d5ee82 --- /dev/null +++ b/test/unordered/narrow_cast_tests.cpp @@ -0,0 +1,107 @@ +// Copyright 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#include +#include + +// want to prove that for the wider type, the higher bits of the value +// represenation don't affect the results of the narrowing, which in this case +// is masking out the high bits when comapred to the narrow type + +static void signed_integral_narrowing() +{ + // test positive range, fits + // [0, 127] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int8_t k = (boost::int8_t)i; + BOOST_TEST_GE(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), k); + } + + // test positive range, doesn't fit + // [0xff00, 0xff7f] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + 0xff00; + boost::int8_t k = (boost::int8_t)i; + BOOST_TEST_GE(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + // test negative range, fits + // [-128, -1] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + (boost::int32_t)0xffffff80; + boost::int8_t k = (boost::int8_t)j; + BOOST_TEST_LT(j, 0); + BOOST_TEST_LT(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + // test negative range, doesn't fit + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + (boost::int32_t)0x80000000; + boost::int8_t k = (boost::int8_t)(i); + BOOST_TEST_LT(j, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + for (boost::int32_t i = 0x00; i < 0x100; ++i) { + boost::int32_t j = (boost::int32_t)0x80ff0000 + i; + BOOST_TEST_LT(j, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), + (boost::int8_t)i); + } + + // test special values + { + boost::int32_t x = 0xff; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), -1); + } + + { + boost::int32_t x = (boost::int32_t)0xffffff00; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)0x00); + } + + { + boost::int32_t x = (boost::int32_t)0xffffff7f; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)0x7f); + } + + { + boost::int32_t x = (boost::int32_t)0xffffffff; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)-1); + } +} + +static void unsigned_integral_narrowing() +{ + // test range: [0x00, 0xff] + for (boost::uint32_t i = 0x00; i < 0x100; ++i) { + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); + } + + // test range: [0xffffff00, 0xffffffff] + boost::uint32_t i = 0xffffff00; + for (; i < 0xffffffff; ++i) { + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); + } + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); +} + +int main() +{ + signed_integral_narrowing(); + unsigned_integral_narrowing(); + + return boost::report_errors(); +} From f0037d336dc2b520705b96ccc675d2343eb3643c Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 5 Dec 2022 11:50:18 +0100 Subject: [PATCH 2/2] added narrow_cast --- include/boost/unordered/detail/foa.hpp | 31 +++---------- .../boost/unordered/detail/narrow_cast.hpp | 44 +++++++++++++++++++ include/boost/unordered/detail/prime_fmod.hpp | 9 +--- 3 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 include/boost/unordered/detail/narrow_cast.hpp diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 41a1f153..ebeed0f7 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -269,20 +270,12 @@ private: 0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu, }; -#if defined(__MSVC_RUNTIME_CHECKS) - return (int)word[hash&0xffu]; -#else - return (int)word[(unsigned char)hash]; -#endif + return (int)word[narrow_cast(hash)]; } inline static unsigned char reduced_hash(std::size_t hash) { -#if defined(__MSVC_RUNTIME_CHECKS) - return match_word(hash)&0xffu; -#else - return (unsigned char)match_word(hash); -#endif + return narrow_cast(match_word(hash)); } inline unsigned char& at(std::size_t pos) @@ -533,11 +526,7 @@ struct group15 std::size_t pos=reinterpret_cast(pc)%sizeof(group15); group15 *pg=reinterpret_cast(pc-pos); boost::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull; -#if defined(__MSVC_RUNTIME_CHECKS) - boost::uint32_t y=(x|(x>>15)|(x>>30))&0xffffffffu; -#else - boost::uint32_t y=static_cast(x|(x>>15)|(x>>30)); -#endif + boost::uint32_t y=narrow_cast(x|(x>>15)|(x>>30)); return !pg->is_not_overflowed(y); }; @@ -552,11 +541,7 @@ struct group15 inline int match_occupied()const { boost::uint64_t x=m[0]|m[1]; -#if defined(__MSVC_RUNTIME_CHECKS) - boost::uint32_t y=(x|(x>>32))&0xffffffffu; -#else - boost::uint32_t y=static_cast(x|(x>>32)); -#endif + boost::uint32_t y=narrow_cast(x|(x>>32)); y|=y>>16; return y&0x7FFF; } @@ -591,11 +576,7 @@ private: 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, }; -#if defined(__MSVC_RUNTIME_CHECKS) - return table[hash&0xffu]; -#else - return table[(unsigned char)hash]; -#endif + return table[narrow_cast(hash)]; } inline void set_impl(std::size_t pos,std::size_t n) diff --git a/include/boost/unordered/detail/narrow_cast.hpp b/include/boost/unordered/detail/narrow_cast.hpp new file mode 100644 index 00000000..da89f67f --- /dev/null +++ b/include/boost/unordered/detail/narrow_cast.hpp @@ -0,0 +1,44 @@ +/* Copyright 2022 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP +#define BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP + +#include +#include +#include +#include + +namespace boost{ +namespace unordered{ +namespace detail{ + +template +BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT +{ + BOOST_STATIC_ASSERT(boost::is_integral::value); + BOOST_STATIC_ASSERT(boost::is_integral::value); + BOOST_STATIC_ASSERT(sizeof(From)>=sizeof(To)); + + return static_cast( + x + +#if defined(__MSVC_RUNTIME_CHECKS) + /* Avoids VS's "Run-Time Check Failure #1 - A cast to a smaller data type + * has caused a loss of data." + */ + &static_cast::type>(~static_cast(0)) +#endif + ); +} + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/detail/prime_fmod.hpp b/include/boost/unordered/detail/prime_fmod.hpp index 4c538487..fab8f94e 100644 --- a/include/boost/unordered/detail/prime_fmod.hpp +++ b/include/boost/unordered/detail/prime_fmod.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -117,15 +118,9 @@ namespace boost { #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) std::size_t sizes_under_32bit = inv_sizes32_len; if (BOOST_LIKELY(size_index < sizes_under_32bit)) { -#if defined(__MSVC_RUNTIME_CHECKS) return fast_modulo( - boost::uint32_t(hash & 0xffffffffu) + boost::uint32_t(hash >> 32), + narrow_cast(hash) + narrow_cast(hash >> 32), inv_sizes32[size_index], boost::uint32_t(sizes[size_index])); -#else - return fast_modulo( - boost::uint32_t(hash) + boost::uint32_t(hash >> 32), - inv_sizes32[size_index], boost::uint32_t(sizes[size_index])); -#endif } else { return positions[size_index - sizes_under_32bit](hash); }