Add MacAddress-to-bytes conversion 33/35633/4
authorRobert Varga <rovarga@cisco.com>
Mon, 22 Feb 2016 16:38:41 +0000 (17:38 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 4 Mar 2016 01:45:33 +0000 (02:45 +0100)
This is useful and can rely on well-formedness of the mac address.

Change-Id: Ia41ecc74ed8ef3c3560cc920305cc4a9bff028dc
Signed-off-by: Robert Varga <rovarga@cisco.com>
(cherry picked from commit be97010a32253ce4fb5c1be4c5c7eb003c2e9f8e)

model/ietf/ietf-type-util/pom.xml
model/ietf/ietf-type-util/src/main/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtil.java
model/ietf/ietf-type-util/src/test/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtilTest.java [new file with mode: 0644]

index 9c1eaca6b0d58251fb23b6768b72f30eadf0cec5..49a4fd47e7888a372c5f40171d27a52daa6812e6 100644 (file)
       <groupId>org.opendaylight.mdsal</groupId>
       <artifactId>yang-binding</artifactId>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
   </dependencies>
 
   <scm>
index 513a7a0ad97084a7d06496d2915cc4e4ac390f10..dce90fb11d9d664e509526d44fab31bbe4094ccd 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.mdsal.model.ietf.util;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
 import com.google.common.primitives.UnsignedBytes;
+import java.util.Arrays;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.binding.util.StringValueObjectFactory;
 
@@ -19,7 +20,26 @@ import org.opendaylight.yangtools.yang.binding.util.StringValueObjectFactory;
  */
 @Beta
 public abstract class AbstractIetfYangUtil<T> {
+    private static final int MAC_BYTE_LENGTH = 6;
     private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
+    private static final byte[] 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<T> factory;
 
     protected AbstractIetfYangUtil(final Class<T> clazz) {
@@ -65,11 +85,12 @@ public abstract class AbstractIetfYangUtil<T> {
      * @throws IllegalArgumentException if length of input is not 6 bytes
      */
     @Nonnull private static String bytesToString(@Nonnull final byte[] bytes) {
-        Preconditions.checkArgument(bytes.length == 6, "MAC address should have 6 bytes, not %s", bytes.length);
+        Preconditions.checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s",
+                bytes.length);
 
         final StringBuilder sb = new StringBuilder(17);
         appendHexByte(sb, bytes[0]);
-        for (int i = 1; i < bytes.length; ++i) {
+        for (int i = 1; i < MAC_BYTE_LENGTH; ++i) {
             sb.append(':');
             appendHexByte(sb, bytes[i]);
         }
@@ -105,5 +126,33 @@ public abstract class AbstractIetfYangUtil<T> {
         return factory.newInstance(bytesToString(bytes));
     }
 
+    private static byte hexValue(final char c) {
+        byte v;
+        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.
+            v = HEX_VALUES[c];
+        } catch (IndexOutOfBoundsException e) {
+            v = -1;
+        }
+
+        if (v < 0) {
+            throw new IllegalArgumentException("Invalid character '" + c + "' encountered");
+        }
+
+        return v;
+    }
+
+    @Nonnull public final byte[] bytesFor(@Nonnull final T macAddress) {
+        final String mac = getValue(macAddress);
+        final byte[] ret = new byte[MAC_BYTE_LENGTH];
+
+        for (int i = 0, base = 0; i < MAC_BYTE_LENGTH; ++i, base += 3) {
+            ret[i] = (byte) ((hexValue(mac.charAt(base)) << 4) | hexValue(mac.charAt(base + 1)));
+        }
+
+        return ret;
+    }
+
     protected abstract String getValue(T macAddress);
 }
diff --git a/model/ietf/ietf-type-util/src/test/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtilTest.java b/model/ietf/ietf-type-util/src/test/java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtilTest.java
new file mode 100644 (file)
index 0000000..6faeebe
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.model.ietf.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Preconditions;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class AbstractIetfYangUtilTest {
+    public static final class MacClass {
+        final String _value;
+
+        public MacClass(final String value) {
+            this._value = Preconditions.checkNotNull(value);
+        }
+
+        public MacClass(final MacClass template) {
+            this._value = template._value;
+        }
+    }
+
+    private static final class MacUtil extends AbstractIetfYangUtil<MacClass> {
+        MacUtil() {
+            super(MacClass.class);
+        }
+
+        @Override
+        protected String getValue(final MacClass macAddress) {
+            return macAddress._value;
+        }
+    }
+
+    private static final MacUtil UTIL = new MacUtil();
+    private static final byte[] BYTES = new byte[] { 1, 2, 30, 90, -5, -120 };
+    private static final String CANON = "01:02:1e:5a:fb:88";
+
+    @Test
+    public void testBytesToMac() {
+        final MacClass mac = UTIL.macAddressFor(BYTES);
+        assertEquals(CANON, mac._value);
+    }
+
+    @Test
+    public void testMacToBytes() {
+        final byte[] bytes1 = UTIL.bytesFor(new MacClass(CANON));
+        assertTrue(Arrays.equals(BYTES, bytes1));
+
+        final byte[] bytes2 = UTIL.bytesFor(new MacClass("01:02:1E:5a:Fb:88"));
+        assertTrue(Arrays.equals(BYTES, bytes2));
+    }
+}