Manchester big endian support (#1353)

* 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

* #1258 Manchester coding
* Add manchester documentation

* #1258 Manchester coding
* Preparation for GitHub pages

* #1324 Manchester documentation
* Some small fixes

* Print test names at test time (#1343)

* IGN-280 biphasic amplitude as float
* Add big-endian devcontainer

* manchester
* fixed the configuration to work with GitHub Codespaces. The changes use cross-compilation with QEMU emulation instead of trying to use a native s390x container.

* manchester
* Made manchester work for big-endian
* Some updates to the container

* Manchester big-endian support
* Cleanup

* Manchester big-endian support
* add sourcedirectory

* Enable running with ctest

* Manchester big-endian support
* Update documentation

* Manchester big-endian support
* QA

* Manchester big-endian support
* QA

* Enable testing with ctest with cross-compiler (#5)

* Enable testing with ctest and with cross-compiler
* Clean up includes in manchester.h

---------

Co-authored-by: Timon Zijnge <timon.zijnge@imec.nl>

---------

Co-authored-by: Timon Zijnge <timon.zijnge@imec.nl>
Co-authored-by: Roland Reichwein <Roland.Reichwein@bmw.de>
This commit is contained in:
Timon Zijnge 2026-03-26 08:56:50 +01:00 committed by GitHub
parent d5fc8d0cd1
commit 2f6a3e04aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 130 deletions

View File

@ -0,0 +1,40 @@
# s390x Big-Endian Test Environment for ETL
# Uses QEMU user-mode emulation to run s390x binaries on x64 host
FROM debian:trixie
# Avoid prompts from apt
ENV DEBIAN_FRONTEND=noninteractive
# Install QEMU user-mode emulation and s390x cross-compilation tools
RUN dpkg --add-architecture s390x && \
apt-get update && apt-get install -y --no-install-recommends\
qemu-user-static \
qemu-user \
binfmt-support \
gcc-s390x-linux-gnu \
g++-s390x-linux-gnu \
cmake \
make \
ninja-build \
git \
wget \
file \
libc6:s390x \
libstdc++6:s390x \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /workspaces/etl
# Verify QEMU and cross-compilation setup
RUN echo "=== Host Architecture ===" && \
uname -m && \
echo "" && \
echo "=== s390x Cross Compiler ===" && \
s390x-linux-gnu-gcc --version && \
echo "" && \
echo "=== QEMU s390x ===" && \
qemu-s390x-static --version | head -n1
# Default command
CMD ["/bin/bash"]

View File

@ -0,0 +1,29 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "s390x Big Endian (Debian)",
"build": {
"dockerfile": "./Dockerfile",
"context": "."
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-vscode.cmake-tools"
],
"settings": {
"cmake.sourceDirectory": "${workspaceFolder}/test",
"cmake.configureArgs": [
"-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/s390x/toolchain-s390x.cmake",
"-DBUILD_TESTS=ON",
"-DNO_STL=OFF",
"-DETL_CXX_STANDARD=17"
],
"cmake.buildDirectory": "${workspaceFolder}/build-s390x",
"cmake.generator": "Ninja"
}
}
},
"remoteUser": "root"
}

View File

@ -0,0 +1,21 @@
# CMake toolchain file for s390x cross-compilation
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR s390x)
# Specify the cross compiler
set(CMAKE_C_COMPILER s390x-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER s390x-linux-gnu-g++)
set(CMAKE_AR s390x-linux-gnu-ar)
set(CMAKE_RANLIB s390x-linux-gnu-ranlib)
set(CMAKE_STRIP s390x-linux-gnu-strip)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# Set QEMU for running tests
set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-s390x-static CACHE FILEPATH "Path to the emulator for cross-compiled binaries")

View File

@ -13,8 +13,9 @@ Efficient Manchester encoding and decoding of data. The Manchester code represen
- Normal and inverted Manchester encoding
- Support for multiple encoding chunk sizes: 8-bit, 16-bit and 32-bit
- Span-based operations or chunk-based operations
- Constexpr functions for compile-time encoding/decoding (8-bit chunk size only)
- Constexpr functions for compile-time encoding/decoding
- Validation of encoded data
- Chunked span I/O uses little-endian byte order for multi-byte chunks, independent of host platform endianness
## Algorithm background

View File

