#1258 Manchester coding (#1285)

* manchester
* Added manchester code and test

* manchester
* Formatting and added missing file

* manchester
* Some functions can only be constexpr since C++14

* manchester
* Manchester decode and some refactoring

* manchester
* Added some missing typenames

* manchester
* constexpr void function not allowed in C++11

* manchester
* condition on static_assert tests

* manchester
* revert CMakeLists.txt
* Using ETL_STATIC_ASSERT
* Some cleanup

* manchester
* Added static_assert message

* manchester
* Added compile time tests

* manchester
* Added invert manchester
* Some refactoring

* manchester
* Disable test for now
* Move ETL_NODISCARD before static

* manchester
* Test for valid_span

* manchester
* Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template

* manchester
* refactoring to get rid of specialized template functions in template class

* manchester
* cleanup

* manchester
* Added documentation comments
* Some refactoring

* manchester
* introducing namespace detail_manchester

* manchester
* Some refactoring
* Update tests

* manchester
* Some refactoring
* Removed possible undefined behavior by refactoring encode_span
* constexpr version of encode_span
* Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t

* manchester
* renamed valid to is_valid

* manchester
* renamed is_valid_span to is_valid
* Using etl exceptions in ETL_ASSERT

* manchester
* Removed _fast functions
* merged encode_in_place with encode and decode_in_place with decode
* removed _span to create normal overloads of encode and decode for span
* Some renaming and minor refactoring

* manchester
* Fix build issues

* manchester
* Conditionally compile manchester_decoded

* Update test_manchester.cpp

Removed redundant semicolon

* #1258 Manchester coding
* Formatting
* consistency: hex literals with lower case 0x

* #1258 Manchester coding
* Moved copyright to top of file
* Make constexpr encode/decode span functions equal for little and big endian platforms

* #1258 Manchester coding
* Added missing include
* Added missing 8bit/64bit guards
* Fixed is_valid for big endian platforms

* #1258 Manchester coding
* private memcpy alias

* #1258 Manchester coding
* Review comments

* #1258 Manchester coding
* Cleanup
* Fix build error

---------

Co-authored-by: Timon Zijnge <timon.zijnge@imec.nl>
This commit is contained in:
Timon Zijnge 2026-02-27 10:40:42 +01:00 committed by GitHub
parent 7ef01fcaed
commit 810a106bdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 942 additions and 0 deletions

558
include/etl/manchester.h Normal file
View File

