LCOV - code coverage report
Current view: top level - app/util - binding-table.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 153 161 95.0 %
Date: 2024-02-15 08:20:41 Functions: 12 12 100.0 %

          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        1169 : BindingTable::BindingTable()
      30             : {
      31          59 :     memset(mNextIndex, kNextNullIndex, sizeof(mNextIndex));
      32          59 : }
      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.HasValue())
     101             :     {
     102          82 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagCluster), entry.clusterId.Value()));
     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             :     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             :     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.SetValue(clusterId);
     206          12 :         ReturnErrorOnFailure(reader.Next());
     207             :     }
     208             :     else
     209             :     {
     210           9 :         entry.clusterId = NullOptional;
     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 :         ReturnErrorCodeIf(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          21 : }
     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         635 : BindingTable::Iterator BindingTable::end()
     286             : {
     287             :     Iterator iter;
     288         635 :     iter.mTable = this;
     289         635 :     iter.mIndex = kNextNullIndex;
     290         635 :     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 1.14