///\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" ///\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 struct is_encodable { static const bool value = #if ETL_USING_8BIT_TYPES etl::is_same::value || #endif etl::is_same::value #if ETL_USING_64BIT_TYPES || etl::is_same::value #endif ; }; //************************************************************************* /// Type trait to determine if a type is supported as a decode input. ///\tparam TChunk The input type to check. //************************************************************************* template struct is_decodable { static const bool value = #if ETL_USING_8BIT_TYPES etl::is_same::value || #endif etl::is_same::value #if ETL_USING_64BIT_TYPES || etl::is_same::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 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 { typedef uint16_t type; }; #endif template <> struct encoded { typedef uint32_t type; }; #if ETL_USING_64BIT_TYPES template <> struct encoded { 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 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 { typedef uint8_t type; }; #endif template <> struct decoded { typedef uint16_t type; }; #if ETL_USING_64BIT_TYPES template <> struct decoded { 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 }; //************************************************************************* /// 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. //************************************************************************* template static ETL_CONSTEXPR14 T read_little_endian(etl::span bytes, size_t index) { T value = 0; for (size_t j = 0; j < sizeof(T); ++j) { value |= static_cast(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 static ETL_CONSTEXPR14 void write_little_endian(etl::span bytes, size_t index, T value) { for (size_t j = 0; j < sizeof(T); ++j) { bytes[index + j] = static_cast(value >> (j * CHAR_BIT)); } } } // 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 struct manchester_base { ETL_STATIC_ASSERT((etl::is_same::value || etl::is_same::value), "TManchesterType must be manchester_type_normal or manchester_type_inverted"); ETL_STATIC_ASSERT(CHAR_BIT == etl::numeric_limits::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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::encoded::type>::type encode(TDecoded decoded) { typedef typename private_manchester::encoded::type TEncoded; TEncoded encoded = decoded; encoded = static_cast((static_cast(encoded) | (static_cast(encoded) << 4U)) & 0x0F0FU); encoded = static_cast((static_cast(encoded) | (static_cast(encoded) << 2U)) & 0x3333U); encoded = static_cast((static_cast(encoded) | (static_cast(encoded) << 1U)) & 0x5555U); encoded = static_cast((static_cast(encoded) | (static_cast(encoded) << 1U)) ^ (0xAAAAU ^ static_cast(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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::encoded::type>::type encode(TDecoded decoded) { typedef typename private_manchester::encoded::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(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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::encoded::type>::type encode(TDecoded decoded) { typedef typename private_manchester::encoded::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 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 static ETL_CONSTEXPR14 void encode(etl::span decoded, etl::span encoded) { typedef TChunk TDecoded; typedef typename etl::private_manchester::encoded::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(decoded, source_index); const TEncoded encoded_value = encode(decoded_value); private_manchester::write_little_endian(encoded, dest_index, 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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::decoded::type>::type decode(TEncoded encoded) { typedef typename private_manchester::decoded::type TDecoded; encoded = static_cast((static_cast(encoded) ^ (0xAAAAU ^ static_cast(TManchesterType::inversion_mask))) & 0x5555U); encoded = static_cast((static_cast(encoded) | (static_cast(encoded) >> 1)) & 0x3333U); encoded = static_cast((static_cast(encoded) | (static_cast(encoded) >> 2)) & 0x0F0FU); return static_cast(static_cast(encoded) | (static_cast(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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::decoded::type>::type decode(TEncoded encoded) { typedef typename private_manchester::decoded::type TDecoded; encoded = (encoded ^ (0xAAAAAAAAUL ^ static_cast(TManchesterType::inversion_mask))) & 0x55555555UL; encoded = (encoded | (encoded >> 1)) & 0x33333333UL; encoded = (encoded | (encoded >> 2)) & 0x0F0F0F0FUL; encoded = (encoded | (encoded >> 4)) & 0x00FF00FFUL; return static_cast(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 static ETL_CONSTEXPR14 typename etl::enable_if< etl::is_same::value, typename private_manchester::decoded::type>::type decode(TEncoded encoded) { typedef typename private_manchester::decoded::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(encoded | (encoded >> 16U)); } #endif //************************************************************************* /// 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 (default: uint16_t). //************************************************************************* template ::type> static ETL_CONSTEXPR14 void decode(etl::span encoded, etl::span decoded) { typedef typename private_manchester::decoded::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(encoded, source_index); const TDecoded decoded_value = decode(encoded_value); private_manchester::write_little_endian(decoded, dest_index, decoded_value); source_index += sizeof(TEncoded); 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 ETL_NODISCARD static ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type is_valid(TChunk encoded) { const TChunk mask = static_cast(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 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(encoded, i); if (!is_valid(chunk)) { return false; } } return true; } }; //*************************************************************************** ///\ingroup manchester /// Manchester encoder using normal encoding (no inversion). //*************************************************************************** typedef manchester_base manchester; //*************************************************************************** ///\ingroup manchester /// Manchester encoder using inverted encoding. //*************************************************************************** typedef manchester_base manchester_inverted; } // namespace etl #endif