Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #pragma once
18 :
19 : #include <app/ConcreteClusterPath.h>
20 : #include <app/server-cluster/ServerClusterInterface.h>
21 : #include <lib/core/CHIPError.h>
22 : #include <lib/core/DataModelTypes.h>
23 :
24 : #include <iterator>
25 :
26 : namespace chip {
27 : namespace app {
28 :
29 : /// Represents an entry in the server cluster interface registry for
30 : /// a specific interface.
31 : ///
32 : /// In practice this is a single-linked list element.
33 : struct ServerClusterRegistration
34 : {
35 : // A single-linked list of clusters registered for the given `endpointId`
36 : ServerClusterInterface * const serverClusterInterface;
37 : ServerClusterRegistration * next;
38 :
39 : constexpr ServerClusterRegistration(ServerClusterInterface & interface, ServerClusterRegistration * next_item = nullptr) :
40 : serverClusterInterface(&interface), next(next_item)
41 : {}
42 : ServerClusterRegistration(ServerClusterRegistration && other) = default;
43 :
44 : // we generally do not want to allow copies as those may have different "next" entries.
45 : ServerClusterRegistration(const ServerClusterRegistration & other) = delete;
46 : ServerClusterRegistration & operator=(const ServerClusterRegistration & other) = delete;
47 : };
48 :
49 : /// Allows registering and retrieving ServerClusterInterface instances for specific cluster paths.
50 : class ServerClusterInterfaceRegistry
51 : {
52 : public:
53 : /// represents an iterable list of clusters
54 : class ClustersList
55 : {
56 : public:
57 : class Iterator
58 : {
59 : public:
60 33844 : Iterator(ServerClusterRegistration * interface, EndpointId endpoint) : mEndpointId(endpoint), mRegistration(interface)
61 : {
62 33844 : if (mRegistration != nullptr)
63 : {
64 14 : mSpan = interface->serverClusterInterface->GetPaths();
65 : }
66 33844 : AdvanceUntilMatchingEndpoint();
67 33844 : }
68 :
69 206 : Iterator & operator++()
70 : {
71 206 : if (!mSpan.empty())
72 : {
73 206 : mSpan = mSpan.SubSpan(1);
74 : }
75 206 : AdvanceUntilMatchingEndpoint();
76 206 : return *this;
77 : }
78 : bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
79 17124 : bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
80 206 : ClusterId operator*() { return mSpan.begin()->mClusterId; }
81 :
82 : private:
83 : const EndpointId mEndpointId;
84 : ServerClusterRegistration * mRegistration;
85 : Span<const ConcreteClusterPath> mSpan;
86 :
87 34050 : void AdvanceUntilMatchingEndpoint()
88 : {
89 38253 : while (mRegistration != nullptr)
90 : {
91 4409 : if (mSpan.empty())
92 : {
93 2203 : mRegistration = mRegistration->next;
94 2203 : if (mRegistration != nullptr)
95 : {
96 2189 : mSpan = mRegistration->serverClusterInterface->GetPaths();
97 : }
98 2203 : continue;
99 : }
100 2206 : if (mSpan.begin()->mEndpointId == mEndpointId)
101 : {
102 206 : return;
103 : }
104 :
105 : // need to keep searching
106 2000 : mSpan = mSpan.SubSpan(1);
107 : }
108 : }
109 : };
110 :
111 16920 : constexpr ClustersList(ServerClusterRegistration * start, EndpointId endpointId) : mEndpointId(endpointId), mStart(start) {}
112 16920 : Iterator begin() { return { mStart, mEndpointId }; }
113 16924 : Iterator end() { return { nullptr, mEndpointId }; }
114 :
115 : private:
116 : const EndpointId mEndpointId;
117 : ServerClusterRegistration * mStart;
118 : };
119 :
120 : ~ServerClusterInterfaceRegistry();
121 :
122 : /// Add the given entry to the registry.
123 : /// NOTE the requirement of entries to be part of the same endpoint.
124 : ///
125 : /// Requirements:
126 : /// - entry MUST NOT be part of any other registration
127 : /// - paths MUST be part of the same endpoint (requirement for codegen server cluster interface implementations)
128 : /// - LIFETIME of entry must outlive the Registry (or entry must be unregistered)
129 : ///
130 : /// There can be only a single registration for a given `endpointId/clusterId` path.
131 : [[nodiscard]] CHIP_ERROR Register(ServerClusterRegistration & entry);
132 :
133 : /// Remove an existing registration
134 : ///
135 : /// Will return CHIP_ERROR_NOT_FOUND if the given registration is not found.
136 : CHIP_ERROR Unregister(ServerClusterInterface *);
137 :
138 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
139 : ServerClusterInterface * Get(const ConcreteClusterPath & path);
140 :
141 : /// Provides a list of clusters that are registered for the given endpoint.
142 : ///
143 : /// ClustersList points inside the internal registrations of the registry, so
144 : /// the list is only valid as long as the registry is not modified.
145 : ClustersList ClustersOnEndpoint(EndpointId endpointId);
146 :
147 : /// Unregister all registrations for the given endpoint.
148 : void UnregisterAllFromEndpoint(EndpointId endpointId);
149 :
150 : // Set up the underlying context for all clusters that are managed by this registry.
151 : //
152 : // The values within context will be copied and used.
153 : CHIP_ERROR SetContext(ServerClusterContext && context);
154 :
155 : // Invalidates current context.
156 : void ClearContext();
157 :
158 : private:
159 : ServerClusterRegistration * mRegistrations = nullptr;
160 :
161 : // A one-element cache to speed up finding a cluster within an endpoint.
162 : // The endpointId specifies which endpoint the cache belongs to.
163 : ServerClusterInterface * mCachedInterface = nullptr;
164 :
165 : // Managing context for this registry
166 : std::optional<ServerClusterContext> mContext;
167 : };
168 :
169 : } // namespace app
170 : } // namespace chip
|