initial import

Change-Id: Ief25aef0066ea96bd2c329ccef974c072b3a5a73
diff --git a/of/lib/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java b/of/lib/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
new file mode 100644
index 0000000..d7f044e
--- /dev/null
+++ b/of/lib/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
@@ -0,0 +1,207 @@
+package org.projectfloodlight.openflow.types;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+import org.projectfloodlight.openflow.util.HexString;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.Longs;
+
+/**
+ * Wrapper around a 6 byte mac address.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+
+public class MacAddress implements OFValueType<MacAddress> {
+    static final int MacAddrLen = 6;
+    private final long rawValue;
+
+    private final static long NONE_VAL = 0x0L;
+    public static final MacAddress NONE = new MacAddress(NONE_VAL);
+
+    private final static long BROADCAST_VAL = 0x0000FFFFFFFFFFFFL;
+    public static final MacAddress BROADCAST = new MacAddress(BROADCAST_VAL);
+
+    public static final MacAddress NO_MASK = MacAddress.of(0xFFFFFFFFFFFFFFFFl);
+    public static final MacAddress FULL_MASK = MacAddress.of(0x0);
+
+    private static final long LLDP_MAC_ADDRESS_MASK = 0xfffffffffff0L;
+    private static final long LLDP_MAC_ADDRESS_VALUE = 0x0180c2000000L;
+
+    private MacAddress(final long rawValue) {
+        this.rawValue = rawValue;
+    }
+
+    public static MacAddress of(final byte[] address) {
+        if (address.length != MacAddrLen)
+            throw new IllegalArgumentException(
+                    "Mac address byte array must be exactly 6 bytes long; length = " + address.length);
+        long raw =
+                (address[0] & 0xFFL) << 40 | (address[1] & 0xFFL) << 32
+                        | (address[2] & 0xFFL) << 24 | (address[3] & 0xFFL) << 16
+                        | (address[4] & 0xFFL) << 8 | (address[5] & 0xFFL);
+        return MacAddress.of(raw);
+    }
+
+    public static MacAddress of(long raw) {
+        raw &= BROADCAST_VAL;
+        if(raw == NONE_VAL)
+            return NONE;
+        if (raw == BROADCAST_VAL)
+            return BROADCAST;
+        return new MacAddress(raw);
+    }
+
+    /** Parse a mac adress from the canonical string representation as
+     *  6 hex bytes separated by colons (01:02:03:04:05:06).
+     *
+     * @param macString - a mac address in canonical string representation
+     * @return the parsed MacAddress
+     * @throws IllegalArgumentException if macString is not a valid mac adddress
+     */
+    @Nonnull
+    public static MacAddress of(@Nonnull final String macString) throws IllegalArgumentException {
+        if (macString == null) {
+            throw new NullPointerException("macString must not be null");
+        }
+        int index = 0;
+        int shift = 40;
+        final String FORMAT_ERROR = "Mac address is not well-formed. " +
+                "It must consist of 6 hex digit pairs separated by colons: ";
+
+        long raw = 0;
+        if (macString.length() != 6 * 2 + 5)
+            throw new IllegalArgumentException(FORMAT_ERROR + macString);
+
+        while (shift >= 0) {
+            int digit1 = Character.digit(macString.charAt(index++), 16);
+            int digit2 = Character.digit(macString.charAt(index++), 16);
+            if ((digit1 < 0) || (digit2 < 0))
+                throw new IllegalArgumentException(FORMAT_ERROR + macString);
+            raw |= ((long) (digit1 << 4 | digit2)) << shift;
+
+            if (shift == 0)
+                break;
+            if (macString.charAt(index++) != ':')
+                throw new IllegalArgumentException(FORMAT_ERROR + macString);
+            shift -= 8;
+        }
+        return MacAddress.of(raw);
+    }
+
+    private volatile byte[] bytesCache = null;
+
+    public byte[] getBytes() {
+        if (bytesCache == null) {
+            synchronized (this) {
+                if (bytesCache == null) {
+                    bytesCache =
+                            new byte[] { (byte) ((rawValue >> 40) & 0xFF),
+                                    (byte) ((rawValue >> 32) & 0xFF),
+                                    (byte) ((rawValue >> 24) & 0xFF),
+                                    (byte) ((rawValue >> 16) & 0xFF),
+                                    (byte) ((rawValue >> 8) & 0xFF),
+                                    (byte) ((rawValue >> 0) & 0xFF) };
+                }
+            }
+        }
+        return Arrays.copyOf(bytesCache, bytesCache.length);
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is the broadcast address.
+     * @return {@code true} if the MAC address is the broadcast address.
+     */
+    public boolean isBroadcast() {
+        return this == BROADCAST;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is a multicast address.
+     * @return {@code true} if the MAC address is a multicast address.
+     */
+    public boolean isMulticast() {
+        if (isBroadcast()) {
+            return false;
+        }
+        return (rawValue & (0x01L << 40)) != 0;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is an LLDP mac address.
+     * @return {@code true} if the MAC address is an LLDP mac address.
+     */
+    public boolean isLLDPAddress() {
+        return (rawValue & LLDP_MAC_ADDRESS_MASK) == LLDP_MAC_ADDRESS_VALUE;
+    }
+
+    @Override
+    public int getLength() {
+        return MacAddrLen;
+    }
+
+    @Override
+    public String toString() {
+        return HexString.toHexString(rawValue, 6);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (rawValue ^ (rawValue >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        MacAddress other = (MacAddress) obj;
+        if (rawValue != other.rawValue)
+            return false;
+        return true;
+    }
+
+    public long getLong() {
+        return rawValue;
+    }
+
+    public void write6Bytes(ChannelBuffer c) {
+        c.writeInt((int) (this.rawValue >> 16));
+        c.writeShort((int) this.rawValue & 0xFFFF);
+    }
+
+    public static MacAddress read6Bytes(ChannelBuffer c) throws OFParseError {
+        long raw = c.readUnsignedInt() << 16 | c.readUnsignedShort();
+        return MacAddress.of(raw);
+    }
+
+    @Override
+    public MacAddress applyMask(MacAddress mask) {
+        return MacAddress.of(this.rawValue & mask.rawValue);
+    }
+
+    @Override
+    public int compareTo(MacAddress o) {
+        return Longs.compare(rawValue, o.rawValue);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putInt((int) (this.rawValue >> 16));
+        sink.putShort((short) (this.rawValue & 0xFFFF));
+    }
+
+
+
+}