Skip to content

Commit

Permalink
samples: matter: Moved finite map to common util and improved it
Browse files Browse the repository at this point in the history
Moved finite map implementation from bridge_util to common util
to make it available for all samples.

Made the finite map more versatile, by defining the key as
a template of trivial typename.

Signed-off-by: Arkadiusz Balys <[email protected]>
  • Loading branch information
ArekBalysNordic committed Mar 22, 2024
1 parent 9296d17 commit fe6ed1d
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 94 deletions.
3 changes: 2 additions & 1 deletion samples/matter/common/src/bridge/bridge_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "binding/binding_handler.h"
#include "bridge_util.h"
#include "util/finite_map.h"
#include "bridged_device_data_provider.h"
#include "matter_bridged_device.h"

Expand Down Expand Up @@ -177,7 +178,7 @@ class BridgeManager {

static constexpr uint8_t kMaxDataProviders = CONFIG_BRIDGE_MAX_BRIDGED_DEVICES_NUMBER;

using DeviceMap = FiniteMap<BridgedDevicePair, kMaxBridgedDevices>;
using DeviceMap = FiniteMap<uint16_t, BridgedDevicePair, kMaxBridgedDevices>;

/**
* @brief Add pair of single bridged device and its data provider using optional index and endpoint id.
Expand Down
95 changes: 2 additions & 93 deletions samples/matter/common/src/bridge/util/bridge_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#include <functional>
#include <map>

namespace Nrf {
namespace Nrf
{

/*
DeviceFactory template container allows to instantiate a map which
Expand Down Expand Up @@ -55,96 +56,4 @@ template <typename T, typename DeviceType, typename... Args> class DeviceFactory
CreationMap mCreationMap;
};

/*
FiniteMap template container allows to wrap mappings between uint16_t-type key and T-type value.
The size or the container is predefined at compilation time, therefore
the maximum number of stored elements must be well known. FiniteMap owns
inserted values, meaning that once the user inserts a value it will not longer
be valid outside of the container, i.e. FiniteMap cannot return stored object by copy.
FiniteMap's API offers basic operations like:
* inserting new values under provided key (Insert)
* erasing existing item under given key (Erase) - this operation takes care about the duplicated items,
meaning that pointer under given key is released only if the same address is not present elsewhere in the map
* retrieving values stored under provided key ([] operator)
* checking if the map contains a non-null value under given key (Contains)
* retrieving a number of free slots available in the map (FreeSlots)
* iterating though stored item via publicly available mMap member
Prerequisites:
* T must have move semantics and bool()/==operators implemented
*/
template <typename T, std::size_t N> struct FiniteMap {
static constexpr uint16_t kInvalidKey{ N + 1 };
struct Item {
/* Initialize with invalid key (0 is a valid key) */
uint16_t key{ kInvalidKey };
T value;
};

bool Insert(uint16_t key, T &&value)
{
if (Contains(key)) {
/* The key with sane value already exists in the map, return prematurely. */
return false;
} else if (mElementsCount < N) {
mMap[key].key = key;
mMap[key].value = std::move(value);
mElementsCount++;
}
return true;
}

bool Erase(uint16_t key)
{
const auto &it = std::find_if(std::begin(mMap), std::end(mMap),
[key](const Item &item) { return item.key == key; });
if (it != std::end(mMap) && it->value) {
it->value = T{};
it->key = kInvalidKey;
mElementsCount--;
return true;
}
return false;
}

/* Always use Constains() before using operator[]. */
T &operator[](uint16_t key)
{
static T dummyObject;
for (auto &it : mMap) {
if (key == it.key)
return it.value;
}
return dummyObject;
}

bool Contains(uint16_t key)
{
for (auto &it : mMap) {
if (key == it.key)
return true;
}
return false;
}
std::size_t FreeSlots() { return N - mElementsCount; }

uint8_t GetDuplicatesCount(const T &value, uint16_t *key)
{
/* Find the first duplicated item and return its key,
so that the application can handle the duplicate by itself. */
*key = kInvalidKey;
uint8_t numberOfDuplicates = 0;
for (auto it = std::begin(mMap); it != std::end(mMap); ++it) {
if (it->value == value) {
*(key++) = it->key;
numberOfDuplicates++;
}
}

return numberOfDuplicates;
}

Item mMap[N];
uint16_t mElementsCount{ 0 };
};

} /* namespace Nrf */
130 changes: 130 additions & 0 deletions samples/matter/common/src/util/finite_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#pragma once

