/* * Author: Sven Gothel * Copyright (c) 2020 Gothel Software e.K. * Copyright (c) 2020 ZAFENA AB * * 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 JAU_EUI48_HPP_ #define JAU_EUI48_HPP_ #include #include #include #include #include #include #include namespace jau { /** @defgroup NetUtils Network Utilities * Networking types and functionality. * * @{ */ /** * A 48 bit EUI-48 sub-identifier, see EUI48. * * Stores value in endian::native byte order. */ struct EUI48Sub { public: /** EUI48 MAC address matching any device, i.e. `0:0:0:0:0:0`. */ static const EUI48Sub ANY_DEVICE; /** EUI48 MAC address matching all device, i.e. `ff:ff:ff:ff:ff:ff`. */ static const EUI48Sub ALL_DEVICE; /** EUI48 MAC address matching local device, i.e. `0:0:0:ff:ff:ff`. */ static const EUI48Sub LOCAL_DEVICE; /** * The <= 6 byte EUI48 sub-address. */ uint8_t b[6]; // == sizeof(EUI48) /** * The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes. */ jau::nsize_t length; constexpr EUI48Sub() noexcept : b{0}, length{0} { } /** * Copy len_ address bytes from given source and byte_order, * while converting them to endian::native byte order. * * @param b_ sub address bytes in endian::native byte order * @param len_ length * @param byte_order endian::little or endian::big byte order of given sub_address, one may pass endian::native. */ EUI48Sub(const uint8_t * b_, const jau::nsize_t len_, const endian byte_order) noexcept; /** * Fills given EUI48Sub instance via given string representation. *

* Implementation is consistent with EUI48Sub::toString(). *

* @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon, * e.g. `01:02:03:0A:0B:0C`, `01:02:03:0A`, `:`, (empty). * @param dest EUI48Sub to set its value * @param errmsg error parsing message if returning false * @return true if successful, otherwise false * @see EUI48Sub::EUI48Sub * @see EUI48Sub::toString() */ static bool scanEUI48Sub(const std::string& str, EUI48Sub& dest, std::string& errmsg); /** * Construct a sub EUI48 via given string representation. *

* Implementation is consistent with EUI48Sub::toString(). *

* @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon, * e.g. `01:02:03:0A:0B:0C`, `01:02:03:0A`, `:`, (empty). * @see EUI48Sub::scanEUI48Sub() * @see EUI48Sub::toString() * @throws jau::IllegalArgumentException if given string doesn't comply with EUI48 */ EUI48Sub(const std::string& str); constexpr EUI48Sub(const EUI48Sub &o) noexcept = default; EUI48Sub(EUI48Sub &&o) noexcept = default; constexpr EUI48Sub& operator=(const EUI48Sub &o) noexcept = default; EUI48Sub& operator=(EUI48Sub &&o) noexcept = default; constexpr std::size_t hash_code() const noexcept { // 31 * x == (x << 5) - x std::size_t h = length; for(jau::nsize_t i=0; i * If the sub is zero, true is returned. *

*/ bool contains(const EUI48Sub& needle) const noexcept { return 0 <= indexOf(needle, endian::native); } /** * Returns the EUI48 sub-string representation with MSB first (endian::big), * less or equal 17 characters representing less or equal 6 bytes as upper case hexadecimal numbers separated via colon, * e.g. `01:02:03:0A:0B:0C`, `01:02:03:0A`, `:`, (empty). */ std::string toString() const noexcept; }; inline std::string to_string(const EUI48Sub& a) noexcept { return a.toString(); } inline bool operator==(const EUI48Sub& lhs, const EUI48Sub& rhs) noexcept { if( &lhs == &rhs ) { return true; } if( lhs.length != rhs.length ) { return false; } return !memcmp(&lhs.b, &rhs.b, lhs.length); } inline bool operator!=(const EUI48Sub& lhs, const EUI48Sub& rhs) noexcept { return !(lhs == rhs); } /** * A packed 48 bit EUI-48 identifier, formerly known as MAC-48 * or simply network device MAC address (Media Access Control address). * * Stores value in endian::native byte order. */ __pack ( struct EUI48 { /** EUI48 MAC address matching any device, i.e. `0:0:0:0:0:0`. */ static const EUI48 ANY_DEVICE; /** EUI48 MAC address matching all device, i.e. `ff:ff:ff:ff:ff:ff`. */ static const EUI48 ALL_DEVICE; /** EUI48 MAC address matching local device, i.e. `0:0:0:ff:ff:ff`. */ static const EUI48 LOCAL_DEVICE; /** * The 6 byte EUI48 address. */ uint8_t b[6]; // == sizeof(EUI48) constexpr EUI48() noexcept : b{0} { } /** * Copy address bytes from given source and byte_order, * while converting them to endian::native byte order. * * @param source address bytes * @param byte_order endian::little or endian::big byte order of given source, one may pass endian::native. */ EUI48(const uint8_t * source, const endian byte_order) noexcept; /** * Fills given EUI48 instance via given string representation. *

* Implementation is consistent with EUI48::toString(). *

* @param str a string of exactly 17 characters representing 6 bytes as hexadecimal numbers separated via colon `01:02:03:0A:0B:0C`. * @param dest EUI48 to set its value * @param errmsg error parsing message if returning false * @return true if successful, otherwise false * @see EUI48::EUI48 * @see EUI48::toString() */ static bool scanEUI48(const std::string& str, EUI48& dest, std::string& errmsg); /** * Construct instance via given string representation. *

* Implementation is consistent with EUI48::toString(). *

* @param str a string of exactly 17 characters representing 6 bytes as hexadecimal numbers separated via colon `01:02:03:0A:0B:0C`. * @see EUI48::scanEUI48() * @see EUI48::toString() * @throws jau::IllegalArgumentException if given string doesn't comply with EUI48 */ EUI48(const std::string& str); constexpr EUI48(const EUI48 &o) noexcept = default; EUI48(EUI48 &&o) noexcept = default; constexpr EUI48& operator=(const EUI48 &o) noexcept = default; EUI48& operator=(EUI48 &&o) noexcept = default; constexpr std::size_t hash_code() const noexcept { // 31 * x == (x << 5) - x std::size_t h = b[0]; h = ( ( h << 5 ) - h ) + b[1]; h = ( ( h << 5 ) - h ) + b[2]; h = ( ( h << 5 ) - h ) + b[3]; h = ( ( h << 5 ) - h ) + b[4]; h = ( ( h << 5 ) - h ) + b[5]; return h; } /** * Method clears the underlying byte array {@link #b}. */ void clear() { b[0] = 0; b[1] = 0; b[2] = 0; b[3] = 0; b[4] = 0; b[5] = 0; } /** * Finds the index of given EUI48Sub needle within this instance haystack. * * The returned index will be adjusted for the desired byte order. * - endian::big will return index 0 for the leading byte like the string representation from left (MSB) to right (LSB). * - endian::little will return index 5 for the leading byte * * @param needle * @param byte_order byte order will adjust the returned index, endian::big is equivalent to the string representation from left (MSB) to right (LSB). * @return index of first element of needle within this instance haystack or -1 if not found. If the needle length is zero, 0 (found) is returned. * @see indexOf() */ jau::snsize_t indexOf(const EUI48Sub& needle, const endian byte_order) const noexcept { return EUI48Sub::indexOf(b, sizeof(b), needle.b, needle.length, byte_order); } /** * Returns true, if given EUI48Sub needle is contained in this instance haystack. *

* If the sub is zero, true is returned. *

*/ bool contains(const EUI48Sub& needle) const noexcept { return 0 <= indexOf(needle, endian::native); } /** * Returns the EUI48 string representation with MSB first (endian::big), * exactly 17 characters representing 6 bytes as upper case hexadecimal numbers separated via colon `01:02:03:0A:0B:0C`. * @see EUI48::EUI48() */ std::string toString() const noexcept; /** * Method transfers all bytes representing this instance into the given * destination array at the given position and in the given byte order. *

* Implementation is consistent with {@link #EUI48(byte[], int, ByteOrder)}. *

* @param sink the destination array * @param sink_pos starting position in the destination array * @param byte_order destination buffer byte order * @see #EUI48(byte[], int, ByteOrder) */ jau::nsize_t put(uint8_t * const sink, jau::nsize_t const sink_pos, const endian byte_order) const noexcept; } ); inline std::string to_string(const EUI48& a) noexcept { return a.toString(); } inline bool operator==(const EUI48& lhs, const EUI48& rhs) noexcept { if( &lhs == &rhs ) { return true; } //return !memcmp(&lhs, &rhs, sizeof(EUI48)); const uint8_t * a = lhs.b; const uint8_t * b = rhs.b; return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] && a[4] == b[4] && a[5] == b[5]; } inline bool operator!=(const EUI48& lhs, const EUI48& rhs) noexcept { return !(lhs == rhs); } constexpr static void bswap_6bytes(uint8_t* sink, const uint8_t* source) noexcept { sink[0] = source[5]; sink[1] = source[4]; sink[2] = source[3]; sink[3] = source[2]; sink[4] = source[1]; sink[5] = source[0]; } constexpr EUI48 bswap(EUI48 const & source) noexcept { EUI48 dest; bswap_6bytes(dest.b, source.b); return dest; } constexpr EUI48 be_to_cpu(EUI48 const & n) noexcept { static_assert(isLittleOrBigEndian()); // one static_assert is sufficient for whole compilation unit if( isLittleEndian() ) { return bswap(n); } else { return n; } } constexpr EUI48 cpu_to_be(EUI48 const & h) noexcept { if( isLittleEndian() ) { return bswap(h); } else { return h; } } constexpr EUI48 le_to_cpu(EUI48 const & l) noexcept { if( isLittleEndian() ) { return l; } else { return bswap(l); } } constexpr EUI48 cpu_to_le(EUI48 const & h) noexcept { if( isLittleEndian() ) { return h; } else { return bswap(h); } } /**@}*/ } /* namespace jau */ // injecting specialization of std::hash to namespace std of our types above namespace std { /** \addtogroup NetUtils * */ template<> struct hash { std::size_t operator()(jau::EUI48Sub const& a) const noexcept { return a.hash_code(); } }; template<> struct hash { std::size_t operator()(jau::EUI48 const& a) const noexcept { return a.hash_code(); } }; /**@}*/ } #endif /* JAU_EUI48_HPP_ */