summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2021-07-30 21:18:33 +0200
committerSven Gothel <[email protected]>2021-07-30 21:18:33 +0200
commit47d0206cc9d2044379e00619a843101ce9d3778f (patch)
tree255cec13b30c46b1953bae515c3a6119cf1206d9
parentd2158a9db8a42584c292c101f50aea44b6b0acd5 (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.hpp126
-rw-r--r--src/direct_bt/BTTypes0.cpp88
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 );