diff options
author | Sven Gothel <[email protected]> | 2021-07-30 21:18:33 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-07-30 21:18:33 +0200 |
commit | 47d0206cc9d2044379e00619a843101ce9d3778f (patch) | |
tree | 255cec13b30c46b1953bae515c3a6119cf1206d9 | |
parent | d2158a9db8a42584c292c101f50aea44b6b0acd5 (diff) |
C++: EUI48Sub add: hash_code(), clear(), indexOf(), contains(), operator==() etc; EUI48[Sub]: Add static 'scanEUI48[Sub]'(string&) and static 'indexOf()'
-rw-r--r-- | api/direct_bt/BTAddress.hpp | 126 | ||||
-rw-r--r-- | src/direct_bt/BTTypes0.cpp | 88 |
2 files changed, 177 insertions, 37 deletions
diff --git a/api/direct_bt/BTAddress.hpp b/api/direct_bt/BTAddress.hpp index ee7c2cf0..b514db36 100644 --- a/api/direct_bt/BTAddress.hpp +++ b/api/direct_bt/BTAddress.hpp @@ -152,6 +152,13 @@ namespace direct_bt { * A 48 bit EUI-48 sub-identifier, see EUI48. */ struct EUI48Sub { + /** 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. */ @@ -166,21 +173,86 @@ namespace direct_bt { EUI48Sub(const uint8_t * b_, const jau::nsize_t len_) noexcept; /** + * Fills given EUI48Sub instance via given string representation. + * <p> + * Implementation is consistent with EUI48Sub::toString(). + * </p> + * @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. * <p> * Implementation is consistent with EUI48Sub::toString(). * </p> * @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 mac); + 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<length; i++) { + h = ( ( h << 5 ) - h ) + b[i]; + } + return h; + } + + /** + * Method clears the underlying byte array {@link #b} and sets length to zero. + */ + void clear() { + b[0] = 0; b[1] = 0; b[2] = 0; + b[3] = 0; b[4] = 0; b[5] = 0; + length = 0; + } + + /** + * Find index of needle within haystack. + * @param haystack_b haystack data + * @param haystack_length haystack length + * @param needle_b needle data + * @param needle_length needle length + * @return index of first element of needle within haystack or -1 if not found. If the needle length is zero, 0 (found) is returned. + */ + static jau::snsize_t indexOf(const uint8_t haystack_b[], const jau::nsize_t haystack_length, + const uint8_t needle_b[], const jau::nsize_t needle_length) noexcept; + + /** + * Finds the index of given EUI48Sub needle within this instance haystack. + * @param needle + * @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. + */ + jau::snsize_t indexOf(const EUI48Sub& needle) const noexcept { + return indexOf(b, length, needle.b, needle.length); + } + + /** + * Returns true, if given EUI48Sub needle is contained in this instance haystack. + * <p> + * If the sub is zero, true is returned. + * </p> + */ + bool contains(const EUI48Sub& needle) const noexcept { + return 0 <= indexOf(needle); + } + /** * Returns the EUI48 sub-string representation, * less or equal 17 characters representing less or equal 6 bytes as upper case hexadecimal numbers separated via colon, @@ -190,6 +262,20 @@ namespace direct_bt { }; 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). @@ -211,14 +297,30 @@ namespace direct_bt { EUI48(const uint8_t * b_) noexcept; /** + * Fills given EUI48 instance via given string representation. + * <p> + * Implementation is consistent with EUI48::toString(). + * </p> + * @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. * <p> * Implementation is consistent with EUI48::toString(). * </p> * @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 mac); + EUI48(const std::string& str); constexpr EUI48(const EUI48 &o) noexcept = default; EUI48(EUI48 &&o) noexcept = default; @@ -259,17 +361,23 @@ namespace direct_bt { BLERandomAddressType getBLERandomAddressType(const BDAddressType addressType) const noexcept; /** - * Finds the index of given EUI48Sub. + * Finds the index of given EUI48Sub needle within this instance haystack. + * @param needle + * @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. */ - jau::snsize_t indexOf(const EUI48Sub& other) const noexcept; + jau::snsize_t indexOf(const EUI48Sub& needle) const noexcept { + return EUI48Sub::indexOf(b, sizeof(b), needle.b, needle.length); + } /** - * Returns true, if given EUI48Sub is contained in here. + * Returns true, if given EUI48Sub needle is contained in this instance haystack. * <p> * If the sub is zero, true is returned. * </p> */ - bool contains(const EUI48Sub& other) const noexcept; + bool contains(const EUI48Sub& needle) const noexcept { + return 0 <= indexOf(needle); + } /** * Returns the EUI48 string representation, @@ -435,6 +543,12 @@ namespace direct_bt { // injecting specialization of std::hash to namespace std of our types above namespace std { + template<> struct hash<direct_bt::EUI48Sub> { + std::size_t operator()(direct_bt::EUI48Sub const& a) const noexcept { + return a.hash_code(); + } + }; + template<> struct hash<direct_bt::EUI48> { std::size_t operator()(direct_bt::EUI48 const& a) const noexcept { return a.hash_code(); diff --git a/src/direct_bt/BTTypes0.cpp b/src/direct_bt/BTTypes0.cpp index 136a1f0b..92366f07 100644 --- a/src/direct_bt/BTTypes0.cpp +++ b/src/direct_bt/BTTypes0.cpp @@ -181,12 +181,13 @@ std::string EUI48Sub::toString() const noexcept { return str; } -EUI48Sub::EUI48Sub(const std::string str) { +bool EUI48Sub::scanEUI48Sub(const std::string& str, EUI48Sub& dest, std::string& errmsg) { const jau::nsize_t str_len = static_cast<jau::nsize_t>( str.length() ); - length = 0; + dest.clear(); if( 17 < str_len ) { // not exceeding byte_size - throw jau::IllegalArgumentException("EUI48 sub-string must be less or equal length 17 but "+std::to_string(str_len)+": "+str, E_FILE_LINE); + errmsg.append("EUI48 sub-string must be less or equal length 17 but "+std::to_string(str_len)+": "+str); + return false; } const char * str_ptr = str.c_str(); jau::nsize_t j=0; @@ -195,59 +196,72 @@ EUI48Sub::EUI48Sub(const std::string str) { if( ':' == str[j] ) { ++j; } else { - if ( sscanf(str_ptr+j, "%02hhx", &b_[length]) != 1 ) // b_: high->low + if ( sscanf(str_ptr+j, "%02hhx", &b_[dest.length]) != 1 ) // b_: high->low { - std::string msg("EUI48Sub sub-string not in format '01:02:03:0A:0B:0C' but '"+str+"', pos "+std::to_string(j)+", len "+std::to_string(str_len)); - throw jau::IllegalArgumentException(msg, E_FILE_LINE); + errmsg.append("EUI48Sub sub-string not in format '01:02:03:0A:0B:0C' but '"+str+"', pos "+std::to_string(j)+", len "+std::to_string(str_len)); + return false; } j += 2; - ++length; + ++dest.length; } } - for(j=0; j<length; ++j) { // swap low->high - b[j] = b_[length-1-j]; + for(j=0; j<dest.length; ++j) { // swap low->high + dest.b[j] = b_[dest.length-1-j]; } // sscanf provided host data type, in which we store the values, // hence no endian conversion + return true; +} + +EUI48Sub::EUI48Sub(const std::string& str) { + std::string errmsg; + if( !scanEUI48Sub(str, *this, errmsg) ) { + throw jau::IllegalArgumentException(errmsg, E_FILE_LINE); + } } EUI48Sub::EUI48Sub(const uint8_t * b_, const jau::nsize_t len_) noexcept { length = len_; - memcpy(b, b_, std::max<jau::nsize_t>(sizeof(b), len_)); + const jau::nsize_t cpsz = std::max<jau::nsize_t>(sizeof(b), len_); + const jau::nsize_t bzsz = sizeof(b) - cpsz; + memcpy(b, b_, cpsz); + if( bzsz > 0 ) { + bzero(b+cpsz, bzsz); + } } -jau::snsize_t EUI48::indexOf(const EUI48Sub& other) const noexcept { - if( 0 == other.length ) { +jau::snsize_t EUI48Sub::indexOf(const uint8_t haystack_b[], const jau::nsize_t haystack_length, + const uint8_t needle_b[], const jau::nsize_t needle_length) noexcept { + if( 0 == needle_length ) { return 0; } - const uint8_t first = other.b[0]; - const jau::nsize_t outerEnd = 6 - other.length + 1; // exclusive + if( haystack_length < needle_length ) { + return -1; + } + const uint8_t first = needle_b[0]; + const jau::nsize_t outerEnd = haystack_length - needle_length + 1; // exclusive for (jau::nsize_t i = 0; i < outerEnd; i++) { // find first char of other - while( b[i] != first ) { + while( haystack_b[i] != first ) { if( ++i == outerEnd ) { return -1; } } if( i < outerEnd ) { // otherLen chars left to match? // continue matching other chars - const jau::nsize_t innerEnd = i + other.length; // exclusive + const jau::nsize_t innerEnd = i + needle_length; // exclusive jau::nsize_t j = i, k=0; do { if( ++j == innerEnd ) { return i; // gotcha } - } while( b[j] == other.b[++k] ); + } while( haystack_b[j] == needle_b[++k] ); } } return -1; } -bool EUI48::contains(const EUI48Sub& other) const noexcept { - return 0 <= indexOf(other); -} - std::string EUI48::toString() const noexcept { // str_len = 2 * len + ( len - 1 ) // str_len = 3 * len - 1 @@ -264,31 +278,43 @@ std::string EUI48::toString() const noexcept { return str; } -EUI48::EUI48(const std::string str) { +bool EUI48::scanEUI48(const std::string& str, EUI48& dest, std::string& errmsg) { if( 17 != str.length() ) { - std::string msg("EUI48 string not of length 17 but "); - msg.append(std::to_string(str.length())); - msg.append(": "+str); - throw jau::IllegalArgumentException(msg, E_FILE_LINE); + errmsg.append("EUI48 string not of length 17 but "); + errmsg.append(std::to_string(str.length())); + errmsg.append(": "+str); + return false; } if ( sscanf(str.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &b[5], &b[4], &b[3], &b[2], &b[1], &b[0]) != 6 ) + &dest.b[5], &dest.b[4], &dest.b[3], &dest.b[2], &dest.b[1], &dest.b[0]) != 6 ) { - std::string msg("EUI48 string not in format '01:02:03:0A:0B:0C' but '"+str+"'"); - throw jau::IllegalArgumentException(msg, E_FILE_LINE); + errmsg.append("EUI48 string not in format '01:02:03:0A:0B:0C' but '"+str+"'"); + return false; } - // sscanf provided host data type, in which we store the values, // hence no endian conversion + return true; +} + +EUI48::EUI48(const std::string& str) { + std::string errmsg; + if( !scanEUI48(str, *this, errmsg) ) { + throw jau::IllegalArgumentException(errmsg, E_FILE_LINE); + } } EUI48::EUI48(const uint8_t * b_) noexcept { memcpy(b, b_, sizeof(b)); } -const EUI48 direct_bt::EUI48::ANY_DEVICE; // default ctor is zero bytes! static uint8_t _EUI48_ALL_DEVICE[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static uint8_t _EUI48_LOCAL_DEVICE[] = {0x00, 0x00, 0x00, 0xff, 0xff, 0xff}; + +const EUI48Sub direct_bt::EUI48Sub::ANY_DEVICE; // default ctor is zero bytes! +const EUI48Sub direct_bt::EUI48Sub::ALL_DEVICE( _EUI48_ALL_DEVICE, 6 ); +const EUI48Sub direct_bt::EUI48Sub::LOCAL_DEVICE( _EUI48_LOCAL_DEVICE, 6 ); + +const EUI48 direct_bt::EUI48::ANY_DEVICE; // default ctor is zero bytes! const EUI48 direct_bt::EUI48::ALL_DEVICE( _EUI48_ALL_DEVICE ); const EUI48 direct_bt::EUI48::LOCAL_DEVICE( _EUI48_LOCAL_DEVICE ); |