1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_UTIL_SAFE_INT_OPS_HPP
20 #define REALM_UTIL_SAFE_INT_OPS_HPP
23 #undef max // collides with numeric_limits::max called later in this header file
24 #undef min // collides with numeric_limits::min called later in this header file
29 #include <realm/util/features.h>
30 #include <realm/util/assert.hpp>
31 #include <realm/util/type_traits.hpp>
37 /// Perform integral or floating-point promotion on the argument. This
38 /// is useful for example when printing a number of arbitrary numeric
39 /// type to 'stdout', since it will convert values of character-like
40 /// types to regular integer types, which will then be printed as
41 /// numbers rather characters.
43 typename Promote<T>::type promote(T value) noexcept;
46 /// This function allows you to test for a negative value in any
47 /// numeric type, even when the type is unsigned. Normally, when the
48 /// type is unsigned, such a test will produce a compiler warning.
50 bool is_negative(T value) noexcept;
53 /// Cast the specified value to the specified unsigned type reducing
54 /// the value (or in case of negative values, the two's complement
55 /// representation) modulo `2**N` where `N` is the number of value
56 /// bits (or digits) in the unsigned target type. This is usefull in
57 /// cases where the target type may be `bool`, but need not be `bool`.
58 template <class To, class From>
59 To cast_to_unsigned(From) noexcept;
64 /// Compare two integers of the same, or of different type, and
65 /// produce the expected result according to the natural
66 /// interpretation of the operation.
68 /// Note that in general a standard comparison between a signed and an
69 /// unsigned integer type is unsafe, and it often generates a compiler
70 /// warning. An example is a 'less than' comparison between a negative
71 /// value of type 'int' and a small positive value of type
72 /// 'unsigned'. In this case the negative value will be converted to
73 /// 'unsigned' producing a large positive value which, in turn, will
74 /// lead to the counter intuitive result of 'false'.
76 /// Please note that these operation incur absolutely no overhead when
77 /// the two types have the same signedness.
79 /// These functions check at compile time that both types have valid
80 /// specializations of std::numeric_limits<> and that both are indeed
83 /// These functions make absolutely no assumptions about the platform
84 /// except that it complies with at least C++03.
86 template <class A, class B>
87 inline bool int_equal_to(A, B) noexcept;
88 template <class A, class B>
89 inline bool int_not_equal_to(A, B) noexcept;
90 template <class A, class B>
91 inline bool int_less_than(A, B) noexcept;
92 template <class A, class B>
93 inline bool int_less_than_or_equal(A, B) noexcept;
94 template <class A, class B>
95 inline bool int_greater_than(A, B) noexcept;
96 template <class A, class B>
97 inline bool int_greater_than_or_equal(A, B) noexcept;
104 /// Check for overflow in integer variable `lval` while adding integer
105 /// `rval` to it, or while subtracting integer `rval` from it. Returns
106 /// true on positive or negative overflow.
108 /// Both `lval` and `rval` must be of an integer type for which a
109 /// specialization of std::numeric_limits<> exists. The two types need
110 /// not be the same, in particular, one can be signed and the other
111 /// one can be unsigned.
113 /// These functions are especially well suited for cases where \a rval
114 /// is a compile-time constant.
116 /// These functions check at compile time that both types have valid
117 /// specializations of std::numeric_limits<> and that both are indeed
120 /// These functions make absolutely no assumptions about the platform
121 /// except that it complies with at least C++03.
123 template <class L, class R>
124 inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept;
126 template <class L, class R>
127 inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept;
132 /// Check for positive overflow when multiplying two positive integers
133 /// of the same, or of different type. Returns true on overflow.
135 /// \param lval Must not be negative. Both signed and unsigned types
138 /// \param rval Must be stricly greater than zero. Both signed and
139 /// unsigned types can be used.
141 /// This function is especially well suited for cases where \a rval is
142 /// a compile-time constant.
144 /// This function checks at compile time that both types have valid
145 /// specializations of std::numeric_limits<> and that both are indeed
148 /// This function makes absolutely no assumptions about the platform
149 /// except that it complies with at least C++03.
150 template <class L, class R>
151 inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept;
154 /// Checks for positive overflow when performing a bitwise shift to
155 /// the left on a non-negative value of arbitrary integer
156 /// type. Returns true on overflow.
158 /// \param lval Must not be negative. Both signed and unsigned types
161 /// \param i Must be non-negative and such that <tt>L(1)>>i</tt> has a
162 /// value that is defined by the C++03 standard. In particular, the
163 /// value of i must not exceed the number of bits of storage type T as
164 /// shifting by this amount is not defined by the standard.
166 /// This function makes absolutely no assumptions about the platform
167 /// except that it complies with at least C++03.
169 inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept;
174 /// Check for overflow when casting an integer value from one type to
175 /// another. While the first function is a mere check, the second one
176 /// also carries out the cast, but only when there is no
177 /// overflow. Both return true on overflow.
179 /// These functions check at compile time that both types have valid
180 /// specializations of std::numeric_limits<> and that both are indeed
183 /// These functions make absolutely no assumptions about the platform
184 /// except that it complies with at least C++03.
186 template <class To, class From>
187 bool int_cast_has_overflow(From from) noexcept;
189 template <class To, class From>
190 bool int_cast_with_overflow_detect(From from, To& to) noexcept;
195 /// Convert negative values from two's complement representation to the
196 /// platforms native representation.
198 /// If `To` is an unsigned type, this function does nothing beyond casting the
199 /// specified value to `To`. Otherwise, `To` is a signed type, and negative
200 /// values will be converted from two's complement representation in unsigned
201 /// `From` to the platforms native representation in `To`.
203 /// For signed `To` the result is well-defined if, and only if the value with
204 /// the specified two's complement representation is representable in the
205 /// specified signed type. While this is generally the case when using
206 /// corresponding signed/unsigned type pairs, it is not guaranteed by the
207 /// standard. However, if you know that the signed type has at least as many
208 /// value bits as the unsigned type, then the result is always
209 /// well-defined. Note that a 'value bit' in this context is the same as a
210 /// 'digit' from the point of view of `std::numeric_limits`.
212 /// On platforms that use two's complement representation of negative values,
213 /// this function is expected to be completely optimized away. This has been
214 /// observed to be true with both GCC 4.8 and Clang 3.2.
216 /// Note that the **opposite** direction (from the platforms native
217 /// representation to two's complement) is trivially handled by casting the
218 /// signed value to a value of a sufficiently wide unsigned integer type. An
219 /// unsigned type will be sufficiently wide if it has at least one more value
220 /// bit than the signed type.
222 /// Interestingly, the C++ language offers no direct way of doing what this
223 /// function does, yet, this function is implemented in a way that makes no
224 /// assumption about the underlying platform except what is guaranteed by C++11.
226 /// \tparam From The unsigned type used to store the two's complement
229 /// \tparam To A signed or unsigned integer type.
230 template <class To, class From>
231 To from_twos_compl(From twos_compl) noexcept;
237 inline typename Promote<T>::type promote(T value) noexcept
239 typedef typename Promote<T>::type promoted_type;
240 promoted_type value_2 = promoted_type(value);
248 template <class T, bool is_signed>
250 static bool test(T value) noexcept
256 struct IsNegative<T, false> {
257 static bool test(T) noexcept
264 struct CastToUnsigned {
265 template <class From>
266 static To cast(From value) noexcept
272 struct CastToUnsigned<bool> {
273 template <class From>
274 static bool cast(From value) noexcept
276 return bool(unsigned(value) & 1);
280 template <class L, class R, bool l_signed, bool r_signed>
281 struct SafeIntBinopsImpl {
284 // (unsigned, unsigned) (all size combinations)
286 // This implementation utilizes the fact that overflow in unsigned
287 // arithmetic is guaranteed to be handled by reduction modulo 2**N
288 // where N is the number of bits in the unsigned type. The purpose of
289 // the bitwise 'and' with lim_l::max() is to make a cast to bool
290 // behave the same way as casts to other unsigned integer types.
291 // Finally, this implementation uses the fact that if modular addition
292 // overflows, then the result must be a value that is less than both
293 // operands. Also, if modular subtraction overflows, then the result
294 // must be a value that is greater than the first operand.
295 template <class L, class R>
296 struct SafeIntBinopsImpl<L, R, false, false> {
297 typedef std::numeric_limits<L> lim_l;
298 typedef std::numeric_limits<R> lim_r;
299 static const int needed_bits_l = lim_l::digits;
300 static const int needed_bits_r = lim_r::digits;
301 static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
302 typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
303 static bool equal(L l, R r) noexcept
305 return common_unsigned(l) == common_unsigned(r);
307 static bool less(L l, R r) noexcept
309 return common_unsigned(l) < common_unsigned(r);
311 static bool add(L& lval, R rval) noexcept
313 L lval_2 = util::cast_to_unsigned<L>(lval + rval);
314 bool overflow = common_unsigned(lval_2) < common_unsigned(rval);
315 if (REALM_UNLIKELY(overflow))
320 static bool sub(L& lval, R rval) noexcept
322 common_unsigned lval_2 = common_unsigned(lval) - common_unsigned(rval);
323 bool overflow = lval_2 > common_unsigned(lval);
324 if (REALM_UNLIKELY(overflow))
326 lval = util::cast_to_unsigned<L>(lval_2);
331 // (unsigned, signed) (all size combinations)
332 template <class L, class R>
333 struct SafeIntBinopsImpl<L, R, false, true> {
334 typedef std::numeric_limits<L> lim_l;
335 typedef std::numeric_limits<R> lim_r;
336 static const int needed_bits_l = lim_l::digits;
337 static const int needed_bits_r = lim_r::digits + 1;
338 static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
339 typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
340 typedef std::numeric_limits<common_unsigned> lim_cu;
341 static bool equal(L l, R r) noexcept
343 return (lim_l::digits > lim_r::digits) ? r >= 0 && l == util::cast_to_unsigned<L>(r) : R(l) == r;
345 static bool less(L l, R r) noexcept
347 return (lim_l::digits > lim_r::digits) ? r >= 0 && l < util::cast_to_unsigned<L>(r) : R(l) < r;
349 static bool add(L& lval, R rval) noexcept
351 common_unsigned lval_2 = lval + common_unsigned(rval);
353 if (lim_l::digits < lim_cu::digits) {
354 overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max());
357 overflow = (lval_2 < common_unsigned(lval)) == (rval >= 0);
359 if (REALM_UNLIKELY(overflow))
361 lval = util::cast_to_unsigned<L>(lval_2);
364 static bool sub(L& lval, R rval) noexcept
366 common_unsigned lval_2 = lval - common_unsigned(rval);
368 if (lim_l::digits < lim_cu::digits) {
369 overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max());
372 overflow = (common_unsigned(lval_2) > common_unsigned(lval)) == (rval >= 0);
374 if (REALM_UNLIKELY(overflow))
376 lval = util::cast_to_unsigned<L>(lval_2);
381 // (signed, unsigned) (all size combinations)
382 template <class L, class R>
383 struct SafeIntBinopsImpl<L, R, true, false> {
384 typedef std::numeric_limits<L> lim_l;
385 typedef std::numeric_limits<R> lim_r;
386 static const int needed_bits_l = lim_l::digits + 1;
387 static const int needed_bits_r = lim_r::digits;
388 static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
389 typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
390 static bool equal(L l, R r) noexcept
392 return (lim_l::digits < lim_r::digits) ? l >= 0 && util::cast_to_unsigned<R>(l) == r : l == L(r);
394 static bool less(L l, R r) noexcept
396 return (lim_l::digits < lim_r::digits) ? l < 0 || util::cast_to_unsigned<R>(l) < r : l < L(r);
398 static bool add(L& lval, R rval) noexcept
400 common_unsigned max_add = common_unsigned(lim_l::max()) - common_unsigned(lval);
401 bool overflow = common_unsigned(rval) > max_add;
402 if (REALM_UNLIKELY(overflow))
404 lval = util::from_twos_compl<L>(common_unsigned(lval) + rval);
407 static bool sub(L& lval, R rval) noexcept
409 common_unsigned max_sub = common_unsigned(lval) - common_unsigned(lim_l::min());
410 bool overflow = common_unsigned(rval) > max_sub;
411 if (REALM_UNLIKELY(overflow))
413 lval = util::from_twos_compl<L>(common_unsigned(lval) - rval);
418 // (signed, signed) (all size combinations)
419 template <class L, class R>
420 struct SafeIntBinopsImpl<L, R, true, true> {
421 typedef std::numeric_limits<L> lim_l;
422 static bool equal(L l, R r) noexcept
426 static bool less(L l, R r) noexcept
430 static bool add(L& lval, R rval) noexcept
432 // Note that both subtractions below occur in a signed type
433 // that is at least as wide as both of the two types. Note
434 // also that any signed type guarantees that there is no
435 // overflow when subtracting two negative values or two
436 // non-negative value. See C99 (adopted as subset of C++11)
437 // section 6.2.6.2 "Integer types" paragraph 2.
439 if (REALM_UNLIKELY(lval < lim_l::min() - rval))
443 if (REALM_UNLIKELY(lval > lim_l::max() - rval))
446 // The following statement has exactly the same effect as
448 lval = L(lval + rval);
451 static bool sub(L& lval, R rval) noexcept
453 // Note that both subtractions below occur in a signed type
454 // that is at least as wide as both of the two types. Note
455 // also that there can be no overflow when adding a negative
456 // value to a non-negative value, or when adding a
457 // non-negative value to a negative one.
459 if (REALM_UNLIKELY(lval > lim_l::max() + rval))
463 if (REALM_UNLIKELY(lval < lim_l::min() + rval))
466 // The following statement has exactly the same effect as
468 lval = L(lval - rval);
473 template <class L, class R>
474 struct SafeIntBinops : SafeIntBinopsImpl<L, R, std::numeric_limits<L>::is_signed, std::numeric_limits<R>::is_signed> {
475 typedef std::numeric_limits<L> lim_l;
476 typedef std::numeric_limits<R> lim_r;
477 static_assert(lim_l::is_specialized && lim_r::is_specialized,
478 "std::numeric_limits<> must be specialized for both types");
479 static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers");
487 inline bool is_negative(T value) noexcept
489 return _impl::IsNegative<T, std::numeric_limits<T>::is_signed>::test(value);
492 template <class To, class From>
493 inline To cast_to_unsigned(From value) noexcept
495 return _impl::CastToUnsigned<To>::cast(value);
498 template <class A, class B>
499 inline bool int_equal_to(A a, B b) noexcept
501 return _impl::SafeIntBinops<A, B>::equal(a, b);
504 template <class A, class B>
505 inline bool int_not_equal_to(A a, B b) noexcept
507 return !_impl::SafeIntBinops<A, B>::equal(a, b);
510 template <class A, class B>
511 inline bool int_less_than(A a, B b) noexcept
513 return _impl::SafeIntBinops<A, B>::less(a, b);
516 template <class A, class B>
517 inline bool int_less_than_or_equal(A a, B b) noexcept
519 return !_impl::SafeIntBinops<B, A>::less(b, a); // Not greater than
522 template <class A, class B>
523 inline bool int_greater_than(A a, B b) noexcept
525 return _impl::SafeIntBinops<B, A>::less(b, a);
528 template <class A, class B>
529 inline bool int_greater_than_or_equal(A a, B b) noexcept
531 return !_impl::SafeIntBinops<A, B>::less(a, b); // Not less than
534 template <class L, class R>
535 inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept
537 return _impl::SafeIntBinops<L, R>::add(lval, rval);
540 template <class L, class R>
541 inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept
543 return _impl::SafeIntBinops<L, R>::sub(lval, rval);
546 template <class L, class R>
547 inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept
549 // FIXME: Check if the following optimizes better (if it works at all):
550 // L lval_2 = L(lval * rval);
551 // bool overflow = rval != 0 && (lval_2 / rval) != lval;
552 typedef std::numeric_limits<L> lim_l;
553 typedef std::numeric_limits<R> lim_r;
554 static_assert(lim_l::is_specialized && lim_r::is_specialized,
555 "std::numeric_limits<> must be specialized for both types");
556 static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers");
557 REALM_ASSERT(int_greater_than_or_equal(lval, 0));
558 REALM_ASSERT(int_greater_than(rval, 0));
559 if (int_less_than(lim_l::max() / rval, lval))
561 lval = L(lval * rval);
566 inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept
568 typedef std::numeric_limits<T> lim;
569 static_assert(lim::is_specialized, "std::numeric_limits<> must be specialized for T");
570 static_assert(lim::is_integer, "T must be an integer type");
571 REALM_ASSERT(int_greater_than_or_equal(lval, 0));
572 if ((lim::max() >> i) < lval)
578 template <class To, class From>
579 inline bool int_cast_has_overflow(From from) noexcept
581 typedef std::numeric_limits<To> lim_to;
582 return int_less_than(from, lim_to::min()) || int_less_than(lim_to::max(), from);
585 template <class To, class From>
586 inline bool int_cast_with_overflow_detect(From from, To& to) noexcept
588 if (REALM_LIKELY(!int_cast_has_overflow<To>(from))) {
595 template <class To, class From>
596 inline To from_twos_compl(From twos_compl) noexcept
598 typedef std::numeric_limits<From> lim_f;
599 typedef std::numeric_limits<To> lim_t;
600 static_assert(lim_f::is_specialized && lim_t::is_specialized,
601 "std::numeric_limits<> must be specialized for both types");
602 static_assert(lim_f::is_integer && lim_t::is_integer, "Both types must be integers");
603 static_assert(!lim_f::is_signed, "`From` must be unsigned");
605 int sign_bit_pos = lim_f::digits - 1;
606 From sign_bit = From(1) << sign_bit_pos;
607 bool non_negative = !lim_t::is_signed || (twos_compl & sign_bit) == 0;
609 // Non-negative value
610 native = To(twos_compl);
614 native = To(-1 - To(From(-1) - twos_compl));
623 #endif // REALM_UTIL_SAFE_INT_OPS_HPP