blob: 5f2d7d55b8792d5978f8f8fd016238c6bc5c0463 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* 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.
*/
#pragma once
#include <bitset>
#include <cstddef>
#include <type_traits>
#include <lib/core/CHIPError.h>
namespace chip {
namespace Retransmit {
/**
* Handles lifetimes of payloads.
*
* E.g. for buffers, handles reference counting.
*/
template <typename PayloadType>
struct Lifetime
{
static PayloadType Acquire(PayloadType & payload);
static void Release(PayloadType & payload);
};
/**
* This class maintains a cache of data that is sufficient to retransmit.
*
* Typical use is to keep track of unacknowledged packets and resend them
* as needed.
*
* @tparam KeyType the key to identify a single message
* @tparam PayloadType the type of payload to cache for the given peer address
* @tparam N size of the available cache
*
* PayloadType MUST provide a way to reference count, as the data will
* preserved until the cache is freed:
*
* PayloadType chip::Retransmit::Acquire(PayloadType&);
* chip::Retransmit::Release(PayloadType&);
*/
template <typename KeyType, typename PayloadType, size_t N>
class Cache
{
public:
Cache() {}
Cache(const Cache &) = delete;
Cache & operator=(const Cache &) = delete;
~Cache()
{
for (size_t i = 0; i < N; i++)
{
if (mInUse.test(i))
{
Lifetime<PayloadType>::Release(mEntries[i].payload);
}
}
}
/**
* Add a payload to the cache.
*/
CHIP_ERROR Add(const KeyType & key, PayloadType & payload)
{
if (mInUse.count() >= N)
{
return CHIP_ERROR_NO_MEMORY;
}
for (size_t i = 0; i < N; i++)
{
if (!mInUse.test(i))
{
mInUse.set(i);
mEntries[i].key = key;
mEntries[i].payload = Lifetime<PayloadType>::Acquire(payload);
break;
}
}
return CHIP_NO_ERROR;
}
/**
* Remove a payload from the cache given the key.
*/
CHIP_ERROR Remove(const KeyType & key)
{
for (size_t i = 0; i < N; i++)
{
if (mInUse.test(i) && (mEntries[i].key == key))
{
mInUse.reset(i);
Lifetime<PayloadType>::Release(mEntries[i].payload);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_KEY_NOT_FOUND;
}
/**
* Remove any matching payloads. Used for mass removal, e.g. when a connection
* is closed, relevant payloads need/can be cleared for the entire connection.
*
* @tparam Matcher is a generic matcher object defining a bool Matches method.
*/
template <typename Matcher>
void RemoveMatching(const Matcher & matcher)
{
for (size_t i = 0; i < N; i++)
{
if (mInUse.test(i) && matcher.Matches(mEntries[i].key))
{
mInUse.reset(i);
Lifetime<PayloadType>::Release(mEntries[i].payload);
}
}
}
/**
* Search for a specific entry within the cache.
*
* @tparam Matcher is a generic macher object defining a bool Maches method.
*
* @param matcher the entry to find
* @param key - out set the key if found
* @param payload - the payload if found
*
* Key and payload are only valid as long as no remove methods
* are called on the class.
*/
template <typename Matcher>
bool Find(const Matcher & matcher, const KeyType ** key, const PayloadType ** payload)
{
*key = nullptr;
*payload = nullptr;
for (size_t i = 0; i < N; i++)
{
if (mInUse.test(i) && matcher.Matches(mEntries[i].key))
{
*key = &mEntries[i].key;
*payload = &mEntries[i].payload;
return true;
}
}
return false;
}
private:
struct Entry
{
KeyType key;
PayloadType payload;
};
Entry mEntries[N]; // payload entries
std::bitset<N> mInUse; // compact 'in use' marker for payloads
}; // namespace Retransmit
} // namespace Retransmit
} // namespace chip