blob: 240891b7c1d33ec6ae7cc9419415fd9080fad8ba [file] [log] [blame]
/*
*
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Implements TraitPathStore: a data structure to store lists or sets of TraitPaths.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <Weave/Profiles/data-management/TraitPathStore.h>
#include <Weave/Support/WeaveFaultInjection.h>
using namespace ::nl::Weave;
using namespace ::nl::Weave::TLV;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::Common;
using namespace ::nl::Weave::Profiles::DataManagement;
using namespace ::nl::Weave::Profiles::DataManagement_Current;
/**
* Empty constructor
*/
TraitPathStore::TraitPathStore()
: mStore(NULL), mStoreSize(0), mNumItems(0)
{
}
/**
* Inits the TraitPathStore
*
* @param[in] aRecordArray Pointer to an array of Records that will be used
* to store paths and flags.
* @param[in] aArrayLength Length of the storage array in number of items.
*/
void TraitPathStore::Init(TraitPathStore::Record *aRecordArray, size_t aArrayLength)
{
mStore = aRecordArray;
mStoreSize = aArrayLength;
Clear();
}
/**
* @fn bool TraitPathStore::IsEmpty()
* @return Returns true if the store is empty; false otherwise.
*/
/**
* @fn bool TraitPathStore::IsFull()
* @return Returns true if the store is full; false otherwise.
*/
/**
* @fn size_t TraitPathStore::GetNumItems()
* @return Returns the number of TraitPaths in the store.
*/
/**
* @fn size_t TraitPathStore::GetPathStoreSize()
* @return Returns the capacity of the store.
*/
/**
* Adds a TraitPath to the store with a given set of flags
*
* @param[in] aItem The TraitPath to be stored
* @param[in] aFlags The flags to be set to true for the item being added
*
* @retval WEAVE_NO_ERROR in case of success.
* @retval WEAVE_ERROR_WDM_PATH_STORE_FULL if the store is full.
* @retval WEAVE_ERROR_INVALID_ARGUMENT if aFlags contains reserved flags
*/
WEAVE_ERROR TraitPathStore::AddItem(const TraitPath &aItem, Flags aFlags)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
size_t i = 0;
WEAVE_FAULT_INJECT(FaultInjection::kFault_WDM_PathStoreFull, return WEAVE_ERROR_WDM_PATH_STORE_FULL);
VerifyOrExit((aFlags & static_cast<Flags>(kFlag_ReservedFlags)) == 0x0,
err = WEAVE_ERROR_INVALID_ARGUMENT);
i = FindFirstAvailableItem();
VerifyOrExit(i < mStoreSize, err = WEAVE_ERROR_WDM_PATH_STORE_FULL);
SetItem(i, aItem, aFlags);
mNumItems++;
exit:
return err;
}
/**
* Adds a TraitPath to the store.
*
* @param[in] aItem The TraitPath to be stored
*
* @retval WEAVE_NO_ERROR in case of success.
* @retval WEAVE_ERROR_WDM_PATH_STORE_FULL if the store is full.
*/
WEAVE_ERROR TraitPathStore::AddItem(const TraitPath &aItem)
{
return AddItem(aItem, kFlag_None);
}
WEAVE_ERROR TraitPathStore::AddItemDedup(const TraitPath &aItem, const TraitSchemaEngine * const aSchemaEngine)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (Includes(aItem, aSchemaEngine))
{
WeaveLogDetail(DataManagement, "Path already present");
ExitNow();
}
// Remove any paths of which aItem is an ancestor
for (size_t i = GetFirstValidItem(aItem.mTraitDataHandle);
i < GetPathStoreSize();
i = GetNextValidItem(i, aItem.mTraitDataHandle))
{
if (aSchemaEngine->IsParent(mStore[i].mTraitPath.mPropertyPathHandle, aItem.mPropertyPathHandle))
{
WeaveLogDetail(DataManagement, "Removing item %u t%u p%u while adding p%u", i,
mStore[i].mTraitPath.mTraitDataHandle,
mStore[i].mTraitPath.mPropertyPathHandle,
aItem.mPropertyPathHandle);
RemoveItemAt(i);
}
}
err = AddItem(aItem, kFlag_None);
exit:
return err;
}
/**
* Adds an TraitPath to the store, inserting it at a given index.
* Assumes the store has no gaps.
*
* @param[in] aIndex The index at which to insert the TraitPath; the insertion
* has to keep the store compacted.
* @param[in] aFlags The flags to be set to true for the item being added.
*
* @retval WEAVE_ERROR_INCORRECT_STATE if the store has gaps.
* @retval WEAVE_ERROR_INVALID_ARGUMENT if adding the TraitPath at aIndex would make
* the store not compact.
* @retval WEAVE_ERROR_WDM_PATH_STORE_FULL if the store is full.
* @retval WEAVE_NO_ERROR in case of success.
*/
WEAVE_ERROR TraitPathStore::InsertItemAt(size_t aIndex, const TraitPath &aItem, Flags aFlags)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
size_t numItemsToMove;
WEAVE_FAULT_INJECT(FaultInjection::kFault_WDM_PathStoreFull, return WEAVE_ERROR_WDM_PATH_STORE_FULL);
VerifyOrExit(false == IsFull(), err = WEAVE_ERROR_WDM_PATH_STORE_FULL);
VerifyOrExit(FindFirstAvailableItem() == mNumItems, err = WEAVE_ERROR_INCORRECT_STATE);
VerifyOrExit(aIndex <= mNumItems, err = WEAVE_ERROR_INVALID_ARGUMENT);
numItemsToMove = mNumItems - aIndex;
if (numItemsToMove > 0)
{
memmove(&mStore[aIndex+1], &mStore[aIndex],
numItemsToMove * sizeof(mStore[0]));
SetFlags(aIndex, kFlag_InUse, false);
}
SetItem(aIndex, aItem, aFlags);
mNumItems++;
exit:
return err;
}
/**
* Remove all TraitPaths that refer to a given TraitDataHandle
*
* @param[in] aDataHandle The TraitDataHandle
*/
void TraitPathStore::RemoveTrait(TraitDataHandle aDataHandle)
{
for (size_t i = GetFirstValidItem(aDataHandle);
i < mStoreSize;
i = GetNextValidItem(i, aDataHandle))
{
RemoveItemAt(i);
}
}
/**
* @param[in] aDataHandle The TraitDataHandle to look for.
* @return Returns true if the store contains one or more paths referring
* to the given TraitDataHandle
*/
bool TraitPathStore::IsTraitPresent(TraitDataHandle aDataHandle) const
{
size_t i = GetFirstValidItem(aDataHandle);
return i < mStoreSize;
}
/**
* Mark all TraitPaths as failed.
*/
void TraitPathStore::SetFailed()
{
for (size_t i = GetFirstValidItem();
i < mStoreSize;
i = GetNextValidItem(i))
{
SetFailed(i);
}
}
/**
* Mark all TraitPaths referring to the given TraitDataHandle as failed.
*
* @param aDataHandle The TraitDataHandle to look for.
*/
void TraitPathStore::SetFailedTrait(TraitDataHandle aDataHandle)
{
for (size_t i = GetFirstValidItem(aDataHandle);
i < mStoreSize;
i = GetNextValidItem(i, aDataHandle))
{
SetFailed(i);
}
}
void TraitPathStore::RemoveItem(const TraitPath &aItem)
{
for (size_t i = GetFirstValidItem(aItem.mTraitDataHandle);
i < GetPathStoreSize();
i = GetNextValidItem(i, aItem.mTraitDataHandle))
{
if (mStore[i].mTraitPath.mPropertyPathHandle == aItem.mPropertyPathHandle)
{
RemoveItemAt(i);
WeaveLogDetail(DataManagement, "Removing item %u t%u p%u", i,
mStore[i].mTraitPath.mTraitDataHandle,
mStore[i].mTraitPath.mPropertyPathHandle);
}
}
}
void TraitPathStore::RemoveItemAt(size_t aIndex)
{
VerifyOrDie(mNumItems > 0);
VerifyOrDie(IsItemInUse(aIndex));
if (IsItemInUse(aIndex))
{
ClearItem(aIndex);
mNumItems--;
}
}
/**
* Compacts the store moving all items in use down towards
* the start of the array.
* This is useful to use TraitPathStore to implement a list
* that can be edited (like the list of in-progress paths maintained
* by SubscriptionClient).
*/
void TraitPathStore::Compact()
{
size_t numItemsRemaining = mNumItems;
size_t numBytesToMove, numItemsToMove;
const size_t lastIndex = mStoreSize -1;
size_t i = 0;
while (i < mStoreSize && numItemsRemaining > 0)
{
if (IsItemInUse(i))
{
numItemsRemaining--;
i++;
continue;
}
numItemsToMove = lastIndex -i;
numBytesToMove = numItemsToMove * sizeof(mStore[0]);
memmove(&mStore[i], &mStore[i+1], numBytesToMove);
SetFlags(lastIndex, kFlag_InUse, false);
}
}
/**
* Checks if a given TraitPath is already in the store.
*
* @param[in] aItem The TraitPath to look for.
*
* @return Returns true if the store contains aItem.
*/
bool TraitPathStore::IsPresent(const TraitPath &aItem) const
{
for (size_t i = GetFirstValidItem(); i < mStoreSize; i = GetNextValidItem(i))
{
if (mStore[i].mTraitPath == aItem)
{
return true;
}
}
return false;
}
/**
* Check if any of the TraitPaths in the store intersects a given TraitPath.
* Two TraitPaths intersect each other if any of the following is true:
* - the two TraitPaths are the same;
* - one of the two TraitPaths is an ancestor of the other TraitPath.
*
* @param[in] aTraitPath The TraitPath to be checked against the store.
* @param[in] aSchemaEngine A pointer to the TraitSchemaEngine for the trait instance
* aTraitPath refers to.
*
* @return true if the store intersects the given TraitPath; false otherwise.
*/
bool TraitPathStore::Intersects(const TraitPath &aTraitPath, const TraitSchemaEngine * const aSchemaEngine) const
{
bool intersects = false;
TraitDataHandle dataHandle = aTraitPath.mTraitDataHandle;
PropertyPathHandle pathHandle = aTraitPath.mPropertyPathHandle;
for (size_t i = GetFirstValidItem(dataHandle); i < mStoreSize; i = GetNextValidItem(i, dataHandle))
{
if (pathHandle == mStore[i].mTraitPath.mPropertyPathHandle ||
aSchemaEngine->IsParent(pathHandle, mStore[i].mTraitPath.mPropertyPathHandle) ||
aSchemaEngine->IsParent(mStore[i].mTraitPath.mPropertyPathHandle, pathHandle))
{
intersects = true;
break;
}
}
return intersects;
}
/**
* Check if any of the TraitPaths in the store includes a given TraitPath.
* TraitPath A includes TraitPath B if either:
* - the two TraitPaths are the same;
* - A is an ancestor of B.
*
* @param[in] aTraitPath The TraitPath to be checked against the store.
* @param[in] aSchemaEngine A pointer to the TraitSchemaEngine for the trait instance
* aTraitPath refers to.
*
* @return true if the TraitPath is already included by the paths in the store.
*/
bool TraitPathStore::Includes(const TraitPath &aItem, const TraitSchemaEngine * const aSchemaEngine) const
{
bool found = false;
TraitDataHandle dataHandle = aItem.mTraitDataHandle;
PropertyPathHandle pathHandle = aItem.mPropertyPathHandle;
for (size_t i = GetFirstValidItem(dataHandle); i < mStoreSize; i = GetNextValidItem(i, dataHandle))
{
if (pathHandle == mStore[i].mTraitPath.mPropertyPathHandle ||
aSchemaEngine->IsParent(pathHandle, mStore[i].mTraitPath.mPropertyPathHandle))
{
found = true;
break;
}
}
return found;
}
/**
* Empties the store.
*/
void TraitPathStore::Clear()
{
mNumItems = 0;
for (size_t i = 0; i < mStoreSize; i++)
{
ClearItem(i);
}
}
/**
* @return The index of the first item of the store for which IsValidItem()
* returns true.
* If no item is valid, the function returns mStoreSize.
*/
size_t TraitPathStore::GetFirstValidItem() const
{
size_t i = 0;
if (mNumItems == 0)
{
i = mStoreSize;
}
while (i < mStoreSize && false == IsItemValid(i))
{
i++;
}
return i;
}
/**
* @param[in] aIndex An index into the store.
* @return The index of the first item of the store following i for which IsValidItem()
* returns true.
* If i is the last valid item, the function returns mStoreSize.
*/
size_t TraitPathStore::GetNextValidItem(size_t aIndex) const
{
do
{
aIndex++;
}
while (aIndex < mStoreSize && false == IsItemValid(aIndex));
return aIndex;
}
/**
* @param[in] aTDH The TraitDataHandle of the trait instance to iterate on.
*
* @return The index of the first item of the store for which IsValidItem()
* returns true and which refers to the TraitDataHandle passed in.
* If no item is valid, the function returns mStoreSize.
*/
size_t TraitPathStore::GetFirstValidItem(TraitDataHandle aTDH) const
{
size_t i = GetFirstValidItem();
while (i < mStoreSize && mStore[i].mTraitPath.mTraitDataHandle != aTDH)
{
i = GetNextValidItem(i);
}
return i;
}
/**
* @param[in] aIndex An index into the store.
* @param[in] aTDH The TraitDataHandle of the trait instance to iterate on.
*
* @return The index of the first item of the store following i for which IsValidItem()
* returns true and which refers to the TraitDataHandle passed in.
* If i is the last valid item for the trait instance, the function returns mStoreSize.
*/
size_t TraitPathStore::GetNextValidItem(size_t aIndex, TraitDataHandle aTDH) const
{
do
{
aIndex = GetNextValidItem(aIndex);
}
while (aIndex < mStoreSize && mStore[aIndex].mTraitPath.mTraitDataHandle != aTDH);
return aIndex;
}
bool TraitPathStore::AreFlagsSet(size_t aIndex, Flags aFlags) const
{
if ((aFlags & kFlag_ReservedFlags) != 0x0)
{
return false;
}
return AreFlagsSet_private(aIndex, aFlags);
}
// Private members
size_t TraitPathStore::FindFirstAvailableItem() const
{
size_t i = 0;
while (i < mStoreSize && IsItemInUse(i))
{
i++;
}
return i;
}
void TraitPathStore::SetItem(size_t aIndex, const TraitPath &aItem, Flags aFlags)
{
mStore[aIndex].mTraitPath = aItem;
mStore[aIndex].mFlags = aFlags;
SetFlags(aIndex, kFlag_InUse, true);
}
void TraitPathStore::ClearItem(size_t aIndex)
{
mStore[aIndex].mFlags = 0x0;
mStore[aIndex].mTraitPath.mPropertyPathHandle = kNullPropertyPathHandle;
mStore[aIndex].mTraitPath.mTraitDataHandle = UINT16_MAX;
}
void TraitPathStore::SetFlags(size_t aIndex, Flags aFlags, bool aValue)
{
mStore[aIndex].mFlags &= ~aFlags;
if (aValue)
{
mStore[aIndex].mFlags |= aFlags;
}
}