blob: 4f9ecbb38915898a7c8c66cd43464fb5a46e1943 [file] [log] [blame]
/*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <app/ConcreteClusterPath.h>
#include <app/server-cluster/ServerClusterInterface.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <cstdint>
#include <new>
#include <optional>
namespace chip {
namespace app {
/// Represents an entry in the server cluster interface registry for
/// a specific interface.
///
/// In practice this is a single-linked list element.
struct ServerClusterRegistration
{
// A single-linked list of clusters registered for the given `endpointId`
ServerClusterInterface * const serverClusterInterface;
ServerClusterRegistration * next;
constexpr ServerClusterRegistration(ServerClusterInterface & interface, ServerClusterRegistration * next_item = nullptr) :
serverClusterInterface(&interface), next(next_item)
{}
ServerClusterRegistration(ServerClusterRegistration && other) = default;
// we generally do not want to allow copies as those may have different "next" entries.
ServerClusterRegistration(const ServerClusterRegistration & other) = delete;
ServerClusterRegistration & operator=(const ServerClusterRegistration & other) = delete;
};
/// It is very typical to join together a registration and a Server
/// This templates makes this registration somewhat easier/standardized.
template <typename SERVER_CLUSTER>
struct RegisteredServerCluster
{
template <typename... Args>
RegisteredServerCluster(Args &&... args) : cluster(std::forward<Args>(args)...), registration(cluster)
{}
[[nodiscard]] constexpr ServerClusterRegistration & Registration() { return registration; }
[[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return cluster; }
private:
SERVER_CLUSTER cluster;
ServerClusterRegistration registration;
};
/// Lazy-construction of a RegisteredServerCluster to allow at-runtime lifetime management
///
/// If using this class, manamement of Create/Destroy MUST be done correctly.
template <typename SERVER_CLUSTER>
struct LazyRegisteredServerCluster
{
public:
constexpr LazyRegisteredServerCluster() = default;
~LazyRegisteredServerCluster()
{
if (IsConstructed())
{
Destroy();
}
}
void Destroy()
{
VerifyOrDie(IsConstructed());
Registration().~ServerClusterRegistration();
memset(mRegistration, 0, sizeof(mRegistration));
Cluster().~SERVER_CLUSTER();
memset(mCluster, 0, sizeof(mCluster));
}
template <typename... Args>
void Create(Args &&... args)
{
VerifyOrDie(!IsConstructed());
new (mCluster) SERVER_CLUSTER(std::forward<Args>(args)...);
new (mRegistration) ServerClusterRegistration(Cluster());
}
[[nodiscard]] constexpr bool IsConstructed() const
{
// mRegistration is supposed to containt a serverClusterInterface that is NOT null
// so we check for non-zero content. This relies that nullptr is 0
return Registration().serverClusterInterface != nullptr;
}
[[nodiscard]] constexpr ServerClusterRegistration & Registration()
{
return *std::launder(reinterpret_cast<ServerClusterRegistration *>(mRegistration));
}
[[nodiscard]] constexpr const ServerClusterRegistration & Registration() const
{
return *std::launder(reinterpret_cast<const ServerClusterRegistration *>(mRegistration));
}
[[nodiscard]] constexpr SERVER_CLUSTER & Cluster() { return *std::launder(reinterpret_cast<SERVER_CLUSTER *>(mCluster)); }
[[nodiscard]] constexpr const SERVER_CLUSTER & Cluster() const
{
return *std::launder(reinterpret_cast<const SERVER_CLUSTER *>(mCluster));
}
private:
alignas(SERVER_CLUSTER) uint8_t mCluster[sizeof(SERVER_CLUSTER)] = { 0 };
alignas(ServerClusterRegistration) uint8_t mRegistration[sizeof(ServerClusterRegistration)] = { 0 };
};
/// Allows registering and retrieving ServerClusterInterface instances for specific cluster paths.
class ServerClusterInterfaceRegistry
{
public:
~ServerClusterInterfaceRegistry();
/// Add the given entry to the registry.
///
/// Requirements:
/// - entry MUST NOT be part of any other registration
/// - LIFETIME of entry must outlive the Registry (or entry must be unregistered)
///
/// There can be only a single registration for a given `endpointId/clusterId` path.
[[nodiscard]] CHIP_ERROR Register(ServerClusterRegistration & entry);
/// Remove an existing registration
///
/// Will return CHIP_ERROR_NOT_FOUND if the given registration is not found.
CHIP_ERROR Unregister(ServerClusterInterface *);
/// Return the interface registered for the given cluster path or nullptr if one does not exist
ServerClusterInterface * Get(const ConcreteClusterPath & path);
// Set up the underlying context for all clusters that are managed by this registry.
//
// The values within context will be moved and used as-is.
CHIP_ERROR SetContext(ServerClusterContext && context);
// Invalidates current context.
void ClearContext();
// Represents an iterable list of all clusters registered in this registry.
// The list is only valid as long as the registry is not modified.
// The list is not guaranteed to be in any particular order.
class ServerClusterInstances
{
public:
class Iterator
{
public:
Iterator(ServerClusterRegistration * registration) : mRegistration(registration) {}
Iterator & operator++()
{
if (mRegistration)
{
mRegistration = mRegistration->next;
}
return *this;
}
bool operator==(const Iterator & other) const { return mRegistration == other.mRegistration; }
bool operator!=(const Iterator & other) const { return mRegistration != other.mRegistration; }
ServerClusterInterface * operator*() { return mRegistration ? mRegistration->serverClusterInterface : nullptr; }
private:
ServerClusterRegistration * mRegistration;
};
constexpr ServerClusterInstances(ServerClusterRegistration * start) : mStart(start) {}
Iterator begin() { return { mStart }; }
Iterator end() { return { nullptr }; }
private:
ServerClusterRegistration * mStart;
};
ServerClusterInstances AllServerClusterInstances();
protected:
ServerClusterRegistration * mRegistrations = nullptr;
// A one-element cache to speed up finding a cluster within an endpoint.
// The endpointId specifies which endpoint the cache belongs to.
ServerClusterInterface * mCachedInterface = nullptr;
// Managing context for this registry
std::optional<ServerClusterContext> mContext;
};
} // namespace app
} // namespace chip