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