Use HexFormat instead of home-grown formatting 05/100805/2
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Apr 2022 20:09:11 +0000 (22:09 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Apr 2022 20:29:53 +0000 (22:29 +0200)
JDK17 provides a ready-to-use HexFormat facility. Use it to format
mac-address/hex-string/phys-address/ipv6-address.

Change-Id: I3a99a5e27db1e75384e3d8b9879c83400273e715
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
model/ietf/ietf-type-util/src/main/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtil.java
model/ietf/ietf-type-util/src/main/java/org/opendaylight/mdsal/model/ietf/util/Ipv6Utils.java
model/ietf/ietf-type-util/src/test/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtilTest.java

index c43cdcacdaa6c6de5422d8bcd07abc06be4f7adb..5f785b1f15dc941ed166b9d991c1c79b5a0da23c 100644 (file)
@@ -10,7 +10,7 @@ package org.opendaylight.mdsal.model.ietf.util;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.google.common.annotations.Beta;
-import java.util.Arrays;
+import java.util.HexFormat;
 import java.util.UUID;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
@@ -25,26 +25,8 @@ import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
 @Beta
 public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
     private static final int MAC_BYTE_LENGTH = 6;
-    private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
+    private static final HexFormat COLON_HEXFORMAT = HexFormat.ofDelimiter(":");
     private static final byte @NonNull[] EMPTY_BYTES = new byte[0];
-    private static final byte @NonNull[] HEX_VALUES;
-
-    static {
-        final byte[] b = new byte['f' + 1];
-        Arrays.fill(b, (byte)-1);
-
-        for (char c = '0'; c <= '9'; ++c) {
-            b[c] = (byte)(c - '0');
-        }
-        for (char c = 'A'; c <= 'F'; ++c) {
-            b[c] = (byte)(c - 'A' + 10);
-        }
-        for (char c = 'a'; c <= 'f'; ++c) {
-            b[c] = (byte)(c - 'a' + 10);
-        }
-
-        HEX_VALUES = b;
-    }
 
     private final StringValueObjectFactory<M> macFactory;
     private final StringValueObjectFactory<P> physFactory;
@@ -82,9 +64,8 @@ public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
      * @throws IllegalArgumentException if length of input is not 6 bytes
      */
     public final @NonNull M macAddressFor(final byte @NonNull[] bytes) {
-        checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s",
-                bytes.length);
-        return macFactory.newInstance(bytesToString(bytes, 17));
+        checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s", bytes.length);
+        return macFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
     }
 
     public final byte @NonNull[] macAddressBytes(final @NonNull M macAddress) {
@@ -113,7 +94,7 @@ public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
      */
     public final @NonNull P physAddressFor(final byte @NonNull[] bytes) {
         checkArgument(bytes.length > 0, "Physical address should have at least one byte");
-        return physFactory.newInstance(bytesToString(bytes, bytes.length * 3 - 1));
+        return physFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
     }
 
     public final byte @NonNull[] physAddressBytes(final @NonNull P physAddress) {
@@ -123,7 +104,7 @@ public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
 
     public final @NonNull H hexStringFor(final byte @NonNull[] bytes) {
         checkArgument(bytes.length > 0, "Hex string should have at least one byte");
-        return hexFactory.newInstance(bytesToString(bytes, bytes.length * 3 - 1));
+        return hexFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
     }
 
     public final byte @NonNull[] hexStringBytes(final @NonNull H hexString) {
@@ -162,24 +143,6 @@ public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
 
     protected abstract String getQuadValue(Q dottedQuad);
 
-    // FIXME: Replace with HexFormat.fromHexDigit(ch) when we have JDK17+
-    static byte hexValue(final char ch) {
-        byte value;
-        try {
-            // Performance optimization: access the array and rely on the VM for catching
-            // illegal access (which boils down to illegal character, which should never happen.
-            value = HEX_VALUES[ch];
-        } catch (IndexOutOfBoundsException e) {
-            value = -1;
-        }
-
-        if (value < 0) {
-            throw new IllegalArgumentException("Invalid character '" + ch + "' encountered");
-        }
-
-        return value;
-    }
-
     /**
      * Make sure an array of characters does not include capital letters. This method assumes input conforms to
      * MAC address format, e.g. it is composed of 6 groups of hexadecimal digits separated by colons. Behavior is
@@ -204,37 +167,11 @@ public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
         return ret;
     }
 
-    /**
-     * Convert an array of 6 bytes into canonical MAC address representation, that is 6 groups of two hexadecimal
-     * lower-case digits each, separated by colons.
-     *
-     * @param bytes Input bytes, may not be null
-     * @param charHint Hint at how many characters are needed
-     * @return Canonical MAC address string
-     * @throws NullPointerException if input is null
-     * @throws IllegalArgumentException if length of input is not 6 bytes
-     */
-    // TODO: HexFormat.ofDelimiter(":").withUpperCase().formatHex(bytes) when we have JDK17+? Compare performance
-    private static @NonNull String bytesToString(final byte @NonNull[] bytes, final int charHint) {
-        final StringBuilder sb = new StringBuilder(charHint);
-        appendHexByte(sb, bytes[0]);
-        for (int i = 1; i < bytes.length; ++i) {
-            appendHexByte(sb.append(':'), bytes[i]);
-        }
-
-        return sb.toString();
-    }
-
-    // FIXME: Replace with HexFormat.toHexDigits(sb, byteVal) when we have JDK17+, but note we prefer capital letters
-    private static void appendHexByte(final StringBuilder sb, final byte byteVal) {
-        final int intVal = Byte.toUnsignedInt(byteVal);
-        sb.append(HEX_CHARS[intVal >>> 4]).append(HEX_CHARS[intVal & 15]);
-    }
-
     private static byte @NonNull[] stringToBytes(final String str, final int length) {
         final byte[] ret = new byte[length];
         for (int i = 0, base = 0; i < length; ++i, base += 3) {
-            ret[i] = (byte) (hexValue(str.charAt(base)) << 4 | hexValue(str.charAt(base + 1)));
+            ret[i] = (byte) ((HexFormat.fromHexDigit(str.charAt(base)) << 4)
+                + HexFormat.fromHexDigit(str.charAt(base + 1)));
         }
         return ret;
     }
index c5e94e829257385af5b9785dd6a64aaa36db9a21..f2ba8c82f01f3271682aaddf20d63b6506526cb2 100644 (file)
@@ -11,6 +11,7 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
 
 import java.util.Arrays;
+import java.util.HexFormat;
 import org.eclipse.jdt.annotation.NonNull;
 
 /**
@@ -112,8 +113,7 @@ final class Ipv6Utils {
              * the regexp has already verified that we are not being fed
              * anything bigger than 0xffff between the separators.
              */
-            final int chval = AbstractIetfYangUtil.hexValue(ch);
-            val = val << 4 | chval;
+            val = (val << 4) + HexFormat.fromHexDigit(ch);
             haveVal = true;
         }
 
index 4bd7a77777a45eef889c295e807fc6e18bff0806..46458bf9c0673ba7fc7088eb18a680b0f03af926 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.model.ietf.util;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
 
 import java.util.UUID;
 import org.junit.Test;
@@ -94,11 +93,4 @@ public class AbstractIetfYangUtilTest {
     public void canonizeMACTest() {
         assertEquals(CANON, UTIL.canonizeMacAddress(new MacClass("01:02:1E:5A:FB:88")).getValue());
     }
-
-    @Test
-    public void hexValueWithExceptionTest() {
-        final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
-            () -> AbstractIetfYangUtil.hexValue(Character.highSurrogate(1000)));
-        assertEquals("Invalid character 'ퟀ' encountered", ex.getMessage());
-    }
 }