Loading...
Searching...
No Matches
manchester.h
Go to the documentation of this 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 "span.h"
#include "static_assert.h"
#if ETL_USING_CPP11
namespace etl
{
namespace private_manchester
{
//*************************************************************************
//*************************************************************************
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
;
};
//*************************************************************************
//*************************************************************************
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
;
};
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
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
};
//*************************************************************************
//*************************************************************************
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
};
//*************************************************************************
//*************************************************************************
template <typename T>
static ETL_CONSTEXPR14 T read_little_endian(etl::span<const uint_least8_t> bytes, size_t index)
{
T value = 0;
for (size_t j = 0; j < sizeof(T); ++j)
{
value |= static_cast<T>(bytes[index + j]) << (j * CHAR_BIT);
}
return value;
}
//*************************************************************************
//*************************************************************************
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
//***************************************************************************
//***************************************************************************
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_)
{
}
};
//***************************************************************************
//***************************************************************************
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_)
{
}
};
//***************************************************************************
//***************************************************************************
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
//*************************************************************************
//*************************************************************************
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 = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) << 4U)) & 0x0F0FU);
encoded = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) << 2U)) & 0x3333U);
encoded = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) << 1U)) & 0x5555U);
encoded = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) << 1U))
^ (0xAAAAU ^ static_cast<unsigned int>(TManchesterType::inversion_mask)));
return encoded;
}
#endif
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
template <typename TChunk = uint_least8_t>
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;
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 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);
}
}
//*************************************************************************
// Decoding functions
//*************************************************************************
#if ETL_USING_8BIT_TYPES
//*************************************************************************
//*************************************************************************
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 = static_cast<TEncoded>((static_cast<unsigned int>(encoded) ^ (0xAAAAU ^ static_cast<unsigned int>(TManchesterType::inversion_mask)))
& 0x5555U);
encoded = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) >> 1)) & 0x3333U);
encoded = static_cast<TEncoded>((static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) >> 2)) & 0x0F0FU);
return static_cast<TDecoded>(static_cast<unsigned int>(encoded) | (static_cast<unsigned int>(encoded) >> 4U));
}
#endif
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
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
//*************************************************************************
//*************************************************************************
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;
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)
{
const TEncoded encoded_value = private_manchester::read_little_endian<TEncoded>(encoded, source_index);
const TDecoded decoded_value = decode(encoded_value);
private_manchester::write_little_endian<TDecoded>(decoded, dest_index, decoded_value);
source_index += sizeof(TEncoded);
dest_index += sizeof(TDecoded);
}
}
//*************************************************************************
// Validation functions
//*************************************************************************
//*************************************************************************
//*************************************************************************
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);
}
//*************************************************************************
//*************************************************************************
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))
{
const uint16_t chunk = private_manchester::read_little_endian<uint16_t>(encoded, i);
if (!is_valid<uint16_t>(chunk))
{
return false;
}
}
return true;
}
};
//***************************************************************************
//***************************************************************************
typedef manchester_base<private_manchester::manchester_type_normal> manchester;
//***************************************************************************
//***************************************************************************
typedef manchester_base<private_manchester::manchester_type_inverted> manchester_inverted;
} // namespace etl
#endif
#endif
etl::span::size
ETL_NODISCARD ETL_CONSTEXPR size_t size() const ETL_NOEXCEPT
Returns the size of the span.
Definition span.h:564
ETL_ASSERT
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
etl
Definition absolute.h:40
platform.h
span.h