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