@ -32,10 +32,8 @@ SOFTWARE.
#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
@ -172,18 +170,37 @@ namespace etl
};
//*************************************************************************
/// 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
/// Read a multi-byte value from a span in little-endian byte order.
///\tparam T The type to read.
///\param bytes The span to read from.
///\param index The starting index in the span.
///\return The value read from the span.
//*************************************************************************
inline void* memcpy(void* dest, const void* src, std::size_t count) ETL_NOEXCEPT
template <typename T>
static ETL_CONSTEXPR14 T read_little_endian(etl::span<const uint_least8_t> bytes, size_t index)
{
#if ETL_USING_BUILTIN_MEMCPY
return __builtin_memcpy(dest, src, count);
#else
return ::memcpy(dest, src, count);
#endif
T value = 0;
for (size_t j = 0; j < sizeof(T); ++j)
{
value |= static_cast<T>(bytes[index + j]) << (j * CHAR_BIT);
}
return value;
}
//*************************************************************************
/// Write a multi-byte value to a span in little-endian byte order.
///\tparam T The type to write.
///\param bytes The span to write to.
///\param index The starting index in the span.
///\param value The value to write.
//*************************************************************************
template <typename T>
static ETL_CONSTEXPR14 void write_little_endian(etl::span<uint_least8_t> bytes, size_t index, T value)
{
for (size_t j = 0; j < sizeof(T); ++j)
{
bytes[index + j] = static_cast<uint_least8_t>(value >> (j * CHAR_BIT));
}
}
} // namespace private_manchester
@ -297,44 +314,13 @@ namespace etl
#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.
/// Encode a span of data with the specified 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 = 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)
static ETL_CONSTEXPR14 void 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;
@ -346,17 +332,9 @@ namespace etl
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);
}
const TDecoded decoded_value = private_manchester::read_little_endian<TDecoded>(decoded, source_index);
const TEncoded encoded_value = encode(decoded_value);
private_manchester::write_little_endian<TEncoded>(encoded, dest_index, encoded_value);
source_index += sizeof(TDecoded);
dest_index += sizeof(TEncoded);
@ -426,14 +404,13 @@ namespace etl
#endif
//*************************************************************************
/// Decode a span of data using specified chunk type.
/// Decode a span of data using the 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.
///\tparam TChunk The chunk type for decoding (default: uint16_t).
//*************************************************************************
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)
template <typename TChunk = typename private_manchester::encoded<uint_least8_t>::type>
static ETL_CONSTEXPR14 void 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;
@ -445,53 +422,15 @@ namespace etl
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 TEncoded encoded_value = private_manchester::read_little_endian<TEncoded>(encoded, source_index);
const TDecoded decoded_value = decode(encoded_value);
etl::private_manchester::memcpy(&decoded[dest_index], &decoded_value, sizeof(TDecoded));
private_manchester::write_little_endian<TDecoded>(decoded, dest_index, decoded_value);
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
//*************************************************************************
@ -521,15 +460,7 @@ namespace etl
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]);
}
const uint16_t chunk = private_manchester::read_little_endian<uint16_t>(encoded, i);
if (!is_valid<uint16_t>(chunk))
{

View File

@ -532,8 +532,9 @@ target_include_directories(etl_tests
add_subdirectory(UnitTest++)
target_link_libraries(etl_tests PRIVATE UnitTestpp ${EXTRA_LINK_LIBS})
enable_testing()
# Enable the 'make test' CMake target using the executable defined above
add_test(etl_unit_tests etl_tests)
add_test(NAME etl_unit_tests COMMAND etl_tests)
# Since ctest will only show you the results of the single executable
# define a target that will output all of the failing or passing tests

View File

@ -71,12 +71,26 @@ SUITE(test_manchester)
}
#if ETL_USING_CPP14
constexpr etl::array<uint8_t, 8> manchester_encoded(etl::span<const uint_least8_t> decoded)
constexpr etl::array<uint8_t, 8> manchester_encoded_uint8(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;
}
constexpr etl::array<uint8_t, 8> manchester_encoded_uint16(etl::span<const uint_least8_t> decoded)
{
etl::array<uint8_t, 8> encoded{0, 0, 0, 0, 0, 0, 0, 0};
etl::manchester::encode<uint16_t>(decoded, encoded);
return encoded;
}
constexpr etl::array<uint8_t, 8> manchester_encoded_uint32(etl::span<const uint_least8_t> decoded)
{
etl::array<uint8_t, 8> encoded{0, 0, 0, 0, 0, 0, 0, 0};
etl::manchester::encode<uint32_t>(decoded, encoded);
return encoded;
}
#endif
TEST(encode_span)
@ -105,18 +119,41 @@ SUITE(test_manchester)
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_constexpr)
{
constexpr etl::array<const uint8_t, 4> decoded{0x00, 0xFF, 0x01, 0x80};
static_assert(manchester_encoded_uint8(decoded)[0] == 0xAA, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[1] == 0xAA, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[2] == 0x55, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[3] == 0x55, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[4] == 0xA9, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[5] == 0xAA, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[6] == 0xAA, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint8(decoded)[7] == 0x6A, "Compile time encoding with uint8_t failed");
static_assert(manchester_encoded_uint16(decoded)[0] == 0xAA, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[1] == 0xAA, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[2] == 0x55, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[3] == 0x55, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[4] == 0xA9, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[5] == 0xAA, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[6] == 0xAA, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint16(decoded)[7] == 0x6A, "Compile time encoding with uint16_t failed");
static_assert(manchester_encoded_uint32(decoded)[0] == 0xAA, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[1] == 0xAA, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[2] == 0x55, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[3] == 0x55, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[4] == 0xA9, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[5] == 0xAA, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[6] == 0xAA, "Compile time encoding with uint32_t failed");
static_assert(manchester_encoded_uint32(decoded)[7] == 0x6A, "Compile time encoding with uint32_t failed");
}
#endif
TEST(encode_span_inverted)
{
@ -233,12 +270,26 @@ SUITE(test_manchester)
}
#if ETL_USING_CPP14
constexpr etl::array<uint8_t, 4> manchester_decoded(etl::span<const uint_least8_t> encoded)
constexpr etl::array<uint8_t, 4> manchester_decoded_uint16(etl::span<const uint_least8_t> encoded)
{
etl::array<uint8_t, 4> decoded{0, 0, 0, 0};
etl::manchester::decode(encoded, decoded);
return decoded;
}
constexpr etl::array<uint8_t, 4> manchester_decoded_uint32(etl::span<const uint_least8_t> encoded)
{
etl::array<uint8_t, 4> decoded{0, 0, 0, 0};
etl::manchester::decode<uint32_t>(encoded, decoded);
return decoded;
}
constexpr etl::array<uint8_t, 4> manchester_decoded_uint64(etl::span<const uint_least8_t> encoded)
{
etl::array<uint8_t, 4> decoded{0, 0, 0, 0};
etl::manchester::decode<uint64_t>(encoded, decoded);
return decoded;
}
#endif
TEST(decode_span)
@ -260,18 +311,33 @@ SUITE(test_manchester)
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);
}
#if ETL_USING_CPP14
TEST(decode_span_constexpr)
{
constexpr etl::array<const uint8_t, 8> encoded{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0x6A};
static_assert(manchester_decoded_uint16(encoded)[0] == 0x00, "Compile time decoding with uint16_t failed");
static_assert(manchester_decoded_uint16(encoded)[1] == 0xFF, "Compile time decoding with uint16_t failed");
static_assert(manchester_decoded_uint16(encoded)[2] == 0x01, "Compile time decoding with uint16_t failed");
static_assert(manchester_decoded_uint16(encoded)[3] == 0x80, "Compile time decoding with uint16_t failed");
static_assert(manchester_decoded_uint32(encoded)[0] == 0x00, "Compile time decoding with uint32_t failed");
static_assert(manchester_decoded_uint32(encoded)[1] == 0xFF, "Compile time decoding with uint32_t failed");
static_assert(manchester_decoded_uint32(encoded)[2] == 0x01, "Compile time decoding with uint32_t failed");
static_assert(manchester_decoded_uint32(encoded)[3] == 0x80, "Compile time decoding with uint32_t failed");
static_assert(manchester_decoded_uint64(encoded)[0] == 0x00, "Compile time decoding with uint64_t failed");
static_assert(manchester_decoded_uint64(encoded)[1] == 0xFF, "Compile time decoding with uint64_t failed");
static_assert(manchester_decoded_uint64(encoded)[2] == 0x01, "Compile time decoding with uint64_t failed");
static_assert(manchester_decoded_uint64(encoded)[3] == 0x80, "Compile time decoding with uint64_t failed");
}
#endif
TEST(decode_span_inverted)
{
etl::array<const uint8_t, 8> encoded{0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x95};