Matter SDK Coverage Report
Current view: top level - lib/support - PersistedCounter.h (source / functions) Coverage Total Hit
Test: SHA:6e8676be6142bb541fa68048c77f2fc56a21c7b1 Lines: 54.0 % 50 27
Test Date: 2025-01-29 08:09:34 Functions: 44.4 % 18 8

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020 Project CHIP Authors
       4              :  *    Copyright (c) 2016-2017 Nest Labs, Inc.
       5              :  *    All rights reserved.
       6              :  *
       7              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       8              :  *    you may not use this file except in compliance with the License.
       9              :  *    You may obtain a copy of the License at
      10              :  *
      11              :  *        http://www.apache.org/licenses/LICENSE-2.0
      12              :  *
      13              :  *    Unless required by applicable law or agreed to in writing, software
      14              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      15              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16              :  *    See the License for the specific language governing permissions and
      17              :  *    limitations under the License.
      18              :  */
      19              : 
      20              : /**
      21              :  * @file
      22              :  *
      23              :  * @brief
      24              :  *   Class declarations for a monotonically-increasing counter that is periodically
      25              :  *   saved to the provided storage.
      26              :  */
      27              : 
      28              : #pragma once
      29              : 
      30              : #include <lib/core/CHIPEncoding.h>
      31              : #include <lib/core/CHIPPersistentStorageDelegate.h>
      32              : #include <lib/support/CHIPCounter.h>
      33              : #include <lib/support/CodeUtils.h>
      34              : #include <lib/support/DefaultStorageKeyAllocator.h>
      35              : #include <limits>
      36              : 
      37              : namespace chip {
      38              : 
      39              : /**
      40              :  * @class PersistedCounter
      41              :  *
      42              :  * @brief
      43              :  *   A class for managing a counter as an integer value intended to persist
      44              :  *   across reboots.
      45              :  *
      46              :  * Counter values are always set to start at a multiple of a bootup value
      47              :  * "epoch".
      48              :  *
      49              :  * Example:
      50              :  *
      51              :  * - Assuming epoch is 100 via PersistedCounter::Init(_, 100) and GetValue +
      52              :  *   AdvanceValue is called, we get the following outputs:
      53              :  *
      54              :  *   - Output: 0, 1, 2, 3, 4  <reboot/reinit>
      55              :  *   - Output: 100, 101, 102, 103, 104, 105 <reboot/reinit>
      56              :  *   - Output: 200, 201, 202, ...., 299, 300, 301, 302 <reboot/reinit>
      57              :  *   - Output: 400, 401 ...
      58              :  *
      59              :  */
      60              : template <typename T>
      61              : class PersistedCounter : public MonotonicallyIncreasingCounter<T>
      62              : {
      63              : public:
      64           77 :     PersistedCounter() : mKey(StorageKeyName::Uninitialized()) {}
      65           77 :     ~PersistedCounter() override {}
      66              : 
      67              :     /**
      68              :      *  @brief
      69              :      *    Initialize a PersistedCounter object.
      70              :      *
      71              :      *  @param[in] aStorage the storage to use for the counter values.
      72              :      *  @param[in] aKey the key to use for storing the counter values.
      73              :      *  @param[in] aEpoch  On bootup, values we vend will start at a
      74              :      *                     multiple of this parameter.
      75              :      *
      76              :      *  @return CHIP_ERROR_INVALID_ARGUMENT if aStorageDelegate or aKey is NULL
      77              :      *          CHIP_ERROR_INVALID_INTEGER_VALUE if aEpoch is 0.
      78              :      *          CHIP_NO_ERROR otherwise
      79              :      */
      80            1 :     CHIP_ERROR Init(PersistentStorageDelegate * aStorage, StorageKeyName aKey, T aEpoch)
      81              :     {
      82            1 :         VerifyOrReturnError(aStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      83            1 :         VerifyOrReturnError(aKey.IsInitialized(), CHIP_ERROR_INVALID_ARGUMENT);
      84            1 :         VerifyOrReturnError(aEpoch > 0, CHIP_ERROR_INVALID_INTEGER_VALUE);
      85              : 
      86            1 :         mStorage = aStorage;
      87            1 :         mKey     = aKey;
      88            1 :         mEpoch   = aEpoch;
      89              : 
      90              :         T startValue;
      91              : 
      92              :         // Read our previously-stored starting value.
      93            1 :         ReturnErrorOnFailure(ReadStartValue(startValue));
      94              : 
      95              : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
      96              :         if constexpr (std::is_same_v<decltype(startValue), uint64_t>)
      97              :         {
      98              :             ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x" ChipLogFormatX64 " startValue 0x" ChipLogFormatX64,
      99              :                           ChipLogValueX64(aEpoch), ChipLogValueX64(startValue));
     100              :         }
     101              :         else if (std::is_same_v<decltype(startValue), uint32_t>)
     102              :         {
     103              :             ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x%" PRIx32 " startValue 0x%" PRIx32,
     104              :                           static_cast<uint32_t>(aEpoch), static_cast<uint32_t>(startValue));
     105              :         }
     106              : #endif
     107              : 
     108            1 :         ReturnErrorOnFailure(PersistNextEpochStart(static_cast<T>(startValue + aEpoch)));
     109              : 
     110              :         // This will set the starting value, after which we're ready.
     111            1 :         return MonotonicallyIncreasingCounter<T>::Init(startValue);
     112              :     }
     113              : 
     114              :     /**
     115              :      *  @brief Increment the counter by N and write to persisted storage if we've completed the current epoch.
     116              :      *
     117              :      *  @param value value of N
     118              :      *
     119              :      *  @return Any error returned by a write to persisted storage.
     120              :      */
     121            0 :     CHIP_ERROR AdvanceBy(T value) override
     122              :     {
     123            0 :         VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     124            0 :         VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     125              : 
     126              :         // If value is 0, we do not need to do anything
     127            0 :         VerifyOrReturnError(value > 0, CHIP_NO_ERROR);
     128              : 
     129              :         // We should update the persisted epoch value if :
     130              :         // 1- Sum of the current counter and value is greater or equal to the mNextEpoch.
     131              :         //    This is the standard operating case.
     132              :         // 2- Increasing the current counter by value would cause a roll over. This would cause the current value to be < to the
     133              :         //    mNextEpoch so we force an update.
     134            0 :         bool shouldDoEpochUpdate = ((MonotonicallyIncreasingCounter<T>::GetValue() + value) >= mNextEpoch) ||
     135            0 :             (MonotonicallyIncreasingCounter<T>::GetValue() > std::numeric_limits<T>::max() - value);
     136              : 
     137            0 :         ReturnErrorOnFailure(MonotonicallyIncreasingCounter<T>::AdvanceBy(value));
     138              : 
     139            0 :         if (shouldDoEpochUpdate)
     140              :         {
     141              :             // Since AdvanceBy allows the counter to be increased by an arbitrary value, it is possible that the new counter value
     142              :             // is greater than mNextEpoch + mEpoch. As such, we want the next Epoch value to be calculated from the new current
     143              :             // value.
     144            0 :             PersistAndVerifyNextEpochStart(MonotonicallyIncreasingCounter<T>::GetValue());
     145              :         }
     146              : 
     147            0 :         return CHIP_NO_ERROR;
     148              :     }
     149              : 
     150              :     /**
     151              :      *  @brief Increment the counter and write to persisted storage if we've completed the current epoch.
     152              :      *
     153              :      *  @return Any error returned by a write to persisted storage.
     154              :      */
     155            0 :     CHIP_ERROR Advance() override
     156              :     {
     157            0 :         VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     158            0 :         VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     159              : 
     160            0 :         ReturnErrorOnFailure(MonotonicallyIncreasingCounter<T>::Advance());
     161              : 
     162            0 :         if (MonotonicallyIncreasingCounter<T>::GetValue() >= mNextEpoch)
     163              :         {
     164            0 :             ReturnErrorOnFailure(PersistAndVerifyNextEpochStart(mNextEpoch));
     165              :         }
     166              : 
     167            0 :         return CHIP_NO_ERROR;
     168              :     }
     169              : 
     170              : private:
     171            0 :     CHIP_ERROR PersistAndVerifyNextEpochStart(T refEpoch)
     172              :     {
     173              :         // Value advanced past the previously persisted "start point".
     174              :         // Ensure that a new starting point is persisted.
     175            0 :         ReturnErrorOnFailure(PersistNextEpochStart(static_cast<T>(refEpoch + mEpoch)));
     176              : 
     177              :         // Advancing the epoch should have ensured that the current value is valid
     178            0 :         VerifyOrReturnError(static_cast<T>(MonotonicallyIncreasingCounter<T>::GetValue() + mEpoch) == mNextEpoch,
     179              :                             CHIP_ERROR_INTERNAL);
     180              : 
     181              :         // Previous check did not take into consideration that the counter value can be equal to the max counter value or
     182              :         // rollover.
     183              :         // TODO(#33175): PersistedCounter allows rollover so this check is incorrect. We need a Counter class that adequatly
     184              :         // manages rollover behavior for counters that cannot rollover.
     185              :         // VerifyOrReturnError(MonotonicallyIncreasingCounter<T>::GetValue() < mNextEpoch, CHIP_ERROR_INTERNAL);
     186              : 
     187            0 :         return CHIP_NO_ERROR;
     188              :     }
     189              : 
     190              :     /**
     191              :      *  @brief
     192              :      *    Write out the counter value to persistent storage.
     193              :      *
     194              :      *  @param[in] aStartValue  The counter value to write out.
     195              :      *
     196              :      *  @return Any error returned by a write to persistent storage.
     197              :      */
     198              :     CHIP_ERROR
     199            1 :     PersistNextEpochStart(T aStartValue)
     200              :     {
     201            1 :         mNextEpoch = aStartValue;
     202              : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
     203              :         if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
     204              :         {
     205              :             ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x" ChipLogFormatX64,
     206              :                           ChipLogValueX64(aStartValue));
     207              :         }
     208              :         else
     209              :         {
     210              :             ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x%" PRIx32,
     211              :                           static_cast<uint32_t>(aStartValue));
     212              :         }
     213              : #endif
     214              : 
     215            1 :         T valueLE = Encoding::LittleEndian::HostSwap<T>(aStartValue);
     216            1 :         return mStorage->SyncSetKeyValue(mKey.KeyName(), &valueLE, sizeof(valueLE));
     217              :     }
     218              : 
     219              :     /**
     220              :      *  @brief
     221              :      *    Read our starting counter value (if we have one) in from persistent storage.
     222              :      *
     223              :      *  @param[in,out] aStartValue  The value read out.
     224              :      *
     225              :      *  @return Any error returned by a read from persistent storage.
     226              :      */
     227            1 :     CHIP_ERROR ReadStartValue(T & aStartValue)
     228              :     {
     229            1 :         T valueLE     = GetInitialCounterValue();
     230            1 :         uint16_t size = sizeof(valueLE);
     231              : 
     232            1 :         VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     233              : 
     234            1 :         CHIP_ERROR err = mStorage->SyncGetKeyValue(mKey.KeyName(), &valueLE, size);
     235            1 :         if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     236              :         {
     237              :             // No previously-stored value, no worries, the counter is initialized to zero.
     238              :             // Suppress the error.
     239            1 :             err = CHIP_NO_ERROR;
     240              :         }
     241              :         else
     242              :         {
     243              :             // TODO: Figure out how to avoid a bootloop here.  Maybe we should just
     244              :             // init to 0?  Or a random value?
     245            0 :             ReturnErrorOnFailure(err);
     246              :         }
     247              : 
     248            1 :         if (size != sizeof(valueLE))
     249              :         {
     250              :             // TODO: Again, figure out whether this could lead to bootloops.
     251            0 :             return CHIP_ERROR_INCORRECT_STATE;
     252              :         }
     253              : 
     254            1 :         aStartValue = Encoding::LittleEndian::HostSwap<T>(valueLE);
     255              : 
     256              : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
     257              :         if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
     258              :         {
     259              :             ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x" ChipLogFormatX64,
     260              :                           ChipLogValueX64(aStartValue));
     261              :         }
     262              :         else
     263              :         {
     264              :             ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x%" PRIx32,
     265              :                           static_cast<uint32_t>(aStartValue));
     266              :         }
     267              : #endif
     268              : 
     269            1 :         return CHIP_NO_ERROR;
     270              :     }
     271              : 
     272              :     /**
     273              :      * @brief Get the Initial Counter Value
     274              :      *
     275              :      * By default, persisted counters start off at 0.
     276              :      */
     277            1 :     virtual inline T GetInitialCounterValue() { return 0; }
     278              : 
     279              :     PersistentStorageDelegate * mStorage = nullptr; // start value is stored here
     280              :     StorageKeyName mKey;
     281              :     T mEpoch     = 0; // epoch modulus value
     282              :     T mNextEpoch = 0; // next epoch start
     283              : };
     284              : 
     285              : } // namespace chip
        

Generated by: LCOV version 2.0-1