summaryrefslogtreecommitdiffstats
path: root/third_party/rlbox/include/rlbox_conversion.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rlbox/include/rlbox_conversion.hpp')
-rw-r--r--third_party/rlbox/include/rlbox_conversion.hpp273
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