Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
EUI48.java
Go to the documentation of this file.
1 /**
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2021 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 package org.direct_bt;
27 
28 /**
29  * A packed 48 bit EUI-48 identifier, formerly known as MAC-48
30  * or simply network device MAC address (Media Access Control address).
31  * <p>
32  * Implementation caches the hash value {@link #hashCode()},
33  * hence users shall take special care when mutating the
34  * underlying data {@link #b}, read its API notes.
35  * </p>
36  */
37 public class EUI48 {
38  /** EUI48 MAC address matching any device, i.e. '0:0:0:0:0:0'. */
39  public static final EUI48 ANY_DEVICE = new EUI48( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } );
40  /** EUI48 MAC address matching all device, i.e. 'ff:ff:ff:ff:ff:ff'. */
41  public static final EUI48 ALL_DEVICE = new EUI48( new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff } );
42  /** EUI48 MAC address matching local device, i.e. '0:0:0:ff:ff:ff'. */
43  public static final EUI48 LOCAL_DEVICE = new EUI48( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0xff } );
44 
45  /**
46  * The 6 byte EUI48 address.
47  * <p>
48  * If modifying, it is the user's responsibility to avoid data races.<br>
49  * Further, call {@link #clearHash()} after mutation is complete.
50  * </p>
51  */
52  public final byte b[/* 6 octets */];
53 
54  private volatile int hash; // default 0, cache
55 
56  /**
57  * Size of the byte stream representation in bytes
58  * @see #getStream(byte[], int)
59  */
60  /* pp */ static final int byte_size = 6;
61 
62  /**
63  * Fills given EUI48 instance via given string representation.
64  * <p>
65  * Implementation is consistent with {@link #toString()}.
66  * </p>
67  * @param str a string of exactly 17 characters representing 6 bytes as hexadecimal numbers separated via colon {@code "01:02:03:0A:0B:0C"}.
68  * @param dest EUI48 to set its value
69  * @param errmsg error parsing message if returning false
70  * @return true if successful, otherwise false
71  * @see #EUI48(String)
72  * @see #toString()
73  */
74  public static boolean scanEUI48(final String str, final EUI48 dest, final StringBuilder errmsg) {
75  if( 17 != str.length() ) {
76  errmsg.append("EUI48 string not of length 17 but "+str.length()+": "+str);
77  return false;
78  }
79  try {
80  for(int i=0; i<byte_size; i++) {
81  dest.b[byte_size-1-i] = Integer.valueOf(str.substring(i*2+i, i*2+i+2), 16).byteValue();
82  }
83  } catch (final NumberFormatException e) {
84  errmsg.append("EUI48 string not in format '01:02:03:0A:0B:0C' but "+str+"; "+e.getMessage());
85  return false;
86  }
87  return true;
88  }
89 
90  /**
91  * Construct instance via given string representation.
92  * <p>
93  * Implementation is consistent with {@link #toString()}.
94  * </p>
95  * @param str a string of exactly 17 characters representing 6 bytes as hexadecimal numbers separated via colon {@code "01:02:03:0A:0B:0C"}.
96  * @see #scanEUI48(String, byte[], StringBuilder)
97  * @see #toString()
98  * @throws IllegalArgumentException if given string doesn't comply with EUI48
99  */
100  public EUI48(final String str) throws IllegalArgumentException {
101  final StringBuilder errmsg = new StringBuilder();
102  b = new byte[byte_size];
103  if( !scanEUI48(str, this, errmsg) ) {
104  throw new IllegalArgumentException(errmsg.toString());
105  }
106  }
107 
108  /** Construct instance via given source byte array */
109  public EUI48(final byte stream[], final int pos) {
110  if( byte_size > ( stream.length - pos ) ) {
111  throw new IllegalArgumentException("EUI48 stream ( "+stream.length+" - "+pos+" ) < "+byte_size+" bytes");
112  }
113  b = new byte[byte_size];
114  System.arraycopy(stream, pos, b, 0, byte_size);
115  }
116 
117  /** Construct instance using the given address of the byte array */
118  public EUI48(final byte address[]) {
119  if( byte_size != address.length ) {
120  throw new IllegalArgumentException("EUI48 stream "+address.length+" != "+byte_size+" bytes");
121  }
122  b = address;
123  }
124 
125  /** Construct empty unset instance. */
126  public EUI48() {
127  b = new byte[byte_size];
128  }
129 
130  @Override
131  public final boolean equals(final Object obj) {
132  if(this == obj) {
133  return true;
134  }
135  if (obj == null || !(obj instanceof EUI48)) {
136  return false;
137  }
138  final byte[] b2 = ((EUI48)obj).b;
139  return b[0] == b2[0] &&
140  b[1] == b2[1] &&
141  b[2] == b2[2] &&
142  b[3] == b2[3] &&
143  b[4] == b2[4] &&
144  b[5] == b2[5];
145  }
146 
147  /**
148  * {@inheritDoc}
149  * <p>
150  * Implementation uses a lock-free volatile cache.
151  * </p>
152  * @see #clearHash()
153  */
154  @Override
155  public final int hashCode() {
156  int h = hash;
157  if( 0 == h ) {
158  /**
159  // final int p = 92821; // alternative with less collisions?
160  final int p = 31; // traditional prime
161  h = b[0];
162  h = p * h + b[1];
163  h = p * h + b[2];
164  h = p * h + b[3];
165  h = p * h + b[4];
166  h = p * h + b[5];
167  */
168  // 31 * x == (x << 5) - x
169  h = b[0];
170  h = ( ( h << 5 ) - h ) + b[1];
171  h = ( ( h << 5 ) - h ) + b[2];
172  h = ( ( h << 5 ) - h ) + b[3];
173  h = ( ( h << 5 ) - h ) + b[4];
174  h = ( ( h << 5 ) - h ) + b[5];
175  hash = h;
176  }
177  return h;
178  }
179 
180  /**
181  * Method clears the cached hash value.
182  * @see #clear()
183  */
184  public void clearHash() { hash = 0; }
185 
186  /**
187  * Method clears the underlying byte array {@link #b} and cached hash value.
188  * @see #clearHash()
189  */
190  public void clear() {
191  hash = 0;
192  b[0] = 0; b[1] = 0; b[2] = 0;
193  b[3] = 0; b[4] = 0; b[5] = 0;
194  }
195 
196  /**
197  * Method transfers all bytes representing a EUI48 from the given
198  * source array at the given position into this instance and clears its cached hash value.
199  * <p>
200  * Implementation is consistent with {@link #EUI48(byte[], int)}.
201  * </p>
202  * @param source the source array
203  * @param pos starting position in the source array
204  * @see #EUI48(byte[], int)
205  * @see #clearHash()
206  */
207  public final void putStream(final byte[] source, final int pos) {
208  if( byte_size > ( source.length - pos ) ) {
209  throw new IllegalArgumentException("Stream ( "+source.length+" - "+pos+" ) < "+byte_size+" bytes");
210  }
211  hash = 0;
212  System.arraycopy(source, pos, b, 0, byte_size);
213  }
214 
215  /**
216  * Method transfers all bytes representing this instance into the given
217  * destination array at the given position.
218  * <p>
219  * Implementation is consistent with {@link #EUI48(byte[], int)}.
220  * </p>
221  * @param sink the destination array
222  * @param pos starting position in the destination array
223  * @see #EUI48(byte[], int)
224  */
225  public final void getStream(final byte[] sink, final int pos) {
226  if( byte_size > ( sink.length - pos ) ) {
227  throw new IllegalArgumentException("Stream ( "+sink.length+" - "+pos+" ) < "+byte_size+" bytes");
228  }
229  System.arraycopy(b, 0, sink, pos, byte_size);
230  }
231 
232  /**
233  * Returns the {@link BLERandomAddressType}.
234  * <p>
235  * If {@link BDAddressType} is {@link BDAddressType::BDADDR_LE_RANDOM},
236  * method shall return a valid value other than {@link BLERandomAddressType::UNDEFINED}.
237  * </p>
238  * <p>
239  * If {@link BDAddressType} is not {@link BDAddressType::BDADDR_LE_RANDOM},
240  * method shall return {@link BLERandomAddressType::UNDEFINED}.
241  * </p>
242  * @since 2.2.0
243  */
245  if( BDAddressType.BDADDR_LE_RANDOM != addressType ) {
247  }
248  final byte high2 = (byte) ( ( b[5] >> 6 ) & 0x03 );
249  return BLERandomAddressType.get(high2);
250  }
251 
252  /**
253  * Finds the index of given EUI48Sub.
254  */
255  public int indexOf(final EUI48Sub needle) {
256  return EUI48Sub.indexOf(b, 6, needle.b, needle.length);
257  }
258 
259  /**
260  * Returns true, if given EUI48Sub is contained in here.
261  * <p>
262  * If the sub is zero, true is returned.
263  * </p>
264  */
265  public boolean contains(final EUI48Sub needle) {
266  return 0 <= indexOf(needle);
267  }
268 
269  /**
270  * {@inheritDoc}
271  * <p>
272  * Returns the EUI48 string representation,
273  * exactly 17 characters representing 6 bytes as upper case hexadecimal numbers separated via colon {@code "01:02:03:0A:0B:0C"}.
274  * </p>
275  * @see #EUI48(String)
276  */
277  @Override
278  public final String toString() {
279  final StringBuilder sb = new StringBuilder(17);
280  for(int i=byte_size-1; 0 <= i; i--) {
281  BTUtils.byteHexString(sb, b[i], false /* lowerCase */);
282  if( 0 < i ) {
283  sb.append(":");
284  }
285  }
286  return sb.toString();
287  }
288 }
org.direct_bt.EUI48.contains
boolean contains(final EUI48Sub needle)
Returns true, if given EUI48Sub is contained in here.
Definition: EUI48.java:265
org.direct_bt.EUI48.hashCode
final int hashCode()
Definition: EUI48.java:155
org.direct_bt.EUI48.EUI48
EUI48(final String str)
Construct instance via given string representation.
Definition: EUI48.java:100
org.direct_bt.EUI48.toString
final String toString()
Definition: EUI48.java:278
org.direct_bt.BLERandomAddressType.get
static BLERandomAddressType get(final byte value)
Maps the specified byte value to a constant of BLERandomAddressType.
Definition: BLERandomAddressType.java:69
org.direct_bt.EUI48.EUI48
EUI48()
Construct empty unset instance.
Definition: EUI48.java:126
org.direct_bt.EUI48Sub
A 48 bit EUI-48 sub-identifier, see EUI48.
Definition: EUI48Sub.java:31
org.direct_bt.EUI48
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: EUI48.java:37
org.direct_bt.EUI48Sub.b
final byte b[]
The EUI48 sub-address, always 6 bytes reserved.
Definition: EUI48Sub.java:42
org.direct_bt.EUI48.putStream
final void putStream(final byte[] source, final int pos)
Method transfers all bytes representing a EUI48 from the given source array at the given position int...
Definition: EUI48.java:207
org.direct_bt.EUI48.LOCAL_DEVICE
static final EUI48 LOCAL_DEVICE
EUI48 MAC address matching local device, i.e.
Definition: EUI48.java:43
org.direct_bt.EUI48.clear
void clear()
Method clears the underlying byte array b and cached hash value.
Definition: EUI48.java:190
org.direct_bt.BTUtils.byteHexString
static StringBuilder byteHexString(final StringBuilder sb, final byte value, final boolean lowerCase)
Produce a hexadecimal string representation of the given byte value.
Definition: BTUtils.java:168
org.direct_bt.EUI48.ANY_DEVICE
static final EUI48 ANY_DEVICE
EUI48 MAC address matching any device, i.e.
Definition: EUI48.java:39
org.direct_bt.BTUtils
Definition: BTUtils.java:29
org.direct_bt.EUI48.b
final byte b[]
The 6 byte EUI48 address.
Definition: EUI48.java:52
org.direct_bt.EUI48.ALL_DEVICE
static final EUI48 ALL_DEVICE
EUI48 MAC address matching all device, i.e.
Definition: EUI48.java:41
org.direct_bt.EUI48.EUI48
EUI48(final byte address[])
Construct instance using the given address of the byte array.
Definition: EUI48.java:118
org.direct_bt.EUI48Sub.length
int length
The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
Definition: EUI48Sub.java:49
org.direct_bt.EUI48.getStream
final void getStream(final byte[] sink, final int pos)
Method transfers all bytes representing this instance into the given destination array at the given p...
Definition: EUI48.java:225
org.direct_bt.BDAddressType.BDADDR_LE_RANDOM
BDADDR_LE_RANDOM
Bluetooth LE random address, see BLERandomAddressType.
Definition: BDAddressType.java:59
org.direct_bt.EUI48.equals
final boolean equals(final Object obj)
Definition: EUI48.java:131
org.direct_bt.BDAddressType
Bluetooth address type constants.
Definition: BDAddressType.java:53
org.direct_bt.BLERandomAddressType.UNDEFINED
UNDEFINED
Undefined, e.g.
Definition: BLERandomAddressType.java:60
org.direct_bt.EUI48.EUI48
EUI48(final byte stream[], final int pos)
Construct instance via given source byte array.
Definition: EUI48.java:109
org.direct_bt.BLERandomAddressType
Bluetooth LE random address type constants.
Definition: BLERandomAddressType.java:50
org.direct_bt.EUI48.indexOf
int indexOf(final EUI48Sub needle)
Finds the index of given EUI48Sub.
Definition: EUI48.java:255
org.direct_bt.EUI48Sub.indexOf
static int indexOf(final byte haystack_b[], final int haystack_length, final byte needle_b[], final int needle_length)
Find index of needle within haystack.
Definition: EUI48Sub.java:199
org.direct_bt.EUI48.clearHash
void clearHash()
Method clears the cached hash value.
Definition: EUI48.java:184
org.direct_bt.EUI48.scanEUI48
static boolean scanEUI48(final String str, final EUI48 dest, final StringBuilder errmsg)
Fills given EUI48 instance via given string representation.
Definition: EUI48.java:74
org.direct_bt.EUI48.getBLERandomAddressType
BLERandomAddressType getBLERandomAddressType(final BDAddressType addressType)
Returns the BLERandomAddressType.
Definition: EUI48.java:244