Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions doc/modules/ROOT/pages/charconv.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ https://www.boost.org/LICENSE_1_0.txt

The following functions provide character conversion support for safe integer types using https://www.boost.org/doc/libs/master/libs/charconv/doc/html/charconv.html[Boost.Charconv].

IMPORTANT: Using this header requires Boost.Charconv headers to be present.

[#to_chars]
== to_chars

Expand Down
1 change: 1 addition & 0 deletions include/boost/safe_numbers/detail/overflow_policy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum class overflow_policy
throw_exception,
saturate,
overflow_tuple,
checked,
};

} // namespace boost::safe_numbers::detail
Expand Down
219 changes: 216 additions & 3 deletions include/boost/safe_numbers/detail/unsigned_integer_basis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <stdexcept>
#include <cstdint>
#include <utility>
#include <optional>

#endif // BOOST_SAFE_NUMBERS_BUILD_MODULE

Expand Down Expand Up @@ -268,10 +269,42 @@ struct add_helper<overflow_policy::overflow_tuple, BasisType>
}
};

// Partial specialization for overflow_tuple policy
template <unsigned_integral BasisType>
struct add_helper<overflow_policy::checked, BasisType>
{
[[nodiscard]] static constexpr auto apply(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<unsigned_integer_basis<BasisType>>
{
using result_type = unsigned_integer_basis<BasisType>;

const auto lhs_basis {static_cast<BasisType>(lhs)};
const auto rhs_basis {static_cast<BasisType>(rhs)};
BasisType res {};

if constexpr (!std::is_same_v<BasisType, int128::uint128_t>)
{
#if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_add_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_addcarry_u64) || defined(BOOST_SAFENUMBERS_HAS_WINDOWS_X86_INTRIN)

if (!std::is_constant_evaluated())
{
const auto overflowed {impl::unsigned_intrin_add(lhs_basis, rhs_basis, res)};
return overflowed ? std::nullopt : std::make_optional(result_type{res});
}

#endif
}

const auto overflowed {impl::unsigned_no_intrin_add(lhs_basis, rhs_basis, res)};
return overflowed ? std::nullopt : std::make_optional(result_type{res});
}
};

template <overflow_policy Policy, unsigned_integral BasisType>
[[nodiscard]] constexpr auto add_impl(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked)
{
return add_helper<Policy, BasisType>::apply(lhs, rhs);
}
Expand Down Expand Up @@ -567,10 +600,42 @@ struct sub_helper<overflow_policy::overflow_tuple, BasisType>
}
};

// Partial specialization for checked policy
template <unsigned_integral BasisType>
struct sub_helper<overflow_policy::checked, BasisType>
{
[[nodiscard]] static constexpr auto apply(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<unsigned_integer_basis<BasisType>>
{
using result_type = unsigned_integer_basis<BasisType>;

const auto lhs_basis {static_cast<BasisType>(lhs)};
const auto rhs_basis {static_cast<BasisType>(rhs)};
BasisType res {};

if constexpr (!std::is_same_v<BasisType, int128::uint128_t>)
{
#if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_sub_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_subborrow_u64) || defined(BOOST_SAFENUMBERS_HAS_WINDOWS_X86_INTRIN)

if (!std::is_constant_evaluated())
{
const auto underflowed {impl::unsigned_intrin_sub(lhs_basis, rhs_basis, res)};
return underflowed ? std::nullopt : std::make_optional(result_type{res});
}

#endif
}

const auto underflowed {impl::unsigned_no_intrin_sub(lhs_basis, rhs_basis, res)};
return underflowed ? std::nullopt : std::make_optional(result_type{res});
}
};

template <overflow_policy Policy, unsigned_integral BasisType>
[[nodiscard]] constexpr auto sub_impl(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked)
{
return sub_helper<Policy, BasisType>::apply(lhs, rhs);
}
Expand Down Expand Up @@ -767,10 +832,42 @@ struct mul_helper<overflow_policy::overflow_tuple, BasisType>
}
};

// Partial specialization for checked policy
template <unsigned_integral BasisType>
struct mul_helper<overflow_policy::checked, BasisType>
{
[[nodiscard]] static constexpr auto apply(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<unsigned_integer_basis<BasisType>>
{
using result_type = unsigned_integer_basis<BasisType>;

const auto lhs_basis {static_cast<BasisType>(lhs)};
const auto rhs_basis {static_cast<BasisType>(rhs)};
BasisType res {};

if constexpr (!std::is_same_v<BasisType, int128::uint128_t>)
{
#if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_mul_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_umul128)

if (!std::is_constant_evaluated())
{
const auto overflowed {impl::unsigned_intrin_mul(lhs_basis, rhs_basis, res)};
return overflowed ? std::nullopt : std::make_optional(result_type{res});
}

#endif
}

const auto overflowed {impl::no_intrin_mul(lhs_basis, rhs_basis, res)};
return overflowed ? std::nullopt : std::make_optional(result_type{res});
}
};

template <overflow_policy Policy, unsigned_integral BasisType>
[[nodiscard]] constexpr auto mul_impl(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple)
noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked)
{
return mul_helper<Policy, BasisType>::apply(lhs, rhs);
}
Expand Down Expand Up @@ -861,9 +958,37 @@ struct div_helper<overflow_policy::overflow_tuple, BasisType>
}
};

