Matter SDK Coverage Report
Current view: top level - lib/support - SafeInt.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 88.9 % 18 16
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 24 24

            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      4303036 : 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      2781509 :         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      5563001 :         return static_cast<intmax_t>(numeric_limits<T>::min()) <= static_cast<intmax_t>(arg) &&
      78      5563001 :             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      1161717 :         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        48631 :             return true;
      88              :         }
      89              : 
      90      1113086 :         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       304646 :         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       304536 :         return static_cast<uintmax_t>(arg) <= static_cast<uintmax_t>(numeric_limits<T>::max());
     102              :     }
     103              : 
     104        55164 :     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      3708323 : 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      3708323 :     if (arg <= static_cast<T>(numeric_limits<signed_type>::max()))
     134              :     {
     135      3704597 :         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         3726 :     signed_type diff = static_cast<signed_type>(numeric_limits<T>::max() - arg);
     146         3726 :     return static_cast<signed_type>(-diff - 1);
     147              : }
     148              : 
     149              : } // namespace chip
        

Generated by: LCOV version 2.0-1