summaryrefslogtreecommitdiffstats
path: root/third_party/rlbox/include/rlbox_conversion.hpp
blob: e82d0d5da004b34c65a1dbd029952a7f60458188 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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);
  }
}

}