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,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
|
||||
|
||||
+109
@@ -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 */
|
||||
|
||||
+33
@@ -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());
|
||||
}
|
||||
|
||||
+29
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user