Line data Source code
1 : /**
2 : *
3 : * Copyright (c) 2025 Project CHIP Authors
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 :
18 : #pragma once
19 :
20 : #include <app/data-model-provider/ProviderMetadataTree.h>
21 : #include <app/storage/TableEntry.h>
22 : #include <lib/support/CommonIterator.h>
23 : #include <lib/support/PersistentData.h>
24 : #include <lib/support/TypeTraits.h>
25 :
26 : namespace chip {
27 : namespace app {
28 : namespace Storage {
29 :
30 : /**
31 : * @brief Container which exposes various compile-time constants for specializations
32 : * of FabricTableImpl.
33 : */
34 : template <class StorageId, class StorageData>
35 : class DefaultSerializer
36 : {
37 : public:
38 : // gcc bug prevents us from using a static variable; see:
39 : // https://stackoverflow.com/questions/50638053/constexpr-static-data-member-without-initializer
40 : // The number of bytes used by an entry (StorageData) + its metadata when persisting to storage
41 : static constexpr size_t kEntryMaxBytes();
42 : // The number of bytes used by an ID (StorageId)
43 : static constexpr size_t kIdMaxBytes() { return TLV::EstimateStructOverhead(sizeof(StorageId)); }
44 : // The number of bytes used by FabricEntryData, which is dependent on the size of StorageId
45 : static constexpr size_t kFabricMaxBytes()
46 : {
47 : return TLV::EstimateStructOverhead(sizeof(uint8_t), // entry_count
48 : kMaxPerFabric() * kIdMaxBytes());
49 : }
50 : // The max number of entries per fabric; this value directly affects memory usage
51 : static constexpr uint16_t kMaxPerFabric();
52 : // The max number of entries for the endpoint (programmatic limit)
53 : static constexpr uint16_t kMaxPerEndpoint();
54 :
55 : DefaultSerializer() {}
56 : ~DefaultSerializer(){};
57 :
58 : static CHIP_ERROR SerializeId(TLV::TLVWriter & writer, const StorageId & id);
59 : static CHIP_ERROR DeserializeId(TLV::TLVReader & reader, StorageId & id);
60 :
61 : static CHIP_ERROR SerializeData(TLV::TLVWriter & writer, const StorageData & data);
62 : static CHIP_ERROR DeserializeData(TLV::TLVReader & reader, StorageData & data);
63 :
64 : static StorageKeyName EndpointEntryCountKey(EndpointId endpoint);
65 : // The key for persisting the data for a fabric
66 : static StorageKeyName FabricEntryDataKey(FabricIndex fabric, EndpointId endpoint);
67 : // The key for persisting the data for an entry in a fabric; FabricEntryDataKey should be a root prefix
68 : // of this key, such that removing a fabric removes all its entries
69 : static StorageKeyName FabricEntryKey(FabricIndex fabric, EndpointId endpoint, uint16_t idx);
70 :
71 : // Clears the data to default values
72 107 : static void Clear(StorageData & data) { data.Clear(); }
73 : }; // class DefaultSerializer
74 :
75 : /**
76 : * @brief Implementation of a storage accessor in nonvolatile storage of a templatized table that stores by fabric index.
77 : * This class does not actually hold the entries, but rather acts as a wrapper/accessor around the storage layer,
78 : * reading entries from the storage pointed to by calling SetEndpoint.
79 : *
80 : * FabricTableImpl is an implementation that allows to store arbitrary entities using PersistentStorageDelegate.
81 : * It handles the storage of entities by their StorageId and EnpointId over multiple fabrics.
82 : */
83 : template <class StorageId, class StorageData>
84 : class FabricTableImpl
85 : {
86 : using TableEntry = Data::TableEntryRef<StorageId, StorageData>;
87 :
88 : public:
89 : using EntryIterator = CommonIterator<TableEntry>;
90 : using EntryIndex = Data::EntryIndex;
91 : using Serializer = DefaultSerializer<StorageId, StorageData>;
92 :
93 48 : virtual ~FabricTableImpl() { Finish(); };
94 :
95 : CHIP_ERROR Init(PersistentStorageDelegate & storage);
96 : void Finish();
97 :
98 : // Entry count
99 : /**
100 : * @brief Get the total number of stored entries for the entire endpoint
101 : * @param entry_count[out] the count of entries
102 : * @return CHIP_ERROR, CHIP_NO_ERROR if successful or if the Fabric was not found, specific CHIP_ERROR otherwise
103 : */
104 : CHIP_ERROR GetEndpointEntryCount(uint8_t & entry_count);
105 :
106 : /**
107 : * @brief Get the total number of stored entries for the specified fabric on the currently selected endpoint.
108 : * @param fabric_index the fabric to get the count for
109 : * @param entry_count[out] the count of entries
110 : * @return CHIP_ERROR, CHIP_NO_ERROR if successful or if the Fabric was not found, specific CHIP_ERROR otherwise
111 : */
112 : CHIP_ERROR GetFabricEntryCount(FabricIndex fabric_index, uint8_t & entry_count);
113 :
114 : // Data
115 : CHIP_ERROR GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity);
116 :
117 : /**
118 : * @brief Writes the entry to persistent storage.
119 : * @param fabric_index the fabric to write the entry to
120 : * @param entry_id the unique entry identifier
121 : * @param data the source data
122 : * @param writeBuffer the buffer that will be used to write the data before being persisted; PersistentStorageDelegate does not
123 : * offer a way to stream bytes to be written
124 : */
125 : template <size_t kEntryMaxBytes>
126 : CHIP_ERROR SetTableEntry(FabricIndex fabric_index, const StorageId & entry_id, const StorageData & data,
127 : PersistenceBuffer<kEntryMaxBytes> & writeBuffer);
128 :
129 : /**
130 : * @brief Loads the entry from persistent storage.
131 : * @param fabric_index the fabric to load the entry from
132 : * @param entry_id the unique entry identifier
133 : * @param data the target for the loaded data
134 : * @param buffer the buffer that will be used to load from persistence; some data types in the data argument, such as
135 : * DecodableList, point directly into the buffer, and as such for those types of structures the lifetime of the buffer needs to
136 : * be equal to or greater than data
137 : */
138 : template <size_t kEntryMaxBytes>
139 : CHIP_ERROR GetTableEntry(FabricIndex fabric_index, StorageId & entry_id, StorageData & data,
140 : PersistenceBuffer<kEntryMaxBytes> & buffer);
141 : CHIP_ERROR FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id, EntryIndex & idx);
142 : CHIP_ERROR RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id);
143 : CHIP_ERROR RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index, EntryIndex entry_idx);
144 :
145 : // Fabrics
146 : CHIP_ERROR RemoveFabric(DataModel::ProviderMetadataTree & provider, FabricIndex fabric_index);
147 : CHIP_ERROR RemoveEndpoint();
148 :
149 : /**
150 : * @brief Selects the endpoint that the table will point to & entries will be read from.
151 : * @param endpoint the endpoint which entries will be stored to or read from.
152 : */
153 : void SetEndpoint(EndpointId endpoint);
154 : void SetTableSize(uint16_t endpointEntryTableSize, uint16_t maxPerFabric);
155 1352 : bool IsInitialized() { return (mStorage != nullptr); }
156 :
157 : /**
158 : * @brief Iterates through all entries in fabric, calling iterateFn with the allocated iterator.
159 : * @tparam kEntryMaxBytes size of the buffer for loading entries, should match DefaultSerializer::kEntryMaxBytes
160 : * @tparam UnaryFunc a function of type std::function<CHIP_ERROR(EntryIterator & iterator)>; template arg for GCC inlining
161 : * efficiency
162 : * @param fabric the fabric to iterate entries for
163 : * @param store the in-memory buffer that an entry will be read into
164 : * @param iterateFn a function that will be called with the iterator; if this function returns an error result, iteration stops
165 : * and IterateEntries returns that same error result.
166 : */
167 : template <size_t kEntryMaxBytes, class UnaryFunc>
168 : CHIP_ERROR IterateEntries(FabricIndex fabric, PersistenceBuffer<kEntryMaxBytes> & buffer, UnaryFunc iterateFn);
169 :
170 : protected:
171 : // This constructor is meant for test purposes, it allows to change the defined max for entries per fabric and global, which
172 : // allows to simulate OTA where this value was changed
173 48 : FabricTableImpl(uint16_t maxEntriesPerFabric, uint16_t maxEntriesPerEndpoint) :
174 48 : mMaxPerFabric(maxEntriesPerFabric), mMaxPerEndpoint(maxEntriesPerEndpoint)
175 48 : {}
176 :
177 : // Endpoint entry count
178 : CHIP_ERROR SetEndpointEntryCount(const uint8_t & entry_count);
179 :
180 : /**
181 : * @brief Implementation of an iterator over the elements in the FabricTableImpl.
182 : *
183 : * If you would like to expose iterators in your subclass of FabricTableImpl, you can:
184 : * A) Use this class in an ObjectPool<EntryIteratorImpl> field to allow callers to obtain an iterator, with AutoRelease to free
185 : * resources B) Use IterateEntries to allocate on stack
186 : */
187 : template <size_t kEntryMaxBytes>
188 : class EntryIteratorImpl : public EntryIterator
189 : {
190 : public:
191 : EntryIteratorImpl(FabricTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, uint16_t maxEntriesPerFabric,
192 : uint16_t maxEntriesPerEndpoint, PersistenceBuffer<kEntryMaxBytes> & buffer);
193 : size_t Count() override;
194 : bool Next(TableEntry & output) override;
195 : void Release() override;
196 :
197 : protected:
198 : FabricTableImpl & mProvider;
199 : PersistenceBuffer<kEntryMaxBytes> & mBuffer;
200 : FabricIndex mFabric = kUndefinedFabricIndex;
201 : EndpointId mEndpoint = kInvalidEndpointId;
202 : EntryIndex mNextEntryIdx;
203 : EntryIndex mEntryIndex = 0;
204 : uint8_t mTotalEntries = 0;
205 : uint16_t mMaxPerFabric;
206 : uint16_t mMaxPerEndpoint;
207 : };
208 :
209 : uint16_t mMaxPerFabric;
210 : uint16_t mMaxPerEndpoint;
211 : EndpointId mEndpointId = kInvalidEndpointId;
212 : PersistentStorageDelegate * mStorage = nullptr;
213 : }; // class FabricTableImpl
214 :
215 : } // namespace Storage
216 : } // namespace app
217 : } // namespace chip
|