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