blob: 56854654f03a6187dc9b2d255ce204374f11b106 [file] [log] [blame]
/*
*
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2013-2017 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
* This file provides a more logical container representation of a WDM dictionary
* that permits both indexing against the dictionary key as well as a more logical primary
* key
*/
#ifndef _WEAVE_DATA_MANAGEMENT_WDM_DICTIONARY_H
#define _WEAVE_DATA_MANAGEMENT_WDM_DICTIONARY_H
#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveMessageLayer.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Profiles/data-management/MessageDef.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/sequenced_index.hpp>
using namespace boost::multi_index;
namespace nl {
namespace Weave {
namespace Profiles {
namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
//
// This templated class provides a more user-friendly type when interacting with WDL maps.
// WDL maps are constrained in their keys to having 16-bit numerical keys. As such, these maps
// cannot be thought of as traditional maps that have the key being the logical/primary key used to
// index the data in the collection. Rather, the key is merely a unique number assigned to each item,
// lacking any semantic or logical meaning. The actual logical key is embedded in the item itself, and
// be any WDL type. In this sense, WDL maps can be thought of as spare arrays.
//
// Applications will typically want to interact with the collection locally on devices more naturally as a keyed collection,
// with the data index using the logical key. This requires them to maintain a two-map solution:
// one to store the original data in the trait, and another to translate from the logical key to the map key.
//
// This templated class utilizes boost's multi_index_container, which permits indexing the data against multiple keys
// while still retaining efficient storage and look-up of the data. The class wraps the provided boost type and
// provides helper methods to interact with each key's sub-table.
//
// Specializing the template requires passing in both the logical key type as well as the value type as well.
//
// Requirements on the types include:
// Logical Key Type: Has both '<' and '==' operators implemented.
// Value Type: Has the '==' operator implemented.
//
// The class also provides methods to compare against another instance of the collection to easily figure out the
// set of items added, removed or modified against the logical key. This allows for comparisons
//
template <class KeyT, class ValueT>
class WdmDictionary {
public:
//
// This is the Item that is used in the collection, housing both the 16-bit dictionary
// key as well as the logical KeyT typed key.
// It also contains the value as well.
//
struct Item {
Item(uint16_t dictKey) { _dict_key = dictKey; _logical_key = 0; }
Item() { _dict_key = 0; _logical_key = 0; }
bool operator<(const Item& i1) const {
return (_logical_key < i1._logical_key);
}
bool operator ==(const Item& i) const {
return (_logical_key == i._logical_key && _dict_key == i._dict_key && _data == i._data);
}
ValueT _data;
uint16_t _dict_key;
KeyT _logical_key;
};
struct map_key{};
struct logical_key{};
typedef multi_index_container<
Item,
indexed_by<
// Both indexes are of type 'ordered_unique' since it's expected there will and should
// not be collisions in either of the key-spaces.
ordered_unique<tag<map_key>, member<Item, uint16_t, &Item::_dict_key> >,
ordered_unique<tag<logical_key>, member<Item, KeyT, &Item::_logical_key> >
>
> Container;
typedef typename Container::template index<map_key>::type DictKeyTableType;
typedef typename Container::template index<map_key>::type::iterator DictKeyTableTypeIterator;
typedef typename Container::template index<logical_key>::type LogicalKeyTableType;
typedef typename Container::template index<logical_key>::type::iterator LogicalKeyTableTypeIterator;
DictKeyTableType& GetDictKeyTable() { return _container.template get<map_key>(); }
LogicalKeyTableType& GetLogicalKeyTable() { return _container.template get<logical_key>(); }
//
// This is a convenience method that automatically creates an element in the collection with a particular
// dictionary key, or accesses an existing element that already exists, and allows the caller to mutate a member
// through a provided closure.
//
void ModifyItem(uint16_t dictKey, std::function<void(Item&)> func);
//
// The following methods compare against a provided container and enumerates items that have been added, deleted or modified.
// This is specifically done against the logical key table and *not* against the dictionary keys. This is necessary since certain
// implementations of dictionaries (like the Nest cloud service) do not provide key stability for the dictionary keys, occasionally
// re-key'ing them while retaining the logical key + value content.
//
void ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
void ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
void ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
bool IsEqual(WdmDictionary &dictionary);
private:
Container _container;
};
template <typename KeyT, typename ValueT>
bool WdmDictionary<KeyT, ValueT>::IsEqual(WdmDictionary &dictionary)
{
return (dictionary._container.size() == _container.size())
&& std::equal(_container.begin(), _container.end(), dictionary._container.begin());
}
template <typename KeyT, typename ValueT>
void WdmDictionary<KeyT, ValueT>::ModifyItem(uint16_t dictKey, std::function<void(Item &)> func)
{
Item item = Item(dictKey);
DictKeyTableType& mapkey_tbl = _container.template get<map_key>();
mapkey_tbl.modify(mapkey_tbl.insert(item).first, [&func](Item &d) {
func(d);
});
}
template <typename KeyT, typename ValueT>
void WdmDictionary<KeyT, ValueT>::ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore)
{
Container addedItems;
LogicalKeyTableType &addedItemsLogicalTbl = addedItems.template get<logical_key>();
std::set_difference(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(),
GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
std::inserter(addedItemsLogicalTbl, addedItemsLogicalTbl.begin()));
for (auto it = addedItemsLogicalTbl.begin(); it != addedItemsLogicalTbl.end(); it++) {
func(it);
if (updateStore) {
GetLogicalKeyTable().insert(*it);
}
}
}
template <typename KeyT, typename ValueT>
void WdmDictionary<KeyT, ValueT>::ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore)
{
Container removedItems;
LogicalKeyTableType &removedItemsLogicalTbl = removedItems.template get<logical_key>();
std::set_difference(GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(),
std::inserter(removedItemsLogicalTbl, removedItemsLogicalTbl.begin()));
for (auto it = removedItemsLogicalTbl.begin(); it != removedItemsLogicalTbl.end(); it++) {
func(it);
if (updateStore) {
GetLogicalKeyTable().erase(it->_logical_key);
}
}
}
template <typename KeyT, typename ValueT>
void WdmDictionary<KeyT, ValueT>::ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func, bool updateStore)
{
Container modifiedItems;
LogicalKeyTableType &modifiedItemsTbl = modifiedItems.template get<logical_key>();
std::set_intersection(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(),
GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
std::inserter(modifiedItemsTbl, modifiedItemsTbl.begin()));
for (auto it = modifiedItemsTbl.begin(); it != modifiedItemsTbl.end(); it++) {
auto it1 = GetLogicalKeyTable().find(it->_logical_key);
auto it2 = stagedContainer.GetLogicalKeyTable().find(it->_logical_key);
if (!(it1->_data == it2->_data)) {
func(it1, it2);
}
if (updateStore) {
GetLogicalKeyTable().modify(it1, [it2](auto &d) {
d = *it2;
});
}
}
}
}; // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
}; // namespace Profiles
}; // namespace Weave
}; // namespace nl
#endif