LCOV - code coverage report
Current view: top level - app/util - attribute-storage-null-handling.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 0 18 0.0 %
Date: 2024-02-15 08:20:41 Functions: 0 56 0.0 %

          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           0 :     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             :     // We need to make sure we never look like we are assigning NaN to an
      64             :     // integer, even in a not-reached branch.  Without "if constexpr", the best
      65             :     // we can do is these functions using enable_if.
      66             :     template <typename U = T, typename std::enable_if_t<std::is_floating_point<U>::value, int> = 0>
      67             :     static constexpr StorageType GetNullValue()
      68             :     {
      69             :         return std::numeric_limits<T>::quiet_NaN();
      70             :     }
      71             : 
      72             :     template <typename U = T, typename std::enable_if_t<std::is_integral<U>::value, int> = 0>
      73             :     static constexpr StorageType GetNullValue()
      74             :     {
      75             :         return std::is_signed<T>::value ? std::numeric_limits<T>::min() : std::numeric_limits<T>::max();
      76             :     }
      77             : 
      78             :     template <typename U = T, typename std::enable_if_t<std::is_enum<U>::value, int> = 0>
      79             :     static constexpr StorageType GetNullValue()
      80             :     {
      81             :         static_assert(!std::is_signed<std::underlying_type_t<T>>::value, "Enums must be unsigned");
      82             :         return static_cast<StorageType>(std::numeric_limits<std::underlying_type_t<T>>::max());
      83             :     }
      84             : 
      85             : public:
      86             :     // The value reserved in the value space of StorageType to represent null,
      87             :     // for cases when we have a nullable value.  This value must match the value
      88             :     // excluded from the valid value range in the spec, so that we don't confuse
      89             :     // valid values with null.
      90             :     static constexpr StorageType kNullValue = NumericAttributeTraits::GetNullValue();
      91             : 
      92             :     template <typename U = T, typename std::enable_if_t<!std::is_floating_point<U>::value, int> = 0>
      93           0 :     static constexpr bool IsNullValue(StorageType value)
      94             :     {
      95           0 :         return value == kNullValue;
      96             :     }
      97             : 
      98             :     template <typename U = T, typename std::enable_if_t<std::is_floating_point<U>::value, int> = 0>
      99           0 :     static constexpr bool IsNullValue(StorageType value)
     100             :     {
     101             :         // Trying to include math.h (to use isnan()) fails on EFR32, both when
     102             :         // included as "cmath" and when included as "math.h".  For lack of
     103             :         // isnan(), just fall back on the NaN != NaN thing.
     104           0 :         return value != value;
     105             :     }
     106             : 
     107           0 :     static constexpr void SetNull(StorageType & value) { value = kNullValue; }
     108             : 
     109             :     // Test whether a value can be represented in a "not null" value of the
     110             :     // given type, which may be a nullable value or not.  This needs to be
     111             :     // implemented for both T and StorageType if the two are distinct.
     112           0 :     static constexpr bool CanRepresentValue(bool isNullable, T value)
     113             :     {
     114             :         // For now, allow the null-marker value for non-nullable types.  It's
     115             :         // not what the spec says to do at the moment, but that might well
     116             :         // change, and we have quite a number of tests relying on this behavior
     117             :         // for now that we should only change once the spec really decides what
     118             :         // it's doing.
     119           0 :         return !isNullable || !IsNullValue(value);
     120             :     }
     121             : 
     122           0 :     static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
     123             :     {
     124           0 :         return writer.Put(tag, static_cast<T>(value));
     125             :     }
     126             : 
     127             :     // Utility that lets consumers treat a StorageType instance as a uint8_t*
     128             :     // for writing to the attribute store.
     129             :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     130             : };
     131             : 
     132             : template <typename T>
     133             : struct NumericAttributeTraits<BitFlags<T>>
     134             : {
     135             :     using StorageType = T;
     136             :     using WorkingType = BitFlags<T>;
     137             : 
     138             :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue)
     139             :     {
     140             :         storageValue = static_cast<StorageType>(workingValue.Raw());
     141             :     }
     142             : 
     143             :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return WorkingType(storageValue); }
     144             : 
     145             :     static constexpr void SetNull(StorageType & value)
     146             :     {
     147             :         //
     148             :         // When setting to null, store a value that has all bits set. This aliases to the same behavior
     149             :         // we do for other integral types, ensuring consistency across all underlying integral types in the data store as well as
     150             :         // re-using logic for serialization/de-serialization of that data in the IM.
     151             :         //
     152             :         value = static_cast<StorageType>(std::numeric_limits<std::underlying_type_t<T>>::max());
     153             :     }
     154             : 
     155             :     static constexpr bool IsNullValue(StorageType value)
     156             :     {
     157             :         //
     158             :         // While we store a nullable bitmap value by setting all its bits, we can be a bit more conservative when actually
     159             :         // testing for null since the spec only mandates that the MSB be reserved for nullable bitmaps.
     160             :         //
     161             :         constexpr auto msbSetValue = std::underlying_type_t<T>(1) << (std::numeric_limits<std::underlying_type_t<T>>::digits - 1);
     162             :         return !!(std::underlying_type_t<T>(value) & msbSetValue);
     163             :     }
     164             : 
     165             :     static constexpr bool CanRepresentValue(bool isNullable, StorageType value)
     166             :     {
     167             :         //
     168             :         // We permit the full-range of the underlying StorageType if !isNullable,
     169             :         // and the restricted range otherwise.
     170             :         //
     171             :         return !isNullable || !IsNullValue(value);
     172             :     }
     173             : 
     174             :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     175             : };
     176             : 
     177             : template <typename T>
     178             : struct NumericAttributeTraits<BitMask<T>> : public NumericAttributeTraits<BitFlags<T>>
     179             : {
     180             :     using StorageType = T;
     181             :     using WorkingType = BitMask<T>;
     182             : 
     183             :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return WorkingType(storageValue); }
     184             : };
     185             : 
     186             : template <>
     187             : struct NumericAttributeTraits<bool>
     188             : {
     189             :     using StorageType = uint8_t;
     190             :     using WorkingType = bool;
     191             : 
     192           0 :     static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) { storageValue = workingValue; }
     193             : 
     194             :     static constexpr WorkingType StorageToWorking(StorageType storageValue) { return storageValue; }
     195             : 
     196           0 :     static constexpr bool IsNullValue(StorageType value) { return value == kNullValue; }
     197           0 :     static constexpr void SetNull(StorageType & value) { value = kNullValue; }
     198           0 :     static constexpr bool CanRepresentValue(bool isNullable, StorageType value)
     199             :     {
     200             :         // This treats all nonzero values (except the null value) as true.
     201           0 :         return !IsNullValue(value);
     202             :     }
     203           0 :     static constexpr bool CanRepresentValue(bool isNullable, bool value) { return true; }
     204             : 
     205           0 :     static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value)
     206             :     {
     207           0 :         return writer.Put(tag, static_cast<bool>(value));
     208             :     }
     209             : 
     210             :     static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return reinterpret_cast<uint8_t *>(&value); }
     211             : 
     212             : private:
     213             :     static constexpr StorageType kNullValue = 0xFF;
     214             : };
     215             : 
     216             : } // namespace app
     217             : } // namespace chip

Generated by: LCOV version 1.14