// Partial specialization for checked policy
template <unsigned_integral BasisType>
struct div_helper<overflow_policy::checked, BasisType>
{
[[nodiscard]] static constexpr auto apply(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<unsigned_integer_basis<BasisType>>
{
using result_type = unsigned_integer_basis<BasisType>;

const auto divisor {static_cast<BasisType>(rhs)};
if (divisor == 0U) [[unlikely]]
{
return std::nullopt;
}

if constexpr (std::is_same_v<BasisType, std::uint8_t> || std::is_same_v<BasisType, std::uint16_t>)
{
return std::make_optional(result_type{static_cast<BasisType>(static_cast<BasisType>(lhs) / divisor)});
}
else
{
return std::make_optional(result_type{static_cast<BasisType>(lhs) / divisor});
}
}
};

template <overflow_policy Policy, unsigned_integral BasisType>
[[nodiscard]] constexpr auto div_impl(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs)
noexcept(Policy == overflow_policy::checked)
{
return div_helper<Policy, BasisType>::apply(lhs, rhs);
}
Expand Down Expand Up @@ -954,9 +1079,37 @@ struct mod_helper<overflow_policy::overflow_tuple, BasisType>
}
};

// Partial specialization for checked policy
template <unsigned_integral BasisType>
struct mod_helper<overflow_policy::checked, BasisType>
{
[[nodiscard]] static constexpr auto apply(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<unsigned_integer_basis<BasisType>>
{
using result_type = unsigned_integer_basis<BasisType>;

const auto divisor {static_cast<BasisType>(rhs)};
if (divisor == 0U) [[unlikely]]
{
return std::nullopt;
}

if constexpr (std::is_same_v<BasisType, std::uint8_t> || std::is_same_v<BasisType, std::uint16_t>)
{
return std::make_optional(result_type{static_cast<BasisType>(static_cast<BasisType>(lhs) % divisor)});
}
else
{
return std::make_optional(result_type{static_cast<BasisType>(lhs) % divisor});
}
}
};

template <overflow_policy Policy, unsigned_integral BasisType>
[[nodiscard]] constexpr auto mod_impl(const unsigned_integer_basis<BasisType> lhs,
const unsigned_integer_basis<BasisType> rhs)
noexcept(Policy == overflow_policy::checked)
{
return mod_helper<Policy, BasisType>::apply(lhs, rhs);
}
Expand Down Expand Up @@ -1107,6 +1260,8 @@ template <detail::unsigned_integral BasisType>
return detail::add_impl<detail::overflow_policy::overflow_tuple>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("overflowing addition", overflowing_add)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto overflowing_sub(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
Expand All @@ -1115,6 +1270,8 @@ template <detail::unsigned_integral BasisType>
return detail::sub_impl<detail::overflow_policy::overflow_tuple>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("overflowing subtraction", overflowing_sub)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto overflowing_mul(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
Expand All @@ -1123,6 +1280,8 @@ template <detail::unsigned_integral BasisType>
return detail::mul_impl<detail::overflow_policy::overflow_tuple>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("overflowing multiplication", overflowing_mul)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto overflowing_div(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs)
Expand All @@ -1131,6 +1290,8 @@ template <detail::unsigned_integral BasisType>
return detail::div_impl<detail::overflow_policy::overflow_tuple>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("overflowing division", overflowing_div)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto overflowing_mod(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs)
Expand All @@ -1139,6 +1300,58 @@ template <detail::unsigned_integral BasisType>
return detail::mod_impl<detail::overflow_policy::overflow_tuple>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("overflowing modulo", overflowing_mod)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto checked_add(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<detail::unsigned_integer_basis<BasisType>>
{
return detail::add_impl<detail::overflow_policy::checked>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked addition", checked_add)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto checked_sub(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<detail::unsigned_integer_basis<BasisType>>
{
return detail::sub_impl<detail::overflow_policy::checked>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked subtraction", checked_sub)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto checked_mul(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<detail::unsigned_integer_basis<BasisType>>
{
return detail::mul_impl<detail::overflow_policy::checked>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked multiplication", checked_mul)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto checked_div(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<detail::unsigned_integer_basis<BasisType>>
{
return detail::div_impl<detail::overflow_policy::checked>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked division", checked_div)

template <detail::unsigned_integral BasisType>
[[nodiscard]] constexpr auto checked_mod(const detail::unsigned_integer_basis<BasisType> lhs,
const detail::unsigned_integer_basis<BasisType> rhs) noexcept
-> std::optional<detail::unsigned_integer_basis<BasisType>>
{
return detail::mod_impl<detail::overflow_policy::checked>(lhs, rhs);
}

BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked modulo", checked_mod)

} // namespace boost::safe_numbers

#undef BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP
Expand Down
5 changes: 5 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ run test_unsigned_overflowing_subtraction.cpp ;
run test_unsigned_overflowing_multiplication.cpp ;
run test_unsigned_overflowing_division.cpp ;
run test_unsigned_overflowing_mod.cpp ;
run test_unsigned_checked_addition.cpp ;
run test_unsigned_checked_subtraction.cpp ;
run test_unsigned_checked_multiplication.cpp ;
run test_unsigned_checked_division.cpp ;
run test_unsigned_checked_mod.cpp ;

# Compile Tests
compile compile_tests/compile_test_unsigned_integers.cpp ;
Expand Down
Loading
Loading