Matter SDK Coverage Report
Current view: top level - app/util - odd-sized-integers.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 59.3 % 27 16
Test Date: 2025-01-17 19:00:11 Functions: 71.4 % 7 5

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 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              : #pragma once
      19              : 
      20              : #include <app/util/attribute-storage-null-handling.h>
      21              : #include <lib/support/TypeTraits.h>
      22              : 
      23              : #include <cstdint>
      24              : #include <limits>
      25              : 
      26              : namespace chip {
      27              : namespace app {
      28              : 
      29              : namespace detail {
      30              : template <int ByteSize, bool IsSigned>
      31              : struct WorkingTypeMapper
      32              : {
      33              : };
      34              : template <int ByteSize>
      35              : struct WorkingTypeMapper<ByteSize, true>
      36              : {
      37              :     using WorkingType = int64_t;
      38              : };
      39              : template <>
      40              : struct WorkingTypeMapper<3, true>
      41              : {
      42              :     using WorkingType = int32_t;
      43              : };
      44              : template <int ByteSize>
      45              : struct WorkingTypeMapper<ByteSize, false>
      46              : {
      47              :     using WorkingType = uint64_t;
      48              : };
      49              : template <>
      50              : struct WorkingTypeMapper<3, false>
      51              : {
      52              :     using WorkingType = uint32_t;
      53              : };
      54              : } // namespace detail
      55              : 
      56              : template <int ByteSize, bool IsSigned>
      57              : struct OddSizedInteger
      58              : {
      59              :     // WorkingType is the type we use to represent the value as an actual
      60              :     // integer that we can do arithmetic, greater/less-than compares, etc on.
      61              :     using WorkingType = typename detail::WorkingTypeMapper<ByteSize, IsSigned>::WorkingType;
      62              : 
      63              :     // StorageType is the type "at rest" in the attribute store.  It's a
      64              :     // native-endian byte buffer.
      65              :     using StorageType = uint8_t[ByteSize];
      66              : };
      67              : 
      68              : namespace detail {
      69              : template <int ByteSize, bool IsBigEndian>
      70              : struct IntegerByteIndexing;
      71              : 
      72              : template <int ByteSize>
      73              : struct IntegerByteIndexing<ByteSize, true>
      74              : {
      75              :     static constexpr int highIndex     = 0;
      76              :     static constexpr int lowIndex      = ByteSize - 1;
      77              :     static constexpr int lowerIndex    = 1;
      78              :     static constexpr int raiseIndex    = -1;
      79              :     static constexpr int pastLowIndex  = ByteSize;
      80              :     static constexpr int pastHighIndex = -1;
      81              : };
      82              : 
      83              : template <int ByteSize>
      84              : struct IntegerByteIndexing<ByteSize, false>
      85              : {
      86              :     static constexpr int highIndex     = ByteSize - 1;
      87              :     static constexpr int lowIndex      = 0;
      88              :     static constexpr int lowerIndex    = -1;
      89              :     static constexpr int raiseIndex    = 1;
      90              :     static constexpr int pastLowIndex  = -1;
      91              :     static constexpr int pastHighIndex = ByteSize;
      92              : };
      93              : } // namespace detail
      94              : 
      95              : namespace NumericLimits {
      96              : 
      97              : // Generic size information for unsigned values.
      98              : //
      99              : // Assumes non-nullable types. Nullable types reserve one of the values as NULL (the max)
     100          146 : inline constexpr uint64_t MaxUnsignedValue(unsigned ByteSize)
     101              : {
     102          146 :     if (ByteSize == 8)
     103              :     {
     104           19 :         return std::numeric_limits<uint64_t>::max();
     105              :     }
     106          127 :     return (1ULL << (8 * ByteSize)) - 1;
     107              : }
     108              : 
     109              : /// Readability-method to express that the maximum unsigned value is a null value
     110              : ///
     111              : /// Our encoding states that max int value is the NULL value
     112          116 : inline constexpr uint64_t UnsignedMaxValueToNullValue(uint64_t value)
     113              : {
     114          116 :     return value;
     115              : }
     116              : 
     117              : // Generic size information for signed values.
     118              : //
     119              : // Assumes non-nullable types. Nullable types reserve one of the values as NULL (the min)
     120          123 : inline constexpr int64_t MaxSignedValue(unsigned ByteSize)
     121              : {
     122          123 :     if (ByteSize == 8)
     123              :     {
     124           16 :         return std::numeric_limits<int64_t>::max();
     125              :     }
     126          107 :     return (static_cast<int64_t>(1) << (8 * ByteSize - 1)) - 1;
     127              : }
     128              : 
     129          185 : inline constexpr int64_t MinSignedValue(unsigned ByteSize)
     130              : {
     131          185 :     if (ByteSize == 8)
     132              :     {
     133           25 :         return std::numeric_limits<int64_t>::min();
     134              :     }
     135          160 :     return -(static_cast<int64_t>(1) << (8 * ByteSize - 1));
     136              : }
     137              : 
     138              : /// Readability-method to express that the maximum signed value is a null value
     139              : ///
     140              : /// Our encoding states that min int value is the NULL value
     141          133 : inline constexpr int64_t SignedMinValueToNullValue(int64_t value)
     142              : {
     143          133 :     return value;
     144              : }
     145              : 
     146              : } // namespace NumericLimits
     147              : 
     148              : template <int ByteSize, bool IsSigned, bool IsBigEndian>
     149              : struct NumericAttributeTraits<OddSizedInteger<ByteSize, IsSigned>, IsBigEndian> : detail::IntegerByteIndexing<ByteSize, IsBigEndian>
     150              : {
     151              :     using IntType = OddSizedInteger<ByteSize, IsSigned>;
     152              :     // StorageType is the type "at rest" in the attribute store.  It's a
     153              :     // native-endian byte buffer.
     154              :     using StorageType = typename IntType::StorageType;
     155              :     // WorkingType is the type we use to represent the value as an actual
     156              :     // integer that we can do arithmetic, greater/less-than compares, etc on.
     157              :     using WorkingType = typename IntType::WorkingType;
     158              : 
     159              :     using Indexing = detail::IntegerByteIndexing<ByteSize, IsBigEndian>;
     160              :     using Indexing::highIndex;
     161              :     using Indexing::lowerIndex;
     162              :     using Indexing::lowIndex;
     163              :     using Indexing::pastHighIndex;
     164              :     using Indexing::pastLowIndex;
     165              :     using Indexing::raiseIndex;
     166              : 
     167              :     static constexpr WorkingType StorageToWorking(StorageType storageValue)
     168              :     {
     169              :         // WorkingType can always fit all of our bit-shifting, because it has at
     170              :         // least one extra byte.
     171              :         WorkingType value = 0;
     172              :         for (int i = highIndex; i != pastLowIndex; i += lowerIndex)
     173              :         {
     174              :             value = (value << 8) | storageValue[i];
     175              :         }
     176              : 
     177              :         // If unsigned, we are done.  If signed, we need to make sure our high
     178              :         // bit gets treated as a sign bit, not a value bit, with our bits in 2s
     179              :         // complement.
     180              :         if (IsSigned)
     181              :         {
     182              :             constexpr WorkingType MaxPositive = (static_cast<WorkingType>(1) << (8 * ByteSize - 1)) - 1;
     183              :             if (value > MaxPositive)
     184              :             {
     185              :                 value = value - (static_cast<WorkingType>(1) << (8 * ByteSize));
     186              :             }
     187              :         }
     188              : 
     189              :         return value;
     190              :     }
     191              : 
     192              :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue)
     193              :     {
     194              :         // We can just grab the low ByteSize bytes of workingValue.
     195              :         for (int i = lowIndex; i != pastHighIndex; i += raiseIndex)
     196              :         {
     197              :             // Casting to uint8_t exactly grabs the lowest byte.
     198              :             storageValue[i] = static_cast<uint8_t>(workingValue);
     199              :             workingValue    = workingValue >> 8;
     200              :         }
     201              :     }
     202              : 
     203            0 :     static constexpr bool IsNullValue(StorageType value)
     204              :     {
     205              :         if (IsSigned)
     206              :         {
     207              :             // Check for the equivalent of the most negative integer, in 2s
     208              :             // complement notation.
     209            0 :             if (value[highIndex] != 0x80)
     210              :             {
     211            0 :                 return false;
     212              :             }
     213            0 :             for (int i = highIndex + lowerIndex; i != pastLowIndex; i += lowerIndex)
     214              :             {
     215            0 :                 if (value[i] != 0x00)
     216              :                 {
     217            0 :                     return false;
     218              :                 }
     219              :             }
     220            0 :             return true;
     221              :         }
     222              : 
     223              :         // Check for the equivalent of the largest unsigned integer.
     224            0 :         for (int i = highIndex; i != pastLowIndex; i += lowerIndex)
     225              :         {
     226            0 :             if (value[i] != 0xFF)
     227              :             {
     228            0 :                 return false;
     229              :             }
     230              :         }
     231            0 :         return true;
     232              :     }
     233              : 
     234              :     static constexpr void SetNull(StorageType & value)
     235              :     {
     236              :         if (IsSigned)
     237              :         {
     238              :             value[highIndex] = 0x80;
     239              :             for (int i = highIndex + lowerIndex; i != pastLowIndex; i += lowerIndex)
     240              :             {
     241              :                 value[i] = 0x00;
     242              :             }
     243              :         }
     244              :         else
     245              :         {
     246              :             for (int i = highIndex; i != pastLowIndex; i += lowerIndex)
     247              :             {
     248              :                 value[i] = 0xFF;
     249              :             }
     250              :         }
     251              :     }
     252              : 
     253              :     static constexpr bool CanRepresentValue(bool isNullable, StorageType value) { return !isNullable || !IsNullValue(value); }
     254              : 
     255              :     static constexpr bool CanRepresentValue(bool isNullable, WorkingType value)
     256              :     {
     257              :         return MinValue(isNullable) <= value && value <= MaxValue(isNullable);
     258              :     }
     259              : 
     260              :     static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
     261              :     {
     262              :         return writer.Put(tag, StorageToWorking(value));
     263              :     }
     264              : 
     265              :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return value; }
     266              : 
     267              :     static WorkingType MinValue(bool isNullable)
     268              :     {
     269              :         if constexpr (!IsSigned)
     270              :         {
     271              :             return 0;
     272              :         }
     273              : 
     274              :         // Since WorkingType has at least one extra byte, the bitshift cannot overflow.
     275              :         constexpr WorkingType signedMin = -(static_cast<WorkingType>(1) << (8 * ByteSize - 1));
     276              :         if (isNullable)
     277              :         {
     278              :             // Smallest negative value is excluded for nullable signed types.
     279              :             return signedMin + 1;
     280              :         }
     281              : 
     282              :         return signedMin;
     283              :     }
     284              : 
     285              :     static WorkingType MaxValue(bool isNullable)
     286              :     {
     287              :         // Since WorkingType has at least one extra byte, none of our bitshifts
     288              :         // overflow.
     289              :         if constexpr (IsSigned)
     290              :         {
     291              :             return (static_cast<WorkingType>(1) << (8 * ByteSize - 1)) - 1;
     292              :         }
     293              : 
     294              :         constexpr WorkingType unsignedMax = (static_cast<WorkingType>(1) << (8 * ByteSize)) - 1;
     295              :         if (isNullable)
     296              :         {
     297              :             // Largest value is excluded for nullable unsigned types.
     298              :             return unsignedMax - 1;
     299              :         }
     300              : 
     301              :         return unsignedMax;
     302              :     }
     303              : };
     304              : 
     305              : } // namespace app
     306              : } // namespace chip
        

Generated by: LCOV version 2.0-1