| #pragma once |
| // IWYU pragma: private, include "rlbox.hpp" |
| // IWYU pragma: friend "rlbox_.*\.hpp" |
| |
| #include <array> |
| #include <cstring> |
| #include <limits> |
| #include <type_traits> |
| |
| #include "rlbox_helpers.hpp" |
| #include "rlbox_type_traits.hpp" |
| #include "rlbox_types.hpp" |
| |
| namespace rlbox::detail { |
| |
| template<typename T_To, typename T_From> |
| inline constexpr void convert_type_fundamental(T_To& to, |
| const volatile T_From& from) |
| { |
| using namespace std; |
| |
| if_constexpr_named(cond1, !is_fundamental_or_enum_v<T_To>) |
| { |
| rlbox_detail_static_fail_because( |
| cond1, "Conversion target should be fundamental or enum type"); |
| } |
| else if_constexpr_named(cond2, !is_fundamental_or_enum_v<T_From>) |
| { |
| rlbox_detail_static_fail_because( |
| cond2, "Conversion source should be fundamental or enum type"); |
| } |
| else if_constexpr_named(cond3, is_enum_v<T_To> || is_enum_v<T_From>) |
| { |
| static_assert(std::is_same_v<detail::remove_cv_ref_t<T_To>, |
| detail::remove_cv_ref_t<T_From>>); |
| to = from; |
| } |
| else if_constexpr_named( |
| cond4, is_floating_point_v<T_To> || is_floating_point_v<T_From>) |
| { |
| static_assert(is_floating_point_v<T_To> && is_floating_point_v<T_From>); |
| // language coerces different float types |
| to = from; |
| } |
| else if_constexpr_named(cond5, is_integral_v<T_To> || is_integral_v<T_From>) |
| { |
| static_assert(is_integral_v<T_To> && is_integral_v<T_From>); |
| |
| const char* err_msg = |
| "Over/Underflow when converting between integer types"; |
| |
| // Some branches don't use the param |
| RLBOX_UNUSED(err_msg); |
| |
| if constexpr (is_signed_v<T_To> == is_signed_v<T_From> && |
| sizeof(T_To) >= sizeof(T_From)) { |
| // Eg: int64_t from int32_t, uint64_t from uint32_t |
| } else if constexpr (is_unsigned_v<T_To> && is_unsigned_v<T_From>) { |
| // Eg: uint32_t from uint64_t |
| dynamic_check(from <= numeric_limits<T_To>::max(), err_msg); |
| } else if constexpr (is_signed_v<T_To> && is_signed_v<T_From>) { |
| // Eg: int32_t from int64_t |
| dynamic_check(from >= numeric_limits<T_To>::min(), err_msg); |
| dynamic_check(from <= numeric_limits<T_To>::max(), err_msg); |
| } else if constexpr (is_unsigned_v<T_To> && is_signed_v<T_From>) { |
| if constexpr (sizeof(T_To) < sizeof(T_From)) { |
| // Eg: uint32_t from int64_t |
| dynamic_check(from >= 0, err_msg); |
| auto to_max = numeric_limits<T_To>::max(); |
| dynamic_check(from <= static_cast<T_From>(to_max), err_msg); |
| } else { |
| // Eg: uint32_t from int32_t, uint64_t from int32_t |
| dynamic_check(from >= 0, err_msg); |
| } |
| } else if constexpr (is_signed_v<T_To> && is_unsigned_v<T_From>) { |
| if constexpr (sizeof(T_To) <= sizeof(T_From)) { |
| // Eg: int32_t from uint32_t, int32_t from uint64_t |
| auto to_max = numeric_limits<T_To>::max(); |
| dynamic_check(from <= static_cast<T_From>(to_max), err_msg); |
| } else { |
| // Eg: int64_t from uint32_t |
| } |
| } |
| to = static_cast<T_To>(from); |
| } |
| else |
| { |
| constexpr auto unknownCase = !(cond1 || cond2 || cond3 || cond4 || cond5); |
| rlbox_detail_static_fail_because( |
| unknownCase, "Unexpected case for convert_type_fundamental"); |
| } |
| } |
| |
| template<typename T_To, typename T_From> |
| inline constexpr void convert_type_fundamental_or_array(T_To& to, |
| const T_From& from) |
| { |
| using namespace std; |
| |
| using T_To_C = std_array_to_c_arr_t<T_To>; |
| using T_From_C = std_array_to_c_arr_t<T_From>; |
| using T_To_El = remove_all_extents_t<T_To_C>; |
| using T_From_El = remove_all_extents_t<T_From_C>; |
| |
| if_constexpr_named(cond1, is_array_v<T_To_C> != is_array_v<T_From_C>) |
| { |
| rlbox_detail_static_fail_because( |
| cond1, "Conversion should not go between array and non array types"); |
| } |
| else if constexpr (!is_array_v<T_To_C>) |
| { |
| convert_type_fundamental(to, from); |
| } |
| else if_constexpr_named(cond2, !all_extents_same<T_To_C, T_From_C>) |
| { |
| rlbox_detail_static_fail_because( |
| cond2, "Conversion between arrays should have same dimensions"); |
| } |
| else if_constexpr_named(cond3, |
| is_pointer_v<T_To_El> || is_pointer_v<T_From_El>) |
| { |
| rlbox_detail_static_fail_because(cond3, |
| "convert_type_fundamental_or_array " |
| "does not allow arrays of pointers"); |
| } |
| else |
| { |
| // Explicitly using size to check for element type as we may be going across |
| // different types of the same width such as void* and uintptr_t |
| if constexpr (sizeof(T_To_El) == sizeof(T_From_El) && |
| is_signed_v<T_To_El> == is_signed_v<T_From_El>) { |
| // Sanity check - this should definitely be true |
| static_assert(sizeof(T_From_C) == sizeof(T_To_C)); |
| std::memcpy(&to, &from, sizeof(T_To_C)); |
| } else { |
| for (size_t i = 0; i < std::extent_v<T_To_C>; i++) { |
| convert_type_fundamental_or_array(to[i], from[i]); |
| } |
| } |
| } |
| } |
| |
| enum class adjust_type_direction |
| { |
| TO_SANDBOX, |
| TO_APPLICATION, |
| NO_CHANGE |
| }; |
| |
| enum class adjust_type_context |
| { |
| EXAMPLE, |
| SANDBOX |
| }; |
| |
| template<typename T_Sbx, |
| adjust_type_direction Direction, |
| adjust_type_context Context, |
| typename T_To, |
| typename T_From> |
| inline constexpr void convert_type_non_class( |
| T_To& to, |
| const T_From& from, |
| const void* example_unsandboxed_ptr, |
| rlbox_sandbox<T_Sbx>* sandbox_ptr) |
| { |
| using namespace std; |
| |
| // Some branches don't use the param |
| RLBOX_UNUSED(example_unsandboxed_ptr); |
| RLBOX_UNUSED(sandbox_ptr); |
| |
| using T_To_C = std_array_to_c_arr_t<T_To>; |
| using T_From_C = std_array_to_c_arr_t<T_From>; |
| using T_To_El = remove_all_extents_t<T_To_C>; |
| using T_From_El = remove_all_extents_t<T_From_C>; |
| |
| if constexpr (is_pointer_v<T_To_C> || is_pointer_v<T_From_C>) { |
| |
| if constexpr (Direction == adjust_type_direction::NO_CHANGE) { |
| |
| static_assert(is_pointer_v<T_To_C> && is_pointer_v<T_From_C> && |
| sizeof(T_To_C) == sizeof(T_From_C)); |
| to = from; |
| |
| } else if constexpr (Direction == adjust_type_direction::TO_SANDBOX) { |
| |
| static_assert(is_pointer_v<T_From_C>); |
| // Maybe a function pointer, so convert |
| auto from_c = reinterpret_cast<const void*>(from); |
| if constexpr (Context == adjust_type_context::SANDBOX) { |
| RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr); |
| to = sandbox_ptr->template get_sandboxed_pointer<T_From_C>(from_c); |
| } else { |
| RLBOX_DEBUG_ASSERT(from_c == nullptr || |
| example_unsandboxed_ptr != nullptr); |
| to = |
| rlbox_sandbox<T_Sbx>::template get_sandboxed_pointer_no_ctx<T_From_C>( |
| from_c, example_unsandboxed_ptr); |
| } |
| |
| } else if constexpr (Direction == adjust_type_direction::TO_APPLICATION) { |
| |
| static_assert(is_pointer_v<T_To_C>); |
| if constexpr (Context == adjust_type_context::SANDBOX) { |
| RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr); |
| to = sandbox_ptr->template get_unsandboxed_pointer<T_To_C>(from); |
| } else { |
| RLBOX_DEBUG_ASSERT(from == 0 || example_unsandboxed_ptr != nullptr); |
| to = |
| rlbox_sandbox<T_Sbx>::template get_unsandboxed_pointer_no_ctx<T_To_C>( |
| from, example_unsandboxed_ptr); |
| } |
| } |
| |
| } else if constexpr (is_pointer_v<T_To_El> || is_pointer_v<T_From_El>) { |
| |
| if constexpr (Direction == adjust_type_direction::NO_CHANGE) { |
| // Sanity check - this should definitely be true |
| static_assert(sizeof(T_To_El) == sizeof(T_From_El) && |
| sizeof(T_From_C) == sizeof(T_To_C)); |
| memcpy(&to, &from, sizeof(T_To_C)); |
| } else { |
| for (size_t i = 0; i < std::extent_v<T_To_C>; i++) { |
| convert_type_non_class<T_Sbx, Direction, Context>( |
| to[i], from[i], example_unsandboxed_ptr, sandbox_ptr); |
| } |
| } |
| |
| } else { |
| convert_type_fundamental_or_array(to, from); |
| } |
| } |
| |
| // Structs implement their own convert_type by specializing this class |
| // Have to do this via a class, as functions can't be partially specialized |
| template<typename T_Sbx, |
| adjust_type_direction Direction, |
| adjust_type_context Context, |
| typename T_To, |
| typename T_From> |
| class convert_type_class; |
| // The specialization implements the following |
| // { |
| // static inline void run(T_To& to, |
| // const T_From& from, |
| // const void* example_unsandboxed_ptr); |
| // } |
| |
| template<typename T_Sbx, |
| adjust_type_direction Direction, |
| adjust_type_context Context, |
| typename T_To, |
| typename T_From> |
| inline void convert_type(T_To& to, |
| const T_From& from, |
| const void* example_unsandboxed_ptr, |
| rlbox_sandbox<T_Sbx>* sandbox_ptr) |
| { |
| if constexpr ((std::is_class_v<T_To> || |
| std::is_class_v<T_From>)&&!detail::is_std_array_v<T_To> && |
| !detail::is_std_array_v<T_From>) { |
| // Sanity check |
| static_assert(std::is_class_v<T_From> && std::is_class_v<T_To>); |
| convert_type_class<T_Sbx, Direction, Context, T_To, T_From>::run( |
| to, from, example_unsandboxed_ptr, sandbox_ptr); |
| } else { |
| convert_type_non_class<T_Sbx, Direction, Context>( |
| to, from, example_unsandboxed_ptr, sandbox_ptr); |
| } |
| } |
| |
| } |