jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
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 */
24package org.jau.net;
25
26import java.nio.ByteOrder;
27import java.util.Arrays;
28
29import org.jau.util.BasicTypes;
30
31/**
32 * A 48 bit EUI-48 sub-identifier, see {@link EUI48}.
33 * <p>
34 * Stores value in {@link ByteOrder#nativeOrder()} byte order.
35 * </p>
36 */
37public class EUI48Sub {
38 /** EUI48Sub MAC address matching any device, i.e. '0:0:0:0:0:0'. */
39 public static final EUI48Sub ANY_DEVICE = new EUI48Sub( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }, 0, 6, ByteOrder.LITTLE_ENDIAN );
40 /** EUI48Sub MAC address matching all device, i.e. 'ff:ff:ff:ff:ff:ff'. */
41 public static final EUI48Sub ALL_DEVICE = new EUI48Sub( new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff }, 0, 6, ByteOrder.LITTLE_ENDIAN );
42 /** EUI48Sub MAC address matching local device, i.e. '0:0:0:ff:ff:ff'. */
43 public static final EUI48Sub LOCAL_DEVICE = new EUI48Sub( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0xff }, 0, 6, ByteOrder.LITTLE_ENDIAN );
44
45 /**
46 * The EUI48 sub-address, always 6 bytes reserved.
47 */
48 public final byte b[/* 6 octets */];
49
50 private volatile int hash; // default 0, cache
51
52 /**
53 * The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
54 */
55 public int length;
56
57 /**
58 * Fills given EUI48Sub instance via given string representation.
59 * <p>
60 * Implementation is consistent with {@link #toString()}.
61 * </p>
62 * @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon,
63 * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
64 * @param dest EUI48Sub to set its value
65 * @param errmsg error parsing message if returning false
66 * @return true if successful, otherwise false
67 * @see #EUI48Sub(String)
68 * @see #toString()
69 */
70 public static boolean scanEUI48Sub(final String str, final EUI48Sub dest, final StringBuilder errmsg) {
71 final int str_len = str.length();
72 dest.clear();
73
74 if( 17 < str_len ) { // not exceeding EUI48.byte_size
75 errmsg.append("EUI48 sub-string must be less or equal length 17 but "+str.length()+": "+str);
76 return false;
77 }
78 final byte b_[] = new byte[ 6 ]; // intermediate result high -> low
79 int len_ = 0;
80 int j=0;
81 try {
82 boolean exp_colon = false;
83 while( j+1 < str_len /* && byte_count_ < byte_size */ ) { // min 2 chars left
84 final boolean is_colon = ':' == str.charAt(j);
85 if( exp_colon && !is_colon ) {
86 errmsg.append("EUI48Sub sub-string not in format '01:02:03:0A:0B:0C', but '"+str+"', colon missing, pos "+j+", len "+str_len);
87 return false;
88 } else if( is_colon ) {
89 ++j;
90 exp_colon = false;
91 } else {
92 b_[len_] = Integer.valueOf(str.substring(j, j+2), 16).byteValue(); // b_: high->low
93 j += 2;
94 ++len_;
95 exp_colon = true;
96 }
97 }
98 dest.length = len_;
99
100 if( ByteOrder.LITTLE_ENDIAN == ByteOrder.nativeOrder() ) {
101 for(j=0; j<len_; ++j) { // swap low->high
102 dest.b[j] = b_[len_-1-j];
103 }
104 } else {
105 System.arraycopy(b_, 0, dest.b, 0, len_);
106 }
107
108 } catch (final NumberFormatException e) {
109 errmsg.append("EUI48 sub-string not in format '01:02:03:0A:0B:0C' but "+str+", pos "+j+", len "+str_len);
110 return false;
111 }
112 return true;
113 }
114
115 /** Construct empty unset instance. */
116 public EUI48Sub() {
117 b = new byte[6];
118 length = 0;
119 }
120
121 /**
122 * Construct a sub EUI48 via given string representation.
123 * <p>
124 * Implementation is consistent with {@link #toString()}.
125 * </p>
126 * @param str a string of less or equal of 17 characters representing less or equal of 6 bytes as hexadecimal numbers separated via colon,
127 * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
128 * @see #toString()
129 * @throws IllegalArgumentException if given string doesn't comply with EUI48
130 */
131 public EUI48Sub(final String str) throws IllegalArgumentException {
132 final StringBuilder errmsg = new StringBuilder();
133 b = new byte[ 6 ];
134 if( !scanEUI48Sub(str, this, errmsg) ) {
135 throw new IllegalArgumentException(errmsg.toString());
136 }
137 }
138
139 /**
140 * Copy len_ address bytes from given source and byte order,
141 * while converting them to {@link ByteOrder#nativeOrder()}.
142 *
143 * @param stream address bytes
144 * @param pos position in stream at address
145 * @param len_ number of address bytes
146 * @param byte_order {@link ByteOrder#LITTLE_ENDIAN} or {@link ByteOrder#BIG_ENDIAN} byte order of given address bytes, one may pass {@link ByteOrder#nativeOrder()}.
147 */
148 public EUI48Sub(final byte stream[], final int pos, final int len_, final ByteOrder byte_order) {
149 if( len_ > EUI48.byte_size || pos + len_ > stream.length ) {
150 throw new IllegalArgumentException("EUI48 stream ( pos "+pos+", len "+len_+" > EUI48 size "+EUI48.byte_size+" or stream.length "+stream.length);
151 }
152 b = new byte[6];
153 if( byte_order == ByteOrder.nativeOrder() ) {
154 System.arraycopy(stream, pos, b, 0, len_);
155 } else {
156 BasicTypes.bswap(stream, pos, b, 0, len_);
157 }
158 length = len_;
159 }
160
161 @Override
162 public final boolean equals(final Object obj) {
163 if(this == obj) {
164 return true;
165 }
166 if (obj == null || !(obj instanceof EUI48Sub)) {
167 return false;
168 }
169 final int length2 = ((EUI48Sub)obj).length;
170 if( length != length2 ) {
171 return false;
172 }
173 final byte[] b2 = ((EUI48Sub)obj).b;
174 return Arrays.equals(b, 0, length, b2, 0, length2);
175 }
176
177 /**
178 * {@inheritDoc}
179 * <p>
180 * Implementation uses a lock-free volatile cache.
181 * </p>
182 * @see #clearHash()
183 */
184 @Override
185 public final int hashCode() {
186 int h = hash;
187 if( 0 == h ) {
188 // 31 * x == (x << 5) - x
189 h = length;
190 for(int i=0; i<length; i++) {
191 h = ( ( h << 5 ) - h ) + b[i];
192 }
193 hash = h;
194 }
195 return h;
196 }
197
198 /**
199 * Method clears the cached hash value.
200 * @see #clear()
201 */
202 public void clearHash() { hash = 0; }
203
204 /**
205 * Method clears the underlying byte array {@link #b} and sets length to zero. The cached hash value is also cleared.
206 * @see #clearHash()
207 */
208 public void clear() {
209 hash = 0;
210 b[0] = 0; b[1] = 0; b[2] = 0;
211 b[3] = 0; b[4] = 0; b[5] = 0;
212 length = 0;
213 }
214
215 /**
216 * Find index of needle within haystack in the given byte order.
217 *
218 * The returned index will be adjusted for the desired byte order.
219 * - {@link ByteOrder#BIG_ENDIAN} will return index 0 for the leading byte like the {@link #toString()} representation from left (MSB) to right (LSB).
220 * - {@link ByteOrder#LITTLE_ENDIAN} will return index 5 for the leading byte
221 *
222 * @param haystack_b haystack data
223 * @param haystack_length haystack length
224 * @param needle_b needle data
225 * @param needle_length needle length
226 * @param byte_order byte order will adjust the returned index, {@link ByteOrder#BIG_ENDIAN} is equivalent with {@link #toString()} representation from left (MSB) to right (LSB).
227 * @return index of first element of needle within haystack or -1 if not found. If the needle length is zero, 0 (found) is returned.
228 */
229 public static int indexOf(final byte haystack_b[], final int haystack_length,
230 final byte needle_b[], final int needle_length,
231 final ByteOrder byte_order) {
232 if( 0 == needle_length ) {
233 return 0;
234 }
235 if( haystack_length < needle_length ) {
236 return -1;
237 }
238 final byte first = needle_b[0];
239 final int outerEnd = haystack_length - needle_length + 1; // exclusive
240
241 for (int i = 0; i < outerEnd; i++) {
242 // find first char of other
243 while( haystack_b[i] != first ) {
244 if( ++i == outerEnd ) {
245 return -1;
246 }
247 }
248 if( i < outerEnd ) { // otherLen chars left to match?
249 // continue matching other chars
250 final int innerEnd = i + needle_length; // exclusive
251 int j = i, k=0;
252 do {
253 if( ++j == innerEnd ) {
254 // gotcha
255 if( ByteOrder.nativeOrder() == byte_order ) {
256 return i;
257 } else {
258 return 5 - i - ( needle_length - 1 );
259 }
260 }
261 } while( haystack_b[j] == needle_b[++k] );
262 }
263 }
264 return -1;
265 }
266
267 /**
268 * Finds the index of given EUI48Sub needle within this instance haystack in the given byte order.
269 *
270 * The returned index will be adjusted for the desired byte order.
271 * - {@link ByteOrder#BIG_ENDIAN} will return index 0 for the leading byte like the {@link #toString()} representation from left (MSB) to right (LSB).
272 * - {@link ByteOrder#LITTLE_ENDIAN} will return index 5 for the leading byte
273 *
274 * @param needle
275 * @param byte_order byte order will adjust the returned index, {@link ByteOrder#BIG_ENDIAN} is equivalent with {@link #toString()} representation from left (MSB) to right (LSB).
276 * @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.
277 * @see #indexOf(byte[], int, byte[], int, ByteOrder)
278 */
279 public int indexOf(final EUI48Sub needle, final ByteOrder byte_order) {
280 return indexOf(b, length, needle.b, needle.length, byte_order);
281 }
282
283 /**
284 * Returns true, if given EUI48Sub needle is contained in this instance haystack.
285 * <p>
286 * If the sub is zero, true is returned.
287 * </p>
288 */
289 public boolean contains(final EUI48Sub needle) {
290 return 0 <= indexOf(needle, ByteOrder.nativeOrder());
291 }
292
293 /**
294 * {@inheritDoc}
295 * <p>
296 * Returns the EUI48 sub-string representation,
297 * less or equal 17 characters representing less or equal 6 bytes as upper case hexadecimal numbers separated via colon,
298 * e.g. {@code "01:02:03:0A:0B:0C"}, {@code "01:02:03:0A"}, {@code ":"}, {@code ""}.
299 * </p>
300 */
301 @Override
302 public final String toString() {
303 // str_len = 2 * len + ( len - 1 )
304 // str_len = 3 * len - 1
305 // len = ( str_len + 1 ) / 3
306 if( 0 < length ) {
307 final StringBuilder sb = new StringBuilder(3 * length - 1);
308 if( ByteOrder.LITTLE_ENDIAN == ByteOrder.nativeOrder() ) {
309 for(int i=length-1; 0 <= i; --i) {
310 BasicTypes.byteHexString(sb, b[i], false /* lowerCase */);
311 if( 0 < i ) {
312 sb.append(":");
313 }
314 }
315 } else {
316 for(int i=0; i < length; ++i) {
317 if( 0 < i ) {
318 sb.append(":");
319 }
320 BasicTypes.byteHexString(sb, b[i], false /* lowerCase */);
321 }
322 }
323 return sb.toString();
324 } else {
325 return new String(":");
326 }
327 }
328}
final int hashCode()
void clearHash()
Method clears the cached hash value.
static final EUI48Sub ALL_DEVICE
EUI48Sub MAC address matching all device, i.e.
Definition EUI48Sub.java:41
static final EUI48Sub LOCAL_DEVICE
EUI48Sub MAC address matching local device, i.e.
Definition EUI48Sub.java:43
final byte b[]
The EUI48 sub-address, always 6 bytes reserved.
Definition EUI48Sub.java:48
int indexOf(final EUI48Sub needle, final ByteOrder byte_order)
Finds the index of given EUI48Sub needle within this instance haystack in the given byte order.
static boolean scanEUI48Sub(final String str, final EUI48Sub dest, final StringBuilder errmsg)
Fills given EUI48Sub instance via given string representation.
Definition EUI48Sub.java:70
EUI48Sub(final String str)
Construct a sub EUI48 via given string representation.
static int indexOf(final byte haystack_b[], final int haystack_length, final byte needle_b[], final int needle_length, final ByteOrder byte_order)
Find index of needle within haystack in the given byte order.
EUI48Sub()
Construct empty unset instance.
int length
The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
Definition EUI48Sub.java:55
final String toString()
EUI48Sub(final byte stream[], final int pos, final int len_, final ByteOrder byte_order)
Copy len_ address bytes from given source and byte order, while converting them to ByteOrder#nativeOr...
static final EUI48Sub ANY_DEVICE
EUI48Sub MAC address matching any device, i.e.
Definition EUI48Sub.java:39
boolean contains(final EUI48Sub needle)
Returns true, if given EUI48Sub needle is contained in this instance haystack.
final boolean equals(final Object obj)
void clear()
Method clears the underlying byte array b and sets length to zero.
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition EUI48.java:42
static StringBuilder byteHexString(final StringBuilder sb, final byte value, final boolean lowerCase)
Produce a hexadecimal string representation of the given byte value.
static void bswap(final byte[] source, final int source_pos, final byte[] sink, final int sink_pos, final int len)