diff options
Diffstat (limited to 'third_party/rlbox/include/rlbox_conversion.hpp')
-rw-r--r-- | third_party/rlbox/include/rlbox_conversion.hpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/third_party/rlbox/include/rlbox_conversion.hpp b/third_party/rlbox/include/rlbox_conversion.hpp new file mode 100644 index 0000000000..e82d0d5da0 --- /dev/null +++ b/third_party/rlbox/include/rlbox_conversion.hpp @@ -0,0 +1,273 @@ +#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); + } +} + +}
\ No newline at end of file |