Matter SDK Coverage Report
Current view: top level - app/util - binding-table.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 95.1 % 162 154
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 12 12

            Line data    Source code
       1              : /**
       2              :  *
       3              :  *    Copyright (c) 2020 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              : /**
      19              :  * @file Basic implementation of a binding table.
      20              :  */
      21              : 
      22              : #include <app/util/binding-table.h>
      23              : #include <app/util/config.h>
      24              : 
      25              : namespace chip {
      26              : 
      27              : BindingTable BindingTable::sInstance;
      28              : 
      29         1327 : BindingTable::BindingTable()
      30              : {
      31           67 :     memset(mNextIndex, kNextNullIndex, sizeof(mNextIndex));
      32           67 : }
      33              : 
      34           89 : CHIP_ERROR BindingTable::Add(const EmberBindingTableEntry & entry)
      35              : {
      36           89 :     if (entry.type == MATTER_UNUSED_BINDING)
      37              :     {
      38            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
      39              :     }
      40           88 :     uint8_t newIndex = GetNextAvaiableIndex();
      41           88 :     if (newIndex >= MATTER_BINDING_TABLE_SIZE)
      42              :     {
      43            1 :         return CHIP_ERROR_NO_MEMORY;
      44              :     }
      45           87 :     mBindingTable[newIndex] = entry;
      46           87 :     CHIP_ERROR error        = SaveEntryToStorage(newIndex, kNextNullIndex);
      47           87 :     if (error == CHIP_NO_ERROR)
      48              :     {
      49           86 :         if (mTail == kNextNullIndex)
      50              :         {
      51            6 :             error = SaveListInfo(newIndex);
      52              :         }
      53              :         else
      54              :         {
      55           80 :             error = SaveEntryToStorage(mTail, newIndex);
      56              :         }
      57           86 :         if (error != CHIP_NO_ERROR)
      58              :         {
      59            0 :             mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(newIndex).KeyName());
      60              :         }
      61              :     }
      62           87 :     if (error != CHIP_NO_ERROR)
      63              :     {
      64              :         // Roll back
      65            1 :         mBindingTable[newIndex].type = MATTER_UNUSED_BINDING;
      66            1 :         return error;
      67              :     }
      68              : 
      69           86 :     if (mTail == kNextNullIndex)
      70              :     {
      71            6 :         mTail = newIndex;
      72            6 :         mHead = newIndex;
      73              :     }
      74              :     else
      75              :     {
      76           80 :         mNextIndex[mTail]    = newIndex;
      77           80 :         mNextIndex[newIndex] = kNextNullIndex;
      78           80 :         mTail                = newIndex;
      79              :     }
      80              : 
      81           86 :     mSize++;
      82           86 :     return CHIP_NO_ERROR;
      83              : }
      84              : 
      85           56 : const EmberBindingTableEntry & BindingTable::GetAt(uint8_t index)
      86              : {
      87           56 :     return mBindingTable[index];
      88              : }
      89              : 
      90          170 : CHIP_ERROR BindingTable::SaveEntryToStorage(uint8_t index, uint8_t nextIndex)
      91              : {
      92          170 :     EmberBindingTableEntry & entry    = mBindingTable[index];
      93          170 :     uint8_t buffer[kEntryStorageSize] = { 0 };
      94          170 :     TLV::TLVWriter writer;
      95          170 :     writer.Init(buffer);
      96              :     TLV::TLVType container;
      97          170 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::TLVType::kTLVType_Structure, container));
      98          170 :     ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagFabricIndex), entry.fabricIndex));
      99          170 :     ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagLocalEndpoint), entry.local));
     100          170 :     if (entry.clusterId.has_value())
     101              :     {
     102           82 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagCluster), *entry.clusterId));
     103              :     }
     104          170 :     if (entry.type == MATTER_UNICAST_BINDING)
     105              :     {
     106          167 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagRemoteEndpoint), entry.remote));
     107          167 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagNodeId), entry.nodeId));
     108              :     }
     109              :     else
     110              :     {
     111            3 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagGroupId), entry.groupId));
     112              :     }
     113          170 :     ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagNextEntry), nextIndex));
     114          170 :     ReturnErrorOnFailure(writer.EndContainer(container));
     115          170 :     ReturnErrorOnFailure(writer.Finalize());
     116          340 :     return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(index).KeyName(), buffer,
     117          340 :                                      static_cast<uint16_t>(writer.GetLengthWritten()));
     118              : }
     119              : 
     120           30 : CHIP_ERROR BindingTable::SaveListInfo(uint8_t head)
     121              : {
     122           30 :     uint8_t buffer[kListInfoStorageSize] = { 0 };
     123           30 :     TLV::TLVWriter writer;
     124           30 :     writer.Init(buffer);
     125              :     TLV::TLVType container;
     126           30 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::TLVType::kTLVType_Structure, container));
     127           30 :     ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagStorageVersion), kStorageVersion));
     128           30 :     ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagHead), head));
     129           30 :     ReturnErrorOnFailure(writer.EndContainer(container));
     130           30 :     ReturnErrorOnFailure(writer.Finalize());
     131           60 :     return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::BindingTable().KeyName(), buffer,
     132           60 :                                      static_cast<uint16_t>(writer.GetLengthWritten()));
     133              : }
     134              : 
     135            6 : CHIP_ERROR BindingTable::LoadFromStorage()
     136              : {
     137            6 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     138            6 :     uint8_t buffer[kListInfoStorageSize] = { 0 };
     139            6 :     uint16_t size                        = sizeof(buffer);
     140              :     CHIP_ERROR error;
     141              : 
     142            6 :     ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::BindingTable().KeyName(), buffer, size));
     143            6 :     TLV::TLVReader reader;
     144            6 :     reader.Init(buffer, size);
     145              : 
     146            6 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     147              : 
     148              :     TLV::TLVType container;
     149            6 :     ReturnErrorOnFailure(reader.EnterContainer(container));
     150              : 
     151            6 :     ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagStorageVersion)));
     152              :     uint32_t version;
     153            6 :     ReturnErrorOnFailure(reader.Get(version));
     154            6 :     VerifyOrReturnError(version == kStorageVersion, CHIP_ERROR_VERSION_MISMATCH);
     155            6 :     ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagHead)));
     156              :     uint8_t index;
     157            6 :     ReturnErrorOnFailure(reader.Get(index));
     158            6 :     mHead = index;
     159           27 :     while (index != kNextNullIndex)
     160              :     {
     161              :         uint8_t nextIndex;
     162           21 :         error = LoadEntryFromStorage(index, nextIndex);
     163           21 :         if (error != CHIP_NO_ERROR)
     164              :         {
     165            0 :             mHead = kNextNullIndex;
     166            0 :             mTail = kNextNullIndex;
     167            0 :             return error;
     168              :         }
     169           21 :         mTail = index;
     170           21 :         index = nextIndex;
     171           21 :         mSize++;
     172              :     }
     173            6 :     error = reader.ExitContainer(container);
     174            6 :     if (error != CHIP_NO_ERROR)
     175              :     {
     176            0 :         mHead = kNextNullIndex;
     177            0 :         mTail = kNextNullIndex;
     178              :     }
     179            6 :     return error;
     180              : }
     181              : 
     182           21 : CHIP_ERROR BindingTable::LoadEntryFromStorage(uint8_t index, uint8_t & nextIndex)
     183              : {
     184           21 :     uint8_t buffer[kEntryStorageSize] = { 0 };
     185           21 :     uint16_t size                     = sizeof(buffer);
     186           21 :     EmberBindingTableEntry entry;
     187              : 
     188           21 :     ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(index).KeyName(), buffer, size));
     189           21 :     TLV::TLVReader reader;
     190           21 :     reader.Init(buffer, size);
     191              : 
     192           21 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     193              : 
     194              :     TLV::TLVType container;
     195           21 :     ReturnErrorOnFailure(reader.EnterContainer(container));
     196           21 :     ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagFabricIndex)));
     197           21 :     ReturnErrorOnFailure(reader.Get(entry.fabricIndex));
     198           21 :     ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagLocalEndpoint)));
     199           21 :     ReturnErrorOnFailure(reader.Get(entry.local));
     200           21 :     ReturnErrorOnFailure(reader.Next());
     201           21 :     if (reader.GetTag() == TLV::ContextTag(kTagCluster))
     202              :     {
     203              :         ClusterId clusterId;
     204           12 :         ReturnErrorOnFailure(reader.Get(clusterId));
     205           12 :         entry.clusterId.emplace(clusterId);
     206           12 :         ReturnErrorOnFailure(reader.Next());
     207              :     }
     208              :     else
     209              :     {
     210            9 :         entry.clusterId = std::nullopt;
     211              :     }
     212           21 :     if (reader.GetTag() == TLV::ContextTag(kTagRemoteEndpoint))
     213              :     {
     214           10 :         entry.type = MATTER_UNICAST_BINDING;
     215           10 :         ReturnErrorOnFailure(reader.Get(entry.remote));
     216           10 :         ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagNodeId)));
     217           10 :         ReturnErrorOnFailure(reader.Get(entry.nodeId));
     218              :     }
     219              :     else
     220              :     {
     221           11 :         entry.type = MATTER_MULTICAST_BINDING;
     222           11 :         VerifyOrReturnError(reader.GetTag() == TLV::ContextTag(kTagGroupId), CHIP_ERROR_INVALID_TLV_TAG);
     223           11 :         ReturnErrorOnFailure(reader.Get(entry.groupId));
     224              :     }
     225           21 :     ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagNextEntry)));
     226           21 :     ReturnErrorOnFailure(reader.Get(nextIndex));
     227           21 :     ReturnErrorOnFailure(reader.ExitContainer(container));
     228           21 :     mBindingTable[index] = entry;
     229           21 :     mNextIndex[index]    = nextIndex;
     230           21 :     return CHIP_NO_ERROR;
     231              : }
     232              : 
     233           27 : CHIP_ERROR BindingTable::RemoveAt(Iterator & iter)
     234              : {
     235              :     CHIP_ERROR error;
     236           27 :     if (iter.mTable != this || iter.mIndex == kNextNullIndex)
     237              :     {
     238            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     239              :     }
     240           27 :     if (iter.mIndex == mTail)
     241              :     {
     242            2 :         mTail = iter.mPrevIndex;
     243              :     }
     244           27 :     uint8_t next = mNextIndex[iter.mIndex];
     245           27 :     if (iter.mIndex != mHead)
     246              :     {
     247            3 :         error = SaveEntryToStorage(iter.mPrevIndex, next);
     248            3 :         if (error == CHIP_NO_ERROR)
     249              :         {
     250            2 :             mNextIndex[iter.mPrevIndex] = next;
     251              :         }
     252              :     }
     253              :     else
     254              :     {
     255           24 :         error = SaveListInfo(next);
     256           24 :         if (error == CHIP_NO_ERROR)
     257              :         {
     258           23 :             mHead = next;
     259              :         }
     260              :     }
     261           27 :     if (error == CHIP_NO_ERROR)
     262              :     {
     263              :         // The remove is considered "submitted" once the change on prev node takes effect
     264           25 :         if (mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(iter.mIndex).KeyName()) != CHIP_NO_ERROR)
     265              :         {
     266            0 :             ChipLogError(AppServer, "Failed to remove binding table entry %u from storage", iter.mIndex);
     267              :         }
     268           25 :         mBindingTable[iter.mIndex].type = MATTER_UNUSED_BINDING;
     269           25 :         mNextIndex[iter.mIndex]         = kNextNullIndex;
     270           25 :         mSize--;
     271              :     }
     272           27 :     iter.mIndex = next;
     273           27 :     return error;
     274              : }
     275              : 
     276           88 : BindingTable::Iterator BindingTable::begin()
     277              : {
     278              :     Iterator iter;
     279           88 :     iter.mTable     = this;
     280           88 :     iter.mPrevIndex = kNextNullIndex;
     281           88 :     iter.mIndex     = mHead;
     282           88 :     return iter;
     283              : }
     284              : 
     285          601 : BindingTable::Iterator BindingTable::end()
     286              : {
     287              :     Iterator iter;
     288          601 :     iter.mTable = this;
     289          601 :     iter.mIndex = kNextNullIndex;
     290          601 :     return iter;
     291              : }
     292              : 
     293           88 : uint8_t BindingTable::GetNextAvaiableIndex()
     294              : {
     295          879 :     for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++)
     296              :     {
     297          878 :         if (mBindingTable[i].type == MATTER_UNUSED_BINDING)
     298              :         {
     299           87 :             return i;
     300              :         }
     301              :     }
     302            1 :     return MATTER_BINDING_TABLE_SIZE;
     303              : }
     304              : 
     305          558 : BindingTable::Iterator BindingTable::Iterator::operator++()
     306              : {
     307          558 :     if (mIndex != kNextNullIndex)
     308              :     {
     309          558 :         mPrevIndex = mIndex;
     310          558 :         mIndex     = mTable->mNextIndex[mIndex];
     311              :     }
     312          558 :     return *this;
     313              : }
     314              : 
     315              : } // namespace chip
        

Generated by: LCOV version 2.0-1