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 <cstdint>
25 : #include <new>
26 :
27 : namespace chip {
28 : namespace app {
29 :
30 : /// Represents an entry in the server cluster interface registry for
31 : /// a specific interface.
32 : ///
33 : /// In practice this is a single-linked list element.
34 : struct ServerClusterRegistration
35 : {
36 : // A single-linked list of clusters registered for the given `endpointId`
37 : ServerClusterInterface * const serverClusterInterface;
38 : ServerClusterRegistration * next;
39 :
40 : constexpr ServerClusterRegistration(ServerClusterInterface & interface, ServerClusterRegistration * next_item = nullptr) :
41 : serverClusterInterface(&interface), next(next_item)
42 : {}
43 : ServerClusterRegistration(ServerClusterRegistration && other) = default;
44 :
45 : // we generally do not want to allow copies as those may have different "next" entries.
46 : ServerClusterRegistration(const ServerClusterRegistration & other) = delete;
47 : ServerClusterRegistration & operator=(const ServerClusterRegistration & other) = delete;
48 : };
49 :
50 : /// It is very typical to join together a registration and a Server
51 : /// This templates makes this registration somewhat easier/standardized.
52 : template <typename SERVER_CLUSTER>
53 : struct RegisteredServerCluster
54 : {
55 : template <typename... Args>
56 : RegisteredServerCluster(Args &&... args) : cluster(std::forward<Args>(args)...), registration(cluster)
57 : {}
58 :
59 : [[nodiscard]] constexpr ServerClusterRegistration & Registration() { return registration; }
60 : [[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return cluster; }
61 :
62 : private:
63 : SERVER_CLUSTER cluster;
64 : ServerClusterRegistration registration;
65 : };
66 :
67 : /// Lazy-construction of a RegisteredServerCluster to allow at-runtime lifetime management
68 : ///
69 : /// If using this class, manamement of Create/Destroy MUST be done correctly.
70 : template <typename SERVER_CLUSTER>
71 : struct LazyRegisteredServerCluster
72 : {
73 : public:
74 : constexpr LazyRegisteredServerCluster() = default;
75 : ~LazyRegisteredServerCluster()
76 : {
77 : if (IsConstructed())
78 : {
79 : Destroy();
80 : }
81 : }
82 :
83 : void Destroy()
84 : {
85 : VerifyOrDie(IsConstructed());
86 : Registration().~ServerClusterRegistration();
87 : memset(mRegistration, 0, sizeof(mRegistration));
88 :
89 : Cluster().~SERVER_CLUSTER();
90 : memset(mCluster, 0, sizeof(mCluster));
91 : }
92 :
93 : template <typename... Args>
94 : void Create(Args &&... args)
95 : {
96 : VerifyOrDie(!IsConstructed());
97 :
98 : new (mCluster) SERVER_CLUSTER(std::forward<Args>(args)...);
99 : new (mRegistration) ServerClusterRegistration(Cluster());
100 : }
101 :
102 : [[nodiscard]] constexpr bool IsConstructed() const
103 : {
104 : // mRegistration is supposed to containt a serverClusterInterface that is NOT null
105 : // so we check for non-zero content. This relies that nullptr is 0
106 : return Registration().serverClusterInterface != nullptr;
107 : }
108 :
109 : [[nodiscard]] constexpr ServerClusterRegistration & Registration()
110 : {
111 : return *std::launder(reinterpret_cast<ServerClusterRegistration *>(mRegistration));
112 : }
113 :
114 : [[nodiscard]] constexpr const ServerClusterRegistration & Registration() const
115 : {
116 : return *std::launder(reinterpret_cast<const ServerClusterRegistration *>(mRegistration));
117 : }
118 :
119 : [[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return *std::launder(reinterpret_cast<SERVER_CLUSTER *>(mCluster)); }
120 :
121 : [[nodiscard]] constexpr const SERVER_CLUSTER & Cluster() const
122 : {
123 : return *std::launder(reinterpret_cast<const SERVER_CLUSTER *>(mCluster));
124 : }
125 :
126 : private:
127 : alignas(SERVER_CLUSTER) uint8_t mCluster[sizeof(SERVER_CLUSTER)] = { 0 };
128 : alignas(ServerClusterRegistration) uint8_t mRegistration[sizeof(ServerClusterRegistration)] = { 0 };
129 : };
130 :
131 : /// Allows registering and retrieving ServerClusterInterface instances for specific cluster paths.
132 : class ServerClusterInterfaceRegistry
133 : {
134 : public:
135 : /// represents an iterable list of clusters
136 : class ClustersList
137 : {
138 : public:
139 : class Iterator
140 : {
141 : public:
142 33912 : Iterator(ServerClusterRegistration * interface, EndpointId endpoint) : mEndpointId(endpoint), mRegistration(interface)
143 : {
144 33912 : if (mRegistration != nullptr)
145 : {
146 14 : mSpan = interface->serverClusterInterface->GetPaths();
147 : }
148 33912 : AdvanceUntilMatchingEndpoint();
149 33912 : }
150 :
151 206 : Iterator & operator++()
152 : {
153 206 : if (!mSpan.empty())
154 : {
155 206 : mSpan = mSpan.SubSpan(1);
156 : }
157 206 : AdvanceUntilMatchingEndpoint();
158 206 : return *this;
159 : }
160 : bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
161 17158 : bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
162 206 : ClusterId operator*() { return mSpan.begin()->mClusterId; }
163 :
164 : private:
165 : const EndpointId mEndpointId;
166 : ServerClusterRegistration * mRegistration;
167 : Span<const ConcreteClusterPath> mSpan;
168 :
169 34118 : void AdvanceUntilMatchingEndpoint()
170 : {
171 38321 : while (mRegistration != nullptr)
172 : {
173 4409 : if (mSpan.empty())
174 : {
175 2203 : mRegistration = mRegistration->next;
176 2203 : if (mRegistration != nullptr)
177 : {
178 2189 : mSpan = mRegistration->serverClusterInterface->GetPaths();
179 : }
180 2203 : continue;
181 : }
182 2206 : if (mSpan.begin()->mEndpointId == mEndpointId)
183 : {
184 206 : return;
185 : }
186 :
187 : // need to keep searching
188 2000 : mSpan = mSpan.SubSpan(1);
189 : }
190 : }
191 : };
192 :
193 16954 : constexpr ClustersList(ServerClusterRegistration * start, EndpointId endpointId) : mEndpointId(endpointId), mStart(start) {}
194 16954 : Iterator begin() { return { mStart, mEndpointId }; }
195 16958 : Iterator end() { return { nullptr, mEndpointId }; }
196 :
197 : private:
198 : const EndpointId mEndpointId;
199 : ServerClusterRegistration * mStart;
200 : };
201 :
202 : ~ServerClusterInterfaceRegistry();
203 :
204 : /// Add the given entry to the registry.
205 : /// NOTE the requirement of entries to be part of the same endpoint.
206 : ///
207 : /// Requirements:
208 : /// - entry MUST NOT be part of any other registration
209 : /// - paths MUST be part of the same endpoint (requirement for codegen server cluster interface implementations)
210 : ///
211 : /// - LIFETIME of entry must outlive the Registry (or entry must be unregistered)
212 : ///
213 : /// There can be only a single registration for a given `endpointId/clusterId` path.
214 : [[nodiscard]] CHIP_ERROR Register(ServerClusterRegistration & entry);
215 :
216 : /// Remove an existing registration
217 : ///
218 : /// Will return CHIP_ERROR_NOT_FOUND if the given registration is not found.
219 : CHIP_ERROR Unregister(ServerClusterInterface *);
220 :
221 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
222 : ServerClusterInterface * Get(const ConcreteClusterPath & path);
223 :
224 : /// Provides a list of clusters that are registered for the given endpoint.
225 : ///
226 : /// ClustersList points inside the internal registrations of the registry, so
227 : /// the list is only valid as long as the registry is not modified.
228 : ClustersList ClustersOnEndpoint(EndpointId endpointId);
229 :
230 : /// Unregister all registrations for the given endpoint.
231 : void UnregisterAllFromEndpoint(EndpointId endpointId);
232 :
233 : // Set up the underlying context for all clusters that are managed by this registry.
234 : //
235 : // The values within context will be copied and used.
236 : CHIP_ERROR SetContext(ServerClusterContext && context);
237 :
238 : // Invalidates current context.
239 : void ClearContext();
240 :
241 : private:
242 : ServerClusterRegistration * mRegistrations = nullptr;
243 :
244 : // A one-element cache to speed up finding a cluster within an endpoint.
245 : // The endpointId specifies which endpoint the cache belongs to.
246 : ServerClusterInterface * mCachedInterface = nullptr;
247 :
248 : // Managing context for this registry
249 : std::optional<ServerClusterContext> mContext;
250 : };
251 :
252 : } // namespace app
253 : } // namespace chip
|