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/FabricTableImpl.h>
21 : #include <app/util/endpoint-config-api.h>
22 : #include <lib/support/DefaultStorageKeyAllocator.h>
23 : #include <lib/support/TypeTraits.h>
24 : #include <stdlib.h>
25 :
26 : namespace chip {
27 : namespace app {
28 : namespace Storage {
29 :
30 : using EntryIndex = Data::EntryIndex;
31 :
32 : /// @brief Tags Used to serialize entries so they can be stored in flash memory.
33 : /// kEndpointEntryCount: Number of entries in an endpoint
34 : /// kEntryCount: Number of entries in a Fabric
35 : /// kStorageIdArray: Array of StorageId struct
36 : enum class TagEntry : uint8_t
37 : {
38 : kEndpointEntryCount = 1,
39 : kEntryCount,
40 : kStorageIdArray,
41 : kFabricTableFirstSpecializationReservedTag,
42 : kFabricTableLastSpecializationReservedTag = 127,
43 : // Add new entries here; kFabricTableFirstSpecializationReservedTag through
44 : // kFabricTableLastSpecializationReservedTag are reserved for specializations
45 : };
46 :
47 : // Currently takes 5 Bytes to serialize Container and value in a TLV: 1 byte start struct, 2 bytes control + tag for the value, 1
48 : // byte value, 1 byte end struct. 8 Bytes leaves space for potential increase in count_value size.
49 : static constexpr size_t kPersistentBufferEntryCountBytes = 8;
50 :
51 : template <class StorageId, class StorageData>
52 : struct EndpointEntryCount : public PersistentData<kPersistentBufferEntryCountBytes>
53 : {
54 : using Serializer = DefaultSerializer<StorageId, StorageData>;
55 :
56 : EndpointId endpoint_id = kInvalidEndpointId;
57 : uint8_t count_value = 0;
58 :
59 264 : EndpointEntryCount(EndpointId endpoint, uint8_t count = 0) : endpoint_id(endpoint), count_value(count) {}
60 264 : ~EndpointEntryCount() {}
61 :
62 264 : void Clear() override { count_value = 0; }
63 :
64 442 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
65 : {
66 442 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
67 442 : key = Serializer::EndpointEntryCountKey(endpoint_id);
68 442 : return CHIP_NO_ERROR;
69 : }
70 :
71 178 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
72 : {
73 : TLV::TLVType container;
74 178 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
75 178 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEndpointEntryCount), count_value));
76 178 : return writer.EndContainer(container);
77 : }
78 :
79 252 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
80 : {
81 252 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
82 :
83 : TLV::TLVType container;
84 252 : ReturnErrorOnFailure(reader.EnterContainer(container));
85 252 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEndpointEntryCount)));
86 252 : ReturnErrorOnFailure(reader.Get(count_value));
87 252 : return reader.ExitContainer(container);
88 : }
89 :
90 264 : CHIP_ERROR Load(PersistentStorageDelegate * storage) override
91 : {
92 264 : CHIP_ERROR err = PersistentData::Load(storage);
93 264 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
94 264 : if (CHIP_ERROR_NOT_FOUND == err)
95 : {
96 12 : count_value = 0;
97 : }
98 :
99 264 : return CHIP_NO_ERROR;
100 : }
101 : };
102 :
103 : // Prevent mutations from happening in TableEntryData::Serialize
104 : // If we just used a raw reference for TableEntryData::mEntry, C++ allows us
105 : // to mutate mEntry.mStorageId & mEntry.mStorageData in TableEntryData::Serialize
106 : // without having to do a const_cast; as an example, if we were to accidentally introduce
107 : // the following code in TableEntryData::Serialize (a const method):
108 : //
109 : // this->mEntry->mStorageData = StorageData();
110 : //
111 : // If TableEntryData::mEntry is a reference, it allows this with no compilation error;
112 : // But with ConstCorrectRef, we get a compile-time error that TableEntryData::mEntry->mStorageData
113 : // cannot be modified because it is a const value
114 : template <typename T>
115 : class ConstCorrectRef
116 : {
117 : T & mRef;
118 :
119 : public:
120 474 : inline ConstCorrectRef(T & ref) : mRef(ref) {}
121 :
122 : inline const T * operator->() const { return &mRef; }
123 : inline T * operator->() { return &mRef; }
124 :
125 200 : inline const T & operator*() const { return mRef; }
126 213 : inline T & operator*() { return mRef; }
127 : };
128 :
129 : template <class StorageId, class StorageData>
130 : struct TableEntryData : DataAccessor
131 : {
132 : using Serializer = DefaultSerializer<StorageId, StorageData>;
133 :
134 : EndpointId endpoint_id = kInvalidEndpointId;
135 : FabricIndex fabric_index = kUndefinedFabricIndex;
136 : EntryIndex index = 0;
137 : bool first = true;
138 : ConstCorrectRef<StorageId> storage_id;
139 : ConstCorrectRef<StorageData> storage_data;
140 :
141 237 : TableEntryData(EndpointId endpoint, FabricIndex fabric, StorageId & id, StorageData & data, EntryIndex idx = 0) :
142 237 : endpoint_id(endpoint), fabric_index(fabric), index(idx), storage_id(id), storage_data(data)
143 237 : {}
144 :
145 171 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
146 : {
147 171 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
148 171 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
149 171 : key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
150 171 : return CHIP_NO_ERROR;
151 : }
152 :
153 71 : void Clear() override { Serializer::Clear(*storage_data); }
154 :
155 100 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
156 : {
157 : TLV::TLVType container;
158 100 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
159 :
160 100 : ReturnErrorOnFailure(Serializer::SerializeId(writer, *storage_id));
161 :
162 100 : ReturnErrorOnFailure(Serializer::SerializeData(writer, *storage_data));
163 :
164 100 : return writer.EndContainer(container);
165 : }
166 :
167 71 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
168 : {
169 71 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
170 :
171 : TLV::TLVType container;
172 71 : ReturnErrorOnFailure(reader.EnterContainer(container));
173 :
174 71 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, *storage_id));
175 :
176 71 : ReturnErrorOnFailure(Serializer::DeserializeData(reader, *storage_data));
177 :
178 71 : return reader.ExitContainer(container);
179 : }
180 : };
181 :
182 : /**
183 : * @brief Class that holds a map to all entries in a fabric for a specific endpoint
184 : *
185 : * FabricEntryData is an access to a linked list of entries
186 : */
187 : template <class StorageId, class StorageData, size_t kEntryMaxBytes, size_t kFabricMaxBytes, uint16_t kMaxPerFabric>
188 : struct FabricEntryData : public PersistentData<kFabricMaxBytes>
189 : {
190 : using Serializer = DefaultSerializer<StorageId, StorageData>;
191 : using TypedTableEntryData = TableEntryData<StorageId, StorageData>;
192 : using Store = PersistentStore<kEntryMaxBytes>;
193 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
194 :
195 : EndpointId endpoint_id;
196 : FabricIndex fabric_index;
197 : uint8_t entry_count = 0;
198 : uint16_t max_per_fabric;
199 : uint16_t max_per_endpoint;
200 : StorageId entry_map[kMaxPerFabric];
201 :
202 744 : FabricEntryData(EndpointId endpoint = kInvalidEndpointId, FabricIndex fabric = kUndefinedFabricIndex,
203 : uint16_t maxPerFabric = kMaxPerFabric, uint16_t maxPerEndpoint = Serializer::kMaxPerEndpoint()) :
204 744 : endpoint_id(endpoint),
205 8184 : fabric_index(fabric), max_per_fabric(maxPerFabric), max_per_endpoint(maxPerEndpoint)
206 744 : {}
207 :
208 932 : CHIP_ERROR UpdateKey(StorageKeyName & key) const override
209 : {
210 932 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
211 932 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
212 932 : key = Serializer::FabricEntryDataKey(fabric_index, endpoint_id);
213 932 : return CHIP_NO_ERROR;
214 : }
215 :
216 744 : void Clear() override
217 : {
218 744 : entry_count = 0;
219 7602 : for (uint16_t i = 0; i < max_per_fabric; i++)
220 : {
221 6858 : entry_map[i].Clear();
222 : }
223 744 : }
224 :
225 178 : CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
226 : {
227 : TLV::TLVType fabricEntryContainer;
228 178 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, fabricEntryContainer));
229 178 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEntryCount), entry_count));
230 : TLV::TLVType entryMapContainer;
231 178 : ReturnErrorOnFailure(
232 : writer.StartContainer(TLV::ContextTag(TagEntry::kStorageIdArray), TLV::kTLVType_Array, entryMapContainer));
233 :
234 : // Storing the entry map
235 1450 : for (uint16_t i = 0; i < max_per_fabric; i++)
236 : {
237 : TLV::TLVType entryIdContainer;
238 1272 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, entryIdContainer));
239 1272 : ReturnErrorOnFailure(Serializer::SerializeId(writer, entry_map[i]));
240 1272 : ReturnErrorOnFailure(writer.EndContainer(entryIdContainer));
241 : }
242 178 : ReturnErrorOnFailure(writer.EndContainer(entryMapContainer));
243 178 : return writer.EndContainer(fabricEntryContainer);
244 : }
245 :
246 : /// @brief This Deserialize method is implemented only to allow compilation. It is not used throughout the code.
247 : /// @param reader TLV reader
248 : /// @return CHIP_NO_ERROR
249 0 : CHIP_ERROR Deserialize(TLV::TLVReader & reader) override { return CHIP_ERROR_INCORRECT_STATE; }
250 :
251 : /// @brief This Deserialize method checks that the recovered entries from the deserialization fit in the current max and if
252 : /// there are too many entries in nvm, it deletes them. The method sets the deleted_entries output parameter to true if entries
253 : /// were deleted so that the load function can know it needs to save the Fabric entry data to update the entry_count and the
254 : /// entry map in stored memory.
255 : /// @param reade [in] TLV reader, must be big enough to hold the entry size
256 : /// @param storage [in] Persistent Storage Delegate, required to delete entries if the number of entries in storage is greater
257 : /// than the maximum allowed
258 : /// @param deleted_entries_count [out] uint8_t letting the caller (in this case the load method) know how many entries were
259 : /// deleted so it can adjust the fabric and global entry count accordingly. Even if Deserialize fails, this value will return
260 : /// the number of entries deleted before the failure happened.
261 : /// @return CHIP_NO_ERROR on success, specific CHIP_ERROR otherwise
262 330 : CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate & storage, uint8_t & deleted_entries_count)
263 : {
264 330 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
265 : TLV::TLVType fabricEntryContainer;
266 330 : ReturnErrorOnFailure(reader.EnterContainer(fabricEntryContainer));
267 330 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEntryCount)));
268 330 : ReturnErrorOnFailure(reader.Get(entry_count));
269 330 : entry_count = std::min(entry_count, static_cast<uint8_t>(max_per_fabric));
270 330 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::ContextTag(TagEntry::kStorageIdArray)));
271 : TLV::TLVType entryMapContainer;
272 330 : ReturnErrorOnFailure(reader.EnterContainer(entryMapContainer));
273 :
274 330 : uint16_t i = 0;
275 : CHIP_ERROR err;
276 330 : deleted_entries_count = 0;
277 :
278 2665 : while ((err = reader.Next(TLV::AnonymousTag())) == CHIP_NO_ERROR)
279 : {
280 : TLV::TLVType entryIdContainer;
281 2335 : if (i < max_per_fabric)
282 : {
283 2325 : ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
284 2325 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, entry_map[i]));
285 2325 : ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
286 : }
287 : else
288 : {
289 10 : StorageId unused;
290 10 : ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
291 10 : ReturnErrorOnFailure(Serializer::DeserializeId(reader, unused));
292 10 : ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
293 10 : ReturnErrorOnFailure(DeleteValue(storage, i));
294 10 : deleted_entries_count++;
295 : }
296 :
297 2335 : i++;
298 : }
299 :
300 330 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
301 330 : ReturnErrorOnFailure(reader.ExitContainer(entryMapContainer));
302 330 : return reader.ExitContainer(fabricEntryContainer);
303 : }
304 :
305 : /// @brief Finds the id of the entry with the specified index
306 : /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found
307 72 : CHIP_ERROR FindByIndex(PersistentStorageDelegate & storage, EntryIndex index, StorageId & entry_id)
308 : {
309 72 : VerifyOrReturnError(entry_map[index].IsValid(), CHIP_ERROR_NOT_FOUND);
310 59 : VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
311 59 : VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
312 59 : if (!storage.SyncDoesKeyExist(Serializer::FabricEntryKey(fabric_index, endpoint_id, index).KeyName()))
313 : {
314 0 : return CHIP_ERROR_NOT_FOUND;
315 : }
316 59 : entry_id = entry_map[index];
317 59 : return CHIP_NO_ERROR;
318 : }
319 :
320 : /// @brief Finds the index where the current entry should be inserted by going through the endpoint's table and checking
321 : /// whether the entry is already there. If the target is not in the table, sets idx to the first empty space
322 : /// @param target_entry StorageId of entry to find
323 : /// @param idx Index where target or space is found
324 : /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found and space left
325 : /// CHIP_ERROR_NO_MEMORY if target was not found and table is full
326 258 : CHIP_ERROR Find(const StorageId & target_entry, EntryIndex & idx)
327 : {
328 258 : EntryIndex firstFreeIdx = Data::kUndefinedEntryIndex; // storage index if entry not found
329 258 : uint16_t index = 0;
330 :
331 1420 : while (index < max_per_fabric)
332 : {
333 1315 : if (entry_map[index] == target_entry)
334 : {
335 153 : idx = index;
336 153 : return CHIP_NO_ERROR; // return entry at current index if entry found
337 : }
338 1162 : if (!entry_map[index].IsValid() && firstFreeIdx == Data::kUndefinedEntryIndex)
339 : {
340 164 : firstFreeIdx = index;
341 : }
342 1162 : index++;
343 : }
344 :
345 105 : if (firstFreeIdx < max_per_fabric)
346 : {
347 102 : idx = firstFreeIdx;
348 102 : return CHIP_ERROR_NOT_FOUND;
349 : }
350 :
351 3 : return CHIP_ERROR_NO_MEMORY;
352 : }
353 :
354 103 : CHIP_ERROR SaveEntry(PersistentStorageDelegate & storage, const StorageId & id, const StorageData & data,
355 : Store & persistentStore)
356 : {
357 103 : CHIP_ERROR err = CHIP_NO_ERROR;
358 : // Look for empty storage space
359 :
360 : EntryIndex index;
361 103 : err = this->Find(id, index);
362 :
363 : // C++ doesn't have const constructors; variable is declared const
364 103 : const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<StorageId &>(id), const_cast<StorageData &>(data),
365 : index);
366 :
367 103 : if (CHIP_NO_ERROR == err)
368 : {
369 3 : return persistentStore.Save(entry, &storage);
370 : }
371 :
372 100 : if (CHIP_ERROR_NOT_FOUND == err) // If not found, entry.index should be the first free index
373 : {
374 : // Update the global entry count
375 99 : TypedEndpointEntryCount endpoint_count(endpoint_id);
376 99 : ReturnErrorOnFailure(endpoint_count.Load(&storage));
377 99 : VerifyOrReturnError(endpoint_count.count_value < max_per_endpoint, CHIP_ERROR_NO_MEMORY);
378 97 : endpoint_count.count_value++;
379 97 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
380 :
381 97 : entry_count++;
382 97 : entry_map[entry.index] = id;
383 :
384 97 : err = this->Save(&storage);
385 97 : if (CHIP_NO_ERROR != err)
386 : {
387 0 : endpoint_count.count_value--;
388 0 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
389 0 : return err;
390 : }
391 :
392 97 : err = persistentStore.Save(entry, &storage);
393 :
394 : // on failure to save the entry, undoes the changes to Fabric Entry Data
395 97 : if (CHIP_NO_ERROR != err)
396 : {
397 0 : endpoint_count.count_value--;
398 0 : ReturnErrorOnFailure(endpoint_count.Save(&storage));
399 :
400 0 : entry_count--;
401 0 : entry_map[entry.index].Clear();
402 0 : ReturnErrorOnFailure(this->Save(&storage));
403 0 : return err;
404 : }
405 99 : }
406 :
407 98 : return err;
408 103 : }
409 :
410 : /// @brief Removes an entry from the non-volatile memory and clears its index in the entry map. Decreases the number of entries
411 : /// in the global entry count and in the entry fabric data if successful. As the entry map size is not compressed upon removal,
412 : /// this only clears the entry corresponding to the entry from the entry map.
413 : /// @param storage Storage delegate to access the entry
414 : /// @param entry_id Entry to remove
415 : /// @return CHIP_NO_ERROR if successful, specific CHIP_ERROR otherwise
416 80 : CHIP_ERROR RemoveEntry(PersistentStorageDelegate & storage, const StorageId & entry_id)
417 : {
418 80 : CHIP_ERROR err = CHIP_NO_ERROR;
419 : EntryIndex entryIndex;
420 :
421 : // Empty Entry Fabric Data returns CHIP_NO_ERROR on remove
422 80 : if (entry_count > 0)
423 : {
424 : // If Find doesn't return CHIP_NO_ERROR, the entry wasn't found, which doesn't return an error
425 80 : VerifyOrReturnValue(this->Find(entry_id, entryIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
426 :
427 : // Update the global entry count
428 79 : TypedEndpointEntryCount endpoint_entry_count(endpoint_id);
429 79 : ReturnErrorOnFailure(endpoint_entry_count.Load(&storage));
430 79 : endpoint_entry_count.count_value--;
431 79 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
432 :
433 79 : entry_count--;
434 79 : entry_map[entryIndex].Clear();
435 79 : err = this->Save(&storage);
436 :
437 : // On failure to update the entry map, undo the global count modification
438 79 : if (CHIP_NO_ERROR != err)
439 : {
440 0 : endpoint_entry_count.count_value++;
441 0 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
442 0 : return err;
443 : }
444 :
445 79 : err = DeleteValue(storage, entryIndex);
446 :
447 : // On failure to delete entry, undo the change to the Fabric Entry Data and the global entry count
448 79 : if (CHIP_NO_ERROR != err)
449 : {
450 0 : endpoint_entry_count.count_value++;
451 0 : ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
452 :
453 0 : entry_count++;
454 0 : entry_map[entryIndex] = entry_id;
455 0 : ReturnErrorOnFailure(this->Save(&storage));
456 0 : return err;
457 : }
458 79 : }
459 79 : return err;
460 : }
461 :
462 744 : CHIP_ERROR Load(PersistentStorageDelegate * storage) override
463 : {
464 744 : VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
465 744 : uint8_t deleted_entries_count = 0;
466 :
467 744 : uint8_t buffer[kFabricMaxBytes] = { 0 };
468 744 : StorageKeyName key = StorageKeyName::Uninitialized();
469 :
470 : // Set data to defaults
471 744 : Clear();
472 :
473 : // Update storage key
474 744 : ReturnErrorOnFailure(UpdateKey(key));
475 :
476 : // Load the serialized data
477 744 : uint16_t size = static_cast<uint16_t>(sizeof(buffer));
478 744 : CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
479 744 : VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
480 330 : ReturnErrorOnFailure(err);
481 :
482 : // Decode serialized data
483 330 : TLV::TLVReader reader;
484 330 : reader.Init(buffer, size);
485 :
486 330 : err = Deserialize(reader, *storage, deleted_entries_count);
487 :
488 : // If Deserialize sets the "deleted_entries" variable, the table in flash memory held too many entries (can happen
489 : // if max_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then
490 : // be updated
491 330 : if (deleted_entries_count)
492 : {
493 2 : TypedEndpointEntryCount global_count(endpoint_id);
494 2 : ReturnErrorOnFailure(global_count.Load(storage));
495 2 : global_count.count_value = static_cast<uint8_t>(global_count.count_value - deleted_entries_count);
496 2 : ReturnErrorOnFailure(global_count.Save(storage));
497 2 : ReturnErrorOnFailure(this->Save(storage));
498 2 : }
499 :
500 330 : return err;
501 744 : }
502 :
503 : private:
504 89 : CHIP_ERROR DeleteValue(PersistentStorageDelegate & storage, EntryIndex index)
505 : {
506 89 : StorageKeyName key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
507 89 : return storage.SyncDeleteKeyValue(key.KeyName());
508 89 : }
509 : };
510 :
511 : template <class StorageId, class StorageData>
512 5 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::Init(PersistentStorageDelegate & storage)
513 : {
514 : // Verify the initialized parameter respects the maximum allowed values for entry capacity
515 5 : VerifyOrReturnError(mMaxPerFabric <= Serializer::kMaxPerFabric() && mMaxPerEndpoint <= Serializer::kMaxPerEndpoint(),
516 : CHIP_ERROR_INVALID_INTEGER_VALUE);
517 3 : this->mStorage = &storage;
518 3 : return CHIP_NO_ERROR;
519 : }
520 :
521 : template <class StorageId, class StorageData>
522 128 : void FabricTableImpl<StorageId, StorageData>::Finish()
523 128 : {}
524 :
525 : template <class StorageId, class StorageData>
526 33 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetFabricEntryCount(FabricIndex fabric_index, uint8_t & entry_count)
527 : {
528 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
529 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
530 :
531 33 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
532 :
533 33 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
534 33 : CHIP_ERROR err = fabric.Load(mStorage);
535 33 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
536 :
537 33 : entry_count = (CHIP_ERROR_NOT_FOUND == err) ? 0 : fabric.entry_count;
538 :
539 33 : return CHIP_NO_ERROR;
540 33 : }
541 :
542 : template <class StorageId, class StorageData>
543 84 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetEndpointEntryCount(uint8_t & entry_count)
544 : {
545 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
546 :
547 84 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
548 :
549 84 : TypedEndpointEntryCount endpoint_entry_count(mEndpointId);
550 :
551 84 : ReturnErrorOnFailure(endpoint_entry_count.Load(mStorage));
552 84 : entry_count = endpoint_entry_count.count_value;
553 :
554 84 : return CHIP_NO_ERROR;
555 84 : }
556 :
557 : template <class StorageId, class StorageData>
558 0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetEndpointEntryCount(const uint8_t & entry_count)
559 : {
560 : using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
561 0 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
562 :
563 0 : TypedEndpointEntryCount endpoint_entry_count(mEndpointId, entry_count);
564 0 : return endpoint_entry_count.Save(mStorage);
565 0 : }
566 :
567 : template <class StorageId, class StorageData>
568 58 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity)
569 : {
570 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
571 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
572 :
573 58 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
574 :
575 58 : uint8_t endpoint_entry_count = 0;
576 58 : ReturnErrorOnFailure(GetEndpointEntryCount(endpoint_entry_count));
577 :
578 : // If the global entry count is higher than the maximal Global entry capacity, this returns a capacity of 0 until enough entries
579 : // have been deleted to bring the global number of entries under the global maximum.
580 58 : if (endpoint_entry_count > mMaxPerEndpoint)
581 : {
582 6 : capacity = 0;
583 6 : return CHIP_NO_ERROR;
584 : }
585 52 : uint8_t remaining_capacity_global = static_cast<uint8_t>(mMaxPerEndpoint - endpoint_entry_count);
586 52 : uint8_t remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric);
587 :
588 52 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
589 :
590 : // Load fabric data (defaults to zero)TypedFabricEntryData
591 52 : CHIP_ERROR err = fabric.Load(mStorage);
592 52 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
593 :
594 52 : if (err == CHIP_NO_ERROR)
595 : {
596 27 : remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric - fabric.entry_count);
597 : }
598 :
599 52 : capacity = std::min(remaining_capacity_fabric, remaining_capacity_global);
600 :
601 52 : return CHIP_NO_ERROR;
602 52 : }
603 :
604 : template <class StorageId, class StorageData>
605 : template <size_t kEntryMaxBytes>
606 103 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetTableEntry(FabricIndex fabric_index, const StorageId & id,
607 : const StorageData & data,
608 : PersistentStore<kEntryMaxBytes> & writeBuffer)
609 : {
610 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
611 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
612 :
613 103 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
614 :
615 103 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
616 :
617 : // Load fabric data (defaults to zero)
618 103 : CHIP_ERROR err = fabric.Load(mStorage);
619 103 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
620 :
621 103 : err = fabric.SaveEntry(*mStorage, id, data, writeBuffer);
622 103 : return err;
623 103 : }
624 :
625 : template <class StorageId, class StorageData>
626 : template <size_t kEntryMaxBytes>
627 134 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetTableEntry(FabricIndex fabric_index, StorageId & entry_id,
628 : StorageData & data, PersistentStore<kEntryMaxBytes> & buffer)
629 : {
630 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
631 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
632 134 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
633 :
634 134 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
635 134 : TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry_id, data);
636 :
637 134 : ReturnErrorOnFailure(fabric.Load(mStorage));
638 75 : VerifyOrReturnError(fabric.Find(entry_id, table_entry.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
639 :
640 71 : CHIP_ERROR err = buffer.Load(table_entry, mStorage);
641 :
642 : // If entry.Load returns "buffer too small", the entry in memory is too big to be retrieved (this could happen if the
643 : // kEntryMaxBytes was reduced by OTA) and therefore must be deleted as is is no longer considered accessible.
644 71 : if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
645 : {
646 0 : ReturnErrorOnFailure(this->RemoveTableEntry(fabric_index, entry_id));
647 : }
648 71 : ReturnErrorOnFailure(err);
649 :
650 71 : return CHIP_NO_ERROR;
651 134 : }
652 :
653 : template <class StorageId, class StorageData>
654 0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id,
655 : EntryIndex & idx)
656 : {
657 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
658 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
659 0 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
660 :
661 0 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
662 :
663 0 : ReturnErrorOnFailure(fabric.Load(mStorage));
664 0 : VerifyOrReturnError(fabric.Find(entry_id, idx) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
665 :
666 0 : return CHIP_NO_ERROR;
667 0 : }
668 :
669 : template <class StorageId, class StorageData>
670 15 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id)
671 : {
672 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
673 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
674 :
675 15 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
676 15 : TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
677 :
678 15 : ReturnErrorOnFailure(fabric.Load(mStorage));
679 :
680 15 : return fabric.RemoveEntry(*mStorage, entry_id);
681 15 : }
682 :
683 : /// @brief This function is meant to provide a way to empty the entry table without knowing any specific entry Id. Outside of this
684 : /// specific use case, RemoveTableEntry should be used.
685 : /// @param fabric_index Fabric in which the entry belongs
686 : /// @param entry_idx Position in the Table
687 : /// @return CHIP_NO_ERROR if removal was successful, errors if failed to remove the entry or to update the fabric after removing it
688 : template <class StorageId, class StorageData>
689 72 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index,
690 : EntryIndex entry_idx)
691 : {
692 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
693 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
694 :
695 72 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
696 :
697 72 : TypedFabricEntryData fabric(endpoint, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
698 :
699 72 : ReturnErrorOnFailure(fabric.Load(mStorage));
700 72 : StorageId entryId;
701 72 : CHIP_ERROR err = fabric.FindByIndex(*mStorage, entry_idx, entryId);
702 72 : VerifyOrReturnValue(CHIP_ERROR_NOT_FOUND != err, CHIP_NO_ERROR);
703 59 : ReturnErrorOnFailure(err);
704 :
705 59 : return fabric.RemoveEntry(*mStorage, entryId);
706 72 : }
707 :
708 : template <class StorageId, class StorageData>
709 17 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveFabric(FabricIndex fabric_index)
710 : {
711 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
712 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
713 :
714 17 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
715 :
716 153 : for (uint16_t index = 0; index < emberAfEndpointCount(); index++)
717 : {
718 68 : if (!emberAfEndpointIndexIsEnabled(index))
719 : {
720 60 : continue;
721 : }
722 68 : EndpointId endpoint = emberAfEndpointFromIndex(index);
723 68 : TypedFabricEntryData fabric(endpoint, fabric_index);
724 68 : EntryIndex idx = 0;
725 68 : CHIP_ERROR err = fabric.Load(mStorage);
726 68 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
727 68 : if (CHIP_ERROR_NOT_FOUND == err)
728 : {
729 60 : continue;
730 : }
731 :
732 64 : while (idx < mMaxPerFabric)
733 : {
734 56 : err = RemoveTableEntryAtPosition(endpoint, fabric_index, idx);
735 56 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
736 56 : idx++;
737 : }
738 :
739 : // Remove fabric entries on endpoint
740 8 : ReturnErrorOnFailure(fabric.Delete(mStorage));
741 : }
742 :
743 17 : return CHIP_NO_ERROR;
744 : }
745 :
746 : template <class StorageId, class StorageData>
747 1 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveEndpoint()
748 : {
749 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
750 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
751 :
752 1 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
753 :
754 507 : for (FabricIndex fabric_index = kMinValidFabricIndex; fabric_index < kMaxValidFabricIndex; fabric_index++)
755 : {
756 253 : TypedFabricEntryData fabric(mEndpointId, fabric_index);
757 253 : CHIP_ERROR err = fabric.Load(mStorage);
758 253 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
759 253 : if (CHIP_ERROR_NOT_FOUND == err)
760 : {
761 251 : continue;
762 : }
763 :
764 2 : EntryIndex idx = 0;
765 16 : while (idx < mMaxPerFabric)
766 : {
767 14 : err = RemoveTableEntryAtPosition(mEndpointId, fabric_index, idx);
768 14 : VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
769 14 : idx++;
770 : };
771 :
772 : // Remove fabric entries on endpoint
773 2 : ReturnErrorOnFailure(fabric.Delete(mStorage));
774 : }
775 :
776 1 : return CHIP_NO_ERROR;
777 : }
778 :
779 : template <class StorageId, class StorageData>
780 51 : void FabricTableImpl<StorageId, StorageData>::SetEndpoint(EndpointId endpoint)
781 : {
782 51 : mEndpointId = endpoint;
783 51 : }
784 :
785 : template <class StorageId, class StorageData>
786 49 : void FabricTableImpl<StorageId, StorageData>::SetTableSize(uint16_t endpointTableSize, uint16_t maxPerFabric)
787 : {
788 : // Verify the endpoint passed size respects the limits of the device configuration
789 49 : VerifyOrDie(Serializer::kMaxPerFabric() > 0);
790 49 : VerifyOrDie(Serializer::kMaxPerEndpoint() > 0);
791 49 : mMaxPerEndpoint = std::min(Serializer::kMaxPerEndpoint(), endpointTableSize);
792 49 : mMaxPerFabric = std::min(endpointTableSize, std::min(Serializer::kMaxPerFabric(), maxPerFabric));
793 49 : }
794 :
795 : template <class StorageId, class StorageData>
796 : template <size_t kEntryMaxBytes, class UnaryFunc>
797 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::IterateEntries(FabricIndex fabric, PersistentStore<kEntryMaxBytes> & store,
798 : UnaryFunc iterateFn)
799 : {
800 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
801 :
802 : EntryIteratorImpl<kEntryMaxBytes> iterator(*this, fabric, mEndpointId, mMaxPerFabric, mMaxPerEndpoint, store);
803 : return iterateFn(iterator);
804 : }
805 :
806 : template <class StorageId, class StorageData>
807 : template <size_t kEntryMaxBytes>
808 : FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::EntryIteratorImpl(
809 : FabricTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, uint16_t maxPerFabric, uint16_t maxPerEndpoint,
810 : PersistentStore<kEntryMaxBytes> & store) :
811 : mProvider(provider),
812 : mFabric(fabricIdx), mEndpoint(endpoint), mMaxPerFabric(maxPerFabric), mMaxPerEndpoint(maxPerEndpoint), mStore(store)
813 : {
814 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
815 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
816 :
817 : TypedFabricEntryData fabric(mEndpoint, fabricIdx, mMaxPerFabric, mMaxPerEndpoint);
818 : ReturnOnFailure(fabric.Load(provider.mStorage));
819 : mTotalEntries = fabric.entry_count;
820 : mEntryIndex = 0;
821 : }
822 :
823 : template <class StorageId, class StorageData>
824 : template <size_t kEntryMaxBytes>
825 : size_t FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Count()
826 : {
827 : return mTotalEntries;
828 : }
829 :
830 : template <class StorageId, class StorageData>
831 : template <size_t kEntryMaxBytes>
832 : bool FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Next(TableEntry & output)
833 : {
834 : using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
835 : Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
836 :
837 : TypedFabricEntryData fabric(mEndpoint, mFabric);
838 :
839 : VerifyOrReturnError(fabric.Load(mProvider.mStorage) == CHIP_NO_ERROR, false);
840 :
841 : // looks for next available entry
842 : while (mEntryIndex < mMaxPerFabric)
843 : {
844 : if (fabric.entry_map[mEntryIndex].IsValid())
845 : {
846 : TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output.mStorageId, output.mStorageData, mEntryIndex);
847 : VerifyOrReturnError(mStore.Load(entry, mProvider.mStorage) == CHIP_NO_ERROR, false);
848 : mEntryIndex++;
849 :
850 : return true;
851 : }
852 :
853 : mEntryIndex++;
854 : }
855 :
856 : return false;
857 : }
858 :
859 : template <class StorageId, class StorageData>
860 : template <size_t kEntryMaxBytes>
861 : void FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Release()
862 : {}
863 : } // namespace Storage
864 : } // namespace app
865 : } // namespace chip
|