Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
EUI48Sub.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  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 package org.direct_bt;
25 
26 import java.util.Arrays;
27 
28 /**
29  * A 48 bit EUI-48 sub-identifier, see {@link EUI48}.
30  */
31 public class EUI48Sub {
32  /** EUI48Sub MAC address matching any device, i.e. '0:0:0:0:0:0'. */
33  public static final EUI48Sub ANY_DEVICE = new EUI48Sub( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, 0, 6 );
34  /** EUI48Sub MAC address matching all device, i.e. 'ff:ff:ff:ff:ff:ff'. */
35  public static final EUI48Sub ALL_DEVICE = new EUI48Sub( new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff }, 0, 6 );
36  /** EUI48Sub MAC address matching local device, i.e. '0:0:0:ff:ff:ff'. */
37  public static final EUI48Sub LOCAL_DEVICE = new EUI48Sub( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0xff }, 0, 6 );
38 
39  /**
40  * The EUI48 sub-address, always 6 bytes reserved.
41  */
42  public final byte b[/* 6 octets */];
43 
44  private volatile int hash; // default 0, cache
45 
46  /**
47  * The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
48  */
49  public int length;
50 
51  /**
52  * Fills given EUI48Sub instance via given string representation.
53  * <p>
54  * Implementation is consistent with {@link #toString()}.
55  * </p>
56  * @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon,
57  * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
58  * @param dest EUI48Sub to set its value
59  * @param errmsg error parsing message if returning false
60  * @return true if successful, otherwise false
61  * @see #EUI48Sub(String)
62  * @see #toString()
63  */
64  public static boolean scanEUI48Sub(final String str, final EUI48Sub dest, final StringBuilder errmsg) {
65  final int str_len = str.length();
66  dest.clear();
67 
68  if( 17 < str_len ) { // not exceeding EUI48.byte_size
69  errmsg.append("EUI48 sub-string must be less or equal length 17 but "+str.length()+": "+str);
70  return false;
71  }
72  final byte b_[] = new byte[ 6 ]; // intermediate result high -> low
73  int len_ = 0;
74  int j=0;
75  try {
76  boolean exp_colon = false;
77  while( j+1 < str_len /* && byte_count_ < byte_size */ ) { // min 2 chars left
78  final boolean is_colon = ':' == str.charAt(j);
79  if( exp_colon && !is_colon ) {
80  errmsg.append("EUI48Sub sub-string not in format '01:02:03:0A:0B:0C', but '"+str+"', colon missing, pos "+j+", len "+str_len);
81  return false;
82  } else if( is_colon ) {
83  ++j;
84  exp_colon = false;
85  } else {
86  b_[len_] = Integer.valueOf(str.substring(j, j+2), 16).byteValue(); // b_: high->low
87  j += 2;
88  ++len_;
89  exp_colon = true;
90  }
91  }
92  dest.length = len_;
93  for(j=0; j<len_; ++j) { // swap low->high
94  dest.b[j] = b_[len_-1-j];
95  }
96  } catch (final NumberFormatException e) {
97  errmsg.append("EUI48 sub-string not in format '01:02:03:0A:0B:0C' but "+str+", pos "+j+", len "+str_len);
98  return false;
99  }
100  return true;
101  }
102 
103  /** Construct empty unset instance. */
104  public EUI48Sub() {
105  b = new byte[6];
106  length = 0;
107  }
108 
109  /**
110  * Construct a sub EUI48 via given string representation.
111  * <p>
112  * Implementation is consistent with {@link #toString()}.
113  * </p>
114  * @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon,
115  * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
116  * @see #toString()
117  * @throws IllegalArgumentException if given string doesn't comply with EUI48
118  */
119  public EUI48Sub(final String str) throws IllegalArgumentException {
120  final StringBuilder errmsg = new StringBuilder();
121  b = new byte[ 6 ];
122  if( !scanEUI48Sub(str, this, errmsg) ) {
123  throw new IllegalArgumentException(errmsg.toString());
124  }
125  }
126 
127  /** Construct instance via given source byte array */
128  public EUI48Sub(final byte stream[], final int pos, final int len_) {
129  if( len_ > EUI48.byte_size || pos + len_ > stream.length ) {
130  throw new IllegalArgumentException("EUI48 stream ( pos "+pos+", len "+len_+" > EUI48 size "+EUI48.byte_size+" or stream.length "+stream.length);
131  }
132  b = new byte[6];
133  System.arraycopy(stream, pos, b, 0, len_);
134  length = len_;
135  }
136 
137  @Override
138  public final boolean equals(final Object obj) {
139  if(this == obj) {
140  return true;
141  }
142  if (obj == null || !(obj instanceof EUI48Sub)) {
143  return false;
144  }
145  final int length2 = ((EUI48Sub)obj).length;
146  if( length != length2 ) {
147  return false;
148  }
149  final byte[] b2 = ((EUI48Sub)obj).b;
150  return Arrays.equals(b, 0, length, b2, 0, length2);
151  }
152 
153  /**
154  * {@inheritDoc}
155  * <p>
156  * Implementation uses a lock-free volatile cache.
157  * </p>
158  * @see #clearHash()
159  */
160  @Override
161  public final int hashCode() {
162  int h = hash;
163  if( 0 == h ) {
164  // 31 * x == (x << 5) - x
165  h = length;
166  for(int i=0; i<length; i++) {
167  h = ( ( h << 5 ) - h ) + b[i];
168  }
169  hash = h;
170  }
171  return h;
172  }
173 
174  /**
175  * Method clears the cached hash value.
176  * @see #clear()
177  */
178  public void clearHash() { hash = 0; }
179 
180  /**
181  * Method clears the underlying byte array {@link #b} and sets length to zero. The cached hash value is also cleared.
182  * @see #clearHash()
183  */
184  public void clear() {
185  hash = 0;
186  b[0] = 0; b[1] = 0; b[2] = 0;
187  b[3] = 0; b[4] = 0; b[5] = 0;
188  length = 0;
189  }
190 
191  /**
192  * Find index of needle within haystack.
193  * @param haystack_b haystack data
194  * @param haystack_length haystack length
195  * @param needle_b needle data
196  * @param needle_length needle length
197  * @return index of first element of needle within haystack or -1 if not found. If the needle length is zero, 0 (found) is returned.
198  */
199  public static int indexOf(final byte haystack_b[], final int haystack_length,
200  final byte needle_b[], final int needle_length) {
201  if( 0 == needle_length ) {
202  return 0;
203  }
204  if( haystack_length < needle_length ) {
205  return -1;
206  }
207  final byte first = needle_b[0];
208  final int outerEnd = haystack_length - needle_length + 1; // exclusive
209 
210  for (int i = 0; i < outerEnd; i++) {
211  // find first char of other
212  while( haystack_b[i] != first ) {
213  if( ++i == outerEnd ) {
214  return -1;
215  }
216  }
217  if( i < outerEnd ) { // otherLen chars left to match?
218  // continue matching other chars
219  final int innerEnd = i + needle_length; // exclusive
220  int j = i, k=0;
221  do {
222  if( ++j == innerEnd ) {
223  return i; // gotcha
224  }
225  } while( haystack_b[j] == needle_b[++k] );
226  }
227  }
228  return -1;
229  }
230 
231  /**
232  * Finds the index of given EUI48Sub needle within this instance haystack.
233  * @param needle
234  * @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.
235  */
236  public int indexOf(final EUI48Sub needle) {
237  return indexOf(b, length, needle.b, needle.length);
238  }
239 
240  /**
241  * Returns true, if given EUI48Sub needle is contained in this instance haystack.
242  * <p>
243  * If the sub is zero, true is returned.
244  * </p>
245  */
246  public boolean contains(final EUI48Sub needle) {
247  return 0 <= indexOf(needle);
248  }
249 
250  /**
251  * {@inheritDoc}
252  * <p>
253  * Returns the EUI48 sub-string representation,
254  * less or equal 17 characters representing less or equal 6 bytes as upper case hexadecimal numbers separated via colon,
255  * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
256  * </p>
257  */
258  @Override
259  public final String toString() {
260  // str_len = 2 * len + ( len - 1 )
261  // str_len = 3 * len - 1
262  // len = ( str_len + 1 ) / 3
263  if( 0 < length ) {
264  final StringBuilder sb = new StringBuilder(3 * length - 1);
265  for(int i=length-1; 0 <= i; i--) {
266  BTUtils.byteHexString(sb, b[i], false /* lowerCase */);
267  if( 0 < i ) {
268  sb.append(":");
269  }
270  }
271  return sb.toString();
272  } else {
273  return new String(":");
274  }
275  }
276 }
org.direct_bt.EUI48Sub.clearHash
void clearHash()
Method clears the cached hash value.
Definition: EUI48Sub.java:178
org.direct_bt.EUI48Sub.EUI48Sub
EUI48Sub()
Construct empty unset instance.
Definition: EUI48Sub.java:104
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.ANY_DEVICE
static final EUI48Sub ANY_DEVICE
EUI48Sub MAC address matching any device, i.e.
Definition: EUI48Sub.java:33
org.direct_bt.EUI48Sub.b
final byte b[]
The EUI48 sub-address, always 6 bytes reserved.
Definition: EUI48Sub.java:42
org.direct_bt.EUI48Sub.contains
boolean contains(final EUI48Sub needle)
Returns true, if given EUI48Sub needle is contained in this instance haystack.
Definition: EUI48Sub.java:246
org.direct_bt.EUI48Sub.ALL_DEVICE
static final EUI48Sub ALL_DEVICE
EUI48Sub MAC address matching all device, i.e.
Definition: EUI48Sub.java:35
org.direct_bt.EUI48Sub.equals
final boolean equals(final Object obj)
Definition: EUI48Sub.java:138
org.direct_bt.EUI48Sub.clear
void clear()
Method clears the underlying byte array b and sets length to zero.
Definition: EUI48Sub.java:184
org.direct_bt.EUI48Sub.EUI48Sub
EUI48Sub(final byte stream[], final int pos, final int len_)
Construct instance via given source byte array.
Definition: EUI48Sub.java:128
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.BTUtils
Definition: BTUtils.java:29
org.direct_bt.EUI48Sub.EUI48Sub
EUI48Sub(final String str)
Construct a sub EUI48 via given string representation.
Definition: EUI48Sub.java:119
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.EUI48Sub.LOCAL_DEVICE
static final EUI48Sub LOCAL_DEVICE
EUI48Sub MAC address matching local device, i.e.
Definition: EUI48Sub.java:37
org.direct_bt.EUI48Sub.hashCode
final int hashCode()
Definition: EUI48Sub.java:161
org.direct_bt.EUI48Sub.scanEUI48Sub
static boolean scanEUI48Sub(final String str, final EUI48Sub dest, final StringBuilder errmsg)
Fills given EUI48Sub instance via given string representation.
Definition: EUI48Sub.java:64
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.EUI48Sub.toString
final String toString()
Definition: EUI48Sub.java:259
org.direct_bt.EUI48Sub.indexOf
int indexOf(final EUI48Sub needle)
Finds the index of given EUI48Sub needle within this instance haystack.
Definition: EUI48Sub.java:236