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