Matter SDK Coverage Report
Current view: top level - access/examples - ExampleAccessControlDelegate.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 89.4 % 435 389
Test Date: 2025-01-17 19:00:11 Functions: 93.5 % 77 72

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #include "ExampleAccessControlDelegate.h"
      20              : 
      21              : #include <lib/core/CHIPConfig.h>
      22              : 
      23              : #include <algorithm>
      24              : #include <cstdint>
      25              : #include <string>
      26              : #include <type_traits>
      27              : 
      28              : namespace {
      29              : 
      30              : using chip::ClusterId;
      31              : using chip::DeviceTypeId;
      32              : using chip::EndpointId;
      33              : using chip::FabricIndex;
      34              : using chip::NodeId;
      35              : 
      36              : using chip::kUndefinedNodeId;
      37              : 
      38              : using chip::Access::AccessControl;
      39              : using chip::Access::AuthMode;
      40              : using chip::Access::Privilege;
      41              : using chip::Access::RequestPath;
      42              : using chip::Access::SubjectDescriptor;
      43              : 
      44              : using Entry         = chip::Access::AccessControl::Entry;
      45              : using EntryIterator = chip::Access::AccessControl::EntryIterator;
      46              : using Target        = Entry::Target;
      47              : 
      48              : // Pool sizes
      49              : constexpr int kEntryStoragePoolSize          = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_STORAGE_POOL_SIZE;
      50              : constexpr int kEntryDelegatePoolSize         = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_DELEGATE_POOL_SIZE;
      51              : constexpr int kEntryIteratorDelegatePoolSize = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_ENTRY_ITERATOR_DELEGATE_POOL_SIZE;
      52              : 
      53              : /*
      54              :               +---+   +---+   +---+   +---+
      55              :               | 1 |   | 2 |   | A |   | C |             ENTRIES
      56              :               +---+   +---+   +---+   +---+
      57              :                  |      |       |       |
      58              :                  |      +-+   +-+       |
      59              :                  +----+   |   |   +-----+
      60              :                       |   |   |   |
      61              :                       v   v   v   v
      62              :                     +---+---+---+---+
      63              :                     | 1 | 2 | A | C |                   ENTRY DELEGATE POOL
      64              :                     +---+---+---+---+
      65              :                       |   |   |   |
      66              :           +-----------+   |   |   +-----------+
      67              :           |   +-----------+   +-------+       |
      68              :           |   |                       |       |
      69              :           v   v                       v       v
      70              :     +---+---+---+---+               +---+---+---+---+
      71              :     | 0 | 1 | 2 | X |               | A | X | C | X |   ACL ENTRY STORAGE & POOL
      72              :     +---+---+---+---+               +---+---+---+---+
      73              :       ^       ^
      74              :       |       |
      75              :       |       +-----------+
      76              :       +---------------+   |
      77              :                       |   |
      78              :                     +---+---+---+---+
      79              :                     | 0 | 2 | X | X |                   ENTRY ITERATOR DELEGATE POOL
      80              :                     +---+---+---+---+
      81              :                       ^   ^
      82              :                       |   |
      83              :                       |   +-------+
      84              :                       |           |
      85              :                     +---+       +---+
      86              :                     | 0 |       | 2 |                   ENTRY ITERATORS
      87              :                     +---+       +---+
      88              : */
      89              : 
      90              : class SubjectStorage
      91              : {
      92              : public:
      93         6826 :     bool IsEmpty() const { return mNode == kUndefinedNodeId; }
      94              : 
      95         7112 :     void Clear() { mNode = kUndefinedNodeId; }
      96              : 
      97         1975 :     CHIP_ERROR Get(NodeId & node) const
      98              :     {
      99         1975 :         if (!IsEmpty())
     100              :         {
     101         1974 :             node = mNode;
     102         1974 :             return CHIP_NO_ERROR;
     103              :         }
     104            1 :         return CHIP_ERROR_SENTINEL;
     105              :     }
     106              : 
     107           32 :     CHIP_ERROR Set(NodeId node)
     108              :     {
     109           32 :         if (!IsEmpty())
     110              :         {
     111           32 :             if (IsValid(node))
     112              :             {
     113           32 :                 mNode = node;
     114           32 :                 return CHIP_NO_ERROR;
     115              :             }
     116            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     117              :         }
     118            0 :         return CHIP_ERROR_SENTINEL;
     119              :     }
     120              : 
     121          652 :     CHIP_ERROR Add(NodeId node)
     122              :     {
     123          652 :         if (IsValid(node))
     124              :         {
     125          652 :             mNode = node;
     126          652 :             return CHIP_NO_ERROR;
     127              :         }
     128            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     129              :     }
     130              : 
     131              : private:
     132          684 :     static bool IsValid(NodeId node) { return node != kUndefinedNodeId; }
     133              : 
     134              :     static_assert(sizeof(NodeId) == 8, "Expecting 8 byte node ID");
     135              : 
     136              :     NodeId mNode;
     137              : };
     138              : 
     139              : class TargetStorage
     140              : {
     141              : public:
     142         6296 :     bool IsEmpty() const { return mCluster == kClusterEmpty && mDeviceType == kDeviceTypeEmpty; }
     143              : 
     144         5335 :     void Clear()
     145              :     {
     146         5335 :         mCluster    = kClusterEmpty;
     147         5335 :         mDeviceType = kDeviceTypeEmpty;
     148         5335 :     }
     149              : 
     150         1671 :     CHIP_ERROR Get(Target & target) const
     151              :     {
     152         1671 :         if (!IsEmpty())
     153              :         {
     154         1670 :             Decode(target);
     155         1670 :             return CHIP_NO_ERROR;
     156              :         }
     157            1 :         return CHIP_ERROR_SENTINEL;
     158              :     }
     159              : 
     160          356 :     CHIP_ERROR Set(const Target & target)
     161              :     {
     162          356 :         if (!IsEmpty())
     163              :         {
     164          356 :             if (IsValid(target))
     165              :             {
     166          356 :                 Encode(target);
     167          356 :                 return CHIP_NO_ERROR;
     168              :             }
     169            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
     170              :         }
     171            0 :         return CHIP_ERROR_SENTINEL;
     172              :     }
     173              : 
     174          530 :     CHIP_ERROR Add(const Target & target)
     175              :     {
     176          530 :         if (IsValid(target))
     177              :         {
     178          530 :             Encode(target);
     179          530 :             return CHIP_NO_ERROR;
     180              :         }
     181            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     182              :     }
     183              : 
     184              : private:
     185              :     // TODO: eventually this functionality should live where the type itself is defined
     186          771 :     static bool IsValidCluster(ClusterId cluster)
     187              :     {
     188          771 :         const auto id     = cluster & kClusterIdMask;
     189          771 :         const auto vendor = cluster & kClusterVendorMask;
     190          771 :         return ((id <= kClusterIdMaxStd) || (kClusterIdMinMs <= id && id <= kClusterIdMaxMs)) && (vendor <= kClusterVendorMax);
     191              :     }
     192              : 
     193              :     // TODO: eventually this functionality should live where the type itself is defined
     194          324 :     static constexpr bool IsValidEndpoint(EndpointId endpoint) { return true; }
     195              : 
     196              :     // TODO: eventually this functionality should live where the type itself is defined
     197          272 :     static bool IsValidDeviceType(DeviceTypeId deviceType)
     198              :     {
     199          272 :         const auto id     = deviceType & kDeviceTypeIdMask;
     200          272 :         const auto vendor = deviceType & kDeviceTypeVendorMask;
     201          272 :         return (id <= kDeviceTypeIdMax) && (vendor <= kDeviceTypeVendorMax);
     202              :     }
     203              : 
     204              :     // TODO: eventually this functionality should live where the type itself is defined
     205          886 :     static bool IsValid(const Target & target)
     206              :     {
     207          886 :         constexpr Target::Flags kNotAll     = Target::kEndpoint | Target::kDeviceType;
     208          886 :         constexpr Target::Flags kAtLeastOne = kNotAll | Target::kCluster;
     209          886 :         constexpr Target::Flags kNone       = ~kAtLeastOne;
     210          886 :         return ((target.flags & kNone) == 0) && ((target.flags & kAtLeastOne) != 0) && ((target.flags & kNotAll) != kNotAll) &&
     211          886 :             !((target.flags & Target::kCluster) && !IsValidCluster(target.cluster)) &&
     212         2658 :             !((target.flags & Target::kEndpoint) && !IsValidEndpoint(target.endpoint)) &&
     213         1772 :             !((target.flags & Target::kDeviceType) && !IsValidDeviceType(target.deviceType));
     214              :     }
     215              : 
     216         1670 :     void Decode(Target & target) const
     217              :     {
     218         1670 :         auto & flags      = target.flags;
     219         1670 :         auto & cluster    = target.cluster;
     220         1670 :         auto & endpoint   = target.endpoint;
     221         1670 :         auto & deviceType = target.deviceType;
     222         1670 :         flags             = 0;
     223         1670 :         if (mCluster != kClusterEmpty)
     224              :         {
     225         1481 :             cluster = mCluster;
     226         1481 :             flags |= Target::kCluster;
     227              :         }
     228         1670 :         if (mDeviceType != kDeviceTypeEmpty)
     229              :         {
     230         1083 :             if ((mDeviceType & kDeviceTypeIdMask) == kEndpointMagic)
     231              :             {
     232          539 :                 endpoint = static_cast<EndpointId>(mDeviceType >> kEndpointShift);
     233          539 :                 flags |= Target::kEndpoint;
     234              :             }
     235              :             else
     236              :             {
     237          544 :                 deviceType = mDeviceType;
     238          544 :                 flags |= Target::kDeviceType;
     239              :             }
     240              :         }
     241         1670 :     }
     242              : 
     243          886 :     void Encode(const Target & target)
     244              :     {
     245          886 :         const auto flags      = target.flags;
     246          886 :         const auto cluster    = target.cluster;
     247          886 :         const auto endpoint   = target.endpoint;
     248          886 :         const auto deviceType = target.deviceType;
     249          886 :         if (flags & Target::kCluster)
     250              :         {
     251          771 :             mCluster = cluster;
     252              :         }
     253              :         else
     254              :         {
     255          115 :             mCluster = kClusterEmpty;
     256              :         }
     257          886 :         if (flags & Target::kEndpoint)
     258              :         {
     259          324 :             mDeviceType = (static_cast<DeviceTypeId>(endpoint) << kEndpointShift) | kEndpointMagic;
     260              :         }
     261          562 :         else if (flags & Target::kDeviceType)
     262              :         {
     263          272 :             mDeviceType = deviceType;
     264              :         }
     265              :         else
     266              :         {
     267          290 :             mDeviceType = kDeviceTypeEmpty;
     268              :         }
     269          886 :     }
     270              : 
     271              :     static_assert(sizeof(ClusterId) == 4, "Expecting 4 byte cluster ID");
     272              :     static_assert(sizeof(EndpointId) == 2, "Expecting 2 byte endpoint ID");
     273              :     static_assert(sizeof(DeviceTypeId) == 4, "Expecting 4 byte device type ID");
     274              : 
     275              :     // TODO: some (not all) of these values should live where the type itself is defined
     276              : 
     277              :     // (mCluster == kClusterEmpty) --> mCluster contains no cluster
     278              :     static constexpr ClusterId kClusterEmpty = 0xFFFFFFFF;
     279              : 
     280              :     // (mCluster & kClusterIdMask) --> cluster id portion
     281              :     static constexpr ClusterId kClusterIdMask = 0x0000FFFF;
     282              : 
     283              :     // ((mCluster & kClusterIdMask) < kClusterIdMaxStd) --> invalid
     284              :     static constexpr ClusterId kClusterIdMaxStd = 0x00007FFF;
     285              : 
     286              :     // ((mCluster & kClusterIdMask) < kClusterIdMinMs) --> invalid
     287              :     static constexpr ClusterId kClusterIdMinMs = 0x0000FC00;
     288              : 
     289              :     // ((mCluster & kClusterIdMask) < kClusterIdMaxMs) --> invalid
     290              :     static constexpr ClusterId kClusterIdMaxMs = 0x0000FFFE;
     291              : 
     292              :     // (mCluster & kClusterVendorMask) --> cluster vendor portion
     293              :     static constexpr ClusterId kClusterVendorMask = 0xFFFF0000;
     294              : 
     295              :     // ((mCluster & kClusterVendorMask) > kClusterVendorMax) --> invalid
     296              :     static constexpr ClusterId kClusterVendorMax = 0xFFFE0000;
     297              : 
     298              :     // (mDeviceType == kDeviceTypeEmpty) --> mDeviceType contains neither endpoint nor device type
     299              :     static constexpr DeviceTypeId kDeviceTypeEmpty = 0xFFFFFFFF;
     300              : 
     301              :     // (mDeviceType & kDeviceTypeIdMask) --> device type id portion
     302              :     static constexpr DeviceTypeId kDeviceTypeIdMask = 0x0000FFFF;
     303              : 
     304              :     // ((mDeviceType & kDeviceTypeIdMask) < kDeviceTypeIdMax) --> invalid
     305              :     static constexpr DeviceTypeId kDeviceTypeIdMax = 0x0000BFFF;
     306              : 
     307              :     // (mDeviceType & kDeviceTypeVendorMask) --> device type vendor portion
     308              :     static constexpr DeviceTypeId kDeviceTypeVendorMask = 0xFFFF0000;
     309              : 
     310              :     // ((mDeviceType & kDeviceTypeVendorMask) > kDeviceTypeVendorMax) --> invalid
     311              :     static constexpr DeviceTypeId kDeviceTypeVendorMax = 0xFFFE0000;
     312              : 
     313              :     // ((mDeviceType & kDeviceTypeIdMask) == kEndpointMagic) --> mDeviceType contains endpoint
     314              :     static constexpr DeviceTypeId kEndpointMagic = 0x0000EEEE;
     315              : 
     316              :     // (mDeviceType >> kEndpointShift) --> extract endpoint from mDeviceType
     317              :     static constexpr int kEndpointShift = 16;
     318              : 
     319              :     ClusterId mCluster;
     320              :     DeviceTypeId mDeviceType;
     321              : };
     322              : 
     323              : class EntryStorage
     324              : {
     325              : public:
     326              :     // ACL support
     327              :     static constexpr size_t kNumberOfFabrics  = CHIP_CONFIG_MAX_FABRICS;
     328              :     static constexpr size_t kEntriesPerFabric = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC;
     329              :     static EntryStorage acl[kNumberOfFabrics * kEntriesPerFabric];
     330              : 
     331              :     // Find the next unused entry storage in the access control list, if one exists.
     332         1009 :     static EntryStorage * FindUnusedInAcl()
     333              :     {
     334         3645 :         for (auto & storage : acl)
     335              :         {
     336         3645 :             if (!storage.InUse())
     337              :             {
     338         1009 :                 return &storage;
     339              :             }
     340              :         }
     341            0 :         return nullptr;
     342              :     }
     343              : 
     344              :     // Find the specified used entry storage in the access control list, if it exists.
     345        22433 :     static EntryStorage * FindUsedInAcl(size_t index, const FabricIndex * fabricIndex)
     346              :     {
     347        22433 :         if (fabricIndex != nullptr)
     348              :         {
     349           27 :             ConvertIndex(index, *fabricIndex, ConvertDirection::kRelativeToAbsolute);
     350              :         }
     351        22433 :         if (index < ArraySize(acl))
     352              :         {
     353        22424 :             auto * storage = acl + index;
     354        22424 :             if (storage->InUse())
     355              :             {
     356         1799 :                 return storage;
     357              :             }
     358              :         }
     359        20634 :         return nullptr;
     360              :     }
     361              : 
     362              :     // Pool support
     363              :     static EntryStorage pool[kEntryStoragePoolSize];
     364              : 
     365              :     // Find an unused entry storage in the pool, if one is available.
     366              :     // The candidate is preferred if provided and it is in the pool,
     367              :     // regardless of whether it is already in use.
     368          651 :     static EntryStorage * Find(EntryStorage * candidate)
     369              :     {
     370          651 :         if (candidate && candidate->InPool())
     371              :         {
     372          523 :             return candidate;
     373              :         }
     374          128 :         for (auto & storage : pool)
     375              :         {
     376          128 :             if (!storage.InUse())
     377              :             {
     378          128 :                 return &storage;
     379              :             }
     380              :         }
     381            0 :         return nullptr;
     382              :     }
     383              : 
     384         5283 :     bool InPool() const
     385              :     {
     386         5283 :         constexpr auto * end = pool + ArraySize(pool);
     387         5283 :         return pool <= this && this < end;
     388              :     }
     389              : 
     390              :     EntryStorage() = default;
     391              : 
     392         1251 :     void Init()
     393              :     {
     394         1251 :         if (!mInUse)
     395              :         {
     396          650 :             Clear();
     397          650 :             mInUse = true;
     398              :         }
     399         1251 :     }
     400              : 
     401        29808 :     bool InUse() const { return mInUse; }
     402              : 
     403         1251 :     void Release()
     404              :     {
     405         1251 :         if (InPool())
     406              :         {
     407          651 :             mInUse = false;
     408              :         }
     409         1251 :     }
     410              : 
     411         1778 :     void Clear()
     412              :     {
     413         1778 :         mInUse       = false;
     414         1778 :         mFabricIndex = chip::kUndefinedFabricIndex;
     415         1778 :         mAuthMode    = AuthMode::kPase;
     416         1778 :         mPrivilege   = Privilege::kView;
     417         8890 :         for (auto & subject : mSubjects)
     418              :         {
     419         7112 :             subject.Clear();
     420              :         }
     421         7112 :         for (auto & target : mTargets)
     422              :         {
     423         5334 :             target.Clear();
     424              :         }
     425         1778 :     }
     426              : 
     427              :     enum class ConvertDirection
     428              :     {
     429              :         kAbsoluteToRelative,
     430              :         kRelativeToAbsolute
     431              :     };
     432              : 
     433              :     // Entries have a position in the access control list, denoted by an "absolute" index.
     434              :     // Because entries are scoped to a fabric, a "fabric relative" index can be inferred.
     435              :     //
     436              :     // For example: suppose there are 8 entries for fabrics A, B, and C (as fabric indexes 1, 2, 3).
     437              :     //
     438              :     //        0   1   2   3   4   5   6   7     ABSOLUTE INDEX
     439              :     //      +---+---+---+---+---+---+---+---+
     440              :     //      | A0| A1| B0| A2| B1| B2| C0| C1|   FABRIC RELATIVE INDEX
     441              :     //      +---+---+---+---+---+---+---+---+
     442              :     //
     443              :     // While the entry at (absolute) index 2 is the third entry, it is the first entry scoped to
     444              :     // fabric B. So relative to fabric index 2, the entry is at (relative) index 0.
     445              :     //
     446              :     // The opposite is true: the second entry scoped to fabric B, at (relative) index 1, is the
     447              :     // fifth entry overall, at (absolute) index 4.
     448              :     //
     449              :     // Not all conversions are possible. For example, absolute index 3 is not scoped to fabric B, so
     450              :     // attempting to convert it to be relative to fabric index 2 will fail. Likewise, fabric B does
     451              :     // not contain a fourth entry, so attempting to convert index 3 (relative to fabric index 2) to
     452              :     // an absolute index will also fail. Such failures are denoted by use of an index that is one
     453              :     // past the end of the access control list. (So in this example, failure produces index 8.)
     454           54 :     static void ConvertIndex(size_t & index, const FabricIndex fabricIndex, ConvertDirection direction)
     455              :     {
     456           54 :         size_t absoluteIndex = 0;
     457           54 :         size_t relativeIndex = 0;
     458           54 :         size_t & fromIndex   = (direction == ConvertDirection::kAbsoluteToRelative) ? absoluteIndex : relativeIndex;
     459           54 :         size_t & toIndex     = (direction == ConvertDirection::kAbsoluteToRelative) ? relativeIndex : absoluteIndex;
     460           54 :         bool found           = false;
     461          299 :         for (const auto & storage : acl)
     462              :         {
     463          299 :             if (!storage.InUse())
     464              :             {
     465            9 :                 break;
     466              :             }
     467          290 :             if (storage.mFabricIndex == fabricIndex)
     468              :             {
     469          126 :                 if (index == fromIndex)
     470              :                 {
     471           45 :                     found = true;
     472           45 :                     break;
     473              :                 }
     474           81 :                 relativeIndex++;
     475              :             }
     476          245 :             absoluteIndex++;
     477              :         }
     478           54 :         index = found ? toIndex : ArraySize(acl);
     479           54 :     }
     480              : 
     481              :     static constexpr size_t kMaxSubjects = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY;
     482              :     static constexpr size_t kMaxTargets  = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY;
     483              : 
     484              :     bool mInUse;
     485              :     FabricIndex mFabricIndex;
     486              :     AuthMode mAuthMode;
     487              :     Privilege mPrivilege;
     488              :     SubjectStorage mSubjects[kMaxSubjects];
     489              :     TargetStorage mTargets[kMaxTargets];
     490              : };
     491              : 
     492              : class EntryDelegate : public Entry::Delegate
     493              : {
     494              : public:
     495              :     // Pool support
     496              :     static EntryDelegate pool[kEntryDelegatePoolSize];
     497              : 
     498              :     // Find an unused entry delegate in the pool, if one is available.
     499              :     // The candidate is preferred if it is in the pool, regardless of whether
     500              :     // it is already in use.
     501         1251 :     static EntryDelegate * Find(Entry::Delegate & candidate)
     502              :     {
     503         1251 :         if (InPool(candidate))
     504              :         {
     505          999 :             return &static_cast<EntryDelegate &>(candidate);
     506              :         }
     507          252 :         for (auto & delegate : pool)
     508              :         {
     509          252 :             if (!delegate.InUse())
     510              :             {
     511          252 :                 return &delegate;
     512              :             }
     513              :         }
     514            0 :         return nullptr;
     515              :     }
     516              : 
     517         2667 :     static bool InPool(const Entry::Delegate & delegate)
     518              :     {
     519         2667 :         constexpr auto * end = pool + ArraySize(pool);
     520         2667 :         return pool <= &delegate && &delegate < end;
     521              :     }
     522              : 
     523         1251 :     void Release() override
     524              :     {
     525         1251 :         mStorage->Release();
     526         1251 :         mStorage = nullptr;
     527         1251 :     }
     528              : 
     529         2047 :     CHIP_ERROR GetAuthMode(AuthMode & authMode) const override
     530              :     {
     531         2047 :         authMode = mStorage->mAuthMode;
     532         2047 :         return CHIP_NO_ERROR;
     533              :     }
     534              : 
     535         1856 :     CHIP_ERROR GetFabricIndex(FabricIndex & fabricIndex) const override
     536              :     {
     537         1856 :         fabricIndex = mStorage->mFabricIndex;
     538         1856 :         return CHIP_NO_ERROR;
     539              :     }
     540              : 
     541         1983 :     CHIP_ERROR GetPrivilege(Privilege & privilege) const override
     542              :     {
     543         1983 :         privilege = mStorage->mPrivilege;
     544         1983 :         return CHIP_NO_ERROR;
     545              :     }
     546              : 
     547          654 :     CHIP_ERROR SetAuthMode(AuthMode authMode) override
     548              :     {
     549          654 :         ReturnErrorOnFailure(EnsureStorageInPool());
     550          654 :         mStorage->mAuthMode = authMode;
     551          654 :         return CHIP_NO_ERROR;
     552              :     }
     553              : 
     554          653 :     CHIP_ERROR SetFabricIndex(FabricIndex fabricIndex) override
     555              :     {
     556          653 :         ReturnErrorOnFailure(EnsureStorageInPool());
     557          653 :         mStorage->mFabricIndex = fabricIndex;
     558          653 :         return CHIP_NO_ERROR;
     559              :     }
     560              : 
     561          628 :     CHIP_ERROR SetPrivilege(Privilege privilege) override
     562              :     {
     563          628 :         ReturnErrorOnFailure(EnsureStorageInPool());
     564          628 :         mStorage->mPrivilege = privilege;
     565          628 :         return CHIP_NO_ERROR;
     566              :     }
     567              : 
     568         2636 :     CHIP_ERROR GetSubjectCount(size_t & count) const override
     569              :     {
     570         2636 :         count = 0;
     571         4819 :         for (const auto & subject : mStorage->mSubjects)
     572              :         {
     573         4819 :             if (subject.IsEmpty())
     574              :             {
     575         2636 :                 break;
     576              :             }
     577         2183 :             count++;
     578              :         }
     579         2636 :         return CHIP_NO_ERROR;
     580              :     }
     581              : 
     582         1975 :     CHIP_ERROR GetSubject(size_t index, NodeId & subject) const override
     583              :     {
     584         1975 :         if (index < EntryStorage::kMaxSubjects)
     585              :         {
     586         1975 :             return mStorage->mSubjects[index].Get(subject);
     587              :         }
     588            0 :         return CHIP_ERROR_SENTINEL;
     589              :     }
     590              : 
     591           32 :     CHIP_ERROR SetSubject(size_t index, NodeId subject) override
     592              :     {
     593           32 :         ReturnErrorOnFailure(EnsureStorageInPool());
     594           32 :         if (index < EntryStorage::kMaxSubjects)
     595              :         {
     596           32 :             return mStorage->mSubjects[index].Set(subject);
     597              :         }
     598            0 :         return CHIP_ERROR_SENTINEL;
     599              :     }
     600              : 
     601          652 :     CHIP_ERROR AddSubject(size_t * index, NodeId subject) override
     602              :     {
     603          652 :         ReturnErrorOnFailure(EnsureStorageInPool());
     604          652 :         size_t count = 0;
     605          652 :         GetSubjectCount(count);
     606          652 :         if (count < EntryStorage::kMaxSubjects)
     607              :         {
     608          652 :             CHIP_ERROR err = mStorage->mSubjects[count].Add(subject);
     609          652 :             if (err == CHIP_NO_ERROR && index != nullptr)
     610              :             {
     611            0 :                 *index = count;
     612              :             }
     613          652 :             return err;
     614              :         }
     615            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
     616              :     }
     617              : 
     618            2 :     CHIP_ERROR RemoveSubject(size_t index) override
     619              :     {
     620            2 :         ReturnErrorOnFailure(EnsureStorageInPool());
     621            2 :         size_t count = 0;
     622            2 :         GetSubjectCount(count);
     623            2 :         if (index < count)
     624              :         {
     625              :             // The storage at the specified index will be deleted by copying any subsequent storage
     626              :             // over it, ideally also including one unused storage past the ones in use. If all
     627              :             // storage was in use, this isn't possible, so the final storage is manually cleared.
     628            2 :             auto * dest      = mStorage->mSubjects + index;
     629            2 :             const auto * src = dest + 1;
     630            2 :             const auto n     = std::min(count, EntryStorage::kMaxSubjects - 1) - index;
     631            2 :             memmove(dest, src, n * sizeof(*dest));
     632            2 :             if (count == EntryStorage::kMaxSubjects)
     633              :             {
     634            0 :                 mStorage->mSubjects[EntryStorage::kMaxSubjects - 1].Clear();
     635              :             }
     636            2 :             return CHIP_NO_ERROR;
     637              :         }
     638            0 :         return CHIP_ERROR_SENTINEL;
     639              :     }
     640              : 
     641         2451 :     CHIP_ERROR GetTargetCount(size_t & count) const override
     642              :     {
     643         2451 :         count = 0;
     644         4403 :         for (const auto & target : mStorage->mTargets)
     645              :         {
     646         4269 :             if (target.IsEmpty())
     647              :             {
     648         2317 :                 break;
     649              :             }
     650         1952 :             count++;
     651              :         }
     652         2451 :         return CHIP_NO_ERROR;
     653              :     }
     654              : 
     655         1671 :     CHIP_ERROR GetTarget(size_t index, Target & target) const override
     656              :     {
     657         1671 :         if (index < EntryStorage::kMaxTargets)
     658              :         {
     659         1671 :             return mStorage->mTargets[index].Get(target);
     660              :         }
     661            0 :         return CHIP_ERROR_SENTINEL;
     662              :     }
     663              : 
     664          356 :     CHIP_ERROR SetTarget(size_t index, const Target & target) override
     665              :     {
     666          356 :         ReturnErrorOnFailure(EnsureStorageInPool());
     667          356 :         if (index < EntryStorage::kMaxTargets)
     668              :         {
     669          356 :             return mStorage->mTargets[index].Set(target);
     670              :         }
     671            0 :         return CHIP_ERROR_SENTINEL;
     672              :     }
     673              : 
     674          530 :     CHIP_ERROR AddTarget(size_t * index, const Target & target) override
     675              :     {
     676          530 :         ReturnErrorOnFailure(EnsureStorageInPool());
     677          530 :         size_t count = 0;
     678          530 :         GetTargetCount(count);
     679          530 :         if (count < EntryStorage::kMaxTargets)
     680              :         {
     681          530 :             CHIP_ERROR err = mStorage->mTargets[count].Add(target);
     682          530 :             if (err == CHIP_NO_ERROR && index != nullptr)
     683              :             {
     684            0 :                 *index = count;
     685              :             }
     686          530 :             return err;
     687              :         }
     688            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
     689              :     }
     690              : 
     691            1 :     CHIP_ERROR RemoveTarget(size_t index) override
     692              :     {
     693            1 :         ReturnErrorOnFailure(EnsureStorageInPool());
     694            1 :         size_t count = 0;
     695            1 :         GetTargetCount(count);
     696            1 :         if (index < count)
     697              :         {
     698              :             // The storage at the specified index will be deleted by copying any subsequent storage
     699              :             // over it, ideally also including one unused storage past the ones in use. If all
     700              :             // storage was in use, this isn't possible, so the final storage is manually cleared.
     701            1 :             auto * dest      = mStorage->mTargets + index;
     702            1 :             const auto * src = dest + 1;
     703            1 :             const auto n     = std::min(count, EntryStorage::kMaxTargets - 1) - index;
     704            1 :             memmove(dest, src, n * sizeof(*dest));
     705            1 :             if (count == EntryStorage::kMaxTargets)
     706              :             {
     707            1 :                 mStorage->mTargets[EntryStorage::kMaxTargets - 1].Clear();
     708              :             }
     709            1 :             return CHIP_NO_ERROR;
     710              :         }
     711            0 :         return CHIP_ERROR_SENTINEL;
     712              :     }
     713              : 
     714         1251 :     void Init(Entry & entry, EntryStorage & storage)
     715              :     {
     716         1251 :         entry.SetDelegate(*this);
     717         1251 :         storage.Init();
     718         1251 :         mEntry   = &entry;
     719         1251 :         mStorage = &storage;
     720         1251 :     }
     721              : 
     722          252 :     bool InUse() const { return mStorage != nullptr; }
     723              : 
     724         1416 :     const EntryStorage * GetStorage() const { return mStorage; }
     725              : 
     726          650 :     EntryStorage * GetStorage() { return mStorage; }
     727              : 
     728              :     // A storage is about to be deleted. If this delegate was
     729              :     // using it, make a best effort to copy it to the pool.
     730         1000 :     void FixBeforeDelete(EntryStorage & storage)
     731              :     {
     732         1000 :         if (mStorage == &storage)
     733              :         {
     734              :             // Best effort, OK if it fails.
     735            0 :             EnsureStorageInPool();
     736              :         }
     737         1000 :     }
     738              : 
     739              :     // A storage was deleted, and others shuffled into its place.
     740              :     // Fix this delegate (if necessary) to ensure it's using the
     741              :     // correct storage.
     742         1000 :     void FixAfterDelete(EntryStorage & storage)
     743              :     {
     744         1000 :         constexpr auto & acl = EntryStorage::acl;
     745         1398 :         constexpr auto * end = acl + ArraySize(acl);
     746         1000 :         if (mStorage == &storage)
     747              :         {
     748            0 :             mEntry->ResetDelegate();
     749              :         }
     750         1000 :         else if (&storage < mStorage && mStorage < end)
     751              :         {
     752            0 :             mStorage--;
     753              :         }
     754         1000 :     }
     755              : 
     756              :     // Ensure the delegate is using storage from the pool (not the access control list),
     757              :     // by copying (from the access control list to the pool) if necessary.
     758         3508 :     CHIP_ERROR EnsureStorageInPool()
     759              :     {
     760         3508 :         if (mStorage->InPool())
     761              :         {
     762         3507 :             return CHIP_NO_ERROR;
     763              :         }
     764            1 :         if (auto * storage = EntryStorage::Find(nullptr))
     765              :         {
     766            1 :             *storage = *mStorage;
     767            1 :             mStorage = storage;
     768            1 :             return CHIP_NO_ERROR;
     769              :         }
     770            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
     771              :     }
     772              : 
     773              : private:
     774              :     Entry * mEntry          = nullptr;
     775              :     EntryStorage * mStorage = nullptr;
     776              : };
     777              : 
     778              : class EntryIteratorDelegate : public EntryIterator::Delegate
     779              : {
     780              : public:
     781              :     // Pool support
     782              :     static EntryIteratorDelegate pool[kEntryIteratorDelegatePoolSize];
     783              : 
     784              :     // Find an unused entry iterator delegate in the pool, if one is available.
     785              :     // The candidate is preferred if it is in the pool, regardless of whether
     786              :     // it is already in use.
     787           56 :     static EntryIteratorDelegate * Find(EntryIterator::Delegate & candidate)
     788              :     {
     789           56 :         if (InPool(candidate))
     790              :         {
     791            3 :             return static_cast<EntryIteratorDelegate *>(&candidate);
     792              :         }
     793           53 :         for (auto & delegate : pool)
     794              :         {
     795           53 :             if (!delegate.InUse())
     796              :             {
     797           53 :                 return &delegate;
     798              :             }
     799              :         }
     800            0 :         return nullptr;
     801              :     }
     802              : 
     803           56 :     static bool InPool(const EntryIterator::Delegate & delegate)
     804              :     {
     805           56 :         constexpr auto * end = pool + ArraySize(pool);
     806           56 :         return pool <= &delegate && &delegate < end;
     807              :     }
     808              : 
     809           56 :     void Release() override { mInUse = false; }
     810              : 
     811          243 :     CHIP_ERROR Next(Entry & entry) override
     812              :     {
     813          243 :         constexpr auto & acl = EntryStorage::acl;
     814          243 :         constexpr auto * end = acl + ArraySize(acl);
     815              :         while (true)
     816              :         {
     817          434 :             if (mStorage == nullptr)
     818              :             {
     819              :                 // Start at beginning of access control list...
     820           56 :                 mStorage = acl;
     821              :             }
     822          378 :             else if (mStorage < end)
     823              :             {
     824              :                 // ...and continue iterating entries...
     825          378 :                 mStorage++;
     826              :             }
     827          434 :             if (mStorage == end || !mStorage->InUse())
     828              :             {
     829              :                 // ...but only used ones...
     830           34 :                 mStorage = end;
     831           34 :                 break;
     832              :             }
     833          400 :             if (mFabricFiltered && mStorage->mFabricIndex != mFabricIndex)
     834              :             {
     835              :                 // ...skipping those that aren't scoped to a specified fabric...
     836          191 :                 continue;
     837              :             }
     838          209 :             if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
     839              :             {
     840              :                 // ...returning any next entry via a delegate.
     841          209 :                 delegate->Init(entry, *mStorage);
     842          209 :                 return CHIP_NO_ERROR;
     843              :             }
     844            0 :             return CHIP_ERROR_BUFFER_TOO_SMALL;
     845          191 :         }
     846           34 :         return CHIP_ERROR_SENTINEL;
     847              :     }
     848              : 
     849           56 :     void Init(EntryIterator & iterator, const FabricIndex * fabricIndex)
     850              :     {
     851           56 :         iterator.SetDelegate(*this);
     852           56 :         mInUse          = true;
     853           56 :         mFabricFiltered = fabricIndex != nullptr;
     854           56 :         if (mFabricFiltered)
     855              :         {
     856           55 :             mFabricIndex = *fabricIndex;
     857              :         }
     858           56 :         mStorage = nullptr;
     859           56 :     }
     860              : 
     861           53 :     bool InUse() const { return mInUse; }
     862              : 
     863              :     // A storage was deleted, and others shuffled into its place.
     864              :     // Fix this delegate (if necessary) to ensure it's using the
     865              :     // correct storage.
     866         1000 :     void FixAfterDelete(EntryStorage & storage)
     867              :     {
     868         1000 :         constexpr auto & acl = EntryStorage::acl;
     869         1022 :         constexpr auto * end = acl + ArraySize(acl);
     870         1000 :         if (&storage <= mStorage && mStorage < end)
     871              :         {
     872            9 :             if (mStorage == acl)
     873              :             {
     874            1 :                 mStorage = nullptr;
     875              :             }
     876              :             else
     877              :             {
     878            8 :                 mStorage--;
     879              :             }
     880              :         }
     881         1000 :     }
     882              : 
     883              : private:
     884              :     bool mInUse = false;
     885              :     bool mFabricFiltered;
     886              :     FabricIndex mFabricIndex;
     887              :     EntryStorage * mStorage;
     888              : };
     889              : 
     890            0 : CHIP_ERROR CopyViaInterface(const Entry & entry, EntryStorage & storage)
     891              : {
     892              : #if CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_FLEXIBLE_COPY_SUPPORT
     893              :     // NOTE: uses sizeof(EntryStorage) on stack as a temporary and is only necessary if using this
     894              :     //       file with other Entry::Delegate implementations that are not EntryDelegate in this file
     895              :     EntryStorage temp;
     896              :     temp.Clear();
     897              : 
     898              :     FabricIndex fabricIndex = kUndefinedFabricIndex;
     899              :     ReturnErrorOnFailure(entry.GetFabricIndex(fabricIndex));
     900              :     temp.mFabricIndex = fabricIndex;
     901              : 
     902              :     AuthMode authMode = AuthMode::kNone;
     903              :     ReturnErrorOnFailure(entry.GetAuthMode(authMode));
     904              :     temp.mAuthMode = authMode;
     905              : 
     906              :     Privilege privilege = Privilege::kView;
     907              :     ReturnErrorOnFailure(entry.GetPrivilege(privilege));
     908              :     temp.mPrivilege = privilege;
     909              : 
     910              :     size_t subjectCount = 0;
     911              :     ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
     912              :     VerifyOrReturnError(subjectCount <= EntryStorage::kMaxSubjects, CHIP_ERROR_BUFFER_TOO_SMALL);
     913              :     for (size_t i = 0; i < subjectCount; ++i)
     914              :     {
     915              :         NodeId subject = kUndefinedNodeId;
     916              :         ReturnErrorOnFailure(entry.GetSubject(i, subject));
     917              :         temp.mSubjects[i].Add(subject);
     918              :     }
     919              : 
     920              :     size_t targetCount = 0;
     921              :     ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
     922              :     VerifyOrReturnError(targetCount <= EntryStorage::kMaxTargets, CHIP_ERROR_BUFFER_TOO_SMALL);
     923              :     for (size_t i = 0; i < targetCount; ++i)
     924              :     {
     925              :         Target target;
     926              :         ReturnErrorOnFailure(entry.GetTarget(i, target));
     927              :         temp.mTargets[i].Add(target);
     928              :     }
     929              : 
     930              :     temp.mInUse = true;
     931              :     storage     = temp;
     932              :     return CHIP_NO_ERROR;
     933              : #else
     934              :     // NOTE: save space by not implementing function
     935            0 :     VerifyOrDie(false);
     936              :     return CHIP_ERROR_NOT_IMPLEMENTED;
     937              : #endif
     938              : }
     939              : 
     940         1416 : CHIP_ERROR Copy(const Entry & entry, EntryStorage & storage)
     941              : {
     942              : #if CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_FAST_COPY_SUPPORT
     943         1416 :     auto & delegate = entry.GetDelegate();
     944         1416 :     if (EntryDelegate::InPool(delegate))
     945              :     {
     946              :         // NOTE: if an entry's delegate is in the pool, it must be an EntryDelegate,
     947              :         //       which must have a storage that is in use, which can be copied as POD
     948         1416 :         storage = *static_cast<const EntryDelegate &>(delegate).GetStorage();
     949         1416 :         return CHIP_NO_ERROR;
     950              :     }
     951              : #endif
     952            0 :     return CopyViaInterface(entry, storage);
     953              : }
     954              : 
     955              : class AccessControlDelegate : public AccessControl::Delegate
     956              : {
     957              : public:
     958            2 :     CHIP_ERROR Init() override
     959              :     {
     960            2 :         ChipLogProgress(DataManagement, "Examples::AccessControlDelegate::Init");
     961          130 :         for (auto & storage : EntryStorage::acl)
     962              :         {
     963          128 :             storage.Clear();
     964              :         }
     965            2 :         return CHIP_NO_ERROR;
     966              :     }
     967              : 
     968            2 :     void Finish() override { ChipLogProgress(DataManagement, "Examples::AccessControlDelegate::Finish"); }
     969              : 
     970            0 :     CHIP_ERROR GetMaxEntriesPerFabric(size_t & value) const override
     971              :     {
     972            0 :         value = EntryStorage::kEntriesPerFabric;
     973            0 :         return CHIP_NO_ERROR;
     974              :     }
     975              : 
     976            0 :     CHIP_ERROR GetMaxSubjectsPerEntry(size_t & value) const override
     977              :     {
     978            0 :         value = EntryStorage::kMaxSubjects;
     979            0 :         return CHIP_NO_ERROR;
     980              :     }
     981              : 
     982            0 :     CHIP_ERROR GetMaxTargetsPerEntry(size_t & value) const override
     983              :     {
     984            0 :         value = EntryStorage::kMaxTargets;
     985            0 :         return CHIP_NO_ERROR;
     986              :     }
     987              : 
     988            0 :     CHIP_ERROR GetMaxEntryCount(size_t & value) const override
     989              :     {
     990            0 :         value = ArraySize(EntryStorage::acl);
     991            0 :         return CHIP_NO_ERROR;
     992              :     }
     993              : 
     994            3 :     CHIP_ERROR GetEntryCount(FabricIndex fabric, size_t & value) const override
     995              :     {
     996            3 :         value = 0;
     997           22 :         for (const auto & storage : EntryStorage::acl)
     998              :         {
     999           22 :             if (!storage.InUse())
    1000              :             {
    1001            3 :                 break;
    1002              :             }
    1003           19 :             if (storage.mFabricIndex == fabric)
    1004              :             {
    1005           14 :                 value++;
    1006              :             }
    1007              :         }
    1008            3 :         return CHIP_NO_ERROR;
    1009              :     }
    1010              : 
    1011            2 :     CHIP_ERROR GetEntryCount(size_t & value) const override
    1012              :     {
    1013            2 :         value = 0;
    1014            7 :         for (const auto & storage : EntryStorage::acl)
    1015              :         {
    1016            7 :             if (!storage.InUse())
    1017              :             {
    1018            2 :                 break;
    1019              :             }
    1020            5 :             value++;
    1021              :         }
    1022            2 :         return CHIP_NO_ERROR;
    1023              :     }
    1024              : 
    1025          650 :     CHIP_ERROR PrepareEntry(Entry & entry) override
    1026              :     {
    1027          650 :         if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
    1028              :         {
    1029          650 :             if (auto * storage = EntryStorage::Find(delegate->GetStorage()))
    1030              :             {
    1031          650 :                 delegate->Init(entry, *storage);
    1032          650 :                 return CHIP_NO_ERROR;
    1033              :             }
    1034              :         }
    1035            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
    1036              :     }
    1037              : 
    1038         1009 :     CHIP_ERROR CreateEntry(size_t * index, const Entry & entry, FabricIndex * fabricIndex) override
    1039              :     {
    1040         1009 :         if (auto * storage = EntryStorage::FindUnusedInAcl())
    1041              :         {
    1042         1009 :             CHIP_ERROR err = Copy(entry, *storage);
    1043         1009 :             if (err == CHIP_NO_ERROR)
    1044              :             {
    1045         1009 :                 if (fabricIndex != nullptr)
    1046              :                 {
    1047           27 :                     *fabricIndex = storage->mFabricIndex;
    1048              :                 }
    1049         1009 :                 if (index != nullptr)
    1050              :                 {
    1051           31 :                     *index = size_t(storage - EntryStorage::acl);
    1052           31 :                     if (fabricIndex != nullptr)
    1053              :                     {
    1054           27 :                         EntryStorage::ConvertIndex(*index, *fabricIndex, EntryStorage::ConvertDirection::kAbsoluteToRelative);
    1055              :                     }
    1056              :                 }
    1057              :             }
    1058         1009 :             return err;
    1059              :         }
    1060            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
    1061              :     }
    1062              : 
    1063          465 :     CHIP_ERROR ReadEntry(size_t index, Entry & entry, const FabricIndex * fabricIndex) const override
    1064              :     {
    1065          465 :         if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
    1066              :         {
    1067          392 :             if (auto * delegate = EntryDelegate::Find(entry.GetDelegate()))
    1068              :             {
    1069          392 :                 delegate->Init(entry, *storage);
    1070          392 :                 return CHIP_NO_ERROR;
    1071              :             }
    1072            0 :             return CHIP_ERROR_BUFFER_TOO_SMALL;
    1073              :         }
    1074           73 :         return CHIP_ERROR_SENTINEL;
    1075              :     }
    1076              : 
    1077          407 :     CHIP_ERROR UpdateEntry(size_t index, const Entry & entry, const FabricIndex * fabricIndex) override
    1078              :     {
    1079          407 :         if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
    1080              :         {
    1081          407 :             return Copy(entry, *storage);
    1082              :         }
    1083            0 :         return CHIP_ERROR_SENTINEL;
    1084              :     }
    1085              : 
    1086        21561 :     CHIP_ERROR DeleteEntry(size_t index, const FabricIndex * fabricIndex) override
    1087              :     {
    1088        21561 :         if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
    1089              :         {
    1090              :             // Best effort attempt to preserve any outstanding delegates...
    1091         2000 :             for (auto & delegate : EntryDelegate::pool)
    1092              :             {
    1093         1000 :                 delegate.FixBeforeDelete(*storage);
    1094              :             }
    1095              : 
    1096              :             // ...then go through the access control list starting at the deleted storage...
    1097         1000 :             constexpr auto & acl = EntryStorage::acl;
    1098         6698 :             constexpr auto * end = acl + ArraySize(acl);
    1099         2849 :             for (auto * next = storage + 1; storage < end; ++storage, ++next)
    1100              :             {
    1101              :                 // ...copying over each storage with its next one...
    1102         2849 :                 if (next < end && next->InUse())
    1103              :                 {
    1104         1849 :                     *storage = *next;
    1105              :                 }
    1106              :                 else
    1107              :                 {
    1108              :                     // ...clearing the last previously used one...
    1109         1000 :                     storage->Clear();
    1110         1000 :                     break;
    1111              :                 }
    1112              :             }
    1113              : 
    1114              :             // ...then fix up all the delegates so they still use the proper storage.
    1115         1000 :             storage = acl + index;
    1116         2000 :             for (auto & delegate : EntryDelegate::pool)
    1117              :             {
    1118         1000 :                 delegate.FixAfterDelete(*storage);
    1119              :             }
    1120         2000 :             for (auto & delegate : EntryIteratorDelegate::pool)
    1121              :             {
    1122         1000 :                 delegate.FixAfterDelete(*storage);
    1123              :             }
    1124              : 
    1125         1000 :             return CHIP_NO_ERROR;
    1126              :         }
    1127              : 
    1128        20561 :         return CHIP_ERROR_SENTINEL;
    1129              :     }
    1130              : 
    1131           56 :     CHIP_ERROR Entries(EntryIterator & iterator, const FabricIndex * fabricIndex) const override
    1132              :     {
    1133           56 :         if (auto * delegate = EntryIteratorDelegate::Find(iterator.GetDelegate()))
    1134              :         {
    1135           56 :             delegate->Init(iterator, fabricIndex);
    1136           56 :             return CHIP_NO_ERROR;
    1137              :         }
    1138            0 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
    1139              :     }
    1140              : 
    1141           57 :     CHIP_ERROR Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
    1142              :                      Privilege requestPrivilege) override
    1143              :     {
    1144           57 :         return CHIP_ERROR_NOT_IMPLEMENTED;
    1145              :     }
    1146              : };
    1147              : 
    1148              : static_assert(std::is_pod<SubjectStorage>(), "Storage type must be POD");
    1149              : static_assert(std::is_pod<TargetStorage>(), "Storage type must be POD");
    1150              : static_assert(std::is_pod<EntryStorage>(), "Storage type must be POD");
    1151              : 
    1152              : EntryStorage EntryStorage::acl[];
    1153              : EntryStorage EntryStorage::pool[];
    1154              : EntryDelegate EntryDelegate::pool[];
    1155              : EntryIteratorDelegate EntryIteratorDelegate::pool[];
    1156              : 
    1157              : } // namespace
    1158              : 
    1159              : namespace chip {
    1160              : namespace Access {
    1161              : namespace Examples {
    1162              : 
    1163            2 : AccessControl::Delegate * GetAccessControlDelegate()
    1164              : {
    1165            2 :     static AccessControlDelegate accessControlDelegate;
    1166            2 :     return &accessControlDelegate;
    1167              : }
    1168              : 
    1169              : } // namespace Examples
    1170              : } // namespace Access
    1171              : } // namespace chip
        

Generated by: LCOV version 2.0-1