/* * * 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 #include #include #include namespace chip { namespace Retransmit { /** * Handles lifetimes of payloads. * * E.g. for buffers, handles reference counting. */ template 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 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::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::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::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 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::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 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 mInUse; // compact 'in use' marker for payloads }; // namespace Retransmit } // namespace Retransmit } // namespace chip