@ -0,0 +1,558 @@
///\file
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_MANCHESTER_INCLUDED
#define ETL_MANCHESTER_INCLUDED
#include "platform.h"
#include "endianness.h"
#include "span.h"
#include "static_assert.h"
#include <cstring>
///\defgroup manchester manchester
/// Manchester encoding and decoding
///\ingroup utilities
namespace etl
{
namespace private_manchester
{
//*************************************************************************
/// Type trait to determine if a type is supported as an encode input.
///\tparam TChunk The input type to check.
//*************************************************************************
template <typename TChunk>
struct is_encodable
{
static const bool value =
#if ETL_USING_8BIT_TYPES
etl::is_same<TChunk, uint8_t>::value ||
#endif
etl::is_same<TChunk, uint16_t>::value
#if ETL_USING_64BIT_TYPES
|| etl::is_same<TChunk, uint32_t>::value
#endif
;
};
//*************************************************************************
/// Type trait to determine if a type is supported as a decode input.
///\tparam TChunk The input type to check.
//*************************************************************************
template <typename TChunk>
struct is_decodable
{
static const bool value =
#if ETL_USING_8BIT_TYPES
etl::is_same<TChunk, uint16_t>::value ||
#endif
etl::is_same<TChunk, uint32_t>::value
#if ETL_USING_64BIT_TYPES
|| etl::is_same<TChunk, uint64_t>::value
#endif
;
};
//*************************************************************************
/// Type trait to determine the encoded type for a given decoded type.
/// Encoding doubles the bit width: uint8_t->uint16_t, uint16_t->uint32_t, etc.
///\tparam T The decoded type.
//*************************************************************************
template <typename T>
struct encoded
{
ETL_STATIC_ASSERT(sizeof(T) == 0, "Manchester encoding type should be one of [uint8_t, uint16_t, uint32_t]");
};
#if ETL_USING_8BIT_TYPES
template <>
struct encoded<uint8_t>
{
typedef uint16_t type;
};
#endif
template <>
struct encoded<uint16_t>
{
typedef uint32_t type;
};
#if ETL_USING_64BIT_TYPES
template <>
struct encoded<uint32_t>
{
typedef uint64_t type;
};
#endif
//*************************************************************************
/// Type trait to determine the decoded type for a given encoded type.
/// Decoding halves the bit width: uint16_t->uint8_t, uint32_t->uint16_t, etc.
///\tparam T The encoded type.
//*************************************************************************
template <typename T>
struct decoded
{
ETL_STATIC_ASSERT(sizeof(T) == 0, "Manchester decoding type should be one of [uint16_t, uint32_t, uint64_t]");
};
#if ETL_USING_64BIT_TYPES
template <>
struct decoded<uint16_t>
{
typedef uint8_t type;
};
#endif
template <>
struct decoded<uint32_t>
{
typedef uint16_t type;
};
#if ETL_USING_64BIT_TYPES
template <>
struct decoded<uint64_t>
{
typedef uint32_t type;
};
#endif
//*************************************************************************
/// Normal Manchester encoding type (no inversion).
//*************************************************************************
struct manchester_type_normal
{
#if ETL_USING_64BIT_TYPES
static const uint64_t inversion_mask = 0x0000000000000000ULL;
#else
static const uint32_t inversion_mask = 0x00000000UL;
#endif
};
//*************************************************************************
/// Inverted Manchester encoding type.
//*************************************************************************
struct manchester_type_inverted
{
#if ETL_USING_64BIT_TYPES
static const uint64_t inversion_mask = 0xFFFFFFFFFFFFFFFFULL;
#else
static const uint32_t inversion_mask = 0xFFFFFFFFUL;
#endif
};
//*************************************************************************
/// Alias for memcpy. etl::mem_copy is not suitable for the Manchester
/// algorithm because all memory copies are between different types. This
/// alias is a way to respect ETL_USING_BUILTIN_MEMCPY while using the
/// memcpy function signature
//*************************************************************************
inline void* memcpy(void* dest, const void* src, std::size_t count) ETL_NOEXCEPT
{
#if ETL_USING_BUILTIN_MEMCPY
return __builtin_memcpy(dest, src, count);
#else
return ::memcpy(dest, src, count);
#endif
}
} // namespace private_manchester
//***************************************************************************
/// Exception for Manchester.
//***************************************************************************
class manchester_exception : public etl::exception
{
public:
manchester_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
: exception(reason_, file_name_, line_number_)
{
}
};
//***************************************************************************
/// Invalid size exception for Manchester
//***************************************************************************
class manchester_invalid_size : public etl::manchester_exception
{
public:
manchester_invalid_size(string_type file_name_, numeric_type line_number_)
: etl::manchester_exception("manchester:size", file_name_, line_number_)
{
}
};
//***************************************************************************
///\ingroup manchester
/// Base template class for Manchester encoding and decoding.
///\tparam TType The Manchester encoding type (normal or inverted).
//***************************************************************************
template <typename TManchesterType>
struct manchester_base
{
ETL_STATIC_ASSERT((etl::is_same<TManchesterType, private_manchester::manchester_type_normal>::value ||
etl::is_same<TManchesterType, private_manchester::manchester_type_inverted>::value),
"TManchesterType must be manchester_type_normal or manchester_type_inverted");
ETL_STATIC_ASSERT(CHAR_BIT == etl::numeric_limits<uint_least8_t>::digits, "Manchester requires uint_least8_t to have the same number of bits as CHAR (CHAR_BITS)");
//*************************************************************************
// Encoding functions
//*************************************************************************
#if ETL_USING_8BIT_TYPES
//*************************************************************************
/// Encode a 8-bit unsigned value and return 16-bit result.
///\param decoded The value to encode.
///\return The Manchester encoded value.
//*************************************************************************
template <typename TDecoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TDecoded, uint8_t>::value, typename private_manchester::encoded<TDecoded>::type>::type
encode(TDecoded decoded)
{
typedef typename private_manchester::encoded<TDecoded>::type TEncoded;
TEncoded encoded = decoded;
encoded = (encoded | (encoded << 4U)) & 0x0F0FU;
encoded = (encoded | (encoded << 2U)) & 0x3333U;
encoded = (encoded | (encoded << 1U)) & 0x5555U;
encoded = (encoded | (encoded << 1U)) ^ (0xAAAAU ^ static_cast<TEncoded>(TManchesterType::inversion_mask));
return encoded;
}
#endif
//*************************************************************************
/// Encode a 16-bit unsigned value and return the 32-bit result.
///\param decoded The value to encode.
///\return The Manchester encoded value.
//*************************************************************************
template <typename TDecoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TDecoded, uint16_t>::value, typename private_manchester::encoded<TDecoded>::type>::type
encode(TDecoded decoded)
{
typedef typename private_manchester::encoded<TDecoded>::type TEncoded;
TEncoded encoded = decoded;
encoded = (encoded | (encoded << 8U)) & 0x00FF00FFUL;
encoded = (encoded | (encoded << 4U)) & 0x0F0F0F0FUL;
encoded = (encoded | (encoded << 2U)) & 0x33333333UL;
encoded = (encoded | (encoded << 1U)) & 0x55555555UL;
encoded = (encoded | (encoded << 1U)) ^ (0xAAAAAAAAUL ^ static_cast<TEncoded>(TManchesterType::inversion_mask));
return encoded;
}
#if ETL_USING_64BIT_TYPES
//*************************************************************************
/// Encode a 32-bit unsigned value and return the 64-bit result.
///\param decoded The value to encode.
///\return The Manchester encoded value.
//*************************************************************************
template <typename TDecoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TDecoded, uint32_t>::value, typename private_manchester::encoded<TDecoded>::type>::type
encode(TDecoded decoded)
{
typedef typename private_manchester::encoded<TDecoded>::type TEncoded;
TEncoded encoded = decoded;
encoded = (encoded | (encoded << 16U)) & 0x0000FFFF0000FFFFULL;
encoded = (encoded | (encoded << 8U)) & 0x00FF00FF00FF00FFULL;
encoded = (encoded | (encoded << 4U)) & 0x0F0F0F0F0F0F0F0FULL;
encoded = (encoded | (encoded << 2U)) & 0x3333333333333333ULL;
encoded = (encoded | (encoded << 1U)) & 0x5555555555555555ULL;
encoded = (encoded | (encoded << 1U)) ^ (0xAAAAAAAAAAAAAAAAULL ^ TManchesterType::inversion_mask);
return encoded;
}
#endif
//*************************************************************************
/// Encode a span of data with the selected chunk size.
///\param source The source data to encode.
///\param destination The destination buffer for encoded data.
///\tparam TChunk The chunk size for encoding (default: uint_least8_t).
//*************************************************************************
template <typename TChunk>
static typename etl::enable_if<!etl::is_same<TChunk, uint_least8_t>::value, void>::type
encode(etl::span<const uint_least8_t> decoded, etl::span<uint_least8_t> encoded)
{
typedef TChunk TDecoded;
typedef typename etl::private_manchester::encoded<TChunk>::type TEncoded;
ETL_ASSERT(encoded.size() >= decoded.size() * 2, ETL_ERROR(manchester_invalid_size));
ETL_ASSERT(decoded.size() % sizeof(TDecoded) == 0, ETL_ERROR(manchester_invalid_size));
size_t dest_index = 0;
size_t source_index = 0;
for (size_t i = 0; i < decoded.size() / sizeof(TDecoded); ++i)
{
TDecoded decoded_value = 0;
etl::private_manchester::memcpy(&decoded_value, &decoded[source_index], sizeof(TDecoded));
const TEncoded encoded_value = encode(decoded_value);
etl::private_manchester::memcpy(&encoded[dest_index], &encoded_value, sizeof(TEncoded));
source_index += sizeof(TDecoded);
dest_index += sizeof(TEncoded);
}
}
//*************************************************************************
/// Encode a span of data with the minimum chunk size. This version is
/// constexpr so that it can be used to encode data at compile time.
///\param source The source data to encode.
///\param destination The destination buffer for encoded data.
///\tparam TChunk The chunk size for encoding (default: uint_least8_t).
//*************************************************************************
template <typename TChunk = uint_least8_t>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TChunk, uint_least8_t>::value, void>::type encode(etl::span<const uint_least8_t> decoded, etl::span<uint_least8_t> encoded)
{
typedef TChunk TDecoded;
typedef typename etl::private_manchester::encoded<TChunk>::type TEncoded;
ETL_ASSERT(encoded.size() >= decoded.size() * 2, ETL_ERROR(manchester_invalid_size));
ETL_ASSERT(decoded.size() % sizeof(TDecoded) == 0, ETL_ERROR(manchester_invalid_size));
size_t dest_index = 0;
size_t source_index = 0;
for (size_t i = 0; i < decoded.size() / sizeof(TDecoded); ++i)
{
const TEncoded encoded_value = encode(decoded[source_index]);
if (etl::endianness::value() == etl::endian::little)
{
encoded[dest_index] = static_cast<uint_least8_t>(encoded_value);
encoded[dest_index + 1] = static_cast<uint_least8_t>(encoded_value >> CHAR_BIT);
}
else
{
encoded[dest_index] = static_cast<uint_least8_t>(encoded_value >> CHAR_BIT);
encoded[dest_index + 1] = static_cast<uint_least8_t>(encoded_value);
}
source_index += sizeof(TDecoded);
dest_index += sizeof(TEncoded);
}
}
//*************************************************************************
// Decoding functions
//*************************************************************************
#if ETL_USING_8BIT_TYPES
//*************************************************************************
/// Decode a 16-bit value and return the 8-bit result.
///\param encoded The value to decode.
///\return The Manchester decoded value.
//*************************************************************************
template <typename TEncoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TEncoded, uint16_t>::value, typename private_manchester::decoded<TEncoded>::type>::type
decode(TEncoded encoded)
{
typedef typename private_manchester::decoded<TEncoded>::type TDecoded;
encoded = (encoded ^ (0xAAAAU ^ static_cast<TEncoded>(TManchesterType::inversion_mask))) & 0x5555U;
encoded = (encoded | (encoded >> 1)) & 0x3333U;
encoded = (encoded | (encoded >> 2)) & 0x0F0FU;
return static_cast<TDecoded>(encoded | (encoded >> 4U));
}
#endif
//*************************************************************************
/// Decode a 32-bit value and return the 16-bit result.
///\param encoded The value to decode.
///\return The Manchester decoded value.
//*************************************************************************
template <typename TEncoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TEncoded, uint32_t>::value, typename private_manchester::decoded<TEncoded>::type>::type
decode(TEncoded encoded)
{
typedef typename private_manchester::decoded<TEncoded>::type TDecoded;
encoded = (encoded ^ (0xAAAAAAAAUL ^ static_cast<TEncoded>(TManchesterType::inversion_mask))) & 0x55555555UL;
encoded = (encoded | (encoded >> 1)) & 0x33333333UL;
encoded = (encoded | (encoded >> 2)) & 0x0F0F0F0FUL;
encoded = (encoded | (encoded >> 4)) & 0x00FF00FFUL;
return static_cast<TDecoded>(encoded | (encoded >> 8U));
}
#if ETL_USING_64BIT_TYPES
//*************************************************************************
/// Decode a 64-bit value and return the 32-bit result.
///\param encoded The value to decode.
///\return The Manchester decoded value.
//*************************************************************************
template <typename TEncoded>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TEncoded, uint64_t>::value, typename private_manchester::decoded<TEncoded>::type>::type
decode(TEncoded encoded)
{
typedef typename private_manchester::decoded<TEncoded>::type TDecoded;
encoded = (encoded ^ (0xAAAAAAAAAAAAAAAAULL ^ TManchesterType::inversion_mask)) & 0x5555555555555555ULL;
encoded = (encoded | (encoded >> 1)) & 0x3333333333333333ULL;
encoded = (encoded | (encoded >> 2)) & 0x0F0F0F0F0F0F0F0FULL;
encoded = (encoded | (encoded >> 4)) & 0x00FF00FF00FF00FFULL;
encoded = (encoded | (encoded >> 8)) & 0x0000FFFF0000FFFFULL;
return static_cast<TDecoded>(encoded | (encoded >> 16U));
}
#endif
//*************************************************************************
/// Decode a span of data using specified chunk type.
///\param source The source encoded data to decode.
///\param destination The destination buffer for decoded data.
///\tparam TChunk The chunk type for decoding.
//*************************************************************************
template <typename TChunk>
static typename etl::enable_if<!etl::is_same<TChunk, typename private_manchester::encoded<uint_least8_t>::type>::value, void>::type
decode(etl::span<const uint_least8_t> encoded, etl::span<uint_least8_t> decoded)
{
typedef typename private_manchester::decoded<TChunk>::type TDecoded;
typedef TChunk TEncoded;
ETL_ASSERT(decoded.size() * 2 >= encoded.size(), ETL_ERROR(manchester_invalid_size));
ETL_ASSERT(encoded.size() % sizeof(TChunk) == 0, ETL_ERROR(manchester_invalid_size));
size_t dest_index = 0;
size_t source_index = 0;
for (size_t i = 0; i < encoded.size() / sizeof(TEncoded); ++i)
{
TChunk encoded_value = 0;
etl::private_manchester::memcpy(&encoded_value, &encoded[source_index], sizeof(TEncoded));
const TDecoded decoded_value = decode(encoded_value);
etl::private_manchester::memcpy(&decoded[dest_index], &decoded_value, sizeof(TDecoded));
source_index += sizeof(TEncoded);
dest_index += sizeof(TDecoded);
}
}
//*************************************************************************
/// Decode a span of data using the smallest chunk type. This version is
/// constexpr so that it can be used to decode data at compile time.
///\param source The source encoded data to decode.
///\param destination The destination buffer for decoded data.
///\tparam TChunk The chunk type for decoding (default type).
//*************************************************************************
template <typename TChunk = typename private_manchester::encoded<uint_least8_t>::type>
static ETL_CONSTEXPR14 typename etl::enable_if<etl::is_same<TChunk, typename private_manchester::encoded<uint_least8_t>::type>::value, void>::type
decode(etl::span<const uint_least8_t> encoded, etl::span<uint_least8_t> decoded)
{
typedef uint_least8_t TDecoded;
ETL_ASSERT(decoded.size() * 2 >= encoded.size(), ETL_ERROR(manchester_invalid_size));
ETL_ASSERT(encoded.size() % sizeof(TChunk) == 0, ETL_ERROR(manchester_invalid_size));
size_t dest_index = 0;
size_t source_index = 0;
for (size_t i = 0; i < encoded.size() / sizeof(TChunk); ++i)
{
TChunk encoded_value{};
if (etl::endianness::value() == etl::endian::little)
{
encoded_value = static_cast<TChunk>((encoded[source_index + 1] << CHAR_BIT) | encoded[source_index]);
}
else
{
encoded_value = static_cast<TChunk>((encoded[source_index] << CHAR_BIT) | encoded[source_index + 1]);
}
decoded[dest_index] = decode<TChunk>(encoded_value);
source_index += sizeof(TChunk);
dest_index += sizeof(TDecoded);
}
}
//*************************************************************************
// Validation functions
//*************************************************************************
//*************************************************************************
/// Validate that a value contains valid Manchester encoded data.
///\param encoded The encoded value to validate.
///\return True if the value is valid Manchester encoding.
//*************************************************************************
template <typename TChunk>
ETL_NODISCARD static ETL_CONSTEXPR14
typename etl::enable_if<private_manchester::is_decodable<TChunk>::value, bool>::type
is_valid(TChunk encoded)
{
const TChunk mask = static_cast<TChunk>(0x5555555555555555ULL);
return (((encoded ^ (encoded >> 1)) & mask) == mask);
}
//*************************************************************************
/// Validate that a span contains valid Manchester encoded data.
///\param encoded The span of encoded data to validate.
///\return True if all data is valid Manchester encoding.
//*************************************************************************
ETL_NODISCARD static ETL_CONSTEXPR14 bool is_valid(etl::span<const uint_least8_t> encoded)
{
ETL_ASSERT(encoded.size() % sizeof(uint16_t) == 0, ETL_ERROR(manchester_invalid_size));
for (size_t i = 0; i < encoded.size(); i += sizeof(uint16_t))
{
uint16_t chunk{};
if (etl::endianness::value() == etl::endian::little)
{
chunk = static_cast<uint16_t>((encoded[i + 1] << CHAR_BIT) | encoded[i]);
}
else
{
chunk = static_cast<uint16_t>((encoded[i] << CHAR_BIT) | encoded[i + 1]);
}
if (!is_valid<uint16_t>(chunk))
{
return false;
}
}
return true;
}
};
//***************************************************************************
///\ingroup manchester
/// Manchester encoder using normal encoding (no inversion).
//***************************************************************************
typedef manchester_base<private_manchester::manchester_type_normal> manchester;
//***************************************************************************
///\ingroup manchester
/// Manchester encoder using inverted encoding.
//***************************************************************************
typedef manchester_base<private_manchester::manchester_type_inverted> manchester_inverted;
} // namespace etl
#endif

