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:
Ashin Walpola
2026-09-24 10:56:05 +02:00
parent 2f60623e18
commit d107534eb2
9781 changed files with 1560475 additions and 17 deletions
@@ -0,0 +1,16 @@
set(CXX_SOURCES
archives.cpp
byte_buffer_convertible.cpp
byte_buffer_sink.cpp
byte_buffer_source.cpp
byte_sequence.cpp
byte_view.cpp
clock.cpp
manual_runtime.cpp
position_fix.cpp
unit_interval.cpp
)
add_vanetza_component(common ${CXX_SOURCES})
target_link_libraries(common PUBLIC Boost::date_time)
add_test_subdirectory(tests)
@@ -0,0 +1,11 @@
#pragma once
namespace vanetza
{
template<typename T>
constexpr void mark_unused(const T&) noexcept
{
}
} // namespace vanetza
@@ -0,0 +1,87 @@
#include <vanetza/common/archives.hpp>
namespace vanetza
{
InputArchive::InputArchive(InputStream& is) :
m_stream_buffer(is.rdbuf())
{
}
InputArchive::InputArchive(StreamBuffer& buf) :
m_stream_buffer(&buf)
{
}
void InputArchive::load_binary(unsigned char* data, std::size_t len)
{
load_binary(reinterpret_cast<char*>(data), len);
}
void InputArchive::load_binary(char* data, std::size_t len)
{
std::size_t read_bytes = m_stream_buffer->sgetn(data, len);
if (read_bytes != len) {
fail(ErrorCode::IncompleteData);
throw Exception("incomplete read");
}
}
char InputArchive::peek_byte()
{
auto got = m_stream_buffer->sgetc();
if (got == StreamBuffer::traits_type::eof()) {
fail(ErrorCode::IncompleteData);
throw Exception("impossible peek at end of stream");
} else {
return StreamBuffer::traits_type::to_char_type(got);
}
}
bool InputArchive::is_good() const
{
return m_error_code == ErrorCode::Ok;
}
InputArchive::ErrorCode InputArchive::error_code() const
{
return m_error_code;
}
void InputArchive::fail(ErrorCode error_code)
{
// do not overwrite prior error code except "ok"
if (m_error_code == ErrorCode::Ok) {
m_error_code = error_code;
}
}
std::size_t InputArchive::remaining_bytes()
{
return m_stream_buffer->in_avail();
}
OutputArchive::OutputArchive(OutputStream& os) :
m_stream_buffer(os.rdbuf())
{
}
OutputArchive::OutputArchive(StreamBuffer& buf) :
m_stream_buffer(&buf)
{
}
void OutputArchive::save_binary(const unsigned char* data, std::size_t len)
{
save_binary(reinterpret_cast<const char*>(data), len);
}
void OutputArchive::save_binary(const char* data, std::size_t len)
{
std::size_t written_bytes = m_stream_buffer->sputn(data, len);
if (written_bytes != len) {
throw Exception("incomplete write");
}
}
} // namespace vanetza
@@ -0,0 +1,92 @@
#ifndef ARCHIVES_HPP_TLVURDQK
#define ARCHIVES_HPP_TLVURDQK
#include <vanetza/common/byte_order.hpp>
#include <exception>
#include <istream>
#include <ostream>
#include <streambuf>
namespace vanetza
{
/**
* This is a drop-in replacement for boost::archive::binary_iarchive
*/
class InputArchive
{
public:
using InputStream = std::basic_istream<char>;
using StreamBuffer = std::basic_streambuf<char>;
class Exception : public std::runtime_error {
using std::runtime_error::runtime_error;
};
enum class ErrorCode {
Ok,
IncompleteData,
ExcessiveLength,
ConstraintViolation,
};
InputArchive(InputStream& is);
InputArchive(StreamBuffer& buf);
template<typename T>
InputArchive& operator>>(T& t)
{
static_assert(std::is_integral<T>::value == true, "only integral types are supported");
char* ptr = reinterpret_cast<char*>(&t);
load_binary(ptr, sizeof(T));
return *this;
}
void load_binary(unsigned char* data, std::size_t len);
void load_binary(char* data, std::size_t len);
char peek_byte();
std::size_t remaining_bytes();
bool is_good() const;
ErrorCode error_code() const;
void fail(ErrorCode error_code);
private:
StreamBuffer* m_stream_buffer;
ErrorCode m_error_code = ErrorCode::Ok;
};
/**
* This is a drop-in replacement for boost::archive::binary_oarchive
*/
class OutputArchive
{
public:
using OutputStream = std::basic_ostream<char>;
using StreamBuffer = std::basic_streambuf<char>;
class Exception : public std::runtime_error {
using std::runtime_error::runtime_error;
};
OutputArchive(OutputStream& os);
OutputArchive(StreamBuffer& buf);
template<typename T>
OutputArchive& operator<<(const T& t)
{
static_assert(std::is_integral<T>::value == true, "only integral types are supported");
const char* ptr = reinterpret_cast<const char*>(&t);
save_binary(ptr, sizeof(T));
return *this;
}
void save_binary(const unsigned char* data, std::size_t len);
void save_binary(const char* data, std::size_t len);
private:
StreamBuffer* m_stream_buffer;
};
} // namespace vanetza
#endif /* ARCHIVES_HPP_TLVURDQK */
@@ -0,0 +1,44 @@
#ifndef BIT_NUMBER_HPP_H3ODBQR7
#define BIT_NUMBER_HPP_H3ODBQR7
#include <boost/operators.hpp>
#include <cstddef>
#include <type_traits>
namespace vanetza {
/**
* BitNumber restricts the value range by the given width
*
* \tparam T underlying integral type
* \tparam WIDTH number of bits used to represent a number
*/
template<typename T, std::size_t WIDTH>
class BitNumber : public boost::totally_ordered<BitNumber<T, WIDTH>>
{
static_assert(std::is_integral<T>::value == true,
"only integral types are supported");
static_assert(sizeof(T) * 8 > WIDTH,
"width has to be less than size of underlying type");
public:
static constexpr T mask = (1 << WIDTH) - 1; /**< excessive bits are masked */
static constexpr std::size_t bits = WIDTH; /**< number of bits used */
typedef T value_type; /**< underlying (integral) value type */
BitNumber() : mValue(0) {}
BitNumber(T value) : mValue(value & mask) {}
BitNumber& operator=(T value) { mValue = value & mask; return *this; }
T raw() const { return mValue; }
bool operator<(BitNumber other) const { return mValue < other.mValue; }
bool operator==(BitNumber other) const { return mValue == other.mValue; }
private:
T mValue;
};
} // namespace vanetza
#endif /* BIT_NUMBER_HPP_H3ODBQR7 */
@@ -0,0 +1,55 @@
#ifndef BYTE_BUFFER_HPP_7NOEQO4F
#define BYTE_BUFFER_HPP_7NOEQO4F
#include <cstdint>
#include <type_traits>
#include <vector>
namespace vanetza
{
typedef std::vector<uint8_t> ByteBuffer;
/**
* Cast byte buffer to a POD object
* \param buffer byte buffer containing requested object
* \return pointer to object or nullptr if cast is impossible
*/
template<typename MASK>
MASK* buffer_cast(ByteBuffer& buffer)
{
static_assert(std::is_standard_layout<MASK>::value, "MASK has to be standard layout type");
static_assert(std::is_trivially_copyable<MASK>::value, "MASK has to be trivially copyable");
static_assert(std::is_object<MASK>::value, "MASK has to be an object");
MASK* mask = nullptr;
if (sizeof(MASK) <= buffer.size()) {
mask = reinterpret_cast<MASK*>(&buffer[0]);
}
return mask;
}
template<typename MASK>
const MASK* buffer_cast(const ByteBuffer& buffer)
{
// const_cast is safe, const qualifier is added to return type
return buffer_cast<MASK>(const_cast<ByteBuffer&>(buffer));
}
/**
* Create byte buffer with copy of POD object
* \param obj POD object
* \return ByteBuffer object with copy
*/
template<class T>
ByteBuffer buffer_copy(const T& object)
{
static_assert(std::is_standard_layout<T>::value, "T has to be standard layout type");
auto ptr = reinterpret_cast<const uint8_t*>(&object);
return ByteBuffer(ptr, ptr + sizeof(T));
}
} // namespace vanetza
#endif /* BYTE_BUFFER_HPP_7NOEQO4F */
@@ -0,0 +1,53 @@
#include "byte_buffer_convertible.hpp"
#include <algorithm>
#include <iterator>
namespace vanetza
{
namespace convertible
{
std::unique_ptr<byte_buffer> byte_buffer::duplicate() const
{
ByteBuffer duplicate;
this->convert(duplicate);
std::unique_ptr<byte_buffer> result {
new byte_buffer_impl<ByteBuffer>(std::move(duplicate))
};
return result;
}
byte_buffer_impl<std::string>::byte_buffer_impl(const std::string& str) : m_buffer(str) {}
byte_buffer_impl<std::string>::byte_buffer_impl(std::string&& str) : m_buffer(std::move(str)) {}
void byte_buffer_impl<std::string>::convert(ByteBuffer& buffer) const
{
buffer.clear();
std::copy(m_buffer.begin(), m_buffer.end(), std::back_inserter(buffer));
}
std::size_t byte_buffer_impl<std::string>::size() const
{
return m_buffer.size();
}
std::unique_ptr<byte_buffer> byte_buffer_impl<std::nullptr_t>::duplicate() const
{
return std::unique_ptr<byte_buffer> { new byte_buffer_impl<std::nullptr_t>() };
}
} // namespace convertible
ByteBufferConvertible::ByteBufferConvertible(const ByteBufferConvertible& other) :
m_wrapper(other.m_wrapper->duplicate())
{
}
ByteBufferConvertible& ByteBufferConvertible::operator=(const ByteBufferConvertible& other)
{
m_wrapper = other.m_wrapper->duplicate();
return *this;
}
} // namespace vanetza
@@ -0,0 +1,109 @@
#ifndef BYTE_BUFFER_CONVERTIBLE_HPP_CFOQNR35
#define BYTE_BUFFER_CONVERTIBLE_HPP_CFOQNR35
#include <vanetza/common/byte_buffer.hpp>
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
namespace vanetza
{
namespace convertible
{
struct byte_buffer
{
virtual void convert(ByteBuffer&) const = 0;
virtual std::size_t size() const = 0;
virtual std::unique_ptr<byte_buffer> duplicate() const;
virtual ~byte_buffer() {}
};
template<class T>
struct byte_buffer_impl;
template<>
struct byte_buffer_impl<ByteBuffer> : public byte_buffer
{
byte_buffer_impl(ByteBuffer&& buffer) : m_buffer(std::move(buffer)) {}
void convert(ByteBuffer& buf) const override { buf = m_buffer; }
std::size_t size() const override { return m_buffer.size(); }
ByteBuffer m_buffer;
};
template<>
struct byte_buffer_impl<std::unique_ptr<ByteBuffer>> : public byte_buffer
{
byte_buffer_impl(std::unique_ptr<ByteBuffer> buf) :
m_buffer(std::move(buf)) {}
void convert(ByteBuffer& buf) const override { buf = *m_buffer; }
std::size_t size() const override { return m_buffer->size(); }
std::unique_ptr<ByteBuffer> m_buffer;
};
template<>
struct byte_buffer_impl<std::string> : public byte_buffer
{
byte_buffer_impl(const std::string& str);
byte_buffer_impl(std::string&& str);
void convert(ByteBuffer& buffer) const override;
std::size_t size() const override;
std::string m_buffer;
};
template<>
struct byte_buffer_impl<std::nullptr_t> : public byte_buffer
{
void convert(ByteBuffer& buffer) const override { buffer.clear(); }
std::size_t size() const override { return 0; }
std::unique_ptr<byte_buffer> duplicate() const override;
};
} // namespace convertible
/**
* ByteBufferConvertible is an extensible mechanism for providing
* ByteBuffer representations of various data structures and objects.
*
* ByteBufferConvertible utilizes type erasure and accesses byte buffer data
* through the convertible::byte_buffer interface.
* Extending ByteBufferConvertible is possible by providing specializations
* of convertible::byte_buffer_impl<T> implementing convertible::byte_buffer.
*/
class ByteBufferConvertible
{
public:
ByteBufferConvertible() :
m_wrapper(new convertible::byte_buffer_impl<std::nullptr_t>()) {}
ByteBufferConvertible(std::unique_ptr<convertible::byte_buffer> ptr) :
m_wrapper(std::move(ptr)) {}
template<class T>
ByteBufferConvertible(T&& t) :
m_wrapper(new convertible::byte_buffer_impl<typename std::decay<T>::type>(std::forward<T>(t))) {}
ByteBufferConvertible(const ByteBufferConvertible&);
ByteBufferConvertible& operator=(const ByteBufferConvertible&);
ByteBufferConvertible(ByteBufferConvertible&& other) = default;
ByteBufferConvertible& operator=(ByteBufferConvertible&& other) = default;
void convert(ByteBuffer& destination) const { m_wrapper->convert(destination); }
std::size_t size() const { return m_wrapper->size(); }
const convertible::byte_buffer* ptr() const { return m_wrapper.get(); }
convertible::byte_buffer* ptr() { return m_wrapper.get(); }
private:
std::unique_ptr<convertible::byte_buffer> m_wrapper;
};
} // namespace vanetza
#endif /* BYTE_BUFFER_CONVERTIBLE_HPP_CFOQNR35 */
@@ -0,0 +1,19 @@
#include "byte_buffer_sink.hpp"
#include <algorithm>
#include <iterator>
namespace vanetza
{
byte_buffer_sink::byte_buffer_sink(ByteBuffer& buffer) : m_buffer(buffer)
{
}
std::streamsize byte_buffer_sink::write(const char_type* s, std::streamsize n)
{
std::copy(s, s + n, std::back_inserter(m_buffer));
return n;
}
} // namespace vanetza
@@ -0,0 +1,29 @@
#ifndef BYTE_BUFFER_SINK_HPP_XQSBHPDQ
#define BYTE_BUFFER_SINK_HPP_XQSBHPDQ
#include <vanetza/common/byte_buffer.hpp>
#include <boost/iostreams/categories.hpp>
#include <ios>
namespace vanetza
{
class byte_buffer_sink
{
public:
typedef char char_type;
typedef boost::iostreams::sink_tag category;
static_assert(sizeof(char_type) == sizeof(ByteBuffer::value_type),
"size mismatch of char_type and ByteBuffer::value_type");
byte_buffer_sink(ByteBuffer& buffer);
std::streamsize write(const char_type* s, std::streamsize n);
private:
ByteBuffer& m_buffer;
};
} // namespace vanetza
#endif /* BYTE_BUFFER_SINK_HPP_XQSBHPDQ */
@@ -0,0 +1,30 @@
#include "byte_buffer_source.hpp"
#include <algorithm>
namespace vanetza
{
byte_buffer_source::byte_buffer_source(const ByteBuffer& buffer) :
m_begin(buffer.cbegin()), m_end(buffer.cend()) {}
byte_buffer_source::byte_buffer_source(range r) :
m_begin(r.begin()), m_end(r.end()) {}
byte_buffer_source::byte_buffer_source(iterator begin, iterator end) :
m_begin(begin), m_end(end) {}
std::streamsize byte_buffer_source::read(char_type* buf, std::streamsize n)
{
if (m_begin == m_end) {
return -1;
} else {
std::streamsize remaining = std::distance(m_begin, m_end);
std::streamsize consume = std::min(remaining, n);
std::copy(m_begin, m_begin + consume, buf);
std::advance(m_begin, consume);
return consume;
}
}
} // namespace vanetza
@@ -0,0 +1,39 @@
#ifndef BYTE_BUFFER_SOURCE_HPP_6IBYOI0T
#define BYTE_BUFFER_SOURCE_HPP_6IBYOI0T
#include <vanetza/common/byte_buffer.hpp>
#include <boost/iostreams/categories.hpp>
#include <boost/range/iterator_range.hpp>
namespace vanetza
{
/**
* Implementation of boost::iostreams' source device concept.
* Allows to use a byte buffer as input stream.
*/
class byte_buffer_source
{
public:
typedef char char_type;
typedef boost::iostreams::source_tag category;
typedef ByteBuffer::const_iterator iterator;
typedef boost::iterator_range<iterator> range;
static_assert(sizeof(char_type) == sizeof(ByteBuffer::value_type),
"size mismatch of char_type and ByteBuffer::value_type");
byte_buffer_source(const ByteBuffer& buffer);
byte_buffer_source(range r);
byte_buffer_source(iterator begin, iterator end);
std::streamsize read(char_type* buf, std::streamsize n);
private:
iterator m_begin;
iterator m_end;
};
} // namespace vanetza
#endif /* BYTE_BUFFER_SOURCE_HPP_6IBYOI0T */
@@ -0,0 +1,229 @@
#ifndef BYTE_ORDER_HPP_LPUJ094I
#define BYTE_ORDER_HPP_LPUJ094I
#include <cstdint>
#include <functional>
#include <iosfwd>
#include <type_traits>
#include <boost/endian/conversion.hpp>
namespace vanetza
{
template<class T>
T hton(T host_value)
{
return boost::endian::native_to_big(host_value);
}
template<class T>
T ntoh(T network_value)
{
return boost::endian::big_to_native(network_value);
}
enum class ByteOrder {
BigEndian,
LittleEndian
};
namespace detail
{
#if BYTE_ORDER == LITTLE_ENDIAN
static constexpr ByteOrder host_byte_order = ByteOrder::LittleEndian;
#elif BYTE_ORDER == BIG_ENDIAN
static constexpr ByteOrder host_byte_order = ByteOrder::BigEndian;
#else
# error "Unknown byte order"
#endif
} // namespace detail
constexpr ByteOrder getHostByteOrder() { return detail::host_byte_order; }
template<typename T, ByteOrder ORDER = getHostByteOrder()>
class EndianType;
/**
* Explicitly forge a plain type to an EndianType.
* It is assumed the passed value is already in the stated byte order,
* i.e. endian_cast does _not_ trigger any automatic conversions.
* \param value A plain value in byte order ORDER
* \return EndianType capable to carry value type
*/
template<ByteOrder ORDER, typename T>
EndianType<T, ORDER> endian_cast(T value)
{
return static_cast< EndianType<T, ORDER> >(value);
}
/**
* Cast POD type to EndianType in host byte order
* \param value POD in host byte order
* \return EndianType carrying value
*/
template<typename T>
EndianType<T, getHostByteOrder()> host_cast(T value)
{
return endian_cast<getHostByteOrder()>(value);
}
/**
* Cast POD type to EndianType in network byte order
* \param value POD in network byte order
* \return EndianType carrying value
*/
template<typename T>
EndianType<T, ByteOrder::BigEndian> network_cast(T value)
{
return endian_cast<ByteOrder::BigEndian>(value);
}
namespace detail
{
template<typename T, ByteOrder FROM, ByteOrder TO>
struct EndianConverter
{
T operator()(const T&) const;
};
template<typename T, ByteOrder ORDER>
struct EndianConverter<T, ORDER, ORDER>
{
T operator()(const T& t) const { return t; }
};
template<typename T>
struct EndianConverter<T, ByteOrder::LittleEndian, ByteOrder::BigEndian>
{
T operator()(const T& t) const { return boost::endian::endian_reverse(t); }
};
template<typename T>
struct EndianConverter<T, ByteOrder::BigEndian, ByteOrder::LittleEndian>
{
T operator()(const T& t) const { return boost::endian::endian_reverse(t); }
};
template<typename T, ByteOrder FROM, ByteOrder TO>
T convert_endian(const T& t)
{
EndianConverter<T, FROM, TO> converter;
return converter(t);
}
} // namespace detail
template<typename T, ByteOrder ORDER>
class EndianType
{
public:
static_assert(std::is_arithmetic<T>::value == true, "EndianType is only availabe for arithmetic types");
typedef T value_type;
typedef EndianType<T, getHostByteOrder()> host_type;
typedef EndianType<T, ByteOrder::BigEndian> network_type;
EndianType() = default;
explicit EndianType(T value) { m_value = value; }
EndianType(const EndianType&) = default;
EndianType& operator=(const EndianType&) = default;
template<ByteOrder OTHER_ORDER>
EndianType(const EndianType<T, OTHER_ORDER>& other) :
m_value(detail::convert_endian<T, OTHER_ORDER, ORDER>(other.m_value))
{
}
template<ByteOrder OTHER_ORDER>
EndianType& operator=(const EndianType<T, OTHER_ORDER>& other)
{
m_value = detail::convert_endian<T, OTHER_ORDER, ORDER>(other.m_value);
return *this;
}
bool operator==(const EndianType& other) const
{
return m_value == other.m_value;
}
bool operator!=(const EndianType& other) const
{
return !(*this == other);
}
bool operator<(const EndianType& other) const
{
return m_value < other.m_value;
}
value_type net() const
{
return detail::convert_endian<T, ORDER, ByteOrder::BigEndian>(m_value);
}
value_type host() const
{
return detail::convert_endian<T, ORDER, getHostByteOrder()>(m_value);
}
value_type get() const
{
return m_value;
}
private:
friend class EndianType<T, ByteOrder::BigEndian>;
friend class EndianType<T, ByteOrder::LittleEndian>;
T m_value;
};
/**
* Print to ostream in network byte order
* \param os output stream
* \param t endian type object
* \return os
*/
template<typename T, ByteOrder ORDER>
std::ostream& operator<<(std::ostream& os, const EndianType<T, ORDER>& t)
{
os << t.net();
return os;
}
typedef EndianType<uint8_t, ByteOrder::BigEndian> uint8be_t;
typedef EndianType<uint16_t, ByteOrder::BigEndian> uint16be_t;
typedef EndianType<uint32_t, ByteOrder::BigEndian> uint32be_t;
typedef EndianType<uint64_t, ByteOrder::BigEndian> uint64be_t;
typedef EndianType<int8_t, ByteOrder::BigEndian> int8be_t;
typedef EndianType<int16_t, ByteOrder::BigEndian> int16be_t;
typedef EndianType<int32_t, ByteOrder::BigEndian> int32be_t;
typedef EndianType<int64_t, ByteOrder::BigEndian> int64be_t;
} // namespace vanetza
namespace std
{
template<typename T, vanetza::ByteOrder ORDER>
struct hash<vanetza::EndianType<T, ORDER>>
{
size_t operator()(const vanetza::EndianType<T, ORDER>& t) const
{
return hash<T>()(t.get());
}
};
} // namespace std
#endif /* BYTE_ORDER_HPP_LPUJ094I */
@@ -0,0 +1,22 @@
#include <vanetza/common/byte_sequence.hpp>
#include <algorithm>
#include <random>
namespace vanetza
{
ByteBuffer random_byte_sequence(std::size_t length, int seed)
{
ByteBuffer buffer(length);
std::generate(buffer.begin(), buffer.end(), random_byte_generator(seed));
return buffer;
}
std::function<uint8_t()> random_byte_generator(int seed)
{
std::mt19937 rng;
rng.seed(seed);
return [rng]() mutable { return rng(); };
}
} // namespace vanetza
@@ -0,0 +1,27 @@
#ifndef BYTE_SEQUENCE_HPP_10RLFUNF
#define BYTE_SEQUENCE_HPP_10RLFUNF
#include <vanetza/common/byte_buffer.hpp>
#include <functional>
namespace vanetza
{
/**
* Create a random sequence of bytes
* \param length Length of generated byte buffer
* \param seed initializing random number generator
*/
ByteBuffer random_byte_sequence(std::size_t length, int seed = 0);
/**
* Create generator function for random bytes
* \param seed initializing random number generator
* \return Generator function producing random bytes
*/
std::function<uint8_t()> random_byte_generator(int seed = 0);
} // namespace vanetza
#endif /* BYTE_SEQUENCE_HPP_10RLFUNF */
@@ -0,0 +1,72 @@
#include <vanetza/common/byte_view.hpp>
#include <vanetza/common/byte_buffer_convertible.hpp>
#include <cassert>
#include <limits>
namespace vanetza
{
namespace
{
boost::iterator_range<byte_view_iterator>
make_safe_range(const ByteBuffer::const_iterator& begin, const ByteBuffer::const_iterator& end)
{
if (begin < end) {
byte_view_iterator vbegin { begin };
byte_view_iterator vend { std::next(vbegin, std::distance(begin, end)) };
return boost::iterator_range<byte_view_iterator> { vbegin, vend };
} else {
byte_view_iterator empty;
return boost::iterator_range<byte_view_iterator> { empty, empty };
}
}
}
byte_view_range::byte_view_range(const ByteBuffer::const_iterator& begin, const ByteBuffer::const_iterator& end) :
iterator_range(make_safe_range(begin, end))
{
}
byte_view_range::byte_view_range(const byte_view_iterator& begin, const byte_view_iterator& end) :
iterator_range(begin, end)
{
}
byte_view_range::byte_view_range(ByteBuffer&& _buffer) :
iterator_range(make_safe_range(_buffer.begin(), _buffer.end())), buffer(std::move(_buffer))
{
}
ByteBuffer::const_pointer byte_view_range::data() const
{
auto begin = this->begin();
return begin != this->end() ? begin.raw() : nullptr;
}
ByteBuffer::value_type byte_view_range::operator[](size_type pos) const
{
static_assert(std::numeric_limits<size_type>::is_signed == false, "size_type shall be unsigned");
assert(pos < size());
return begin()[pos];
}
byte_view_range create_byte_view(ByteBuffer&& buffer)
{
return byte_view_range { std::move(buffer) };
}
byte_view_range create_byte_view(const ByteBuffer& buffer)
{
return byte_view_range { buffer.begin(), buffer.end() };
}
byte_view_range create_byte_view(const ByteBufferConvertible& convertible)
{
ByteBuffer buffer;
convertible.convert(buffer);
return byte_view_range { std::move(buffer) };
}
} // namespace vanetza
@@ -0,0 +1,194 @@
#ifndef BYTE_VIEW_HPP_TXN2ISMB
#define BYTE_VIEW_HPP_TXN2ISMB
#include <vanetza/common/byte_buffer.hpp>
#include <boost/range/iterator_range.hpp>
#include <iterator>
#include <memory>
namespace vanetza
{
// forward declaration
class ByteBufferConvertible;
class byte_view_iterator
{
public:
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::uint8_t;
using pointer = const value_type*;
using reference = const value_type&;
byte_view_iterator() = default;
explicit byte_view_iterator(pointer p) : m_iterator(p) {}
explicit byte_view_iterator(const ByteBuffer::const_iterator& it) : m_iterator(it.operator->()) {}
constexpr value_type operator*() const
{
return *m_iterator;
}
constexpr byte_view_iterator& operator++()
{
++m_iterator;
return *this;
}
constexpr byte_view_iterator operator++(int)
{
byte_view_iterator it = *this;
++m_iterator;
return it;
}
constexpr byte_view_iterator& operator--()
{
--m_iterator;
return *this;
}
constexpr byte_view_iterator operator--(int)
{
byte_view_iterator it = *this;
--m_iterator;
return it;
}
constexpr byte_view_iterator& operator+=(difference_type n)
{
m_iterator += n;
return *this;
}
constexpr byte_view_iterator& operator-=(difference_type n)
{
m_iterator -= n;
return *this;
}
constexpr difference_type operator-(const byte_view_iterator& o) const
{
return m_iterator - o.m_iterator;
}
constexpr reference operator[](difference_type n)
{
return m_iterator[n];
}
constexpr bool operator==(const byte_view_iterator& o) const
{
return m_iterator == o.m_iterator;
}
constexpr bool operator!=(const byte_view_iterator& o) const
{
return m_iterator != o.m_iterator;
}
constexpr bool operator>(const byte_view_iterator& o) const
{
return m_iterator > o.m_iterator;
}
constexpr bool operator<(const byte_view_iterator& o) const
{
return m_iterator < o.m_iterator;
}
constexpr bool operator>=(const byte_view_iterator& o) const
{
return m_iterator >= o.m_iterator;
}
constexpr bool operator<=(const byte_view_iterator& o) const
{
return m_iterator <= o.m_iterator;
}
constexpr pointer raw() const
{
return m_iterator;
}
private:
pointer m_iterator = nullptr;
};
constexpr byte_view_iterator operator+(byte_view_iterator::difference_type n, byte_view_iterator it)
{
return it += n;
}
constexpr byte_view_iterator operator+(byte_view_iterator it, byte_view_iterator::difference_type n)
{
return it += n;
}
/**
* byte_view_range fulfills the range concept and provides a view of contiguous bytes
* \note private inheritance is used to prevent object slicing
*/
class byte_view_range : private boost::iterator_range<byte_view_iterator>
{
using range_type = boost::iterator_range<byte_view_iterator>;
public:
using value_type = byte_view_iterator::value_type;
using pointer = byte_view_iterator::pointer;
/**
* Construct new view from iterator pair.
* \param begin begin iterator of view
* \param end end iterator of view
* \note View is valid as long as passed iterators are valid
*/
byte_view_range(const ByteBuffer::const_iterator&, const ByteBuffer::const_iterator&);
byte_view_range(const byte_view_iterator&, const byte_view_iterator&);
/**
* Create new view and take ownership of passed buffer
* \param buffer pass buffer via rvalue
* \note View is valid without limitation
*/
explicit byte_view_range(ByteBuffer&&);
/**
* Get pointer to start of contiguous buffer memory
* \return pointer (can be nullptr)
*/
ByteBuffer::const_pointer data() const;
/**
* Access a certain byte within range
* \param pos byte position within [0; size()[
* \note Override implementation by boost::iterator_range
* \return byte value
*/
value_type operator[](size_type) const;
// make several funtions from boost::iterator_range accessible
using range_type::size;
using range_type::begin;
using range_type::end;
private:
ByteBuffer buffer;
};
/**
* Create a byte view based on various byte buffer representations.
* View is valid at least as long as passed arguments are valid
* \param byte buffer or byte buffer convertible
* \return byte view representing passed byte buffer
*/
byte_view_range create_byte_view(ByteBuffer&&);
byte_view_range create_byte_view(const ByteBuffer&);
byte_view_range create_byte_view(const ByteBufferConvertible&);
} // namespace vanetza
#endif /* BYTE_VIEW_HPP_TXN2ISMB */
@@ -0,0 +1,34 @@
#include <vanetza/common/clock.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
namespace vanetza
{
const boost::posix_time::ptime& Clock::epoch()
{
static const boost::posix_time::ptime epoch {
boost::gregorian::date(2004, 1, 1),
boost::posix_time::milliseconds(0)
};
return epoch;
}
Clock::time_point Clock::at(const boost::posix_time::ptime& t)
{
auto delta = (t - epoch()).total_microseconds();
Clock::time_point tp { std::chrono::microseconds(delta) };
return tp;
}
Clock::date_time Clock::at(const Clock::time_point& t)
{
std::chrono::microseconds delta = t.time_since_epoch();
return Clock::epoch() + boost::posix_time::microseconds(delta.count());
}
Clock::time_point Clock::at(const std::string& at)
{
return Clock::at(boost::posix_time::time_from_string(at));
}
} // namespace vanetza
@@ -0,0 +1,40 @@
#ifndef CLOCK_HPP_2FCBLXSJ
#define CLOCK_HPP_2FCBLXSJ
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <chrono>
#include <cstdint>
#include <ratio>
namespace vanetza
{
/**
* A Clock similar to std::chrono with epoch at 2004-01-01 at midnight
*/
class Clock
{
public:
typedef int64_t rep;
typedef std::ratio<1, 1000 * 1000> period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<Clock> time_point;
typedef boost::posix_time::ptime date_time;
static constexpr bool is_steady() { return true; }
static time_point at(const date_time&);
static date_time at(const time_point&);
static const date_time& epoch();
/**
* \brief create time point
* \param at time string formatted like 2016-07-15 09:48:32
* \return time point
*/
static time_point at(const std::string& at);
};
} // namespace vanetza
#endif /* CLOCK_HPP_2FCBLXSJ */
@@ -0,0 +1,77 @@
#ifndef CONFIDENT_QUANTITY_HPP_B2XVJERI
#define CONFIDENT_QUANTITY_HPP_B2XVJERI
#include <limits>
namespace vanetza
{
/**
* ConfidentQuantity combines a boost::quantity value with a confidence level.
*
* Usually, a confidence level of 95% is used in ITS specifications.
* If no confidence level is explicitly given a worst case value is used, i.e.
* the maximum value representable by the underlying type or infinity.
*/
template<typename T>
class ConfidentQuantity
{
public:
constexpr T worst_confidence() const
{
using value_type = typename T::value_type;
return T::from_value(std::numeric_limits<value_type>::has_infinity ?
std::numeric_limits<value_type>::infinity() :
std::numeric_limits<value_type>::max());
}
constexpr T default_value() const
{
using value_type = typename T::value_type;
return T::from_value(std::numeric_limits<value_type>::has_quiet_NaN ?
std::numeric_limits<value_type>::quiet_NaN() : value_type());
}
constexpr bool is_nan(const T& t) const
{
return t.value() != t.value();
}
ConfidentQuantity() :
m_value(default_value()), m_confidence(worst_confidence()) {}
ConfidentQuantity(const T& value) :
m_value(value), m_confidence(worst_confidence()) {}
ConfidentQuantity(const T& value, const T& confidence) :
m_value(value), m_confidence(!is_nan(confidence) ? confidence : worst_confidence()) {}
ConfidentQuantity(const ConfidentQuantity&) = default;
ConfidentQuantity& operator=(const ConfidentQuantity&) = default;
ConfidentQuantity(ConfidentQuantity&&) = default;
ConfidentQuantity& operator=(ConfidentQuantity&&) = default;
void assign(const T& value, const T& confidence)
{
m_value = value;
m_confidence = !is_nan(confidence) ? confidence : worst_confidence();
}
const T& value() const
{
return m_value;
}
const T& confidence() const
{
return m_confidence;
}
private:
T m_value;
T m_confidence;
};
} // namespace vanetza
#endif /* CONFIDENT_QUANTITY_HPP_B2XVJERI */
@@ -0,0 +1,85 @@
#ifndef FACTORY_HPP_QLKNHPWZ
#define FACTORY_HPP_QLKNHPWZ
#include <functional>
#include <map>
#include <memory>
#include <utility>
namespace vanetza
{
/**
* Factory for a group of classes implementing T
*/
template<typename T, typename... Args>
class Factory
{
public:
using Result = std::unique_ptr<T>;
using Function = std::function<Result(Args...)>;
Factory() : m_default(m_functions.end())
{
}
/**
* Create an instance of T using a named implementation
* \param name of wanted implementation
* \return created instance or nullptr if not found
*/
Result create(const std::string& name, Args... args) const
{
std::unique_ptr<T> obj;
auto found = m_functions.find(name);
if (found != m_functions.end()) {
obj = found->second(std::forward<Args>(args)...);
}
return obj;
}
/**
* Create object using default implementation
* \return created instance or nullptr if no default is configured
*/
Result create(Args... args) const
{
std::unique_ptr<T> obj;
if (m_default != m_functions.end()) {
obj = m_default->second(std::forward<Args>(args)...);
}
return obj;
}
/**
* Add an implementation to factory
* \param name of implementation
* \param f function creating a new instance of this implementation
* \return true if added successfully, i.e. no previous addition with same name
*/
bool add(const std::string& name, Function f)
{
return m_functions.emplace(name, std::move(f)).second;
}
/**
* Set default implementation
* \param name selected default implementation
* \return true if an implementation exists with selected name
*/
bool configure_default(const std::string& name)
{
m_default = m_functions.find(name);
return m_default != m_functions.end();
}
private:
using map_type = std::map<std::string, Function>;
map_type m_functions;
typename map_type::const_iterator m_default;
};
} // namespace vanetza
#endif /* FACTORY_HPP_QLKNHPWZ */
@@ -0,0 +1,100 @@
#ifndef HOOK_HPP_RNAM6XF4
#define HOOK_HPP_RNAM6XF4
#include <functional>
#include <utility>
namespace vanetza
{
/**
* Hook mechanism for realising extension points
*/
template<typename... Args>
class Hook
{
public:
typedef std::function<void(Args...)> callback_type;
/**
* Assign a callback to hook, replaces previously assigned one
* \param cb A callable used as hook callback, e.g. lambda
*/
void operator=(callback_type&& cb)
{
m_function = std::move(cb);
}
void operator=(const callback_type& cb)
{
m_function = cb;
}
/**
* Execute hook callback if assigned
* \param Args... various arguments passed to assigned callback
*/
void operator()(Args... args)
{
if (m_function) {
// that's an arcane syntax, isn't it?
m_function(std::forward<Args>(args)...);
}
}
/**
* Reset previously assigned callback.
* No callback will be invoked when triggering hook after reset.
*/
void reset()
{
m_function = nullptr;
}
/**
* \deprecated previous name of reset
*/
void clear() { reset(); }
private:
callback_type m_function;
};
/**
* Hook registry (non-callable view of a hook)
*
* Callbacks can be assigned to a hook via the corresponding registry,
* but the callback cannot be invoked through the registry.
*/
template<typename... Args>
class HookRegistry
{
public:
using hook_type = Hook<Args...>;
using callback_type = typename hook_type::callback_type;
HookRegistry(hook_type& hook) : m_hook(hook) {}
void operator=(callback_type&& cb)
{
m_hook = std::move(cb);
}
void operator=(const callback_type& cb)
{
m_hook = cb;
}
void reset()
{
m_hook.reset();
}
private:
hook_type& m_hook;
};
} // namespace vanetza
#endif /* HOOK_HPP_RNAM6XF4 */
@@ -0,0 +1,49 @@
#ifndef ITS_AID_HPP_URKJ51RA
#define ITS_AID_HPP_URKJ51RA
#include <cstdint>
namespace vanetza
{
// uint32_t can hold all relevant ITS AIDs (for now)
using ItsAid = uint32_t;
namespace aid
{
/**
* ITS-AID assigned for ETSI ITS
* \see TS 102 965 V2.4.1 Annex A
*/
constexpr ItsAid CA = 36;
constexpr ItsAid DEN = 37;
constexpr ItsAid TLM = 137;
constexpr ItsAid RLT = 138;
constexpr ItsAid IVI = 139;
constexpr ItsAid TLC_R = 140;
constexpr ItsAid TLC_S = 637;
constexpr ItsAid GN_MGMT = 141;
constexpr ItsAid CRL = 622;
constexpr ItsAid SCR = 623;
constexpr ItsAid CTL = 624;
constexpr ItsAid VRU = 638;
constexpr ItsAid CP = 639;
constexpr ItsAid IMZ = 640;
constexpr ItsAid SA = 540801;
constexpr ItsAid GPC = 540802;
constexpr ItsAid IPV6_ROUTING = 270549118;
constexpr ItsAid MR = 1618;
constexpr ItsAid AVM = 71;
constexpr ItsAid POI = 1619;
constexpr ItsAid RMO = 1620;
#ifndef PI /*< some code defines the mathematical constant as macro */
constexpr ItsAid PI = 1621;
#endif
constexpr ItsAid MDM = 1622;
} // namespace aid
} // namespace vanetza
#endif /* ITS_AID_HPP_URKJ51RA */
@@ -0,0 +1,101 @@
#ifndef LRU_CACHE_HPP_CII58WXX
#define LRU_CACHE_HPP_CII58WXX
#include <cassert>
#include <functional>
#include <list>
#include <unordered_map>
namespace vanetza
{
/**
* \brief Least-Recently-Used cache
*
* If an entry is accessed which is not yet cached, it will created
* by invocation of the generator function.
*
* \tparam KEY key type
* \tparam VALUE value_type, i.e. type of cache entries
* \tparam GENERATOR generator function for creating values from key
*/
template<typename KEY, typename VALUE, typename GENERATOR = std::function<VALUE(const KEY&)>>
class LruCache
{
public:
using generator = GENERATOR;
using key_type = KEY;
using value_type = VALUE;
/**
* \param g user-defined generator function
* \param capacity maximum number of cache entries
*/
LruCache(generator g, std::size_t capacity) :
m_generator(g),
m_capacity(capacity)
{
}
/**
* \brief Access a cache entry
*
* If cache entry does not yet exist it will be created.
* The least recently accessed entry might get dropped.
*
* \param key identifier of cache entry
* \return cached value
*/
value_type& operator[](const key_type& key)
{
auto found = m_cache.find(key);
if (found == m_cache.end()) {
return add(key);
} else {
return refresh(found);
}
}
private:
using list_type = std::list<key_type>;
using entry_type = std::pair<value_type, typename list_type::iterator>;
using map_type = std::unordered_map<key_type, entry_type>;
value_type& add(const key_type& k)
{
if (m_cache.size() >= m_capacity) {
remove();
}
m_index.emplace_front(k);
entry_type entry = std::make_pair(m_generator(k), m_index.begin());
auto insertion = m_cache.insert(std::make_pair(k, std::move(entry)));
assert(insertion.second == true);
return insertion.first->second.first;
}
value_type& refresh(typename map_type::iterator found)
{
m_index.splice(m_index.begin(), m_index, found->second.second);
assert(m_index.begin() == found->second.second);
return found->second.first;
}
void remove()
{
if (!m_index.empty()) {
m_cache.erase(m_index.back());
m_index.pop_back();
}
}
generator m_generator;
std::size_t m_capacity;
list_type m_index;
map_type m_cache;
};
} // namespace vanetza
#endif /* LRU_CACHE_HPP_CII58WXX */
@@ -0,0 +1,89 @@
#include "manual_runtime.hpp"
#include <cassert>
namespace vanetza
{
ManualRuntime::ManualRuntime(Clock::time_point init) : m_now(init)
{
}
void ManualRuntime::schedule(Clock::time_point tp, const Callback& cb, const void* scope)
{
m_queue.emplace(queue_type::value_type { tp, cb, scope });
}
void ManualRuntime::schedule(Clock::duration d, const Callback& cb, const void* scope)
{
schedule(m_now + d, cb, scope);
}
void ManualRuntime::cancel(const void* scope)
{
if (scope) {
auto scope_match_range = m_queue.get<by_scope>().equal_range(scope);
m_queue.get<by_scope>().erase(scope_match_range.first, scope_match_range.second);
}
}
Clock::time_point ManualRuntime::next() const
{
Clock::time_point next_tp = Clock::time_point::max();
if (!m_queue.empty()) {
next_tp = m_queue.get<by_deadline>().begin()->deadline;
}
return next_tp;
}
Clock::time_point ManualRuntime::now() const
{
return m_now;
}
void ManualRuntime::trigger(Clock::time_point tp)
{
// require monotonic clock
assert(tp >= m_now);
m_now = tp;
trigger();
}
void ManualRuntime::trigger(Clock::duration d)
{
m_now += d;
trigger();
}
void ManualRuntime::trigger()
{
// process queue elements separately because callback might modify runtime
while (!m_queue.empty()) {
auto top = m_queue.get<by_deadline>().begin();
const auto deadline = top->deadline; // copy of deadline on purpose (erase before callback)
if (deadline <= m_now) {
Callback cb = top->callback;
m_queue.get<by_deadline>().erase(top);
// callback invocation has to be last action because it might modify runtime
cb(deadline);
} else {
break;
}
}
}
void ManualRuntime::reset(Clock::time_point tp)
{
m_now = tp;
queue_type queue;
swap(queue, m_queue);
// invoke all callbacks once so they can re-schedule
for (auto& item : queue) {
const auto& deadline = item.deadline;
auto& callback = item.callback;
// callback might modify m_queue
callback(deadline);
}
}
} // namespace vanetza
@@ -0,0 +1,102 @@
#ifndef MANUAL_RUNTIME_HPP_IPFSK6ZA
#define MANUAL_RUNTIME_HPP_IPFSK6ZA
#include <vanetza/common/runtime.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
namespace vanetza
{
/**
* ManualRuntime is a manually triggered Runtime implementation.
* Ensure that time progress is triggered monotonically!
*/
class ManualRuntime : public Runtime
{
public:
ManualRuntime() = default;
/**
* Create runtime
* \param init initialization value of internal clock
*/
explicit ManualRuntime(Clock::time_point init);
/**
* Trigger absolute time progress
*
* All expired callbacks will be invoked
* \param tp new time point, has to be greater than now
*/
void trigger(Clock::time_point tp);
/**
* Trigger relative time progress
*
* All expired callbacks will be invoked
* \param d advance time by this duration
*/
void trigger(Clock::duration d);
/**
* Reset runtime
*
* Drops all scheduled callbacks and resets internal clock
* \param tp new time point
*/
void reset(Clock::time_point tp);
/**
* Get time point of next scheduled event
* \note time point might belong to an expired event, i.e. next() < now()
* \return time point of next event or time_point::max if none
*/
Clock::time_point next() const;
// Runtime interface (see header there for details)
void schedule(Clock::time_point, const Callback&, const void* = nullptr) override;
void schedule(Clock::duration, const Callback&, const void* = nullptr) override;
void cancel(const void* scope) override;
Clock::time_point now() const override;
private:
struct ScheduledCallback
{
ScheduledCallback(Clock::time_point tp, const Callback& cb, const void* scope) :
deadline(tp), callback(cb), scope(scope) {}
ScheduledCallback(const ScheduledCallback&) = delete;
ScheduledCallback& operator=(const ScheduledCallback&) = delete;
ScheduledCallback(ScheduledCallback&&) = default;
ScheduledCallback& operator=(ScheduledCallback&&) = default;
Clock::time_point deadline;
Callback callback;
const void* scope;
};
struct by_deadline {};
using time_index = boost::multi_index::ordered_non_unique<
boost::multi_index::tag<by_deadline>,
boost::multi_index::member<ScheduledCallback, Clock::time_point, &ScheduledCallback::deadline>>;
struct by_scope {};
using scope_index = boost::multi_index::hashed_non_unique<
boost::multi_index::tag<by_scope>,
boost::multi_index::member<ScheduledCallback, const void*, &ScheduledCallback::scope>>;
using queue_type = boost::multi_index_container<ScheduledCallback,
boost::multi_index::indexed_by<time_index, scope_index>>;
void trigger();
Clock::time_point m_now;
queue_type m_queue;
};
} // namespace vanetza
#endif /* MANUAL_RUNTIME_HPP_IPFSK6ZA */
@@ -0,0 +1,111 @@
#ifndef OBJECT_CONTAINER_HPP_25SOHVUH
#define OBJECT_CONTAINER_HPP_25SOHVUH
#include <functional>
#include <map>
#include <memory>
#include <typeindex>
#include <type_traits>
namespace vanetza
{
class ObjectContainer
{
private:
struct object_handle
{
template<typename T>
object_handle(std::unique_ptr<T> obj) :
object(obj.release()),
deleter([](void* ptr) { std::default_delete<T>()(static_cast<T*>(ptr)); })
{
}
~object_handle() {
deleter(object);
object = nullptr;
}
void* object;
std::function<void(void*)> deleter;
};
using container_type = std::map<std::type_index, object_handle>;
public:
ObjectContainer() = default;
// no copy
ObjectContainer(const ObjectContainer&) = delete;
ObjectContainer& operator=(const ObjectContainer&) = delete;
// allow move
ObjectContainer(ObjectContainer&&) = default;
ObjectContainer& operator=(ObjectContainer&&) = default;
bool empty() const { return m_container.empty(); }
std::size_t size() const { return m_container.size(); }
void clear() { m_container.clear(); }
template<typename T>
void erase()
{
m_container.erase(std::type_index(typeid(T)));
}
template<typename T>
T* find()
{
T* result = nullptr;
auto found = m_container.find(std::type_index(typeid(T)));
if (found != m_container.end()) {
result = static_cast<T*>(found->second.object);
}
return result;
}
template<typename T>
const T* find() const
{
const T* result = nullptr;
auto found = m_container.find(std::type_index(typeid(T)));
if (found != m_container.end()) {
result = static_cast<const T*>(found->second.object);
}
return result;
}
template<typename T>
bool insert(std::unique_ptr<T> obj)
{
static_assert(std::is_object<T>() && !std::is_const<T>(),
"Only non-const objects are supported by ObjectContainer");
return m_container.emplace(std::type_index(typeid(T)), std::move(obj)).second;
}
template<typename T>
T& get()
{
static_assert(std::is_default_constructible<T>(),
"Only default constructible types are accessible through ObjectContainer::get");
T* result = find<T>();
if (!result) {
std::unique_ptr<T> obj { new T() };
result = obj.get();
if (!insert(std::move(obj)))
result = nullptr;
}
assert(result);
return *result;
}
private:
container_type m_container;
};
} // namespace vanetza
#endif /* OBJECT_CONTAINER_HPP_25SOHVUH */
@@ -0,0 +1,32 @@
#ifndef POSITION_CONFIDENCE_HPP_FNHKVJZL
#define POSITION_CONFIDENCE_HPP_FNHKVJZL
#include <vanetza/units/angle.hpp>
#include <vanetza/units/length.hpp>
#include <cmath>
#include <limits>
namespace vanetza
{
struct PositionConfidence
{
PositionConfidence() :
semi_major(units::Length::from_value(std::numeric_limits<units::Length::value_type>::infinity())),
semi_minor(units::Length::from_value(std::numeric_limits<units::Length::value_type>::infinity()))
{}
operator bool() const
{
return !std::isinf(semi_major.value()) && semi_minor.value() <= semi_major.value() && semi_minor.value() >= 0.0;
}
units::Length semi_major;
units::Length semi_minor;
units::TrueNorth orientation;
};
} // namespace vanetza
#endif /* POSITION_CONFIDENCE_HPP_FNHKVJZL */
@@ -0,0 +1,13 @@
#include <vanetza/common/position_fix.hpp>
#include <boost/units/cmath.hpp>
namespace vanetza
{
bool has_horizontal_position(const PositionFix &posfix)
{
using namespace boost::units;
return isfinite(posfix.latitude) && isfinite(posfix.longitude);
}
} // namespace vanetza
@@ -0,0 +1,37 @@
#ifndef POSITION_FIX_HPP_BGU14Q9D
#define POSITION_FIX_HPP_BGU14Q9D
#include <vanetza/common/clock.hpp>
#include <vanetza/common/confident_quantity.hpp>
#include <vanetza/common/position_confidence.hpp>
#include <vanetza/units/angle.hpp>
#include <vanetza/units/velocity.hpp>
#include <vanetza/units/length.hpp>
#include <boost/optional/optional.hpp>
namespace vanetza
{
struct PositionFix
{
Clock::time_point timestamp;
units::GeoAngle latitude;
units::GeoAngle longitude;
PositionConfidence confidence;
ConfidentQuantity<units::TrueNorth> course;
ConfidentQuantity<units::Velocity> speed;
boost::optional<ConfidentQuantity<units::Length>> altitude;
};
/**
* Check if PositionFix contains a horizontal (2D) position.
* At least latitude and longitude must be valid.
* \param posfix position fix to be checked
* \return true if horizontal position is valid
*/
bool has_horizontal_position(const PositionFix& posfix);
} // namespace vanetza
#endif /* POSITION_FIX_HPP_BGU14Q9D */
@@ -0,0 +1,30 @@
#ifndef POSITION_PROVIDER_HPP_4CZDVALU
#define POSITION_PROVIDER_HPP_4CZDVALU
#include <vanetza/common/position_fix.hpp>
namespace vanetza
{
/**
* PositionProvider is a generic interface to retrieve positioning data.
*
* How the data has been gathered is not defined, i.e. it may come from an attached GNSS receiver or
* something completely different.
*/
class PositionProvider
{
public:
/**
* Get current position fix
* \return position with latitude and longitude
*/
virtual const PositionFix& position_fix() = 0;
virtual ~PositionProvider() = default;
};
} // namespace vanetza
#endif /* POSITION_PROVIDER_HPP_4CZDVALU */
@@ -0,0 +1,54 @@
#ifndef RUNTIME_HPP_KHDIEMRN
#define RUNTIME_HPP_KHDIEMRN
#include <vanetza/common/clock.hpp>
#include <functional>
namespace vanetza
{
/**
* Runtime provides current time and enables scheduling of tasks for later execution.
*
* All calls to Runtime and objects using the same Runtime have to be invoked from same thread!
**/
class Runtime
{
public:
using Callback = std::function<void(Clock::time_point)>;
/**
* Schedule callback for later invocation
* \param tp invoke callback at this time point
* \param cb callback
* \param scope associated scope pointer (used only for identification)
*/
virtual void schedule(Clock::time_point tp, const Callback& cb, const void* scope = nullptr) = 0;
/**
* Schedule callback for later invocation
* \param d duration from now until callback invocation
* \param cb callback
* \param scope associated scope pointer (used only for identification)
*/
virtual void schedule(Clock::duration d, const Callback& cb, const void* scope = nullptr) = 0;
/**
* Cancel all scheduled invocations assigned to certain scope
* \param scope any pointer used as scope at scheduling (nullptr has no effect)
*/
virtual void cancel(const void* scope) = 0;
/**
* Get current time
* \return current time
*/
virtual Clock::time_point now() const = 0;
virtual ~Runtime() = default;
};
} // namespace vanetza
#endif /* RUNTIME_HPP_KHDIEMRN */
@@ -0,0 +1,83 @@
#ifndef SERIALIZATION_HPP_U2YGHSPB
#define SERIALIZATION_HPP_U2YGHSPB
#include <vanetza/common/archives.hpp>
#include <vanetza/common/byte_order.hpp>
#include <boost/units/quantity.hpp>
#include <type_traits>
namespace vanetza
{
template<typename T, ByteOrder ORDER>
void serialize(OutputArchive& ar, EndianType<T, ORDER> value)
{
typedef typename decltype(value)::network_type network_type;
T tmp = static_cast<network_type>(value).get();
ar << tmp;
}
template<typename T, ByteOrder ORDER>
void deserialize(InputArchive& ar, EndianType<T, ORDER>& value)
{
T tmp;
ar >> tmp;
value = network_cast<T>(tmp);
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
serialize(OutputArchive& ar, T value)
{
auto tmp = hton(value);
ar << tmp;
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
deserialize(InputArchive& ar, T& value)
{
T tmp;
ar >> tmp;
value = ntoh(tmp);
}
template<typename U, typename T>
void serialize(OutputArchive& ar, boost::units::quantity<U, T> q)
{
static_assert(std::is_integral<T>::value,
"Only integral based quantities are supported");
auto tmp = hton(q.value());
ar << tmp;
}
template<typename U, typename T>
void deserialize(InputArchive& ar, boost::units::quantity<U, T>& q)
{
static_assert(std::is_integral<T>::value,
"Only integral based quantities are supported");
T tmp;
ar >> tmp;
q = boost::units::quantity<U, T>::from_value(ntoh(tmp));
}
template<class T>
typename std::enable_if<std::is_enum<T>::value>::type
serialize(OutputArchive& ar, const T& t)
{
serialize(ar, host_cast(static_cast<typename std::underlying_type<T>::type const>(t)));
}
template<class T>
typename std::enable_if<std::is_enum<T>::value>::type
deserialize(InputArchive& ar, T& t)
{
typename std::underlying_type<T>::type tmp;
deserialize(ar, tmp);
t = static_cast<T>(tmp);
}
} // namespace vanetza
#endif /* SERIALIZATION_HPP_U2YGHSPB */
@@ -0,0 +1,43 @@
#ifndef SERIALIZATION_BUFFER_HPP_KWLZAXD3
#define SERIALIZATION_BUFFER_HPP_KWLZAXD3
#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/byte_buffer_sink.hpp>
#include <vanetza/common/byte_buffer_source.hpp>
#include <vanetza/common/serialization.hpp>
#include <boost/iostreams/stream_buffer.hpp>
namespace vanetza
{
template<typename T>
void serialize_into_buffer(const T& t, ByteBuffer& buf)
{
byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
OutputArchive ar(stream);
serialize(ar, t);
}
template<typename T>
void deserialize_from_buffer(T& t, const ByteBuffer& buf)
{
byte_buffer_source source(buf);
boost::iostreams::stream_buffer<byte_buffer_source> stream(source);
InputArchive ar(stream);
deserialize(ar, t);
}
template<typename T>
void deserialize_from_range(T& t, typename byte_buffer_source::range range)
{
byte_buffer_source source(range);
boost::iostreams::stream_buffer<byte_buffer_source> stream(source);
InputArchive ar(stream);
deserialize(ar, t);
}
} // namespace vanetza
#endif /* SERIALIZATION_BUFFER_HPP_KWLZAXD3 */
@@ -0,0 +1,33 @@
#ifndef STORED_POSITION_PROVIDER_HPP_12MUJV0K
#define STORED_POSITION_PROVIDER_HPP_12MUJV0K
#include <vanetza/common/position_provider.hpp>
namespace vanetza
{
/**
* StoredPositionProvider is a very simple PositionProvider:
* it always returns the previously stored position fix
*/
class StoredPositionProvider : public PositionProvider
{
public:
const PositionFix& position_fix() override
{
return m_position;
}
void position_fix(const PositionFix& pos)
{
m_position = pos;
}
private:
PositionFix m_position;
};
} // namespace vanetza
#endif /* STORED_POSITION_PROVIDER_HPP_12MUJV0K */
@@ -0,0 +1,15 @@
include(UseGTest)
configure_gtest_directory(LINK_LIBRARIES common)
add_gtest(BitNumber bit_number.cpp)
add_gtest(ByteBuffer byte_buffer.cpp)
add_gtest(ByteBufferSink byte_buffer_sink.cpp)
add_gtest(ByteBufferSource byte_buffer_source.cpp)
add_gtest(ByteOrder byte_order.cpp)
add_gtest(ByteView byte_view.cpp)
add_gtest(Hook hook.cpp)
add_gtest(LruCache lru_cache.cpp)
add_gtest(ObjectContainer object_container.cpp)
add_gtest(ManualRuntime manual_runtime.cpp)
add_gtest(UnitInterval unit_interval.cpp)
@@ -0,0 +1,48 @@
#include <gtest/gtest.h>
#include <vanetza/common/bit_number.hpp>
#include <cstdint>
using namespace vanetza;
TEST(BitNumber, ctor) {
BitNumber<uint32_t, 20> a;
EXPECT_EQ(a.raw(), 0);
BitNumber<uint32_t, 20> b(0x0fffff);
EXPECT_EQ(b.raw(), 0x0fffff);
}
TEST(BitNumber, mask) {
BitNumber<uint32_t, 20> a(0xf01234);
EXPECT_EQ(a.raw(), 0x1234);
BitNumber<uint32_t, 1> b(4);
EXPECT_EQ(b.raw(), 0);
b = 1;
EXPECT_EQ(b.raw(), 1);
}
TEST(BitNumber, equality) {
BitNumber<int8_t, 3> a;
BitNumber<int8_t, 3> b(0);
EXPECT_EQ(a, b);
a = 3;
EXPECT_NE(a, b);
}
TEST(BitNumber, less) {
BitNumber<uint16_t, 4> a(3);
BitNumber<uint16_t, 4> b(4);
EXPECT_LT(a, b);
EXPECT_LE(a, b);
EXPECT_GT(b, a);
a = 4;
EXPECT_LE(a, b);
EXPECT_GE(a, b);
b = 17; // 17 is masked to 1
EXPECT_LE(b, a);
}
@@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer.hpp>
#include <cstring>
using namespace vanetza;
struct A
{
char b[10];
};
TEST(ByteBuffer, buffer_cast) {
ByteBuffer buf = { 'A', ' ', 't', 'e', 's', 't', ' ', 'b', 'u', 'f', 'f', 'e', 'r' };
ASSERT_GE(buf.size(), sizeof(A));
A* a = buffer_cast<A>(buf);
ASSERT_NE(nullptr, a);
EXPECT_EQ(a->b[0], 'A');
EXPECT_EQ(a->b[4], 's');
EXPECT_EQ(a->b[9], 'f');
}
TEST(ByteBuffer, buffer_copy) {
A a;
strcpy(a.b, "123456789");
auto copy = buffer_copy(a);
ASSERT_EQ(copy.size(), 10);
EXPECT_EQ(copy[0], '1');
EXPECT_EQ(copy[3], '4');
EXPECT_EQ(copy[8], '9');
EXPECT_EQ(copy[9], '\0');
a.b[0] = 'X';
EXPECT_EQ(copy[0], '1');
}
@@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer_sink.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <ostream>
#include <string>
using namespace vanetza;
TEST(ByteBufferSink, write) {
ByteBuffer buf;
byte_buffer_sink sink(buf);
boost::iostreams::stream_buffer<byte_buffer_sink> stream(sink);
ASSERT_TRUE(buf.empty());
std::ostream os(&stream);
ASSERT_TRUE(os.good());
const std::string data("Hello World!");
os << data;
os.flush();
EXPECT_EQ(buf.size(), 12);
for (unsigned i = 0; i < buf.size(); ++i) {
EXPECT_EQ(buf[i], data[i]);
}
EXPECT_TRUE(os.good());
}
@@ -0,0 +1,29 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_buffer_source.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <istream>
using namespace vanetza;
TEST(ByteBufferSource, read) {
const ByteBuffer buf = { 0x01, 0x11, 0x12, 0x22, 0x23, 0x33, 0x34, 0x44 };
byte_buffer_source source(buf);
boost::iostreams::stream_buffer<byte_buffer_source> stream(source);
std::istream is(&stream);
ASSERT_TRUE(is.good());
char read = '\0';
unsigned read_bytes = 0;
for (uint8_t byte : buf) {
is >> read;
EXPECT_NE(byte, '\0');
EXPECT_EQ(byte, read);
++read_bytes;
}
EXPECT_EQ(read_bytes, 8);
is >> read;
EXPECT_TRUE(is.eof());
}
@@ -0,0 +1,48 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_order.hpp>
#include <sstream>
using namespace vanetza;
using le_type = EndianType<uint32_t, ByteOrder::LittleEndian>;
using be_type = EndianType<uint32_t, ByteOrder::BigEndian>;
TEST(ByteOrder, equality)
{
EndianType<int, ByteOrder::BigEndian> a(3);
EndianType<int, ByteOrder::BigEndian> b(3);
EXPECT_EQ(a, b);
a = host_cast(static_cast<int>(4));
EXPECT_NE(a, b);
}
TEST(ByteOrder, access)
{
le_type a = host_cast<uint32_t>(0x12345678);
be_type b = host_cast<uint32_t>(0x12345678);
EXPECT_NE(a.get(), b.get());
EXPECT_EQ(a.net(), b.net());
EXPECT_EQ(a.host(), b.host());
}
TEST(ByteOrder, less)
{
uint8be_t a { 3 };
uint8be_t b { 5 };
uint8be_t c { 5 };
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < c);
EXPECT_FALSE(b < a);
}
TEST(ByteOrder, hash)
{
le_type a = host_cast<uint32_t>(0x12345678);
be_type b = host_cast<uint32_t>(0x12345678);
be_type c = host_cast<uint32_t>(0x78563412);
EXPECT_EQ(std::hash<le_type>()(a), std::hash<le_type>()(a));
EXPECT_EQ(std::hash<be_type>()(b), std::hash<be_type>()(b));
EXPECT_NE(std::hash<be_type>()(b), std::hash<be_type>()(c));
}
@@ -0,0 +1,28 @@
#include <gtest/gtest.h>
#include <vanetza/common/byte_view.hpp>
#include <vanetza/common/byte_buffer_convertible.hpp>
using namespace vanetza;
TEST(ByteView, range_buffer) {
ByteBuffer buffer = {1, 2, 3, 4, 5, 6, 7, 8};
byte_view_range view = create_byte_view(buffer);
ASSERT_EQ(buffer.size(), view.size());
EXPECT_EQ(1, *view.begin());
EXPECT_EQ(3, view[2]);
}
TEST(ByteView, range_convertible) {
byte_view_range view = create_byte_view(ByteBuffer());
{
std::string buffer = "temporary data";
ByteBufferConvertible convertible { std::move(buffer) };
// view should now own a byte buffer copy of original string
view = create_byte_view(convertible);
}
ASSERT_EQ(14, view.size());
EXPECT_EQ('t', view[0]);
EXPECT_EQ('y', view[8]);
EXPECT_EQ('d', view[10]);
}
@@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <vanetza/common/hook.hpp>
#include <string>
#include <vector>
using namespace vanetza;
TEST(Hook, variants) {
Hook<int> hook_int;
hook_int(28); // rvalue call
int x = 29;
hook_int(x); // lvalue (exact type)
int& y = x;
hook_int(y); // lvalue (compatible type)
Hook<double, float> hook_fp;
hook_fp(23.0, -42.0f);
Hook<const std::string&, std::vector<int>> hook_objects;
hook_objects("foo", {3, 2});
Hook<std::string&&> hook_rvalue_ref;
hook_rvalue_ref("bar");
}
TEST(Hook, invocation) {
Hook<double, float> hook;
double d = 3.0;
float f = 5.3f;
// empty hook does nothing
hook(0.0, 1.0f);
EXPECT_EQ(3.0, d);
EXPECT_EQ(5.3f, f);
// set hook and test it's magic
hook = [&d, &f](double _d, float _f) { d = _d; f = _f; };
hook(23.1, -384.34f);
EXPECT_EQ(23.1, d);
EXPECT_EQ(-384.34f, f);
// reset hook and it should do nothing again
hook.reset();
hook(0.0, 3.33f);
EXPECT_EQ(23.1, d);
EXPECT_EQ(-384.34f, f);
}
TEST(HookRegistry, registration) {
Hook<double> hook;
HookRegistry<double> registry(hook);
double d = 42.0;
registry = [&d](double _d) { d = _d; };
hook(21.0);
EXPECT_EQ(21.0, d);
}
@@ -0,0 +1,82 @@
#include <gtest/gtest.h>
#include <vanetza/common/lru_cache.hpp>
using namespace vanetza;
class LruCacheTest : public ::testing::Test
{
protected:
void SetUp() override
{
generator_calls = 0;
last_key = 0;
}
std::function<int(int)> generator()
{
return [this](int key) {
++generator_calls;
last_key = key;
return ~key;
};
}
unsigned generator_calls;
int last_key;
};
TEST_F(LruCacheTest, caching)
{
LruCache<int, int> cache(generator(), 5);
// generate one new entry
EXPECT_EQ(~8, cache[8]);
EXPECT_EQ(1, generator_calls);
// refer to cached entry
EXPECT_EQ(~8, cache[8]);
EXPECT_EQ(1, generator_calls);
// further entry
EXPECT_EQ(~4, cache[4]);
EXPECT_EQ(2, generator_calls);
// first entry is still there
EXPECT_EQ(~8, cache[8]);
EXPECT_EQ(2, generator_calls);
}
TEST_F(LruCacheTest, modify)
{
LruCache<int, int> cache(generator(), 5);
// modify new cache entry
cache[8] = 23;
EXPECT_EQ(23, cache[8]);
// modify existing entry
cache[8] = 42;
EXPECT_EQ(42, cache[8]);
EXPECT_EQ(1, generator_calls);
}
TEST_F(LruCacheTest, drop_lru)
{
LruCache<int, int> cache(generator(), 3);
cache[1];
cache[2];
cache[3];
EXPECT_EQ(3, generator_calls);
EXPECT_EQ(~2, cache[2]); // LRU: 1, 3, 2
cache[4]; // drop 1, LRU: 3, 2, 4
cache[1]; // re-create entry, LRU: 2, 4, 1
EXPECT_EQ(5, generator_calls);
EXPECT_EQ(~2, cache[2]);
EXPECT_EQ(~4, cache[4]);
EXPECT_EQ(~1, cache[1]);
EXPECT_EQ(5, generator_calls);
}
@@ -0,0 +1,199 @@
#include <gtest/gtest.h>
#include <vanetza/common/manual_runtime.hpp>
#include <chrono>
#include <functional>
#include <string>
#include <vector>
using namespace vanetza;
using std::chrono::hours;
using std::chrono::minutes;
using std::chrono::seconds;
TEST(ManualRuntime, default_construction)
{
ManualRuntime r;
EXPECT_EQ(std::chrono::milliseconds(0), r.now().time_since_epoch());
}
TEST(ManualRuntime, time_progress_absolute)
{
ManualRuntime r;
const Clock::time_point t1 { hours(27) };
r.trigger(t1);
EXPECT_EQ(t1, r.now());
const Clock::time_point t2 { hours(28) };
r.trigger(t2);
EXPECT_EQ(t2, r.now());
}
TEST(ManualRuntime, time_progress_relative)
{
ManualRuntime r;
r.trigger(hours(3));
EXPECT_EQ(Clock::time_point { hours(3) }, r.now());
r.trigger(hours(2));
EXPECT_EQ(Clock::time_point { hours(5) }, r.now());
}
TEST(ManualRuntime, sorting)
{
ManualRuntime r;
r.trigger(hours(3));
EXPECT_EQ(Clock::time_point::max(), r.next());
auto cb = [](Clock::time_point) {};
const auto tp1 = Clock::time_point { hours(2) };
static_cast<Runtime*>(&r)->schedule(tp1, cb);
EXPECT_EQ(tp1, r.next());
r.schedule(Clock::time_point { hours(3) }, cb);
EXPECT_EQ(tp1, r.next());
const auto tp2 = Clock::time_point { hours(1) };
r.schedule(tp2, cb);
EXPECT_EQ(tp2, r.next());
r.schedule(minutes(30), cb);
EXPECT_EQ(tp2, r.next());
}
TEST(ManualRuntime, scheduling)
{
ManualRuntime r;
r.trigger(hours(5));
namespace ph = std::placeholders;
std::string seq;
std::vector<Clock::time_point> deadlines;
auto cb = [&seq, &deadlines](const char* str, Clock::time_point deadline) {
deadlines.push_back(deadline);
seq.append(str);
};
r.schedule(hours(10), std::bind<void>(cb, "1", ph::_1));
r.schedule(hours(11), std::bind<void>(cb, "2", ph::_1));
r.schedule(hours(11), std::bind<void>(cb, "2", ph::_1));
r.schedule(hours(5), std::bind<void>(cb, "3", ph::_1));
r.trigger(hours(4));
EXPECT_EQ("", seq);
r.trigger(hours(1));
EXPECT_EQ("3", seq);
r.trigger(hours(5));
EXPECT_EQ("31", seq);
// schedule expired callback (immediate invocation at next trigger)
r.schedule(Clock::time_point { hours(2) }, std::bind<void>(cb, "4", ph::_1));
r.trigger(hours(0));
EXPECT_EQ("314", seq);
r.trigger(hours(5));
EXPECT_EQ("31422", seq);
r.trigger(Clock::time_point::max());
EXPECT_EQ("31422", seq);
const std::vector<Clock::time_point> expected_deadlines = {
Clock::time_point { hours(10) },
Clock::time_point { hours(15) },
Clock::time_point { hours(2) },
Clock::time_point { hours(16) },
Clock::time_point { hours(16) },
};
EXPECT_EQ(expected_deadlines, deadlines);
}
TEST(ManualRuntime, reset)
{
ManualRuntime r;
unsigned calls = 0;
auto cb = [&calls](Clock::time_point) { ++calls; };
r.trigger(hours(23));
for (unsigned i = 10; i < 100; ++i) {
r.schedule(seconds(i), cb);
}
r.trigger(seconds(9));
EXPECT_EQ(0, calls);
r.trigger(seconds(2));
EXPECT_EQ(2, calls);
EXPECT_EQ(Clock::time_point { hours(23) + seconds(11) }, r.now());
r.reset(Clock::time_point { hours(10) });
EXPECT_EQ(Clock::time_point { hours(10) }, r.now());
EXPECT_EQ(90, calls);
r.trigger(Clock::duration::max());
EXPECT_EQ(90, calls);
}
TEST(ManualRuntime, cancel)
{
ManualRuntime r;
std::vector<char> calls;
auto cb = [&calls](char c, Clock::time_point) { calls.push_back(c); };
// some dummy scopes
const int foo = 1;
const int bar = 2;
const int doe = 3;
namespace ph = std::placeholders;
r.schedule(minutes(3), std::bind<void>(cb, 'a', ph::_1));
r.schedule(minutes(4), std::bind<void>(cb, 'b', ph::_1), &foo);
r.schedule(minutes(5), std::bind<void>(cb, 'c', ph::_1));
r.schedule(minutes(3), std::bind<void>(cb, 'd', ph::_1), &bar);
r.schedule(minutes(4), std::bind<void>(cb, 'e', ph::_1));
r.schedule(minutes(5), std::bind<void>(cb, 'f', ph::_1), &foo);
r.schedule(minutes(6), std::bind<void>(cb, 'g', ph::_1), &doe);
r.schedule(minutes(7), std::bind<void>(cb, 'h', ph::_1), nullptr);
// cancel single callback
r.cancel(&bar);
r.trigger(minutes(8));
EXPECT_EQ((std::vector<char> {'a', 'b', 'e', 'c', 'f', 'g', 'h'}), calls);
// cancel several callbacks
calls.clear();
r.schedule(minutes(1), std::bind<void>(cb, 'a', ph::_1), &foo);
r.schedule(minutes(1), std::bind<void>(cb, 'b', ph::_1), &bar);
r.schedule(minutes(1), std::bind<void>(cb, 'c', ph::_1), &bar);
r.schedule(minutes(1), std::bind<void>(cb, 'd', ph::_1), &foo);
r.cancel(&foo);
r.trigger(minutes(1));
EXPECT_EQ((std::vector<char> {'b', 'c'}), calls);
}
TEST(ManualRuntime, scope)
{
ManualRuntime r;
std::vector<char> calls;
auto cb = [&calls](char c, Clock::time_point) { calls.push_back(c); };
int scope1, scope2;
namespace ph = std::placeholders;
r.schedule(minutes(3), std::bind<void>(cb, 'a', ph::_1), &scope1);
r.schedule(minutes(1), std::bind<void>(cb, 'b', ph::_1), &scope2);
r.schedule(minutes(2), std::bind<void>(cb, 'c', ph::_1));
r.schedule(minutes(5), std::bind<void>(cb, 'd', ph::_1), &scope1);
r.schedule(minutes(4), std::bind<void>(cb, 'e', ph::_1), &scope2);
r.schedule(minutes(6), std::bind<void>(cb, 'f', ph::_1), "");
// cancel scope 1 and trigger all callbacks within 3 minutes
r.cancel(&scope1);
r.trigger(minutes(3));
EXPECT_EQ((std::vector<char> {'b', 'c'}), calls);
calls.clear();
// canceling nullptr scope has no effect
r.cancel(nullptr);
r.trigger(minutes(10));
EXPECT_EQ((std::vector<char> {'e', 'f'}), calls);
}
@@ -0,0 +1,117 @@
#include <gtest/gtest.h>
#include <vanetza/common/object_container.hpp>
using namespace vanetza;
struct ObjectA
{
int a = 1;
};
struct ObjectB
{
int b = 2;
};
struct ObjectC
{
int c = 3;
};
template<typename T>
std::unique_ptr<T> create_unique()
{
return std::unique_ptr<T> { new T() };
}
TEST(ObjectContainer, size)
{
ObjectContainer c;
EXPECT_EQ(0, c.size());
c.insert(create_unique<ObjectA>());
EXPECT_EQ(1, c.size());
c.insert(create_unique<ObjectA>());
EXPECT_EQ(1, c.size());
c.insert(create_unique<ObjectB>());
EXPECT_EQ(2, c.size());
c.clear();
EXPECT_EQ(0, c.size());
}
TEST(ObjectContainer, insert)
{
ObjectContainer c;
EXPECT_TRUE(c.insert(create_unique<ObjectA>()));
EXPECT_FALSE(c.insert(create_unique<ObjectA>()));
EXPECT_TRUE(c.insert(create_unique<ObjectB>()));
}
TEST(ObjectContainer, find)
{
ObjectContainer c;
EXPECT_EQ(nullptr, c.find<ObjectA>());
auto a = create_unique<ObjectA>();
ObjectA* pa = a.get();
auto b = create_unique<ObjectB>();
ObjectB* pb = b.get();
c.insert(std::move(a));
c.insert(std::move(b));
EXPECT_EQ(pa, c.find<ObjectA>());
EXPECT_EQ(pb, c.find<ObjectB>());
}
TEST(ObjectContainer, erase)
{
ObjectContainer c;
c.insert(create_unique<ObjectA>());
ASSERT_EQ(1, c.size());
c.erase<ObjectB>();
EXPECT_EQ(1, c.size());
c.erase<ObjectA>();
EXPECT_EQ(0, c.size());
c.insert(create_unique<ObjectA>());
c.insert(create_unique<ObjectB>());
c.insert(create_unique<ObjectC>());
c.erase<ObjectB>();
EXPECT_EQ(2, c.size());
EXPECT_NE(nullptr, c.find<ObjectA>());
EXPECT_EQ(nullptr, c.find<ObjectB>());
EXPECT_NE(nullptr, c.find<ObjectC>());
}
TEST(ObjectContainer, move)
{
ObjectContainer c1;
c1.insert(create_unique<ObjectA>());
c1.insert(create_unique<ObjectB>());
c1.insert(create_unique<ObjectC>());
ASSERT_EQ(3, c1.size());
auto* pa = c1.find<ObjectA>();
auto* pb = c1.find<ObjectB>();
auto* pc = c1.find<ObjectC>();
ObjectContainer c2 = std::move(c1);
EXPECT_EQ(0, c1.size());
EXPECT_EQ(3, c2.size());
EXPECT_EQ(pa, c2.find<ObjectA>());
EXPECT_EQ(pb, c2.find<ObjectB>());
EXPECT_EQ(pc, c2.find<ObjectC>());
}
TEST(ObjectContainer, get)
{
ObjectContainer c;
EXPECT_EQ(nullptr, c.find<ObjectA>());
ObjectA& a = c.get<ObjectA>();
EXPECT_EQ(1, a.a);
}
@@ -0,0 +1,168 @@
#include <gtest/gtest.h>
#include <vanetza/common/unit_interval.hpp>
using namespace vanetza;
namespace vanetza {
void PrintTo(const UnitInterval& cl, std::ostream* os) { *os << cl.value(); }
}
TEST(UnitInterval, construction)
{
UnitInterval v1;
EXPECT_DOUBLE_EQ(0.0, v1.value());
UnitInterval v2(0.42);
EXPECT_DOUBLE_EQ(0.42, v2.value());
UnitInterval v3 = v2;
EXPECT_DOUBLE_EQ(0.42, v3.value());
v3 = v1;
EXPECT_DOUBLE_EQ(0.0, v3.value());
}
TEST(UnitInterval, partially_ordered)
{
// only test < and == (other operators are provided by Boost)
EXPECT_LT(UnitInterval(0.3), UnitInterval(0.4));
EXPECT_EQ(UnitInterval(0.5), UnitInterval(0.5));
// stress equality comparison
UnitInterval v1 { 0.0000001 };
UnitInterval v2 { 0.00000005 };
EXPECT_EQ(v1 * 1000000, v2 * 2000000);
EXPECT_NE(UnitInterval(0.000000001), UnitInterval(0.0000000011));
}
TEST(UnitInterval, range)
{
UnitInterval v1 { 3.14 };
EXPECT_EQ(UnitInterval(1.0), v1);
UnitInterval v2 { -42.0 };
EXPECT_EQ(UnitInterval(0.0), v2);
}
TEST(UnitInterval, arithmetic_interval)
{
// only test +=, -=, *=, /= (symmetric operators by Boost)
UnitInterval a1(0.45);
a1 += UnitInterval(0.53);
EXPECT_EQ(UnitInterval(0.98), a1);
UnitInterval a2(0.45);
a2 += UnitInterval(0.6);
EXPECT_EQ(UnitInterval(1.0), a2);
UnitInterval s1(0.45);
s1 -= UnitInterval(0.35);
EXPECT_EQ(UnitInterval(0.1), s1);
UnitInterval s2(0.3);
s2 -= UnitInterval(0.4);
EXPECT_EQ(UnitInterval(0.0), s2);
UnitInterval m1(0.2);
m1 *= UnitInterval(0.5);
EXPECT_EQ(UnitInterval(0.1), m1);
UnitInterval m2(0.4);
m2 *= UnitInterval(0.0);
EXPECT_EQ(UnitInterval(0.0), m2);
UnitInterval m3(1.0);
m3 *= UnitInterval(1.0);
EXPECT_EQ(UnitInterval(1.0), m3);
UnitInterval d1(0.6);
d1 /= UnitInterval(0.8);
EXPECT_EQ(UnitInterval(0.75), d1);
UnitInterval d2(0.5);
d2 /= UnitInterval(0.1);
EXPECT_EQ(UnitInterval(1.0), d2);
}
TEST(UnitInterval, arithmetic_double)
{
// only test +=, -=, *=, /= (symmetric operators by Boost)
UnitInterval a1(0.45);
a1 += 0.53;
EXPECT_EQ(UnitInterval(0.98), a1);
UnitInterval a2(0.45);
a2 += 0.6;
EXPECT_EQ(UnitInterval(1.0), a2);
UnitInterval a3(0.45);
a3 += -0.7;
EXPECT_EQ(UnitInterval(0.0), a3);
UnitInterval s1(0.45);
s1 -= 0.35;
EXPECT_EQ(UnitInterval(0.1), s1);
UnitInterval s2(0.3);
s2 -= 0.4;
EXPECT_EQ(UnitInterval(0.0), s2);
UnitInterval s3(0.3);
s3 -= -0.8;
EXPECT_EQ(UnitInterval(1.0), s3);
UnitInterval m1(0.2);
m1 *= 0.5;
EXPECT_EQ(UnitInterval(0.1), m1);
UnitInterval m2(0.4);
m2 *= 0.0;
EXPECT_EQ(UnitInterval(0.0), m2);
UnitInterval m3(1.0);
m3 *= 1.2;
EXPECT_EQ(UnitInterval(1.0), m3);
UnitInterval m4(0.3);
m4 *= -0.1;
EXPECT_EQ(UnitInterval(0.0), m4);
UnitInterval d1(0.6);
d1 /= 0.8;
EXPECT_EQ(UnitInterval(0.75), d1);
UnitInterval d2(0.5);
d2 /= 0.1;
EXPECT_EQ(UnitInterval(1.0), d2);
UnitInterval d3(0.2);
d3 /= 2.0;
EXPECT_EQ(UnitInterval(0.1), d3);
UnitInterval d4(0.5);
d4 /= -0.4;
EXPECT_EQ(UnitInterval(0.0), d4);
}
TEST(UnitInterval, complement)
{
EXPECT_EQ(UnitInterval(0.0), UnitInterval(1.0).complement());
EXPECT_EQ(UnitInterval(1.0), UnitInterval(0.0).complement());
EXPECT_EQ(UnitInterval(1.0), UnitInterval(0.67) + UnitInterval(0.67).complement());
}
TEST(UnitInterval, mean)
{
EXPECT_EQ(UnitInterval(0.3), mean(UnitInterval(0.1), UnitInterval(0.5)));
EXPECT_EQ(UnitInterval(0.0), mean(UnitInterval(0.0), UnitInterval(0.0)));
EXPECT_EQ(UnitInterval(0.75), mean(UnitInterval(1.0), UnitInterval(0.5)));
}
TEST(UnitInterval, mean_range)
{
UnitInterval a[3] = { UnitInterval (0.4), UnitInterval(0.2), UnitInterval(0.9) };
EXPECT_EQ(UnitInterval(0.0), mean(a, a));
EXPECT_EQ(UnitInterval(0.2), mean(a + 1, a + 2));
EXPECT_EQ(UnitInterval(0.3), mean(a, a + 2 ));
EXPECT_EQ(UnitInterval(0.5), mean(a, a + 3));
}
@@ -0,0 +1,89 @@
#include <vanetza/common/unit_interval.hpp>
#include <algorithm>
#include <cmath>
#include <limits>
namespace vanetza
{
UnitInterval& UnitInterval::operator+=(const UnitInterval& other)
{
m_value += other.m_value;
return clamp();
}
UnitInterval& UnitInterval::operator-=(const UnitInterval& other)
{
m_value -= other.m_value;
return clamp();
}
UnitInterval& UnitInterval::operator*=(const UnitInterval& other)
{
m_value *= other.m_value;
// all unit interval multiplications remain within range
return *this;
}
UnitInterval& UnitInterval::operator/=(const UnitInterval& other)
{
m_value /= other.m_value;
// only upper limit has to be enforced
m_value = std::min(m_value, 1.0);
return *this;
}
UnitInterval& UnitInterval::operator+=(double value)
{
m_value += value;
return clamp();
}
UnitInterval& UnitInterval::operator-=(double value)
{
m_value -= value;
return clamp();
}
UnitInterval& UnitInterval::operator*=(double value)
{
m_value *= value;
return clamp();
}
UnitInterval& UnitInterval::operator/=(double value)
{
m_value /= value;
return clamp();
}
bool UnitInterval::operator<(const UnitInterval& other) const
{
return m_value < other.m_value;
}
bool UnitInterval::operator==(const UnitInterval& other) const
{
// epsilon should be fine for values in [0.0, 1.0]
return std::abs(m_value - other.m_value) < std::numeric_limits<double>::epsilon();
}
UnitInterval& UnitInterval::clamp()
{
m_value = clamp(m_value);
return *this;
}
UnitInterval UnitInterval::complement() const
{
UnitInterval complement;
complement.m_value = 1.0 - m_value;
return complement;
}
UnitInterval mean(UnitInterval lhs, UnitInterval rhs)
{
return UnitInterval { 0.5 * (lhs.value() + rhs.value()) };
}
} // namespace vanetza
@@ -0,0 +1,96 @@
#ifndef UNIT_INTERVAL_HPP_BG1EK7QX
#define UNIT_INTERVAL_HPP_BG1EK7QX
#include <boost/operators.hpp>
#include <iterator>
#include <type_traits>
namespace vanetza
{
/**
* UnitInterval represents a number within the unit interval [0.0, 1.0]
*
* UnitInterval is not an interval on its own but limits all numbers to this interval.
* Mantissa (positive fractional part of a real number) behaves differently, thus:
* - Mantissa(42.1234) = 0.1234
* - UnitInterval(42.1234) = 1.0
* UnitInterval is also related to "(proper) decimal fraction" but latter does not include 1.0.
*/
class UnitInterval :
boost::arithmetic<UnitInterval>,
boost::arithmetic<UnitInterval, double>,
boost::totally_ordered<UnitInterval>
{
public:
constexpr UnitInterval() : UnitInterval(0.0) {}
constexpr explicit UnitInterval(double v) : m_value(clamp(v)) {}
UnitInterval(const UnitInterval&) = default;
UnitInterval& operator=(const UnitInterval&) = default;
// arithmetic
UnitInterval& operator+=(const UnitInterval&);
UnitInterval& operator-=(const UnitInterval&);
UnitInterval& operator*=(const UnitInterval&);
UnitInterval& operator/=(const UnitInterval&);
UnitInterval& operator+=(double);
UnitInterval& operator-=(double);
UnitInterval& operator*=(double);
UnitInterval& operator/=(double);
// partially ordered
bool operator<(const UnitInterval& other) const;
bool operator==(const UnitInterval& other) const;
double value() const { return m_value; }
UnitInterval complement() const;
private:
constexpr static double clamp(double v)
{
return (v > 1.0 ? 1.0 : (v < 0.0 ? 0.0 : v));
}
UnitInterval& clamp();
double m_value;
};
/**
* Calculate mean value of two unit intervals
* \param lhs
* \param rhs
* \return mean unit interval
*/
UnitInterval mean(UnitInterval lhs, UnitInterval rhs);
/**
* Calculate mean of a range of unit intervals
* \param begin of range
* \params end of range
* \reutrn mean unit interval
*/
template<
typename Iterator,
typename std::enable_if<
std::is_convertible<typename std::iterator_traits<Iterator>::value_type, UnitInterval>::value,
int>::type = 0
>
UnitInterval mean(Iterator begin, Iterator end)
{
unsigned count = 0;
double accu = 0.0;
for (Iterator it = begin; it != end; ++it)
{
accu += it->value();
++count;
}
return UnitInterval { count > 1 ? (accu / count) : accu };
}
} // namespace vanetza
#endif /* UNIT_INTERVAL_HPP_BG1EK7QX */