#include <cstdint>
#include <functional>
#include <map>

namespace Nrf
{
/*
FiniteMap template container allows to wrap mappings between T1-type trivial key and T2-type value.
The size or the container is predefined at compilation time, therefore
the maximum number of stored elements must be well known. FiniteMap owns
inserted values, meaning that once the user inserts a value it will not longer
be valid outside of the container, i.e. FiniteMap cannot return stored object by copy.
FiniteMap's API offers basic operations like:
* inserting new values under provided key (Insert)
* erasing existing items under a given key (Erase) - this operation does not take care of the
duplicated items, meaning that the pointer under a given key is released unconditionally
and the responsibility for the duplications is on the application side.
* retrieving values stored under provided key ([] operator)
* checking if the map contains a non-null value under given key (Contains)
* retrieving a number of free slots available in the map (FreeSlots)
* iterating though stored item via publicly available mMap member
* retrieving the first free slot (GetFirstFreeSlot)
Prerequisites:
* T1 must be trivial and the maximum numeric limit for the T1-type value is reserved and assigned as an invalid key.
* T2 must have move semantics and bool()/==operators implemented
*/
template <typename T1, typename T2, std::size_t N> struct FiniteMap {
static_assert(std::is_trivial_v<T1>);

static constexpr T1 kInvalidKey{ std::numeric_limits<T1>::max() };
static constexpr std::size_t kNoSlotsFound{ N + 1 };

struct Item {
/* Initialize with invalid key (0 is a valid key) */
T1 key{ kInvalidKey };
T2 value;
};

bool Insert(T1 key, T2 &&value)
{
if (Contains(key)) {
/* The key with sane value already exists in the map, return prematurely. */
return false;
} else if (mElementsCount < N) {
std::size_t slot = GetFirstFreeSlot();
if (slot == kNoSlotsFound) {
return false;
}
mMap[slot].key = key;
mMap[slot].value = std::move(value);
mElementsCount++;
}
return true;
}

bool Erase(T1 key)
{
const auto &it = std::find_if(std::begin(mMap), std::end(mMap),
[key](const Item &item) { return item.key == key; });
if (it != std::end(mMap)) {
it->value = T2{};
it->key = kInvalidKey;
mElementsCount--;
return true;
}
return false;
}

/* Always use Contains() before using operator[]. */
T2 &operator[](T1 key)
{
static T2 dummyObject;
for (auto &it : mMap) {
if (key == it.key)
return it.value;
}
return dummyObject;
}

bool Contains(T1 key)
{
for (auto &it : mMap) {
if (key == it.key)
return true;
}
return false;
}

std::size_t FreeSlots() { return N - mElementsCount; }

std::size_t GetFirstFreeSlot()
{
std::size_t foundIndex = 0;
for (auto &it : mMap) {
if (kInvalidKey == it.key)
return foundIndex;
foundIndex++;
}
return kNoSlotsFound;
}

uint8_t GetDuplicatesCount(const T2 &value, T1 *key)
{
/* Find the first duplicated item and return its key,
so that the application can handle the duplicate by itself. */
*key = kInvalidKey;
uint8_t numberOfDuplicates = 0;
for (auto it = std::begin(mMap); it != std::end(mMap); ++it) {
if (it->value == value) {
*(key++) = it->key;
numberOfDuplicates++;
}
}

return numberOfDuplicates;
}

Item mMap[N];
uint16_t mElementsCount{ 0 };
};

} /* namespace Nrf */

0 comments on commit fe6ed1d

Please sign in to comment.