aboutsummaryrefslogtreecommitdiffstats
path: root/java/org/direct_bt/EUI48.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/direct_bt/EUI48.java')
-rw-r--r--java/org/direct_bt/EUI48.java251
1 files changed, 251 insertions, 0 deletions
diff --git a/java/org/direct_bt/EUI48.java b/java/org/direct_bt/EUI48.java
new file mode 100644
index 00000000..16c679ab
--- /dev/null
+++ b/java/org/direct_bt/EUI48.java
@@ -0,0 +1,251 @@
+/**
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package org.tinyb;
+
+/**
+ * A packed 48 bit EUI-48 identifier, formerly known as MAC-48
+ * or simply network device MAC address (Media Access Control address).
+ * <p>
+ * Implementation caches the hash value {@link #hashCode()},
+ * hence users shall take special care when mutating the
+ * underlying data {@link #b}, read its API notes.
+ * </p>
+ */
+public class EUI48 {
+ /** EUI48 MAC address matching any device, i.e. '0:0:0:0:0:0'. */
+ public static final EUI48 ANY_DEVICE = new EUI48( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 } );
+ /** EUI48 MAC address matching all device, i.e. 'ff:ff:ff:ff:ff:ff'. */
+ public static final EUI48 ALL_DEVICE = new EUI48( new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff } );
+ /** EUI48 MAC address matching local device, i.e. '0:0:0:ff:ff:ff'. */
+ public static final EUI48 LOCAL_DEVICE = new EUI48( new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0xff } );
+
+ /**
+ * The 6 byte EUI48 address.
+ * <p>
+ * If modifying, it is the user's responsibility to avoid data races.<br>
+ * Further, call {@link #clearHash()} after mutation is complete.
+ * </p>
+ */
+ public final byte b[/* 6 octets */];
+
+ private volatile int hash; // default 0, cache
+
+ /**
+ * Size of the byte stream representation in bytes
+ * @see #getStream(byte[], int)
+ */
+ private static final int byte_size = 6;
+
+ /**
+ * Construct instance via given string representation.
+ * <p>
+ * Implementation is consistent with {@link #toString()}.
+ * </p>
+ * @param str a string of exactly 17 characters representing 6 bytes as hexadecimal numbers separated via colon {@code "01:02:03:0A:0B:0C"}.
+ * @see #toString()
+ */
+ public EUI48(final String str) throws IllegalArgumentException {
+ if( 17 != str.length() ) {
+ throw new IllegalArgumentException("EUI48 string not of length 17 but "+str.length()+": "+str);
+ }
+ b = new byte[byte_size];
+ try {
+ for(int i=0; i<byte_size; i++) {
+ b[byte_size-1-i] = Integer.valueOf(str.substring(i*2+i, i*2+i+2), 16).byteValue();
+ }
+ } catch (final NumberFormatException e) {
+ throw new IllegalArgumentException("EUI48 string not in format '01:02:03:0A:0B:0C' but "+str, e);
+ }
+ }
+
+ /** Construct instance via given source byte array */
+ public EUI48(final byte stream[], final int pos) {
+ if( byte_size > ( stream.length - pos ) ) {
+ throw new IllegalArgumentException("EUI48 stream ( "+stream.length+" - "+pos+" ) < "+byte_size+" bytes");
+ }
+ b = new byte[byte_size];
+ System.arraycopy(stream, pos, b, 0, byte_size);
+ }
+
+ /** Construct instance using the given address of the byte array */
+ public EUI48(final byte address[]) {
+ if( byte_size != address.length ) {
+ throw new IllegalArgumentException("EUI48 stream "+address.length+" != "+byte_size+" bytes");
+ }
+ b = address;
+ }
+
+ /** Construct emoty unset instance. */
+ public EUI48() {
+ b = new byte[byte_size];
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if (obj == null || !(obj instanceof EUI48)) {
+ return false;
+ }
+ final byte[] b2 = ((EUI48)obj).b;
+ return b[0] == b2[0] &&
+ b[1] == b2[1] &&
+ b[2] == b2[2] &&
+ b[3] == b2[3] &&
+ b[4] == b2[4] &&
+ b[5] == b2[5];
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Implementation uses a lock-free volatile cache.
+ * </p>
+ * @see #clearHash()
+ */
+ @Override
+ public final int hashCode() {
+ int h = hash;
+ if( 0 == h ) {
+ /**
+ // final int p = 92821; // alternative with less collisions?
+ final int p = 31; // traditional prime
+ h = b[0];
+ h = p * h + b[1];
+ h = p * h + b[2];
+ h = p * h + b[3];
+ h = p * h + b[4];
+ h = p * h + b[5];
+ */
+ // 31 * x == (x << 5) - x
+ h = b[0];
+ h = ( ( h << 5 ) - h ) + b[1];
+ h = ( ( h << 5 ) - h ) + b[2];
+ h = ( ( h << 5 ) - h ) + b[3];
+ h = ( ( h << 5 ) - h ) + b[4];
+ h = ( ( h << 5 ) - h ) + b[5];
+ hash = h;
+ }
+ return h;
+ }
+
+ /**
+ * Method clears the cached hash value.
+ * @see #clear()
+ */
+ public void clearHash() { hash = 0; }
+
+ /**
+ * Method clears the underlying byte array {@link #b} and cached hash value.
+ * @see #clearHash()
+ */
+ public void clear() {
+ hash = 0;
+ b[0] = 0;
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ b[4] = 0;
+ b[5] = 0;
+ }
+
+ /**
+ * Method transfers all bytes representing a EUI48 from the given
+ * source array at the given position into this instance and clears its cached hash value.
+ * <p>
+ * Implementation is consistent with {@link #EUI48(byte[], int)}.
+ * </p>
+ * @param source the source array
+ * @param pos starting position in the source array
+ * @see #EUI48(byte[], int)
+ * @see #clearHash()
+ */
+ public final void putStream(final byte[] source, final int pos) {
+ if( byte_size > ( source.length - pos ) ) {
+ throw new IllegalArgumentException("Stream ( "+source.length+" - "+pos+" ) < "+byte_size+" bytes");
+ }
+ hash = 0;
+ System.arraycopy(source, pos, b, 0, byte_size);
+ }
+
+ /**
+ * Method transfers all bytes representing this instance into the given
+ * destination array at the given position.
+ * <p>
+ * Implementation is consistent with {@link #EUI48(byte[], int)}.
+ * </p>
+ * @param sink the destination array
+ * @param pos starting position in the destination array
+ * @see #EUI48(byte[], int)
+ */
+ public final void getStream(final byte[] sink, final int pos) {
+ if( byte_size > ( sink.length - pos ) ) {
+ throw new IllegalArgumentException("Stream ( "+sink.length+" - "+pos+" ) < "+byte_size+" bytes");
+ }
+ System.arraycopy(b, 0, sink, pos, byte_size);
+ }
+
+ /**
+ * Returns the {@link BLERandomAddressType}.
+ * <p>
+ * If {@link BDAddressType} is {@link BDAddressType::BDADDR_LE_RANDOM},
+ * method shall return a valid value other than {@link BLERandomAddressType::UNDEFINED}.
+ * </p>
+ * <p>
+ * If {@link BDAddressType} is not {@link BDAddressType::BDADDR_LE_RANDOM},
+ * method shall return {@link BLERandomAddressType::UNDEFINED}.
+ * </p>
+ * @since 2.2.0
+ */
+ public BLERandomAddressType getBLERandomAddressType(final BDAddressType addressType) {
+ if( BDAddressType.BDADDR_LE_RANDOM != addressType ) {
+ return BLERandomAddressType.UNDEFINED;
+ }
+ final byte high2 = (byte) ( ( b[5] >> 6 ) & 0x03 );
+ return BLERandomAddressType.get(high2);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Returns the EUI48 string representation,
+ * exactly 17 characters representing 6 bytes as upper case hexadecimal numbers separated via colon {@code "01:02:03:0A:0B:0C"}.
+ * </p>
+ * @see #EUI48(String)
+ */
+ @Override
+ public final String toString() {
+ final StringBuilder sb = new StringBuilder(17);
+ for(int i=byte_size-1; 0 <= i; i--) {
+ BluetoothUtils.byteHexString(sb, b[i], false /* lowerCase */);
+ if( 0 < i ) {
+ sb.append(":");
+ }
+ }
+ return sb.toString();
+ }
+}