View File

@ -216,6 +216,7 @@ add_executable(etl_tests
test_list_shared_pool.cpp
test_macros.cpp
test_make_string.cpp
test_manchester.cpp
test_map.cpp
test_math.cpp
test_math_functions.cpp

383
test/test_manchester.cpp Normal file
View File

@ -0,0 +1,383 @@
#include "etl/manchester.h"
#include "unit_test_framework.h"
#include <etl/algorithm.h>
#include <etl/array.h>
#include <etl/span.h>
#include <utility>
SUITE(test_manchester)
{
TEST(encode_uint8_t)
{
CHECK_EQUAL(0xAAAA, (etl::manchester::encode<uint8_t>(0x00U)));
CHECK_EQUAL(0x5555, (etl::manchester::encode<uint8_t>(0xFFU)));
CHECK_EQUAL(0xAAA9, (etl::manchester::encode<uint8_t>(0x01U)));
CHECK_EQUAL(0x6AAA, (etl::manchester::encode<uint8_t>(0x80U)));
#if ETL_USING_CPP14
static_assert(0xAAAA == etl::manchester::encode<uint8_t>(0x00U), "Compile time manchester encoding failed");
#endif
}
TEST(encode_uint8_t_inverted)
{
CHECK_EQUAL(0x5555, (etl::manchester_inverted::encode<uint8_t>(0x00U)));
CHECK_EQUAL(0xAAAA, (etl::manchester_inverted::encode<uint8_t>(0xFFU)));
CHECK_EQUAL(0x5556, (etl::manchester_inverted::encode<uint8_t>(0x01U)));
CHECK_EQUAL(0x9555, (etl::manchester_inverted::encode<uint8_t>(0x80U)));
#if ETL_USING_CPP14
static_assert(0x5555 == etl::manchester_inverted::encode<uint8_t>(0x00U), "Compile time manchester encoding failed");
#endif
}
TEST(encode_uint16_t)
{
CHECK_EQUAL(0x5555AAAA, (etl::manchester::encode<uint16_t>(0xFF00UL)));
CHECK_EQUAL(0x6AAAAAA9, (etl::manchester::encode<uint16_t>(0x8001UL)));
#if ETL_USING_CPP14
static_assert(0x5555AAAA == etl::manchester::encode<uint16_t>(0xFF00UL), "Compile time manchester encoding failed");
#endif
}
TEST(encode_uint16_t_inverted)
{
CHECK_EQUAL(0xAAAA5555, (etl::manchester_inverted::encode<uint16_t>(0xFF00UL)));
CHECK_EQUAL(0x95555556, (etl::manchester_inverted::encode<uint16_t>(0x8001UL)));
#if ETL_USING_CPP14
static_assert(0xAAAA5555 == etl::manchester_inverted::encode<uint16_t>(0xFF00UL), "Compile time manchester encoding failed");
#endif
}
TEST(encode_uint32_t)
{
CHECK_EQUAL(0x6AAAAAA95555AAAA, (etl::manchester::encode<uint32_t>(0x8001FF00ULL)));
#if ETL_USING_CPP14
static_assert(0x6AAAAAA95555AAAA == etl::manchester::encode<uint32_t>(0x8001FF00ULL), "Compile time manchester encoding failed");
#endif
}
TEST(encode_uint32_t_inverted)
{
CHECK_EQUAL(0x95555556AAAA5555, (etl::manchester_inverted::encode<uint32_t>(0x8001FF00ULL)));
#if ETL_USING_CPP14
static_assert(0x95555556AAAA5555 == etl::manchester_inverted::encode<uint32_t>(0x8001FF00ULL), "Compile time manchester encoding failed");
#endif
}
#if ETL_USING_CPP14
constexpr etl::array<uint8_t, 8> manchester_encoded(etl::span<const uint_least8_t> decoded)
{
etl::array<uint8_t, 8> encoded{0, 0, 0, 0, 0, 0, 0, 0};
etl::manchester::encode(decoded, encoded);
return encoded;
}
#endif
TEST(encode_span)
{
constexpr etl::array<const uint8_t, 4> decoded{0x00, 0xFF, 0x01, 0x80};
etl::array<uint8_t, 8> encoded0;
etl::array<uint8_t, 8> encoded1;
etl::array<uint8_t, 8> encoded2;
etl::array<uint8_t, 8> encoded3;
etl::manchester::encode(decoded, encoded0);
etl::manchester::encode<uint8_t>(decoded, encoded1);
etl::manchester::encode<uint16_t>(decoded, encoded2);
etl::manchester::encode<uint32_t>(decoded, encoded3);
CHECK_EQUAL(0xAA, encoded0[0]);
CHECK_EQUAL(0xAA, encoded0[1]);
CHECK_EQUAL(0x55, encoded0[2]);
CHECK_EQUAL(0x55, encoded0[3]);
CHECK_EQUAL(0xA9, encoded0[4]);
CHECK_EQUAL(0xAA, encoded0[5]);
CHECK_EQUAL(0xAA, encoded0[6]);
CHECK_EQUAL(0x6A, encoded0[7]);
CHECK_TRUE(encoded0 == encoded1);
CHECK_TRUE(encoded0 == encoded2);
CHECK_TRUE(encoded0 == encoded3);
#if ETL_USING_CPP14
static_assert(manchester_encoded(decoded)[0] == 0xAA, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[1] == 0xAA, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[2] == 0x55, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[3] == 0x55, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[4] == 0xA9, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[5] == 0xAA, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[6] == 0xAA, "Compile time encoding on range failed");
static_assert(manchester_encoded(decoded)[7] == 0x6A, "Compile time encoding on range failed");
#endif
}
TEST(encode_span_inverted)
{
etl::array<const uint8_t, 4> decoded{0x00, 0xFF, 0x01, 0x80};
etl::array<uint8_t, 8> encoded0;
etl::array<uint8_t, 8> encoded1;
etl::array<uint8_t, 8> encoded2;
etl::array<uint8_t, 8> encoded3;
etl::manchester_inverted::encode(decoded, encoded0);
etl::manchester_inverted::encode<uint8_t>(decoded, encoded1);
etl::manchester_inverted::encode<uint16_t>(decoded, encoded2);
etl::manchester_inverted::encode<uint32_t>(decoded, encoded3);
CHECK_EQUAL(0x55, encoded0[0]);
CHECK_EQUAL(0x55, encoded0[1]);
CHECK_EQUAL(0xAA, encoded0[2]);
CHECK_EQUAL(0xAA, encoded0[3]);
CHECK_EQUAL(0x56, encoded0[4]);
CHECK_EQUAL(0x55, encoded0[5]);
CHECK_EQUAL(0x55, encoded0[6]);
CHECK_EQUAL(0x95, encoded0[7]);
CHECK_TRUE(encoded0 == encoded1);
CHECK_TRUE(encoded0 == encoded2);
CHECK_TRUE(encoded0 == encoded3);
}
TEST(encode_span_invalid_source)
{
etl::array<const uint8_t, 3> invalid_source{0x00, 0xFF, 0x01};
etl::array<uint8_t, 8> valid_destination;
CHECK_THROW({ etl::manchester::encode<uint16_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::encode<uint32_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::encode<uint16_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::encode<uint32_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
}
TEST(encode_span_invalid_destination)
{
etl::array<const uint8_t, 4> valid_source{0x00, 0xFF, 0x01, 0x80};
etl::array<uint8_t, 7> invalid_destination;
CHECK_THROW({ etl::manchester::encode<uint8_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::encode<uint16_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::encode<uint32_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::encode<uint8_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::encode<uint16_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::encode<uint32_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
}
TEST(decode_uint16_t)
{
CHECK_EQUAL(0x00, (etl::manchester::decode<uint16_t>(0xAAAAUL)));
CHECK_EQUAL(0xFF, (etl::manchester::decode<uint16_t>(0x5555UL)));
CHECK_EQUAL(0x01, (etl::manchester::decode<uint16_t>(0xAAA9UL)));
CHECK_EQUAL(0x80, (etl::manchester::decode<uint16_t>(0x6AAAUL)));
#if ETL_USING_CPP14
static_assert(0x00 == etl::manchester::decode<uint16_t>(0xAAAAUL), "Compile time manchester decoding failed");
#endif
}
TEST(decode_uint16_t_inverted)
{
CHECK_EQUAL(0x00, (etl::manchester_inverted::decode<uint16_t>(0x5555UL)));
CHECK_EQUAL(0xFF, (etl::manchester_inverted::decode<uint16_t>(0xAAAAUL)));
CHECK_EQUAL(0x01, (etl::manchester_inverted::decode<uint16_t>(0x5556UL)));
CHECK_EQUAL(0x80, (etl::manchester_inverted::decode<uint16_t>(0x9555UL)));
#if ETL_USING_CPP14
static_assert(0x00 == etl::manchester_inverted::decode<uint16_t>(0x5555UL), "Compile time manchester decoding failed");
#endif
}
TEST(decode_uint32_t)
{
CHECK_EQUAL(0xFF00UL, (etl::manchester::decode<uint32_t>(0x5555AAAAUL)));
CHECK_EQUAL(0x8001UL, (etl::manchester::decode<uint32_t>(0x6AAAAAA9UL)));
#if ETL_USING_CPP14
static_assert(0xFF00UL == etl::manchester::decode<uint32_t>(0x5555AAAAUL), "Compile time manchester decoding failed");
#endif
}
TEST(decode_uint32_t_inverted)
{
CHECK_EQUAL(0xFF00UL, (etl::manchester_inverted::decode<uint32_t>(0xAAAA5555UL)));
CHECK_EQUAL(0x8001UL, (etl::manchester_inverted::decode<uint32_t>(0x95555556UL)));
#if ETL_USING_CPP14
static_assert(0xFF00UL == etl::manchester_inverted::decode<uint32_t>(0xAAAA5555UL), "Compile time manchester decoding failed");
#endif
}
TEST(decode_uint64_t)
{
CHECK_EQUAL(0x8001FF00ULL, (etl::manchester::decode<uint64_t>(0x6AAAAAA95555AAAAULL)));
#if ETL_USING_CPP14
static_assert(0x8001FF00ULL == etl::manchester::decode<uint64_t>(0x6AAAAAA95555AAAAULL), "Compile time manchester decoding failed");
#endif
}
TEST(decode_uint64_t_inverted)
{
CHECK_EQUAL(0x8001FF00ULL, (etl::manchester_inverted::decode<uint64_t>(0x95555556AAAA5555ULL)));
#if ETL_USING_CPP14
static_assert(0x8001FF00ULL == etl::manchester_inverted::decode<uint64_t>(0x95555556AAAA5555ULL), "Compile time manchester decoding failed");
#endif
}
#if ETL_USING_CPP14
constexpr etl::array<uint8_t, 4> manchester_decoded(etl::span<const uint_least8_t> encoded)
{
etl::array<uint8_t, 4> decoded{0, 0, 0, 0};
etl::manchester::decode(encoded, decoded);
return decoded;
}
#endif
TEST(decode_span)
{
constexpr etl::array<const uint8_t, 8> encoded{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0x6A};
etl::array<uint8_t, 4> decoded0;
etl::array<uint8_t, 4> decoded1;
etl::array<uint8_t, 4> decoded2;
etl::array<uint8_t, 4> decoded3;
etl::manchester::decode(encoded, decoded0);
etl::manchester::decode<uint16_t>(encoded, decoded1);
etl::manchester::decode<uint32_t>(encoded, decoded2);
etl::manchester::decode<uint64_t>(encoded, decoded3);
CHECK_EQUAL(0x00, decoded0[0]);
CHECK_EQUAL(0xFF, decoded0[1]);
CHECK_EQUAL(0x01, decoded0[2]);
CHECK_EQUAL(0x80, decoded0[3]);
#if ETL_USING_CPP14
static_assert(manchester_decoded(encoded)[0] == 0x00, "Compile time decoding on range failed");
static_assert(manchester_decoded(encoded)[1] == 0xFF, "Compile time decoding on range failed");
static_assert(manchester_decoded(encoded)[2] == 0x01, "Compile time decoding on range failed");
static_assert(manchester_decoded(encoded)[3] == 0x80, "Compile time decoding on range failed");
#endif
CHECK_TRUE(decoded0 == decoded1);
CHECK_TRUE(decoded0 == decoded2);
CHECK_TRUE(decoded0 == decoded3);
}
TEST(decode_span_inverted)
{
etl::array<const uint8_t, 8> encoded{0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x95};
etl::array<uint8_t, 4> decoded0;
etl::array<uint8_t, 4> decoded1;
etl::array<uint8_t, 4> decoded2;
etl::array<uint8_t, 4> decoded3;
etl::manchester_inverted::decode(encoded, decoded0);
etl::manchester_inverted::decode<uint16_t>(encoded, decoded1);
etl::manchester_inverted::decode<uint32_t>(encoded, decoded2);
etl::manchester_inverted::decode<uint64_t>(encoded, decoded3);
CHECK_EQUAL(0x00, decoded0[0]);
CHECK_EQUAL(0xFF, decoded0[1]);
CHECK_EQUAL(0x01, decoded0[2]);
CHECK_EQUAL(0x80, decoded0[3]);
CHECK_TRUE(decoded0 == decoded1);
CHECK_TRUE(decoded0 == decoded2);
CHECK_TRUE(decoded0 == decoded3);
}
TEST(decode_span_invalid_source)
{
etl::array<const uint8_t, 7> invalid_source{0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55};
etl::array<uint8_t, 4> valid_destination;
CHECK_THROW({ etl::manchester::decode<uint16_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::decode<uint32_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::decode<uint64_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint16_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint32_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint64_t>(invalid_source, valid_destination); }, etl::manchester_invalid_size);
}
TEST(decode_span_invalid_destination)
{
etl::array<const uint8_t, 8> valid_source{0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x95};
etl::array<uint8_t, 3> invalid_destination;
CHECK_THROW({ etl::manchester::decode<uint16_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::decode<uint32_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester::decode<uint64_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint16_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint32_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
CHECK_THROW({ etl::manchester_inverted::decode<uint64_t>(valid_source, invalid_destination); }, etl::manchester_invalid_size);
}
TEST(valid16)
{
CHECK_TRUE(etl::manchester::is_valid<uint16_t>(0xAAAAUL));
CHECK_TRUE(etl::manchester_inverted::is_valid<uint16_t>(0xAAAAUL));
CHECK_FALSE(etl::manchester::is_valid<uint16_t>(0xAAA8UL));
CHECK_FALSE(etl::manchester_inverted::is_valid<uint16_t>(0xAAA8UL));
#if ETL_USING_CPP14
static_assert(etl::manchester::is_valid<uint16_t>(0xAAAAUL), "Compile time manchester validity check failed");
static_assert(etl::manchester_inverted::is_valid<uint16_t>(0xAAAAUL), "Compile time manchester validity check failed");
#endif
}
TEST(valid32)
{
CHECK_TRUE(etl::manchester::is_valid<uint32_t>(0xAAAAAAAAUL));
CHECK_TRUE(etl::manchester_inverted::is_valid<uint32_t>(0xAAAAAAAAUL));
CHECK_FALSE(etl::manchester::is_valid<uint32_t>(0xAAAAAAA8UL));
CHECK_FALSE(etl::manchester_inverted::is_valid<uint32_t>(0xAAAAAAA8UL));
#if ETL_USING_CPP14
static_assert(etl::manchester::is_valid<uint32_t>(0xAAAAAAAAUL), "Compile time manchester validity check failed");
static_assert(etl::manchester_inverted::is_valid<uint32_t>(0xAAAAAAAAUL), "Compile time manchester validity check failed");
#endif
}
TEST(valid64)
{
CHECK_TRUE(etl::manchester::is_valid<uint64_t>(0xAAAAAAAAAAAAAAAAULL));
CHECK_TRUE(etl::manchester_inverted::is_valid<uint64_t>(0xAAAAAAAAAAAAAAAAULL));
CHECK_FALSE(etl::manchester::is_valid<uint64_t>(0xAAAAAAAAAAAAAAA8ULL));
CHECK_FALSE(etl::manchester_inverted::is_valid<uint64_t>(0xAAAAAAAAAAAAAAA8ULL));
#if ETL_USING_CPP14
static_assert(etl::manchester::is_valid<uint64_t>(0xAAAAAAAAAAAAAAAAULL), "Compile time manchester validity check failed");
static_assert(etl::manchester_inverted::is_valid<uint64_t>(0xAAAAAAAAAAAAAAAAULL), "Compile time manchester validity check failed");
#endif
}
TEST(valid_span)
{
constexpr etl::array<uint8_t, 8> encoded1{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0x6A};
constexpr etl::array<uint8_t, 8> encoded2{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAB, 0x6A};
CHECK_TRUE(etl::manchester::is_valid(encoded1));
CHECK_FALSE(etl::manchester::is_valid(encoded2));
#if ETL_USING_CPP14
static_assert(etl::manchester::is_valid(encoded1), "Compile time manchester validity check failed");
static_assert(!etl::manchester::is_valid(encoded2), "Compile time manchester validity check failed");
#endif
}
TEST(valid_span_on_invalid_source)
{
constexpr etl::array<uint8_t, 7> invalid_source{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAA};
CHECK_THROW({ std::ignore = etl::manchester::is_valid(invalid_source); }, etl::manchester_invalid_size);
}
}