Coverage Report

Created: 2025-06-24 06:17

/src/connectedhomeip/src/app/AttributeAccessInterfaceCache.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2024 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
#pragma once
19
20
#include <stddef.h>
21
22
#include <app/AttributeAccessInterface.h>
23
#include <lib/core/DataModelTypes.h>
24
25
namespace chip {
26
namespace app {
27
28
/**
29
 * @brief Cache to make look-up of AttributeAccessInterface (AAI) instances faster.
30
 *
31
 * This cache makes use of the fact that looking-up AttributeAccessInterface
32
 * instances is usually done in loops, during read/subscription wildcard
33
 * expansion, and there is a significant amount of locality.
34
 *
35
 * This cache records both "used" (i.e. uses AAI) and the single last
36
 * "unused" (i.e. does NOT use AAI) entries. Combining positive/negative
37
 * lookup led to factor of ~10 reduction of AAI lookups in total for wildcard
38
 * reads on chip-all-clusters-app, with a cache size of 1. Increasing the size did not
39
 * significantly improve the performance.
40
 */
41
class AttributeAccessInterfaceCache
42
{
43
public:
44
    enum class CacheResult
45
    {
46
        kCacheMiss,
47
        kDefinitelyUnused,
48
        kDefinitelyUsed
49
    };
50
51
1
    AttributeAccessInterfaceCache() { Invalidate(); }
52
53
    /**
54
     * @brief Invalidate the whole cache. Must be called every time list of AAI registrations changes.
55
     */
56
    void Invalidate()
57
90
    {
58
90
        for (auto & entry : mCacheSlots)
59
90
        {
60
90
            entry.Invalidate();
61
90
        }
62
90
        mLastUnusedEntry.Invalidate();
63
90
    }
64
65
    /**
66
     * @brief Mark that we know a given <`endpointId`, `clusterId`> uses AAI, with instance `attrInterface`
67
     */
68
    void MarkUsed(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface * attrInterface)
69
0
    {
70
0
        GetCacheSlot(endpointId, clusterId)->Set(endpointId, clusterId, attrInterface);
71
0
    }
72
73
    /**
74
     * @brief Mark that we know a given <`endpointId`, `clusterId`> does NOT use AAI.
75
     */
76
0
    void MarkUnused(EndpointId endpointId, ClusterId clusterId) { mLastUnusedEntry.Set(endpointId, clusterId, nullptr); }
77
78
    /**
79
     * @brief Get the AttributeAccessInterface instance for a given <`endpointId`, `clusterId`>, if present in cache.
80
     *
81
     * @param endpointId - Endpoint ID to look-up.
82
     * @param clusterId - Cluster ID to look-up.
83
     * @param outAttributeAccess - If not null, and Get returns `kDefinitelyUsed`, then this is set to the instance pointer.
84
     * @return a for whether the entry is actually used or not.
85
     */
86
    CacheResult Get(EndpointId endpointId, ClusterId clusterId, AttributeAccessInterface ** outAttributeAccess)
87
0
    {
88
0
        if (mLastUnusedEntry.Matches(endpointId, clusterId))
89
0
        {
90
0
            return CacheResult::kDefinitelyUnused;
91
0
        }
92
93
0
        AttributeAccessCacheEntry * cacheSlot = GetCacheSlot(endpointId, clusterId);
94
0
        if (cacheSlot->Matches(endpointId, clusterId) && (cacheSlot->accessor != nullptr))
95
0
        {
96
0
            if (outAttributeAccess != nullptr)
97
0
            {
98
0
                *outAttributeAccess = cacheSlot->accessor;
99
0
            }
100
0
            return CacheResult::kDefinitelyUsed;
101
0
        }
102
103
0
        return CacheResult::kCacheMiss;
104
0
    }
105
106
private:
107
    struct AttributeAccessCacheEntry
108
    {
109
        EndpointId endpointId               = kInvalidEndpointId;
110
        ClusterId clusterId                 = kInvalidClusterId;
111
        AttributeAccessInterface * accessor = nullptr;
112
113
        void Invalidate()
114
180
        {
115
180
            endpointId = kInvalidEndpointId;
116
180
            clusterId  = kInvalidClusterId;
117
180
            accessor   = nullptr;
118
180
        }
119
120
        void Set(EndpointId theEndpointId, ClusterId theClusterId, AttributeAccessInterface * theAccessor)
121
0
        {
122
0
            endpointId = theEndpointId;
123
0
            clusterId  = theClusterId;
124
0
            accessor   = theAccessor;
125
0
        }
126
127
        bool Matches(EndpointId theEndpointId, ClusterId theClusterId) const
128
0
        {
129
0
            return (endpointId == theEndpointId) && (clusterId == theClusterId);
130
0
        }
131
    };
132
133
    AttributeAccessCacheEntry * GetCacheSlot(EndpointId endpointId, ClusterId clusterId)
134
0
    {
135
0
        (void) endpointId;
136
0
        (void) clusterId;
137
0
        return &mCacheSlots[0];
138
0
    }
139
140
    AttributeAccessCacheEntry mCacheSlots[1];
141
    AttributeAccessCacheEntry mLastUnusedEntry;
142
};
143
144
} // namespace app
145
} // namespace chip