Matter SDK Coverage Report
Current view: top level - app/util - attribute-storage-null-handling.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 60.0 % 5 3
Test Date: 2025-01-17 19:00:11 Functions: 40.0 % 10 4

            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 <lib/core/CHIPConfig.h>
      21              : #include <lib/core/TLV.h>
      22              : #include <lib/support/BitFlags.h>
      23              : #include <lib/support/BitMask.h>
      24              : #include <lib/support/TypeTraits.h>
      25              : 
      26              : #include <limits>
      27              : #include <type_traits>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32              : template <typename T,
      33              :           bool IsBigEndian =
      34              : // CHIP_CONFIG_BIG_ENDIAN_TARGET to match how the attribute store works, because that's
      35              : // what where our data buffer is eventually ending up or coming from.
      36              : #if CHIP_CONFIG_BIG_ENDIAN_TARGET
      37              :               true
      38              : #else  // CHIP_CONFIG_BIG_ENDIAN_TARGET
      39              :               false
      40              : #endif // CHIP_CONFIG_BIG_ENDIAN_TARGET
      41              :           >
      42              : struct NumericAttributeTraits
      43              : {
      44              :     // StorageType is the type used to represent this C++ type in the attribute
      45              :     // store.
      46              :     using StorageType = T;
      47              : 
      48              :     // WorkingType is the type used to represent this C++ type when we are
      49              :     // actually working with it as a value.
      50              :     using WorkingType = T;
      51              : 
      52              :     // Convert a working value to a storage value.  This uses an outparam
      53              :     // instead of a return value because some specializations have complicated
      54              :     // StorageTypes that can't be returned by value.  This function can assume
      55              :     // that WorkingType passed a CanRepresentValue check.
      56              :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) { storageValue = workingValue; }
      57              : 
      58              :     // Convert a storage value to a working value.  Some specializations do more
      59              :     // interesting things here.
      60              :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return storageValue; }
      61              : 
      62              : private:
      63              :     // Ensure that this generic NumericAttributeTraits implementation is being used for some type for which it
      64              :     // actually works.
      65              :     static_assert(std::is_floating_point_v<T> || std::is_integral_v<T> || std::is_enum_v<T>,
      66              :                   "NumericAttributeTraits specialization needed for this type");
      67              : 
      68              :     // We need to make sure we never look like we are assigning NaN to an
      69              :     // integer, even in a not-reached branch.  Without "if constexpr", the best
      70              :     // we can do is these functions using enable_if.
      71              :     template <typename U = T, typename std::enable_if_t<std::is_floating_point_v<U>, int> = 0>
      72              :     static constexpr StorageType GetNullValue()
      73              :     {
      74              :         return std::numeric_limits<T>::quiet_NaN();
      75              :     }
      76              : 
      77              :     template <typename U = T, typename std::enable_if_t<std::is_integral_v<U>, int> = 0>
      78              :     static constexpr StorageType GetNullValue()
      79              :     {
      80              :         return std::is_signed<T>::value ? std::numeric_limits<T>::min() : std::numeric_limits<T>::max();
      81              :     }
      82              : 
      83              :     template <typename U = T, typename std::enable_if_t<std::is_enum_v<U>, int> = 0>
      84              :     static constexpr StorageType GetNullValue()
      85              :     {
      86              :         static_assert(!std::is_signed<std::underlying_type_t<T>>::value, "Enums must be unsigned");
      87              :         return static_cast<StorageType>(std::numeric_limits<std::underlying_type_t<T>>::max());
      88              :     }
      89              : 
      90              : public:
      91              :     // The value reserved in the value space of StorageType to represent null,
      92              :     // for cases when we have a nullable value.  This value must match the value
      93              :     // excluded from the valid value range in the spec, so that we don't confuse
      94              :     // valid values with null.
      95              :     static constexpr StorageType kNullValue = NumericAttributeTraits::GetNullValue();
      96              : 
      97              :     template <typename U = T, typename std::enable_if_t<!std::is_floating_point<U>::value, int> = 0>
      98            0 :     static constexpr bool IsNullValue(StorageType value)
      99              :     {
     100            0 :         return value == kNullValue;
     101              :     }
     102              : 
     103              :     template <typename U = T, typename std::enable_if_t<std::is_floating_point<U>::value, int> = 0>
     104            9 :     static constexpr bool IsNullValue(StorageType value)
     105              :     {
     106              :         // Trying to include math.h (to use isnan()) fails on EFR32, both when
     107              :         // included as "cmath" and when included as "math.h".  For lack of
     108              :         // isnan(), just fall back on the NaN != NaN thing.
     109            9 :         return value != value;
     110              :     }
     111              : 
     112            8 :     static constexpr void SetNull(StorageType & value) { value = kNullValue; }
     113              : 
     114              :     // Test whether a value can be represented in a "not null" value of the
     115              :     // given type, which may be a nullable value or not.  This needs to be
     116              :     // implemented for both T and StorageType if the two are distinct.
     117              :     static constexpr bool CanRepresentValue(bool isNullable, T value)
     118              :     {
     119              :         // For now, allow the null-marker value for non-nullable types.  It's
     120              :         // not what the spec says to do at the moment, but that might well
     121              :         // change, and we have quite a number of tests relying on this behavior
     122              :         // for now that we should only change once the spec really decides what
     123              :         // it's doing.
     124              :         return !isNullable || !IsNullValue(value);
     125              :     }
     126              : 
     127              :     static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
     128              :     {
     129              :         return writer.Put(tag, static_cast<T>(value));
     130              :     }
     131              : 
     132              :     // Utility that lets consumers treat a StorageType instance as a uint8_t*
     133              :     // for writing to the attribute store.
     134              :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     135              : 
     136              :     // Min and max values for the type.
     137              :     static WorkingType MinValue(bool isNullable)
     138              :     {
     139              :         if constexpr (!std::is_signed_v<WorkingType>)
     140              :         {
     141              :             return 0;
     142              :         }
     143              : 
     144              :         if (isNullable)
     145              :         {
     146              :             // Smallest negative value is excluded for nullable signed types.
     147              :             return static_cast<WorkingType>(std::numeric_limits<WorkingType>::min() + 1);
     148              :         }
     149              : 
     150              :         return std::numeric_limits<WorkingType>::min();
     151              :     }
     152              : 
     153              :     static WorkingType MaxValue(bool isNullable)
     154              :     {
     155              :         if constexpr (std::is_signed_v<WorkingType>)
     156              :         {
     157              :             return std::numeric_limits<WorkingType>::max();
     158              :         }
     159              : 
     160              :         if (isNullable)
     161              :         {
     162              :             // Largest value is excluded for nullable unsigned types.
     163              :             return static_cast<WorkingType>(std::numeric_limits<WorkingType>::max() - 1);
     164              :         }
     165              : 
     166              :         return std::numeric_limits<WorkingType>::max();
     167              :     }
     168              : };
     169              : 
     170              : template <typename T>
     171              : struct NumericAttributeTraits<BitFlags<T>>
     172              : {
     173              :     using StorageType = T;
     174              :     using WorkingType = BitFlags<T>;
     175              : 
     176              :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue)
     177              :     {
     178              :         storageValue = static_cast<StorageType>(workingValue.Raw());
     179              :     }
     180              : 
     181              :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return WorkingType(storageValue); }
     182              : 
     183              :     static constexpr void SetNull(StorageType & value)
     184              :     {
     185              :         //
     186              :         // When setting to null, store a value that has all bits set. This aliases to the same behavior
     187              :         // we do for other integral types, ensuring consistency across all underlying integral types in the data store as well as
     188              :         // re-using logic for serialization/de-serialization of that data in the IM.
     189              :         //
     190              :         value = static_cast<StorageType>(std::numeric_limits<std::underlying_type_t<T>>::max());
     191              :     }
     192              : 
     193              :     static constexpr bool IsNullValue(StorageType value)
     194              :     {
     195              :         //
     196              :         // While we store a nullable bitmap value by setting all its bits, we can be a bit more conservative when actually
     197              :         // testing for null since the spec only mandates that the MSB be reserved for nullable bitmaps.
     198              :         //
     199              :         constexpr auto msbSetValue = std::underlying_type_t<T>(1) << (std::numeric_limits<std::underlying_type_t<T>>::digits - 1);
     200              :         return !!(std::underlying_type_t<T>(value) & msbSetValue);
     201              :     }
     202              : 
     203              :     static constexpr bool CanRepresentValue(bool isNullable, StorageType value)
     204              :     {
     205              :         //
     206              :         // We permit the full-range of the underlying StorageType if !isNullable,
     207              :         // and the restricted range otherwise.
     208              :         //
     209              :         return !isNullable || !IsNullValue(value);
     210              :     }
     211              : 
     212              :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     213              : };
     214              : 
     215              : template <typename T>
     216              : struct NumericAttributeTraits<BitMask<T>> : public NumericAttributeTraits<BitFlags<T>>
     217              : {
     218              :     using StorageType = T;
     219              :     using WorkingType = BitMask<T>;
     220              : 
     221              :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return WorkingType(storageValue); }
     222              : };
     223              : 
     224              : template <>
     225              : struct NumericAttributeTraits<bool>
     226              : {
     227              :     using StorageType = uint8_t;
     228              :     using WorkingType = bool;
     229              : 
     230              :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) { storageValue = workingValue; }
     231              : 
     232              :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return storageValue; }
     233              : 
     234              :     static constexpr bool IsNullValue(StorageType value) { return value == kNullValue; }
     235              :     static constexpr void SetNull(StorageType & value) { value = kNullValue; }
     236              :     static constexpr bool CanRepresentValue(bool isNullable, StorageType value)
     237              :     {
     238              :         // This treats all nonzero values (except the null value) as true.
     239              :         return !IsNullValue(value);
     240              :     }
     241              :     static constexpr bool CanRepresentValue(bool isNullable, bool value) { return true; }
     242              : 
     243              :     static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
     244              :     {
     245              :         return writer.Put(tag, static_cast<bool>(value));
     246              :     }
     247              : 
     248              :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     249              : 
     250              :     static uint8_t MinValue(bool isNullable) { return 0; }
     251              : 
     252              :     static uint8_t MaxValue(bool isNullable) { return 1; }
     253              : 
     254              :     static constexpr StorageType kNullValue = 0xFF;
     255              : };
     256              : 
     257              : } // namespace app
     258              : } // namespace chip
        

Generated by: LCOV version 2.0-1