Merge into master from pull request #258:
java_gen: Hash-related functions for U64, renamed OFChecksum128 to U128 (https://github.com/floodlight/loxigen/pull/258)
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 452ac58..471f1a8 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -311,6 +311,10 @@
 u64 = JType('U64', 'long') \
         .op(read='U64.ofRaw(bb.readLong())', write='bb.writeLong($name.getValue())', default="U64.ZERO", pub_type=True) \
         .op(read='bb.readLong()', write='bb.writeLong($name)', pub_type=False)
+u128 = JType("U128") \
+        .op(read='U128.read16Bytes(bb)',
+            write='$name.write16Bytes(bb)',
+            default='U128.ZERO')
 of_port = JType("OFPort") \
          .op(version=1, read="OFPort.read2Bytes(bb)", write="$name.write2Bytes(bb)", default="OFPort.ANY") \
          .op(version=ANY, read="OFPort.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="OFPort.ANY")
@@ -491,10 +495,6 @@
          .op(version=ANY, read="ClassId.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="ClassId.NONE")
 boolean_value = JType('OFBooleanValue', 'OFBooleanValue') \
         .op(read='OFBooleanValue.of(bb.readByte() != 0)', write='bb.writeByte($name.getInt())', default="OFBooleanValue.FALSE")
-checksum = JType("OFChecksum128") \
-        .op(read='OFChecksum128.read16Bytes(bb)',
-            write='$name.write16Bytes(bb)',
-            default='OFChecksum128.ZERO')
 gen_table_id = JType("GenTableId") \
         .op(read='GenTableId.read2Bytes(bb)',
             write='$name.write2Bytes(bb)',
@@ -536,7 +536,7 @@
         'of_oxm_t': oxm,
         'of_meter_features_t': meter_features,
         'of_bitmap_128_t': port_bitmap,
-        'of_checksum_128_t': checksum,
+        'of_checksum_128_t': u128,
         'of_bsn_vport_t': bsn_vport,
         }
 
diff --git a/java_gen/pre-written/pom.xml b/java_gen/pre-written/pom.xml
index b4e81cc..e1c0630 100644
--- a/java_gen/pre-written/pom.xml
+++ b/java_gen/pre-written/pom.xml
@@ -10,7 +10,7 @@
 
     <groupId>org.projectfloodlight</groupId>
     <artifactId>openflowj</artifactId>
-    <version>0.3.4-SNAPSHOT</version>
+    <version>0.3.5-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>OpenFlowJ-Loxi</name>
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashFunc.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashFunc.java
new file mode 100644
index 0000000..f7648d7
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashFunc.java
@@ -0,0 +1,14 @@
+package org.projectfloodlight.openflow.types;
+
+/** A hash function that can hash a {@link PrimitiveSinkable}.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ * @param <H> - the hash.
+ */
+public interface HashFunc<H extends HashValue<H>> {
+    /** Hash the PrimitivateSinkable with this hash function and return the result */
+    public H hash(PrimitiveSinkable v);
+
+    /** Provide a defined null-hash */
+    public H nullHash();
+}
\ No newline at end of file
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValue.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValue.java
new file mode 100644
index 0000000..1dd55d5
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValue.java
@@ -0,0 +1,54 @@
+package org.projectfloodlight.openflow.types;
+
+import javax.annotation.concurrent.Immutable;
+
+/** a hash value that supports bit-wise combinations, mainly to calculate hash values for
+ *  reconciliation operations.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ *
+ * @param <H> - this type, for return type safety.
+ */
+@Immutable
+public interface HashValue<H extends HashValue<H>> {
+    /** return the "numBits" highest-order bits of the hash.
+     *  @param numBits number of higest-order bits to return [0-32].
+     *  @return a numberic value of the 0-32 highest-order bits.
+     */
+    int prefixBits(int numBits);
+
+    /** @return the bitwise inverse of this value */
+    H inverse();
+
+    /** or this value with another value value of the same type */
+    H or(H other);
+
+    /** and this value with another value value of the same type */
+    H and(H other);
+
+    /** xor this value with another value value of the same type */
+    H xor(H other);
+
+    /** calculate a combined hash value of this hash value (the <b>Key</b>) and the hash value
+     *  specified as a parameter (the <b>Value</b>).
+     *  <p>
+     *  The value is constructed as follows:
+     *  <ul>
+     *   <li>the first keyBits bits are taken only from the Key
+     *   <li>the other bits are taken from key xor value.
+     *  </ul>
+     *  The overall result looks like this:
+     *  <pre>
+     *  MSB                      LSB
+     *   +---------+--------------+
+     *   | key     | key ^ value  |
+     *   +---------+--------------+
+     *   |-keyBits-|
+     *  </pre>
+     *
+     * @param value - hash value to be compared with this value (the key)
+     * @param keyBits number of prefix bits that are just taken from key
+     * @return the combined value.
+     */
+    H combineWithValue(H value, int keyBits);
+}
\ No newline at end of file
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValueUtils.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValueUtils.java
new file mode 100644
index 0000000..15f8a9b
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/HashValueUtils.java
@@ -0,0 +1,29 @@
+package org.projectfloodlight.openflow.types;
+
+import com.google.common.base.Preconditions;
+
+public class HashValueUtils {
+    private HashValueUtils() { }
+
+    public static long combineWithValue(long key, long value, int keyBits) {
+        Preconditions.checkArgument(keyBits >= 0 && keyBits <= 64, "keyBits must be [0,64]");
+
+        int valueBits = 64 - keyBits;
+        long valueMask = valueBits == 64 ? 0xFFFFFFFFFFFFFFFFL : (1L << valueBits) - 1;
+
+        return key ^ (value & valueMask);
+    }
+
+    public static int prefixBits(long raw1, int numBits) {
+        Preconditions.checkArgument(numBits >= 0 && numBits <= 32,
+                "numBits must be in range [0, 32]");
+
+        if(numBits == 0)
+            return 0;
+
+        final int shiftDown = 64 - numBits;
+
+        return (int) (raw1 >>> shiftDown);
+    }
+
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFChecksum128.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFChecksum128.java
deleted file mode 100644
index 7578dc6..0000000
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFChecksum128.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.projectfloodlight.openflow.types;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-
-import com.google.common.hash.PrimitiveSink;
-
-public class OFChecksum128 implements OFValueType<OFChecksum128> {
-
-    static final int LENGTH = 16;
-
-    private final long raw1; // MSBs
-    private final long raw2; // LSBs
-
-    public static final OFChecksum128 ZERO = new OFChecksum128(0, 0);
-
-    private OFChecksum128(long raw1, long raw2) {
-        this.raw1 = raw1;
-        this.raw2 = raw2;
-    }
-
-    public static OFChecksum128 of(long raw1, long raw2) {
-        if (raw1 == 0 && raw2 == 0)
-            return ZERO;
-        return new OFChecksum128(raw1, raw2);
-    }
-
-    @Override
-    public int getLength() {
-        return LENGTH;
-    }
-
-    @Override
-    public OFChecksum128 applyMask(OFChecksum128 mask) {
-        return of(this.raw1 & mask.raw1, this.raw2 & mask.raw2);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof OFChecksum128))
-            return false;
-        OFChecksum128 other = (OFChecksum128)obj;
-        return (other.raw1 == this.raw1 && other.raw2 == this.raw2);
-    }
-
-    @Override
-    public int hashCode() {
-        return (int)(31 * raw1 + raw2);
-    }
-
-    public void write16Bytes(ChannelBuffer cb) {
-        cb.writeLong(raw1);
-        cb.writeLong(raw2);
-    }
-
-    public static OFChecksum128 read16Bytes(ChannelBuffer cb) {
-        long raw1 = cb.readLong();
-        long raw2 = cb.readLong();
-        return of(raw1, raw2);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("0x%016x%016x", raw1, raw2);
-    }
-
-    @Override
-    public int compareTo(OFChecksum128 o) {
-        long c = this.raw1 - o.raw1;
-        if (c != 0)
-            return Long.signum(c);
-        return Long.signum(this.raw2 - o.raw2);
-    }
-
-    @Override
-    public void putTo(PrimitiveSink sink) {
-        sink.putLong(raw1);
-        sink.putLong(raw2);
-    }
-
-}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U128.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U128.java
new file mode 100644
index 0000000..35ef846
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U128.java
@@ -0,0 +1,137 @@
+package org.projectfloodlight.openflow.types;
+
+import javax.annotation.Nonnull;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.UnsignedLongs;
+
+public class U128 implements OFValueType<U128>, HashValue<U128> {
+
+    static final int LENGTH = 16;
+
+    private final long raw1; // MSBs
+    private final long raw2; // LSBs
+
+    public static final U128 ZERO = new U128(0, 0);
+
+    private U128(long raw1, long raw2) {
+        this.raw1 = raw1;
+        this.raw2 = raw2;
+    }
+
+    public static U128 of(long raw1, long raw2) {
+        if (raw1 == 0 && raw2 == 0)
+            return ZERO;
+        return new U128(raw1, raw2);
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+
+    public long getMsb() {
+        return raw1;
+    }
+
+    public long getLsb() {
+        return raw2;
+    }
+
+    @Override
+    public U128 applyMask(U128 mask) {
+        return and(mask);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
+        result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        U128 other = (U128) obj;
+        if (raw1 != other.raw1)
+            return false;
+        if (raw2 != other.raw2)
+            return false;
+        return true;
+    }
+
+    public void write16Bytes(ChannelBuffer cb) {
+        cb.writeLong(raw1);
+        cb.writeLong(raw2);
+    }
+
+    public static U128 read16Bytes(ChannelBuffer cb) {
+        long raw1 = cb.readLong();
+        long raw2 = cb.readLong();
+        return of(raw1, raw2);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("0x%016x%016x", raw1, raw2);
+    }
+
+    @Override
+    public int compareTo(@Nonnull U128 o) {
+        int msb = UnsignedLongs.compare(this.raw1, o.raw1);
+        if(msb != 0)
+            return msb;
+        else
+            return UnsignedLongs.compare(this.raw2, o.raw2);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putLong(raw1);
+        sink.putLong(raw2);
+    }
+
+    @Override
+    public U128 inverse() {
+        return U128.of(~raw1, ~raw2);
+    }
+
+    @Override
+    public U128 or(U128 other) {
+        return U128.of(raw1 | other.raw1, raw2 | other.raw2);
+    }
+
+    @Override
+    public U128 and(U128 other) {
+        return U128.of(raw1 & other.raw1, raw2 & other.raw2);
+    }
+
+    @Override
+    public U128 xor(U128 other) {
+        return U128.of(raw1 ^ other.raw1, raw2 ^ other.raw2);
+    }
+
+    @Override
+    public int prefixBits(int numBits) {
+        return HashValueUtils.prefixBits(this.raw1, numBits);
+    }
+
+    @Override
+    public U128 combineWithValue(U128 value, int keyBits) {
+        return U128.of(
+                HashValueUtils.combineWithValue(this.raw1, value.raw1, Math.min(64, keyBits)),
+                HashValueUtils.combineWithValue(this.raw2, value.raw2, Math.max(0,keyBits-64))
+        );
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
index f480c47..9001eb8 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/U64.java
@@ -27,7 +27,7 @@
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.UnsignedLongs;
 
-public class U64 implements Writeable, OFValueType<U64> {
+public class U64 implements Writeable, OFValueType<U64>, HashValue<U64> {
     private static final long UNSIGNED_MASK = 0x7fffffffffffffffL;
     private final static long ZERO_VAL = 0;
     public final static U64 ZERO = new U64(ZERO_VAL);
@@ -110,7 +110,7 @@
 
     @Override
     public U64 applyMask(U64 mask) {
-        return ofRaw(raw & mask.raw);
+        return and(mask);
     }
 
     @Override
@@ -128,6 +128,39 @@
         sink.putLong(raw);
     }
 
+    @Override
+    public U64 inverse() {
+        return U64.of(~raw);
+    }
+
+    @Override
+    public U64 or(U64 other) {
+        return U64.of(raw | other.raw);
+    }
+
+    @Override
+    public U64 and(U64 other) {
+        return ofRaw(raw & other.raw);
+    }
+    @Override
+    public U64 xor(U64 other) {
+        return U64.of(raw ^ other.raw);
+    }
+
+    /** return the "numBits" highest-order bits of the hash.
+     *  @param numBits number of higest-order bits to return [0-32].
+     *  @return a numberic value of the 0-32 highest-order bits.
+     */
+    @Override
+    public int prefixBits(int numBits) {
+        return HashValueUtils.prefixBits(raw, numBits);
+    }
+
+    @Override
+    public U64 combineWithValue(U64 value, int keyBits) {
+        return U64.of(HashValueUtils.combineWithValue(this.raw, value.raw, keyBits));
+    }
+
     public final static Reader READER = new Reader();
 
     private static class Reader implements OFMessageReader<U64> {
@@ -136,4 +169,6 @@
             return U64.ofRaw(bb.readLong());
         }
     }
+
+
 }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java
new file mode 100644
index 0000000..1fe36ac
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/HashValueUtilsTest.java
@@ -0,0 +1,23 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class HashValueUtilsTest {
+    @Test
+    public void testBasic() {
+        long key =        0x1234_5678_1234_5678L;
+        long value =      0x8765_4321_8765_4321L;
+        long firstword  = 0xFFFF_FFFF_0000_0000L;
+        long secondword = 0x0000_0000_FFFF_FFFFL;
+        long xor =        key ^ value;
+
+        assertThat(HashValueUtils.combineWithValue(key, value, 0), equalTo(xor));
+        assertThat(HashValueUtils.combineWithValue(key, value, 64), equalTo(key));
+        assertThat(HashValueUtils.combineWithValue(key, value, 32), equalTo(key & firstword | xor & secondword ));
+        assertThat(HashValueUtils.combineWithValue(key, value, 8), equalTo(0x1251_1559_9551_1559L));
+    }
+
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U128Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U128Test.java
new file mode 100644
index 0000000..fb5cd23
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U128Test.java
@@ -0,0 +1,182 @@
+package org.projectfloodlight.openflow.types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+
+public class U128Test {
+    @Test
+    public void testPositiveRaws() {
+        assertThat(U128.of(0, 0).getMsb(), equalTo(0L));
+        assertThat(U128.of(0, 0).getLsb(), equalTo(0L));
+
+        assertThat(U128.of(1, 2).getMsb(), equalTo(1L));
+        assertThat(U128.of(1, 2).getLsb(), equalTo(2L));
+    }
+
+    @Test
+    public void testPutTo() {
+        U128 h         = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+        U128 hSame     = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+
+        U128 hBothDiff = U128.of(0x1234_5678_90ab_cdefL,0x1234_5678_90ab_cdefL);
+        U128 hMsbDiff  = U128.of(0x0234_5678_90ab_cdefL,0xdeafbeefdeadbeefL);
+        U128 hLsbDiff  = U128.of(0x1234_5678_90ab_cdefL,0xdeafbeefdeadbeeeL);
+
+        assertThat(hash(h), equalTo(hash(hSame)));
+        assertThat(hash(h), not(hash(hBothDiff)));
+        assertThat(hash(h), not(hash(hMsbDiff)));
+        assertThat(hash(h), not(hash(hLsbDiff)));
+    }
+
+    private HashCode hash(U128 f) {
+        Hasher hash = Hashing.murmur3_128().newHasher();
+        f.putTo(hash);
+        return hash.hash();
+
+    }
+
+    @Test
+    public void testEqualHashCode() {
+        U128 h1 = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h2 = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h3 = U128.of(0xeeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        U128 h3_2 = U128.of(0xdeafbeefdeadbeefL, 0xeeafbeefdeadbeefL);
+
+        assertTrue(h1.equals(h1));
+        assertTrue(h1.equals(h2));
+        assertFalse(h1.equals(h3));
+        assertFalse(h1.equals(h3_2));
+        assertTrue(h2.equals(h1));
+
+        assertEquals(h1.hashCode(), h2.hashCode());
+        assertNotEquals(h1.hashCode(), h3.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+        assertNotEquals(h1.hashCode(), h3_2.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+    }
+
+    @Test
+    public void testXor() {
+        U128 hNull = U128.of(0, 0);
+        U128 hDeadBeef = U128.of(0xdeafbeefdeadbeefL, 0xdeafbeefdeadbeefL);
+        assertThat(hNull.xor(hNull), equalTo(hNull));
+        assertThat(hNull.xor(hDeadBeef), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hNull), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hDeadBeef), equalTo(hNull));
+
+
+        U128 h1_0 = U128.of(1L, 0);
+        U128 h8_0 = U128.of(0x8000000000000000L, 0);
+        U128 h81_0 = U128.of(0x8000000000000001L, 0);
+        assertThat(h1_0.xor(h8_0), equalTo(h81_0));
+
+        U128 h0_1 = U128.of(0, 1L);
+        U128 h0_8 = U128.of(0, 0x8000000000000000L);
+        U128 h0_81 = U128.of(0, 0x8000000000000001L);
+        assertThat(h0_1.xor(h0_8), equalTo(h0_81));
+    }
+
+    @Test
+    public void testKeyBits() {
+        U128 zeroU = U128.of(0,0);
+        assertThat(zeroU.prefixBits(0), equalTo(0));
+        assertThat(zeroU.prefixBits(16), equalTo(0));
+        assertThat(zeroU.prefixBits(32), equalTo(0));
+
+        checkInvalidKeyBitSize(zeroU, 33);
+        checkInvalidKeyBitSize(zeroU, 64);
+        assertThat(zeroU.prefixBits(3), equalTo(0));
+
+        U128 positiveU = U128.of(0x1234_5678_1234_5678L, 0x1234_5678_1234_5678L);
+        assertThat(positiveU.prefixBits(0), equalTo(0));
+        assertThat(positiveU.prefixBits(16), equalTo(0x1234));
+        assertThat(positiveU.prefixBits(32), equalTo(0x12345678));
+        checkInvalidKeyBitSize(positiveU, 33);
+        checkInvalidKeyBitSize(positiveU, 64);
+
+        U128 signedBitU = U128.of(0x8765_4321_8765_4321L, 0x1234_5678_1234_5678L);
+        assertThat(signedBitU.prefixBits(0), equalTo(0));
+        assertThat(signedBitU.prefixBits(16), equalTo(0x8765));
+        assertThat(signedBitU.prefixBits(32), equalTo(0x8765_4321));
+        checkInvalidKeyBitSize(signedBitU, 33);
+        checkInvalidKeyBitSize(signedBitU, 64);
+    }
+
+    private void
+    checkInvalidKeyBitSize(U128 u, int prefixBit) {
+        try {
+            u.prefixBits(prefixBit);
+            fail("Expected exception not thrown for "+prefixBit + " bits");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+
+    @Test
+    public void testCompare() {
+        U128 u0_0 = U128.of(0, 0);
+        U128 u0_1 = U128.of(0, 1);
+        U128 u0_8 = U128.of(0, 0x8765_4321_8765_4321L);
+        U128 u1_0 = U128.of(0x1234_5678_1234_5678L, 0);
+        U128 u8_0 = U128.of(0x8765_4321_8765_4321L, 0);
+        U128 uf_0 = U128.of(0xFFFF_FFFF_FFFF_FFFFL, 0);
+
+        U128[] us = new U128[] { u0_0, u0_1, u0_8, u1_0, u8_0, uf_0 };
+
+        for(int i = 0; i< us.length; i++) {
+            U128 u_base = us[i];
+            assertThat(
+                    String.format("%s should be equal to itself (compareTo)", u_base),
+                    u_base.compareTo(u_base), equalTo(0));
+            assertThat(
+                    String.format("%s should be equal to itself (equals)", u_base),
+                    u_base.equals(u_base), equalTo(true));
+            assertThat(
+                    String.format("%s should be equal to itself (equals, by value)", u_base),
+                    u_base.equals(U128.of(u_base.getMsb(), u_base.getLsb())), equalTo(true));
+
+            for(int j = i+1; j< us.length; j++) {
+                U128 u_greater = us[j];
+                assertThat(
+                        String.format("%s should not be equal to %s", u_base, u_greater),
+                        u_base.equals(u_base), equalTo(true));
+                assertThat(
+                        String.format("%s should be smaller than %s", u_base, u_greater),
+                        u_base.compareTo(u_greater), Matchers.lessThan(0));
+                assertThat(
+                        String.format("%s should be greater than %s", u_greater, u_base),
+                        u_greater.compareTo(u_base), Matchers.greaterThan(0));
+            }
+        }
+    }
+
+    @Test
+    public void testCombine() {
+        long key = 0x1234567890abcdefL;
+        long val = 0xdeafbeefdeadbeefL;
+        U128 hkey = U128.of(key, key*2);
+        U128 hVal = U128.of(val, val/2);
+
+        assertThat(hkey.combineWithValue(hVal, 0), equalTo(hkey.xor(hVal)));
+        assertThat(hkey.combineWithValue(hVal, 64), equalTo(U128.of(hkey.getMsb(), hkey.getLsb() ^ hVal.getLsb())));
+        assertThat(hkey.combineWithValue(hVal, 128), equalTo(hkey));
+
+        long mask8 = 0xFF00_0000_0000_0000L;
+
+        assertThat(hkey.combineWithValue(hVal, 8), equalTo(U128.of(hkey.getMsb() & mask8 |  hkey.getMsb() ^ hVal.getMsb() & ~mask8,
+                                                                   hkey.getLsb() ^ hVal.getLsb() )));
+    }
+
+}
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U64Test.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U64Test.java
index e45e8a0..b0cca23 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U64Test.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/U64Test.java
@@ -1,6 +1,12 @@
 package org.projectfloodlight.openflow.types;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.math.BigInteger;
 
@@ -18,9 +24,95 @@
 
     @Test
     public void testNegativeRaws() {
-        long minus_1 = 0xFFffFFffFFffFFffL;
+        long minus_1 = 0xFFFF_FFFF_FFFF_FFFFL;
         assertEquals(minus_1, U64.ofRaw(minus_1).getValue());
-        assertEquals(new BigInteger("FFffFFffFFffFFff", 16),  U64.ofRaw(minus_1).getBigInteger());
+        assertEquals(new BigInteger("FFFF_FFFF_FFFF_FFFF".replace("_", ""), 16),  U64.ofRaw(minus_1).getBigInteger());
         assertEquals(new BigInteger("18446744073709551615"),  U64.ofRaw(minus_1).getBigInteger());
     }
+
+    @Test
+    public void testEqualHashCode() {
+        U64 h1 = U64.of(0xdeafbeefdeadbeefL);
+        U64 h2 = U64.of(0xdeafbeefdeadbeefL);
+        U64 h3 = U64.of(0xeeafbeefdeadbeefL);
+
+        assertTrue(h1.equals(h1));
+        assertTrue(h1.equals(h2));
+        assertFalse(h1.equals(h3));
+        assertTrue(h2.equals(h1));
+
+        assertEquals(h1.hashCode(), h2.hashCode());
+        assertNotEquals(h1.hashCode(), h3.hashCode()); // not technically a requirement, but we'll hopefully be lucky.
+    }
+
+    @Test
+    public void testXor() {
+        U64 hNull = U64.of(0);
+        U64 hDeadBeef = U64.of(0xdeafbeefdeadbeefL);
+        assertThat(hNull.xor(hNull), equalTo(hNull));
+        assertThat(hNull.xor(hDeadBeef), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hNull), equalTo(hDeadBeef));
+        assertThat(hDeadBeef.xor(hDeadBeef), equalTo(hNull));
+
+
+        U64 h1 = U64.of(1L);
+        U64 h8 = U64.of(0x8000000000000000L);
+        U64 h81 = U64.of(0x8000000000000001L);
+        assertThat(h1.xor(h8), equalTo(h81));
+    }
+
+    @Test
+    public void testCombine() {
+        long key = 0x1234567890abcdefL;
+        long val = 0xdeafbeefdeadbeefL;
+        U64 hkey = U64.of(key);
+        U64 hVal = U64.of(val);
+
+        assertThat(hkey.combineWithValue(hVal, 0), equalTo(hkey.xor(hVal)));
+        assertThat(hkey.combineWithValue(hVal, 64), equalTo(hkey));
+        long mask32 = 0x00000000FFFFFFFFL;
+        assertThat(hkey.combineWithValue(hVal, 32),
+                equalTo(U64.of(key & ~mask32| (key ^ val) & mask32)));
+
+        long tenMask = 0x003FFFFFFFFFFFFFL;
+        assertThat(hkey.combineWithValue(hVal, 10),
+                equalTo(U64.of(key & ~tenMask | (key ^ val) & tenMask)));
+    }
+
+    @Test
+    public void testKeyBits() {
+        U64 zeroU = U64.of(0);
+        assertThat(zeroU.prefixBits(0), equalTo(0));
+        assertThat(zeroU.prefixBits(16), equalTo(0));
+        assertThat(zeroU.prefixBits(32), equalTo(0));
+
+        checkInvalidKeyBitSize(zeroU, 33);
+        checkInvalidKeyBitSize(zeroU, 64);
+        assertThat(zeroU.prefixBits(3), equalTo(0));
+
+        U64 positiveU = U64.of(0x1234_5678_1234_5678L);
+        assertThat(positiveU.prefixBits(0), equalTo(0));
+        assertThat(positiveU.prefixBits(16), equalTo(0x1234));
+        assertThat(positiveU.prefixBits(32), equalTo(0x12345678));
+        checkInvalidKeyBitSize(positiveU, 33);
+        checkInvalidKeyBitSize(positiveU, 64);
+
+        U64 signedBitU = U64.of(0x8765_4321_8765_4321L);
+        assertThat(signedBitU.prefixBits(0), equalTo(0));
+        assertThat(signedBitU.prefixBits(16), equalTo(0x8765));
+        assertThat(signedBitU.prefixBits(32), equalTo(0x8765_4321));
+        checkInvalidKeyBitSize(signedBitU, 33);
+        checkInvalidKeyBitSize(signedBitU, 64);
+    }
+
+    private void
+            checkInvalidKeyBitSize(U64 u, int prefixBit) {
+        try {
+            u.prefixBits(prefixBit);
+            fail("Expected exception not thrown for "+prefixBit + " bits");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+    }
+
 }
diff --git a/test_data/of13/bsn_gentable_bucket_stats_reply.data b/test_data/of13/bsn_gentable_bucket_stats_reply.data
index 8e0772f..84a36a6 100644
--- a/test_data/of13/bsn_gentable_bucket_stats_reply.data
+++ b/test_data/of13/bsn_gentable_bucket_stats_reply.data
@@ -22,8 +22,8 @@
 builder.setXid(0x12345678)
     .setEntries(
         ImmutableList.<OFBsnGentableBucketStatsEntry>of(
-            factory.bsnGentableBucketStatsEntry(OFChecksum128.of(0x8877665544332211L, 0xFFEEDDCCBBAA9988L)),
-            factory.bsnGentableBucketStatsEntry(OFChecksum128.of(0x1234234534564567L, 0x56786789789A89ABL))
+            factory.bsnGentableBucketStatsEntry(U128.of(0x8877665544332211L, 0xFFEEDDCCBBAA9988L)),
+            factory.bsnGentableBucketStatsEntry(U128.of(0x1234234534564567L, 0x56786789789A89ABL))
         )
     )
 -- c
diff --git a/test_data/of13/bsn_gentable_clear_request.data b/test_data/of13/bsn_gentable_clear_request.data
index 847af3c..853d463 100644
--- a/test_data/of13/bsn_gentable_clear_request.data
+++ b/test_data/of13/bsn_gentable_clear_request.data
@@ -16,8 +16,8 @@
     checksum_mask=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000)
 -- java
 builder.setXid(0x12345678)
-    .setChecksum(OFChecksum128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA990000L))
-    .setChecksumMask(OFChecksum128.of(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFF0000L))
+    .setChecksum(U128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA990000L))
+    .setChecksumMask(U128.of(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFF0000L))
     .setTableId(GenTableId.of(20))
 -- c
 obj = of_bsn_gentable_clear_request_new(OF_VERSION_1_3);
diff --git a/test_data/of13/bsn_gentable_entry_add.data b/test_data/of13/bsn_gentable_entry_add.data
index 02c2bb4..f813dcd 100644
--- a/test_data/of13/bsn_gentable_entry_add.data
+++ b/test_data/of13/bsn_gentable_entry_add.data
@@ -38,7 +38,7 @@
     ])
 -- java
 builder.setXid(0x12345678)
-    .setChecksum(OFChecksum128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998877L))
+    .setChecksum(U128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998877L))
     .setTableId(GenTableId.of(20))
     .setKey(
         ImmutableList.<OFBsnTlv>of(
diff --git a/test_data/of13/bsn_gentable_entry_desc_stats_reply.data b/test_data/of13/bsn_gentable_entry_desc_stats_reply.data
index 4035f4c..21291ed 100644
--- a/test_data/of13/bsn_gentable_entry_desc_stats_reply.data
+++ b/test_data/of13/bsn_gentable_entry_desc_stats_reply.data
@@ -55,7 +55,7 @@
     .setEntries(
         ImmutableList.<OFBsnGentableEntryDescStatsEntry>of(
             factory.buildBsnGentableEntryDescStatsEntry()
-                .setChecksum(OFChecksum128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998800L))
+                .setChecksum(U128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998800L))
                 .setKey(ImmutableList.<OFBsnTlv>of(
                     factory.bsnTlvs().port(OFPort.of(5))
                 ))
@@ -64,7 +64,7 @@
                 ))
                 .build(),
             factory.buildBsnGentableEntryDescStatsEntry()
-                .setChecksum(OFChecksum128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998801L))
+                .setChecksum(U128.of(0xFEDCBA9876543210L, 0xFFEECCBBAA998801L))
                 .setKey(ImmutableList.<OFBsnTlv>of(
                     factory.bsnTlvs().port(OFPort.of(6))
                 ))