Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
eui48.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <cstdio>
31
32#include <jau/eui48.hpp>
33
34using namespace jau;
35
36std::string EUI48Sub::toString() const noexcept {
37 // str_len = 2 * len + ( len - 1 )
38 // str_len = 3 * len - 1
39 // len = ( str_len + 1 ) / 3
40 std::string str;
41 if( 0 < length ) {
42 str.reserve(3 * length - 1);
43
44 if( is_little_endian() ) {
45 for(jau::nsize_t i=0; i < length; ++i) {
46 const jau::nsize_t idx = length - 1 - i;
47 jau::byteHexString(str, b[idx], false /* lowerCase */);
48 if( 0 < idx ) {
49 str.push_back(':');
50 }
51 }
52 } else {
53 for(jau::nsize_t idx=0; idx < length; ++idx) {
54 if( 0 < idx ) {
55 str.push_back(':');
56 }
57 jau::byteHexString(str, b[idx], false /* lowerCase */);
58 }
59 }
60 } else {
61 str.push_back(':');
62 }
63 return str;
64}
65
66bool EUI48Sub::scanEUI48Sub(const std::string& str, EUI48Sub& dest, std::string& errmsg) {
67 const jau::nsize_t str_len = static_cast<jau::nsize_t>( str.length() );
68 dest.clear();
69
70 if( 17 < str_len ) { // not exceeding byte_size
71 errmsg.append("EUI48 sub-string must be less or equal length 17 but "+std::to_string(str_len)+": "+str);
72 return false;
73 }
74 const char * str_ptr = str.c_str();
75 jau::nsize_t j=0;
76 bool exp_colon = false;
77 uint8_t b_[6]; // intermediate result high -> low (big-endian)
78 while( j+1 < str_len /* && byte_count_ < byte_size */ ) { // min 2 chars left
79 const bool is_colon = ':' == str[j];
80 if( exp_colon && !is_colon ) {
81 errmsg.append("EUI48Sub sub-string not in format '01:02:03:0A:0B:0C', but '"+str+"', colon missing, pos "+std::to_string(j)+", len "+std::to_string(str_len));
82 return false;
83 } else if( is_colon ) {
84 ++j;
85 exp_colon = false;
86 } else {
87 if ( sscanf(str_ptr+j, "%02hhx", &b_[dest.length]) != 1 ) // b_: high->low
88 {
89 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));
90 return false;
91 }
92 j += 2;
93 ++dest.length;
94 exp_colon = true;
95 }
96 }
97 if( is_little_endian() ) {
98 for(j=0; j<dest.length; ++j) { // swap to low->high
99 dest.b[j] = b_[dest.length-1-j];
100 }
101 } else {
102 memcpy(dest.b, b_, dest.length);
103 }
104 return true;
105}
106
107EUI48Sub::EUI48Sub(const std::string& str) {
108 std::string errmsg;
109 if( !scanEUI48Sub(str, *this, errmsg) ) {
111 }
112}
113
114EUI48Sub::EUI48Sub(const uint8_t * b_, const jau::nsize_t len_, const lb_endian_t byte_order) noexcept {
115 length = len_;
116 const jau::nsize_t cpsz = std::max<jau::nsize_t>(sizeof(b), len_);
117 const jau::nsize_t bzsz = sizeof(b) - cpsz;
118
119 if( lb_endian_t::native == byte_order ) {
120 memcpy(b, b_, cpsz);
121 } else {
122 bswap(b, b_, cpsz);
123 }
124 if( bzsz > 0 ) {
125 bzero(b+cpsz, bzsz);
126 }
127}
128
129jau::snsize_t EUI48Sub::indexOf(const uint8_t haystack_b[], const jau::nsize_t haystack_length,
130 const uint8_t needle_b[], const jau::nsize_t needle_length,
131 const lb_endian_t byte_order) noexcept {
132 if( 0 == needle_length ) {
133 return 0;
134 }
135 if( haystack_length < needle_length ) {
136 return -1;
137 }
138 const uint8_t first = needle_b[0];
139 const jau::nsize_t outerEnd = haystack_length - needle_length + 1; // exclusive
140
141 for (jau::nsize_t i = 0; i < outerEnd; i++) {
142 // find first char of other
143 while( haystack_b[i] != first ) {
144 if( ++i == outerEnd ) {
145 return -1;
146 }
147 }
148 if( i < outerEnd ) { // otherLen chars left to match?
149 // continue matching other chars
150 const jau::nsize_t innerEnd = i + needle_length; // exclusive
151 jau::nsize_t j = i, k=0;
152 do {
153 if( ++j == innerEnd ) {
154 // gotcha
155 if( lb_endian_t::native == byte_order ) {
156 return static_cast<jau::snsize_t>(i);
157 } else {
158 return static_cast<jau::snsize_t>(5 - i - ( needle_length - 1 ));
159 }
160 }
161 } while( haystack_b[j] == needle_b[++k] );
162 }
163 }
164 return -1;
165}
166
167std::string EUI48::toString() const noexcept {
168 // str_len = 2 * len + ( len - 1 )
169 // str_len = 3 * len - 1
170 // len = ( str_len + 1 ) / 3
171 std::string str;
172 str.reserve(17); // 6 * 2 + ( 6 - 1 )
173
174 if( is_little_endian() ) {
175 jau::byteHexString(str, b[5], false /* lowerCase */);
176 str.push_back(':');
177 jau::byteHexString(str, b[4], false /* lowerCase */);
178 str.push_back(':');
179 jau::byteHexString(str, b[3], false /* lowerCase */);
180 str.push_back(':');
181 jau::byteHexString(str, b[2], false /* lowerCase */);
182 str.push_back(':');
183 jau::byteHexString(str, b[1], false /* lowerCase */);
184 str.push_back(':');
185 jau::byteHexString(str, b[0], false /* lowerCase */);
186 } else {
187 jau::byteHexString(str, b[0], false /* lowerCase */);
188 str.push_back(':');
189 jau::byteHexString(str, b[1], false /* lowerCase */);
190 str.push_back(':');
191 jau::byteHexString(str, b[2], false /* lowerCase */);
192 str.push_back(':');
193 jau::byteHexString(str, b[3], false /* lowerCase */);
194 str.push_back(':');
195 jau::byteHexString(str, b[4], false /* lowerCase */);
196 str.push_back(':');
197 jau::byteHexString(str, b[5], false /* lowerCase */);
198 }
199 return str;
200}
201
202bool EUI48::scanEUI48(const std::string& str, EUI48& dest, std::string& errmsg) {
203 if( 17 != str.length() ) {
204 errmsg.append("EUI48 string not of length 17 but ");
205 errmsg.append(std::to_string(str.length()));
206 errmsg.append(": "+str);
207 return false;
208 }
209 int scanres;
210 if( is_little_endian() ) {
211 scanres = sscanf(str.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
212 &dest.b[5], &dest.b[4], &dest.b[3], &dest.b[2], &dest.b[1], &dest.b[0]);
213 } else {
214 scanres = sscanf(str.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
215 &dest.b[0], &dest.b[1], &dest.b[2], &dest.b[3], &dest.b[4], &dest.b[5]);
216 }
217 if ( 6 != scanres ) {
218 errmsg.append("EUI48 string not in format '01:02:03:0A:0B:0C' but '"+str+"'");
219 return false;
220 }
221 // sscanf provided host data type, in which we store the values,
222 // hence no endian conversion
223 return true;
224}
225
226EUI48::EUI48(const std::string& str) {
227 std::string errmsg;
228 if( !scanEUI48(str, *this, errmsg) ) {
230 }
231}
232
233EUI48::EUI48(const uint8_t * source, const lb_endian_t byte_order) noexcept {
234 if( lb_endian_t::native == byte_order ) {
235 memcpy(b, source, sizeof(b));
236 } else {
237 bswap_6bytes(b, source);
238 }
239}
240
241jau::nsize_t EUI48::put(uint8_t * const sink, const lb_endian_t byte_order) const noexcept {
242 if( lb_endian_t::native == byte_order ) {
243 memcpy(sink, b, sizeof(b));
244 } else {
245 bswap_6bytes(sink, b);
246 }
247 return 6;
248}
249
250static uint8_t _EUI48_ALL_DEVICE[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
251static uint8_t _EUI48_LOCAL_DEVICE[] = {0x00, 0x00, 0x00, 0xff, 0xff, 0xff};
252
253const EUI48Sub jau::EUI48Sub::ANY_DEVICE; // default ctor is zero bytes!
256
257const EUI48 jau::EUI48::ANY_DEVICE; // default ctor is zero bytes!
260
#define E_FILE_LINE
static uint8_t _EUI48_LOCAL_DEVICE[]
Definition: eui48.cpp:251
static uint8_t _EUI48_ALL_DEVICE[]
Definition: eui48.cpp:250
constexpr uint16_t bswap(uint16_t const source) noexcept
Definition: byte_util.hpp:88
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
Definition: byte_util.hpp:227
constexpr bool is_little_endian(const endian_t byte_order) noexcept
Returns true if given byte_order equals endian::little, otherwise false.
Definition: byte_util.hpp:293
@ native
Identifier for native platform type, one of the above.
@ little
Identifier for little endian, equivalent to endian::little.
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
static constexpr void bswap_6bytes(uint8_t *sink, const uint8_t *source) noexcept
Definition: eui48.hpp:345
std::string & byteHexString(std::string &dest, const uint8_t value, const bool lowerCase) noexcept
Produce a hexadecimal string representation of the given byte value.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
A 48 bit EUI-48 sub-identifier, see EUI48.
Definition: eui48.hpp:51
jau::nsize_t length
The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
Definition: eui48.hpp:68
constexpr EUI48Sub() noexcept
Definition: eui48.hpp:70
uint8_t b[6]
The <= 6 byte EUI48 sub-address.
Definition: eui48.hpp:63
static const EUI48Sub ALL_DEVICE
EUI48 MAC address matching all device, i.e.
Definition: eui48.hpp:56
static const EUI48Sub ANY_DEVICE
EUI48 MAC address matching any device, i.e.
Definition: eui48.hpp:54
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, const lb_endian_t byte_order) noexcept
Find index of needle within haystack in the given byte order.
Definition: eui48.cpp:129
void clear()
Method clears the underlying byte array b and sets length to zero.
Definition: eui48.hpp:127
static const EUI48Sub LOCAL_DEVICE
EUI48 MAC address matching local device, i.e.
Definition: eui48.hpp:58
static bool scanEUI48Sub(const std::string &str, EUI48Sub &dest, std::string &errmsg)
Fills given EUI48Sub instance via given string representation.
Definition: eui48.cpp:66
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
static const EUI48 LOCAL_DEVICE
EUI48 MAC address matching local device, i.e.
Definition: eui48.hpp:324
uint8_t b[6]
Definition: eui48.hpp:324
static const EUI48 ANY_DEVICE
EUI48 MAC address matching any device, i.e.
Definition: eui48.hpp:324
static const EUI48 ALL_DEVICE
EUI48 MAC address matching all device, i.e.
Definition: eui48.hpp:324
static bool scanEUI48(const std::string &str, EUI48 &dest, std::string &errmsg)
Definition: eui48.cpp:202
jau::nsize_t put(uint8_t *const sink, const lb_endian_t byte_order) const noexcept
Definition: eui48.cpp:241
constexpr EUI48() noexcept
Definition: eui48.hpp:324
std::string toString() const noexcept
Definition: eui48.cpp:167