LCOV - code coverage report
Current view: top level - access/examples - ExampleAccessControlDelegate.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 386 435 88.7 %
Date: 2024-02-15 08:20:41 Functions: 72 77 93.5 %

          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        1153 :         constexpr auto * end = acl + ArraySize(acl);
     870        1000 :         if (&storage <= mStorage && mStorage < end)
     871             :         {
     872           0 :             if (mStorage == acl)
     873             :             {
     874           0 :                 mStorage = nullptr;
     875             :             }
     876             :             else
     877             :             {
     878           0 :                 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             :     ReturnErrorCodeIf(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             :     ReturnErrorCodeIf(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 1.14