LCOV - code coverage report
Current view: top level - lib/support - SafeInt.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 16 18 88.9 %
Date: 2024-02-15 08:20:41 Functions: 24 24 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *
       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
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      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.
      16             :  */
      17             : 
      18             : /**
      19             :  *    @file
      20             :  *      Utilities for safely working with integer types.
      21             :  *
      22             :  */
      23             : 
      24             : #pragma once
      25             : 
      26             : #include <limits>
      27             : #include <stdint.h>
      28             : #include <type_traits>
      29             : 
      30             : namespace chip {
      31             : 
      32             : /**
      33             :  * A template function that determines whether it's safe to cast the given value
      34             :  * of type U to the given type T.  It does this by verifying that the value is
      35             :  * in the range of valid values for T.
      36             :  */
      37             : template <typename T, typename U, std::enable_if_t<std::is_integral<T>::value, int> = 0>
      38     5947472 : bool CanCastTo(U arg)
      39             : {
      40             :     using namespace std;
      41             :     // U might be a reference to an integer type, if we're assigning from
      42             :     // something passed by reference.
      43             :     typedef typename remove_reference<U>::type V; // V for "value"
      44             :     static_assert(is_integral<V>::value, "Must be assigning from an integral type");
      45             : 
      46             :     // We want to check that "arg" can fit inside T but without doing any tests
      47             :     // that are always true or always false due to the types involved, which
      48             :     // would trigger compiler warnings themselves.  So for example, we can't
      49             :     // compare arg to max values for T if all U values are representable in T,
      50             :     // etc, because those trigger warnings on some compilers.
      51             : 
      52             :     // We also can't directly compare signed to unsigned values in general,
      53             :     // because that will trigger sign conversion warnings. In fact, it will
      54             :     // trigger them even on runtime-unreached codepaths, so for example we can't
      55             :     // directly compare two min() values to each other!
      56             : 
      57             :     // Oh, and some compilers warn on theoretical signed-to-unsigned compares
      58             :     // even when those can't be reached, and that's known at compile time.
      59             :     // Hence all the casts to intmax_t and uintmax_t below.
      60             : 
      61             :     // A bunch of these tests could sure benefit from "if constexpr", but let's
      62             :     // hope compilers just manage to optimize them properly anyway.
      63             :     // We can't blindly compare "arg" to the minimal or maximal value of T one
      64             :     // of T and V is signed and the other is unsigned: there might not be a
      65             :     // single integer type that can represent _both_ the value of arg and the
      66             :     // minimal/maximal value.
      67             :     if (numeric_limits<T>::is_signed && numeric_limits<V>::is_signed)
      68             :     {
      69     4065783 :         if (static_cast<intmax_t>(numeric_limits<V>::max()) <= static_cast<intmax_t>(numeric_limits<T>::max()) &&
      70           0 :             static_cast<intmax_t>(numeric_limits<V>::min()) >= static_cast<intmax_t>(numeric_limits<T>::min()))
      71             :         {
      72             :             // Any checks on arg would be trivially true; don't even do them, to
      73             :             // avoid warnings.
      74           0 :             return true;
      75             :         }
      76             : 
      77     8131535 :         return static_cast<intmax_t>(numeric_limits<T>::min()) <= static_cast<intmax_t>(arg) &&
      78     8131535 :             static_cast<intmax_t>(arg) <= static_cast<intmax_t>(numeric_limits<T>::max());
      79             :     }
      80             : 
      81             :     if (!numeric_limits<T>::is_signed && !numeric_limits<V>::is_signed)
      82             :     {
      83     1520796 :         if (static_cast<uintmax_t>(numeric_limits<V>::max()) <= static_cast<uintmax_t>(numeric_limits<T>::max()))
      84             :         {
      85             :             // Any checks on arg would be trivially true; don't even do them, to
      86             :             // avoid warnings.
      87         123 :             return true;
      88             :         }
      89             : 
      90     1520673 :         return static_cast<uintmax_t>(arg) <= static_cast<uintmax_t>(numeric_limits<T>::max());
      91             :     }
      92             : 
      93             :     if (numeric_limits<T>::is_signed)
      94             :     {
      95             :         static_assert(numeric_limits<T>::max() >= 0, "What weird type is this?");
      96      296671 :         if (static_cast<uintmax_t>(numeric_limits<V>::max()) <= static_cast<uintmax_t>(numeric_limits<T>::max()))
      97             :         {
      98         110 :             return true;
      99             :         }
     100             : 
     101      296561 :         return static_cast<uintmax_t>(arg) <= static_cast<uintmax_t>(numeric_limits<T>::max());
     102             :     }
     103             : 
     104       64222 :     return 0 <= arg && static_cast<uintmax_t>(arg) <= static_cast<uintmax_t>(numeric_limits<T>::max());
     105             : }
     106             : 
     107             : template <typename T, typename U, std::enable_if_t<std::is_enum<T>::value, int> = 0>
     108             : bool CanCastTo(U arg)
     109             : {
     110             :     return CanCastTo<std::underlying_type_t<T>>(arg);
     111             : }
     112             : 
     113             : /**
     114             :  * A function to reverse the effects of a signed-to-unsigned integer cast.
     115             :  *
     116             :  * If the argument is small enough to be representable as a positive signed
     117             :  * integer, returns that integer.  Otherwise, returns a negative integer which
     118             :  * would, if cast to the type of the argument, produce the given value.
     119             :  *
     120             :  * So for example, if a uint8_t with value 254 is passed in this function will
     121             :  * return an int8_t with value -2.
     122             :  *
     123             :  * @note This function might become unnecessary if C++20 standardizes
     124             :  * 2s-complement signed integers and defines casting of out-of-range values to
     125             :  * signed types.
     126             :  */
     127             : template <typename T>
     128     5420179 : typename std::enable_if<std::is_unsigned<T>::value, typename std::make_signed<T>::type>::type CastToSigned(T arg)
     129             : {
     130             :     using namespace std;
     131             :     typedef typename make_signed<T>::type signed_type;
     132             : 
     133     5420179 :     if (arg <= static_cast<T>(numeric_limits<signed_type>::max()))
     134             :     {
     135     5415976 :         return static_cast<signed_type>(arg);
     136             :     }
     137             : 
     138             :     // We want to return arg - (numeric_limits<T>::max() + 1), but do it without
     139             :     // hitting overflow.  We do this by rewriting it as:
     140             :     //
     141             :     // -(numeric_limits<T>::max() - arg) - 1
     142             :     //
     143             :     // then noting that both (numeric_limits<T>::max() - arg) and its negation
     144             :     // are guaranteed to fit in signed_type.
     145        4203 :     signed_type diff = static_cast<signed_type>(numeric_limits<T>::max() - arg);
     146        4203 :     return static_cast<signed_type>(-diff - 1);
     147             : }
     148             : 
     149             : } // namespace chip

Generated by: LCOV version 1.14