Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware
obu-firmware builds against the vanetza-idf C-ITS library, which until now came from the colleague's microbu-esp32c5 tree beside the repository and was not tracked here, so a clone of this repository could not build the firmware it ships. The library alone is now part of obu-firmware, as obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from there by default; -DVANETZA_IDF_DIR still points the build elsewhere. The rest of the colleague's tree (their own VAM firmware, PKI tooling, station-link Python tools, the V2X2MAP bridge) stays out of this repository and gitignored; nothing is pushed to their repository. NOTES.md, docs/06, TODO.md and the pcap verifier's usage line point at the new location.
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
#ifndef SOFT_STATE_MAP_HPP_B0MARRWZ
|
||||
#define SOFT_STATE_MAP_HPP_B0MARRWZ
|
||||
|
||||
#include <vanetza/common/clock.hpp>
|
||||
#include <vanetza/common/runtime.hpp>
|
||||
#include <boost/heap/binomial_heap.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vanetza
|
||||
{
|
||||
namespace geonet
|
||||
{
|
||||
|
||||
template<typename VALUE>
|
||||
struct SoftStateDefaultCreator
|
||||
{
|
||||
VALUE operator()() { return VALUE(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* SoftStateMap is a map data structure with expiring entries
|
||||
* \tparam KEY key type
|
||||
* \tparam VALUE mapped type
|
||||
* \tparam CTOR optional creator of values
|
||||
*/
|
||||
template<typename KEY, typename VALUE, typename CTOR = SoftStateDefaultCreator<VALUE>>
|
||||
class SoftStateMap
|
||||
{
|
||||
public:
|
||||
using key_type = KEY;
|
||||
using mapped_type = VALUE;
|
||||
using value_type = std::pair<const key_type&, mapped_type&>;
|
||||
using creator_type = CTOR;
|
||||
|
||||
private:
|
||||
class ExpiryWithKey : public Clock::time_point
|
||||
{
|
||||
public:
|
||||
ExpiryWithKey() = default;
|
||||
ExpiryWithKey(const key_type& key, Clock::time_point expiry) :
|
||||
Clock::time_point(expiry), m_key(key) {}
|
||||
|
||||
const key_type& key() const { return m_key; }
|
||||
|
||||
private:
|
||||
key_type m_key;
|
||||
};
|
||||
using heap_type = boost::heap::binomial_heap<ExpiryWithKey, boost::heap::compare<std::greater<ExpiryWithKey>>>;
|
||||
|
||||
struct ValueWithHandle
|
||||
{
|
||||
ValueWithHandle(mapped_type&& v) : value(std::move(v)) {}
|
||||
|
||||
typename heap_type::handle_type handle;
|
||||
mapped_type value;
|
||||
|
||||
mapped_type& operator*() { return value; }
|
||||
const mapped_type& operator*() const { return value; }
|
||||
};
|
||||
using map_type = std::unordered_map<key_type, ValueWithHandle>;
|
||||
|
||||
using data_range = boost::iterator_range<typename map_type::iterator>;
|
||||
using data_filter = std::function<bool(const typename map_type::value_type&)>;
|
||||
using data_transform = std::function<value_type(typename map_type::value_type&)>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct SoftStateMap
|
||||
* \param rt runtime object
|
||||
* \note This constructor is only available if CTOR is default constructible
|
||||
*/
|
||||
template<typename T = CTOR>
|
||||
SoftStateMap(const Runtime& rt, typename std::enable_if<std::is_default_constructible<T>::value>::type* = nullptr) :
|
||||
m_runtime(rt)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct SoftStateMap
|
||||
* \param rt runtime object
|
||||
* \param ctor value creator
|
||||
*/
|
||||
SoftStateMap(const Runtime& rt, creator_type&& ctor) :
|
||||
m_runtime(rt), m_creator(std::move(ctor))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lifetime duration used for new and refreshed entries
|
||||
* \param lifetime entry lieftime
|
||||
*/
|
||||
void set_lifetime(Clock::duration lifetime)
|
||||
{
|
||||
m_lifetime = lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value mapped to key
|
||||
* \param key
|
||||
* \return existing value entry or just created entry
|
||||
*/
|
||||
mapped_type& get_value(const key_type& key)
|
||||
{
|
||||
return get_data(key).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non-expired value pointer mapped to key
|
||||
* \param key
|
||||
* \return pointer to value or nullptr if not existing
|
||||
*/
|
||||
mapped_type* get_value_ptr(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
return data && !is_expired(*data->handle) ? &data->value : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non-expired value pointer mapped to key
|
||||
* \param key
|
||||
* \return pointer to value or nullptr if not existing
|
||||
*/
|
||||
const mapped_type* get_value_ptr(const key_type& key) const
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
return data && !is_expired(*data->handle) ? &data->value : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if non-expired value for given key exists
|
||||
* \param key
|
||||
* \return true if entry exists
|
||||
*/
|
||||
bool has_value(const key_type& key) const
|
||||
{
|
||||
return get_value_ptr(key) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh lifetime of entry associated with given key
|
||||
* \param key
|
||||
* \return associated value (might have been created)
|
||||
*/
|
||||
mapped_type& refresh(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
if (data) {
|
||||
refresh(data->handle);
|
||||
} else {
|
||||
data = &get_data(key);
|
||||
assert(data != nullptr);
|
||||
}
|
||||
return data->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all entries with expired lifetime.
|
||||
* Expired but still stored entries are only hided at retrieval until calling this method.
|
||||
*/
|
||||
void drop_expired()
|
||||
{
|
||||
while (!m_heap.empty() && is_expired(m_heap.top())) {
|
||||
m_map.erase(m_heap.top().key());
|
||||
m_heap.pop();
|
||||
}
|
||||
}
|
||||
|
||||
using map_range = boost::transformed_range<data_transform, const boost::filtered_range<data_filter, data_range>>;
|
||||
|
||||
/**
|
||||
* Create a range of all non-expired entries mimicking STL's map interface
|
||||
*/
|
||||
map_range map()
|
||||
{
|
||||
data_filter filter_fn = [this](const typename map_type::value_type& v) {
|
||||
return !this->is_expired(*v.second.handle);
|
||||
};
|
||||
data_transform transform_fn = [](typename map_type::value_type& v) {
|
||||
return value_type { v.first, v.second.value };
|
||||
};
|
||||
using namespace boost::adaptors;
|
||||
data_range range_all = boost::make_iterator_range(m_map.begin(), m_map.end());
|
||||
return range_all | filtered(filter_fn) | transformed(transform_fn);
|
||||
}
|
||||
|
||||
const map_range map() const
|
||||
{
|
||||
return const_cast<SoftStateMap*>(this)->map();
|
||||
}
|
||||
|
||||
private:
|
||||
ValueWithHandle& get_data(const key_type& key)
|
||||
{
|
||||
auto* data = get_data_ptr(key);
|
||||
if (!data) {
|
||||
auto insertion = m_map.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key), std::forward_as_tuple(m_creator()));
|
||||
data = &insertion.first->second;
|
||||
data->handle = m_heap.push(ExpiryWithKey {key, m_runtime.now() + m_lifetime});
|
||||
} else if (is_expired(*data->handle)) {
|
||||
// resurrect this data element, i.e. pretend it has just been created
|
||||
refresh(data->handle);
|
||||
}
|
||||
return *data;
|
||||
}
|
||||
|
||||
ValueWithHandle* get_data_ptr(const key_type& key)
|
||||
{
|
||||
auto it = m_map.find(key);
|
||||
return it != m_map.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const ValueWithHandle* get_data_ptr(const key_type& key) const
|
||||
{
|
||||
auto it = m_map.find(key);
|
||||
return it != m_map.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool is_expired(const ExpiryWithKey& expiry) const
|
||||
{
|
||||
return m_runtime.now() > expiry;
|
||||
}
|
||||
|
||||
void refresh(typename heap_type::handle_type& handle)
|
||||
{
|
||||
ExpiryWithKey& expiry = *handle;
|
||||
static_cast<Clock::time_point&>(expiry) = m_runtime.now() + m_lifetime;
|
||||
m_heap.update(handle);
|
||||
}
|
||||
|
||||
const Runtime& m_runtime;
|
||||
Clock::duration m_lifetime;
|
||||
creator_type m_creator;
|
||||
heap_type m_heap;
|
||||
map_type m_map;
|
||||
};
|
||||
|
||||
} // namespace geonet
|
||||
} // namespace vanetza
|
||||
|
||||
#endif /* SOFT_STATE_MAP_HPP_B0MARRWZ */
|
||||
Reference in New Issue
Block a user