jaulib v1.3.0
Jau Support Library (C++, Java, ..)
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}
A 48 bit EUI-48 sub-identifier, see EUI48.
Definition: EUI48Sub.java:37
final int hashCode()
Definition: EUI48Sub.java:185
void clearHash()
Method clears the cached hash value.
Definition: EUI48Sub.java:202
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.
Definition: EUI48Sub.java:279
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.
Definition: EUI48Sub.java:131
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.
Definition: EUI48Sub.java:229
EUI48Sub()
Construct empty unset instance.
Definition: EUI48Sub.java:116
int length
The actual length in bytes of the EUI48 sub-address, less or equal 6 bytes.
Definition: EUI48Sub.java:55
final String toString()
Definition: EUI48Sub.java:302
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...
Definition: EUI48Sub.java:148
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.
Definition: EUI48Sub.java:289
final boolean equals(final Object obj)
Definition: EUI48Sub.java:162
void clear()
Method clears the underlying byte array b and sets length to zero.
Definition: EUI48Sub.java:208
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)
Definition: BasicTypes.java:39