#pragma once // IWYU pragma: private, include "rlbox.hpp" // IWYU pragma: friend "rlbox_.*\.hpp" #include #include #include #include #include "rlbox_helpers.hpp" #include "rlbox_type_traits.hpp" #include "rlbox_types.hpp" namespace rlbox::detail { template 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) { rlbox_detail_static_fail_because( cond1, "Conversion target should be fundamental or enum type"); } else if_constexpr_named(cond2, !is_fundamental_or_enum_v) { rlbox_detail_static_fail_because( cond2, "Conversion source should be fundamental or enum type"); } else if_constexpr_named(cond3, is_enum_v || is_enum_v) { static_assert(std::is_same_v, detail::remove_cv_ref_t>); to = from; } else if_constexpr_named( cond4, is_floating_point_v || is_floating_point_v) { static_assert(is_floating_point_v && is_floating_point_v); // language coerces different float types to = from; } else if_constexpr_named(cond5, is_integral_v || is_integral_v) { static_assert(is_integral_v && is_integral_v); 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 == is_signed_v && sizeof(T_To) >= sizeof(T_From)) { // Eg: int64_t from int32_t, uint64_t from uint32_t } else if constexpr (is_unsigned_v && is_unsigned_v) { // Eg: uint32_t from uint64_t dynamic_check(from <= numeric_limits::max(), err_msg); } else if constexpr (is_signed_v && is_signed_v) { // Eg: int32_t from int64_t dynamic_check(from >= numeric_limits::min(), err_msg); dynamic_check(from <= numeric_limits::max(), err_msg); } else if constexpr (is_unsigned_v && is_signed_v) { 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::max(); dynamic_check(from <= static_cast(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 && is_unsigned_v) { if constexpr (sizeof(T_To) <= sizeof(T_From)) { // Eg: int32_t from uint32_t, int32_t from uint64_t auto to_max = numeric_limits::max(); dynamic_check(from <= static_cast(to_max), err_msg); } else { // Eg: int64_t from uint32_t } } to = static_cast(from); } else { constexpr auto unknownCase = !(cond1 || cond2 || cond3 || cond4 || cond5); rlbox_detail_static_fail_because( unknownCase, "Unexpected case for convert_type_fundamental"); } } template 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; using T_From_C = std_array_to_c_arr_t; using T_To_El = remove_all_extents_t; using T_From_El = remove_all_extents_t; if_constexpr_named(cond1, is_array_v != is_array_v) { rlbox_detail_static_fail_because( cond1, "Conversion should not go between array and non array types"); } else if constexpr (!is_array_v) { convert_type_fundamental(to, from); } else if_constexpr_named(cond2, !all_extents_same) { rlbox_detail_static_fail_because( cond2, "Conversion between arrays should have same dimensions"); } else if_constexpr_named(cond3, is_pointer_v || is_pointer_v) { 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 == is_signed_v) { // 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; 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 inline constexpr void convert_type_non_class( T_To& to, const T_From& from, const void* example_unsandboxed_ptr, rlbox_sandbox* 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; using T_From_C = std_array_to_c_arr_t; using T_To_El = remove_all_extents_t; using T_From_El = remove_all_extents_t; if constexpr (is_pointer_v || is_pointer_v) { if constexpr (Direction == adjust_type_direction::NO_CHANGE) { static_assert(is_pointer_v && is_pointer_v && sizeof(T_To_C) == sizeof(T_From_C)); to = from; } else if constexpr (Direction == adjust_type_direction::TO_SANDBOX) { static_assert(is_pointer_v); // Maybe a function pointer, so convert auto from_c = reinterpret_cast(from); if constexpr (Context == adjust_type_context::SANDBOX) { RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr); to = sandbox_ptr->template get_sandboxed_pointer(from_c); } else { RLBOX_DEBUG_ASSERT(from_c == nullptr || example_unsandboxed_ptr != nullptr); to = rlbox_sandbox::template get_sandboxed_pointer_no_ctx( from_c, example_unsandboxed_ptr); } } else if constexpr (Direction == adjust_type_direction::TO_APPLICATION) { static_assert(is_pointer_v); if constexpr (Context == adjust_type_context::SANDBOX) { RLBOX_DEBUG_ASSERT(sandbox_ptr != nullptr); to = sandbox_ptr->template get_unsandboxed_pointer(from); } else { RLBOX_DEBUG_ASSERT(from == 0 || example_unsandboxed_ptr != nullptr); to = rlbox_sandbox::template get_unsandboxed_pointer_no_ctx( from, example_unsandboxed_ptr); } } } else if constexpr (is_pointer_v || is_pointer_v) { 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; i++) { convert_type_non_class( 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 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 inline void convert_type(T_To& to, const T_From& from, const void* example_unsandboxed_ptr, rlbox_sandbox* sandbox_ptr) { if constexpr ((std::is_class_v || std::is_class_v)&&!detail::is_std_array_v && !detail::is_std_array_v) { // Sanity check static_assert(std::is_class_v && std::is_class_v); convert_type_class::run( to, from, example_unsandboxed_ptr, sandbox_ptr); } else { convert_type_non_class( to, from, example_unsandboxed_ptr, sandbox_ptr); } } }