Adding prefix{Ones,Zeros} to ImmutableByteSequence

Change-Id: Ibf8c0d1101d763729478fba5118ed9aa0f22ca50
diff --git a/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java b/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
index 68ec0f8..07dda85 100644
--- a/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
+++ b/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
@@ -164,19 +164,18 @@
     }
 
     /**
-     * Creates a new byte sequence of the given size where alla bits are 0.
+     * Creates a new byte sequence of the given size where all bits are 0.
      *
      * @param size number of bytes
      * @return a new immutable byte sequence
      */
     public static ImmutableByteSequence ofZeros(int size) {
-        byte[] bytes = new byte[size];
-        Arrays.fill(bytes, (byte) 0);
-        return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
+        // array is initialized to all 0's by default
+        return new ImmutableByteSequence(ByteBuffer.wrap(new byte[size]));
     }
 
     /**
-     * Creates a new byte sequence of the given size where alla bits are 1.
+     * Creates a new byte sequence of the given size where all bits are 1.
      *
      * @param size number of bytes
      * @return a new immutable byte sequence
@@ -188,6 +187,51 @@
     }
 
     /**
+     * Creates a new byte sequence that is prefixed with specified number of
+     * zeros if val = 0 or ones if val = 0xff.
+     *
+     * @param size number of total bytes
+     * @param prefixBits number of bits in prefix
+     * @param val 0 for prefix of zeros; 0xff for prefix of ones
+     * @return new immutable byte sequence
+     */
+    static ImmutableByteSequence prefix(int size, long prefixBits, byte val) {
+        checkArgument(val == 0 || val == (byte) 0xff, "Val must be 0 or 0xff");
+        byte[] bytes = new byte[size];
+        int prefixBytes = (int) (prefixBits / Byte.SIZE);
+        Arrays.fill(bytes, 0, prefixBytes, val);
+        Arrays.fill(bytes, prefixBytes, bytes.length, (byte) ~val);
+        int partialBits = (int) (prefixBits % Byte.SIZE);
+        if (partialBits != 0) {
+            bytes[prefixBytes] = val == 0 ?
+                    (byte) (0xff >> partialBits) : (byte) (0xff << Byte.SIZE - partialBits);
+        }
+        return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
+    }
+
+    /**
+     * Creates a new byte sequence that is prefixed with specified number of zeros.
+     *
+     * @param size number of total bytes
+     * @param prefixBits number of bits in prefix
+     * @return new immutable byte sequence
+     */
+    public static ImmutableByteSequence prefixZeros(int size, long prefixBits) {
+        return prefix(size, prefixBits, (byte) 0);
+    }
+
+    /**
+     * Creates a new byte sequence that is prefixed with specified number of ones.
+     *
+     * @param size number of total bytes
+     * @param prefixBits number of bits in prefix
+     * @return new immutable byte sequence
+     */
+    public static ImmutableByteSequence prefixOnes(int size, long prefixBits) {
+        return prefix(size, prefixBits, (byte) 0xff);
+    }
+
+    /**
      * Returns a view of this sequence as a read-only {@link ByteBuffer}.
      * <p>
      * The returned buffer will have position 0, while limit and capacity will
diff --git a/utils/misc/src/test/java/org/onlab/util/ImmutableByteSequenceTest.java b/utils/misc/src/test/java/org/onlab/util/ImmutableByteSequenceTest.java
index 055f824..07aa40e 100644
--- a/utils/misc/src/test/java/org/onlab/util/ImmutableByteSequenceTest.java
+++ b/utils/misc/src/test/java/org/onlab/util/ImmutableByteSequenceTest.java
@@ -17,7 +17,9 @@
 package org.onlab.util;
 
 import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -28,6 +30,8 @@
 import static org.hamcrest.Matchers.is;
 
 public class ImmutableByteSequenceTest {
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
 
     @Test
     public void testCopy() throws Exception {
@@ -115,4 +119,71 @@
                                   ByteOrder.BIG_ENDIAN)
                 .testEquals();
     }
+
+    @Test
+    public void testBitSetMethods() throws Exception {
+        // All zeros tests
+        assertThat("3 bytes, all 0's",
+                ImmutableByteSequence.ofZeros(3),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, 0}))));
+        assertThat("3 bytes, all 0's via prefix",
+                ImmutableByteSequence.prefixZeros(3, 3 * Byte.SIZE),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, 0}))));
+
+        // All ones tests
+        assertThat("3 bytes, all 1's",
+                ImmutableByteSequence.ofZeros(3),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, 0}))));
+        assertThat("3 bytes, all 1's via prefix",
+                ImmutableByteSequence.prefixOnes(3, 3 * Byte.SIZE),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff}))));
+
+        // Zero prefix tests
+        assertThat("2 bytes, prefixed with 5 0's",
+                ImmutableByteSequence.prefix(2, 5, (byte) 0),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0x7, (byte) 0xff}))));
+        assertThat("4 bytes, prefixed with 16 0's",
+                ImmutableByteSequence.prefix(4, 16, (byte) 0),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, (byte) 0xff, (byte) 0xff}))));
+        assertThat("4 bytes, prefixed with 20 0's",
+                ImmutableByteSequence.prefix(4, 20, (byte) 0),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, (byte) 0x0f, (byte) 0xff}))));
+        assertThat("8 bytes, prefixed with 36 0's",
+                ImmutableByteSequence.prefixZeros(8, 38),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{0, 0, 0, 0, (byte) 0x03, (byte) 0xff, (byte) 0xff, (byte) 0xff}))));
+
+        // Ones prefix tests
+        assertThat("2 bytes, prefixed with 5 1's",
+                ImmutableByteSequence.prefix(2, 5, (byte) 0xff),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0xf8, 0}))));
+        assertThat("4 bytes, prefixed with 16 1's",
+                ImmutableByteSequence.prefix(4, 16, (byte) 0xff),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0xff, (byte) 0xff, 0, 0}))));
+        assertThat("4 bytes, prefixed with 20 1's",
+                ImmutableByteSequence.prefix(4, 20, (byte) 0xff),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xf0, 0}))));
+        assertThat("8 bytes, prefixed with 10 1's",
+                ImmutableByteSequence.prefixOnes(8, 10),
+                is(equalTo(ImmutableByteSequence.copyFrom(
+                        new byte[]{(byte) 0xff, (byte) 0xc0, 0, 0, 0, 0, 0, 0}))));
+    }
+
+    @Test
+    public void testBadPrefixVal() {
+        thrown.expect(IllegalArgumentException.class);
+        thrown.reportMissingExceptionWithMessage(
+                "Expect IllegalArgumentException due to val = 0x7");
+        ImmutableByteSequence.prefix(5, 10, (byte) 0x7);
+    }
 }
\ No newline at end of file