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