Support for bitwise AND/OR/XOR in ImmutableByteSequence

Also, minor refactoring of the fit() method to improve code readability

Change-Id: I826650c3fc45573c723d9d2dd8692da174d9ae08
diff --git a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
index c90ea35..47df7f1 100644
--- a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
+++ b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
@@ -54,7 +54,6 @@
 import static java.lang.String.format;
 import static java.util.stream.Collectors.toList;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.PortNumber.CONTROLLER;
 import static org.onosproject.net.PortNumber.FLOOD;
 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
@@ -219,7 +218,7 @@
         try {
             return PiControlMetadata.builder()
                     .withId(PiControlMetadataId.of(EGRESS_PORT))
-                    .withValue(fit(copyFrom(portNumber), PORT_FIELD_BITWIDTH))
+                    .withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
                     .build();
         } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
             throw new PiInterpreterException(format("Port number %d too big, %s", portNumber, e.getMessage()));
diff --git a/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java b/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
index 319afec..6c320ad 100644
--- a/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
+++ b/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
@@ -58,7 +58,6 @@
 import java.util.concurrent.ExecutionException;
 
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onlab.util.ImmutableByteSequence.ofZeros;
 import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
 import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
@@ -98,7 +97,7 @@
             .usePlaintext(true);
     private P4RuntimeClientImpl client;
 
-    private final ImmutableByteSequence ethAddr = fit(copyFrom(1), 48);
+    private final ImmutableByteSequence ethAddr = copyFrom(1).fit(48);
     private final ImmutableByteSequence portValue = copyFrom((short) 1);
     private final PiMatchFieldId ethDstAddrFieldId = PiMatchFieldId.of(ETHERNET + DOT + DST_ADDR);
     private final PiMatchFieldId ethSrcAddrFieldId = PiMatchFieldId.of(ETHERNET + DOT + SRC_ADDR);
@@ -198,11 +197,11 @@
             InterruptedException, ImmutableByteSequence.ByteSequenceTrimException {
 
         PiPacketOperation packetOperation = PiPacketOperation.builder()
-                .withData(fit(copyFrom(1), 48 + 48 + 16))
+                .withData(copyFrom(1).fit(48 + 48 + 16))
                 .withType(PACKET_OUT)
                 .withMetadata(PiControlMetadata.builder()
                                       .withId(PiControlMetadataId.of("egress_port"))
-                                      .withValue(fit(copyFrom(255), 9))
+                                      .withValue(copyFrom(255).fit(9))
                                       .build())
                 .build();
 
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractCriterionTranslator.java b/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractCriterionTranslator.java
index 36e9068..9b2b4a0 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractCriterionTranslator.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractCriterionTranslator.java
@@ -23,7 +23,6 @@
 import java.util.Optional;
 
 import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
-import static org.onlab.util.ImmutableByteSequence.fit;
 
 /**
  * Abstract implementation of a criterion translator that opportunistically tries to generate different types of match
@@ -48,7 +47,7 @@
     void initAsExactMatch(ImmutableByteSequence value, int bitWidth)
             throws ByteSequenceTrimException {
         this.initType = PiMatchType.EXACT;
-        this.value = fit(value, bitWidth);
+        this.value = value.fit(bitWidth);
         this.bitWidth = bitWidth;
     }
 
@@ -63,8 +62,8 @@
     void initAsTernaryMatch(ImmutableByteSequence value, ImmutableByteSequence mask, int bitWidth)
             throws ByteSequenceTrimException {
         this.initType = PiMatchType.TERNARY;
-        this.value = fit(value, bitWidth);
-        this.mask = fit(mask, bitWidth);
+        this.value = value.fit(bitWidth);
+        this.mask = mask.fit(bitWidth);
         this.bitWidth = bitWidth;
     }
 
@@ -79,7 +78,7 @@
     void initAsLpm(ImmutableByteSequence value, int prefixLength, int bitWidth)
             throws ByteSequenceTrimException {
         this.initType = PiMatchType.LPM;
-        this.value = fit(value, bitWidth);
+        this.value = value.fit(bitWidth);
         this.prefixLength = prefixLength;
         this.bitWidth = bitWidth;
     }
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index b4ce830..85c763c 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -59,7 +59,6 @@
 
 import static java.lang.String.format;
 import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
 import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
 import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
@@ -247,7 +246,7 @@
                             "Not such parameter '%s' for action '%s'", param.id(), actionModel)));
             try {
                 newActionBuilder.withParameter(new PiActionParam(param.id(),
-                                                                 fit(param.value(), paramModel.bitWidth())));
+                                                                 param.value().fit(paramModel.bitWidth())));
             } catch (ByteSequenceTrimException e) {
                 throw new PiTranslationException(format(
                         "Size mismatch for parameter '%s' of action '%s': %s",
@@ -413,11 +412,11 @@
             switch (fieldModel.matchType()) {
                 case EXACT:
                     return new PiExactFieldMatch(fieldMatch.fieldId(),
-                                                 fit(((PiExactFieldMatch) fieldMatch).value(), modelBitWidth));
+                                                 ((PiExactFieldMatch) fieldMatch).value().fit(modelBitWidth));
                 case TERNARY:
                     return new PiTernaryFieldMatch(fieldMatch.fieldId(),
-                                                   fit(((PiTernaryFieldMatch) fieldMatch).value(), modelBitWidth),
-                                                   fit(((PiTernaryFieldMatch) fieldMatch).mask(), modelBitWidth));
+                                                   ((PiTernaryFieldMatch) fieldMatch).value().fit(modelBitWidth),
+                                                   ((PiTernaryFieldMatch) fieldMatch).mask().fit(modelBitWidth));
                 case LPM:
                     PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
                     if (lpmfield.prefixLength() > modelBitWidth) {
@@ -426,12 +425,12 @@
                                 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
                     }
                     return new PiLpmFieldMatch(fieldMatch.fieldId(),
-                                               fit(lpmfield.value(), modelBitWidth),
+                                               lpmfield.value().fit(modelBitWidth),
                                                lpmfield.prefixLength());
                 case RANGE:
                     return new PiRangeFieldMatch(fieldMatch.fieldId(),
-                                                 fit(((PiRangeFieldMatch) fieldMatch).lowValue(), modelBitWidth),
-                                                 fit(((PiRangeFieldMatch) fieldMatch).highValue(), modelBitWidth));
+                                                 ((PiRangeFieldMatch) fieldMatch).lowValue().fit(modelBitWidth),
+                                                 ((PiRangeFieldMatch) fieldMatch).highValue().fit(modelBitWidth));
                 case VALID:
                     return fieldMatch;
                 default:
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
index 1d0caee..9af6745 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -65,7 +65,6 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.group.GroupDescription.Type.SELECT;
 import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
 import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
@@ -237,7 +236,7 @@
 
     private static PiActionGroupMember outputMember(int portNum)
             throws ImmutableByteSequence.ByteSequenceTrimException {
-        PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
+        PiActionParam param = new PiActionParam(ACT_PRM_PORT_ID, copyFrom(portNum).fit(PORT_BITWIDTH));
         PiAction piAction = PiAction.builder()
                 .withId(ACT_SET_EGRESS_PORT_ID)
                 .withParameter(param).build();
diff --git a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
index 8e7b399..5ff38ba 100644
--- a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
+++ b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
@@ -49,7 +49,6 @@
 import static java.lang.String.format;
 import static java.util.stream.Collectors.toList;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.PortNumber.CONTROLLER;
 import static org.onosproject.net.PortNumber.FLOOD;
 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
@@ -130,7 +129,7 @@
                 return PiAction.builder()
                         .withId(ACT_SET_EGRESS_PORT_ID)
                         .withParameter(new PiActionParam(ACT_PRM_PORT_ID,
-                                                         fit(copyFrom(port.toLong()), PORT_BITWIDTH)))
+                                                         copyFrom(port.toLong()).fit(PORT_BITWIDTH)))
                         .build();
             } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
                 throw new PiInterpreterException(e.getMessage());
@@ -231,7 +230,7 @@
         try {
             return PiControlMetadata.builder()
                     .withId(PKT_META_EGRESS_PORT_ID)
-                    .withValue(fit(copyFrom(portNumber), PORT_BITWIDTH))
+                    .withValue(copyFrom(portNumber).fit(PORT_BITWIDTH))
                     .build();
         } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
             throw new PiInterpreterException(format(
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
index 29e3a48..e79cd44 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricInterpreter.java
@@ -53,7 +53,6 @@
 import static java.lang.String.format;
 import static java.util.stream.Collectors.toList;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.PortNumber.FLOOD;
 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
 import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
@@ -208,7 +207,7 @@
         try {
             return PiControlMetadata.builder()
                     .withId(FabricConstants.CTRL_META_EGRESS_PORT_ID)
-                    .withValue(fit(copyFrom(portNumber), FabricConstants.PORT_BITWIDTH))
+                    .withValue(copyFrom(portNumber).fit(FabricConstants.PORT_BITWIDTH))
                     .build();
         } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
             throw new PiInterpreterException(format(
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
index 6346302..00c4047 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
@@ -250,7 +250,7 @@
                 MplsLabel mplsLabel = modMplsInst.label();
                 try {
                     ImmutableByteSequence mplsValue =
-                            ImmutableByteSequence.fit(ImmutableByteSequence.copyFrom(mplsLabel.toInt()), 20);
+                            ImmutableByteSequence.copyFrom(mplsLabel.toInt()).fit(20);
                     PiActionParam mplsParam = new PiActionParam(FabricConstants.ACT_PRM_LABEL_ID, mplsValue);
                     return PiAction.builder()
                             // FIXME: fins a way to determine v4 or v6
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
index 8d97f20..c60a5e8 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
@@ -233,7 +233,7 @@
         PiActionParam portParam = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID,
                                                     ImmutableByteSequence.copyFrom(portNumVal));
         ImmutableByteSequence mplsVal =
-                ImmutableByteSequence.fit(ImmutableByteSequence.copyFrom(MPLS_10.toInt()), 20);
+                ImmutableByteSequence.copyFrom(MPLS_10.toInt()).fit(20);
         PiActionParam mplsParam = new PiActionParam(FabricConstants.ACT_PRM_LABEL_ID, mplsVal);
         PiAction expectedAction = PiAction.builder()
                 .withId(FabricConstants.ACT_NEXT_MPLS_ROUTING_V4_ID)
diff --git a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/DefaultPacketInTest.java b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/DefaultPacketInTest.java
index 38fcf6d..5993142 100644
--- a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/DefaultPacketInTest.java
+++ b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/DefaultPacketInTest.java
@@ -21,14 +21,13 @@
 import org.junit.Test;
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.pi.runtime.PiControlMetadata;
 import org.onosproject.net.pi.model.PiControlMetadataId;
+import org.onosproject.net.pi.runtime.PiControlMetadata;
 import org.onosproject.net.pi.runtime.PiPacketOperation;
 
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
-import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
 import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_IN;
+import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
 
 /**
  * Test for DefaultPacketIn class.
@@ -65,7 +64,7 @@
                 .withType(PACKET_OUT)
                 .withMetadata(PiControlMetadata.builder()
                                       .withId(PiControlMetadataId.of("egress_port"))
-                                      .withValue(fit(copyFrom(DEFAULT_ORIGINAL_VALUE), DEFAULT_BIT_WIDTH))
+                                      .withValue(copyFrom(DEFAULT_ORIGINAL_VALUE).fit(DEFAULT_BIT_WIDTH))
                                       .build())
                 .build();
 
@@ -75,7 +74,7 @@
                 .withType(PACKET_IN)
                 .withMetadata(PiControlMetadata.builder()
                                       .withId(PiControlMetadataId.of("ingress_port"))
-                                      .withValue(fit(copyFrom(DEFAULT_ORIGINAL_VALUE), DEFAULT_BIT_WIDTH))
+                                      .withValue(copyFrom(DEFAULT_ORIGINAL_VALUE).fit(DEFAULT_BIT_WIDTH))
                                       .build())
                 .build();
 
@@ -151,4 +150,4 @@
                 .addEqualityGroup(packetIn3)
                 .testEquals();
     }
-}
\ No newline at end of file
+}
diff --git a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
index eebfb6b..709873e 100644
--- a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
+++ b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
@@ -47,7 +47,6 @@
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onlab.util.ImmutableByteSequence.ofOnes;
 import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
 import static org.onosproject.p4runtime.ctl.TableEntryEncoder.decode;
@@ -84,7 +83,7 @@
             .build();
 
     private final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(defaultPipeconf);
-    private final ImmutableByteSequence ethAddr = fit(copyFrom(rand.nextInt()), 48);
+    private final ImmutableByteSequence ethAddr = copyFrom(rand.nextInt()).fit(48);
     private final ImmutableByteSequence portValue = copyFrom((short) rand.nextInt());
     private final PiMatchFieldId ethDstAddrFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + DST_ADDR);
     private final PiMatchFieldId ethSrcAddrFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + SRC_ADDR);
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 2f98cfb..fea71a7 100644
--- a/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
+++ b/utils/misc/src/main/java/org/onlab/util/ImmutableByteSequence.java
@@ -28,8 +28,8 @@
 import static org.apache.commons.lang3.ArrayUtils.reverse;
 
 /**
- * Immutable sequence of bytes, assumed to represent a value in
- * {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN} order.
+ * Immutable sequence of bytes, assumed to represent a value in {@link
+ * ByteOrder#BIG_ENDIAN BIG_ENDIAN} order.
  * <p>
  * Sequences can be created copying from an already existing representation of a
  * sequence of bytes, such as {@link ByteBuffer} or {@code byte[]}; or by
@@ -40,6 +40,12 @@
  */
 public final class ImmutableByteSequence {
 
+    private enum BitwiseOp {
+        AND,
+        OR,
+        XOR
+    }
+
     /*
     Actual bytes are backed by a byte buffer.
     The order of a newly-created byte buffer is always BIG_ENDIAN.
@@ -47,8 +53,8 @@
     private ByteBuffer value;
 
     /**
-     * Private constructor.
-     * Creates a new byte sequence object backed by the passed ByteBuffer.
+     * Private constructor. Creates a new byte sequence object backed by the
+     * passed ByteBuffer.
      *
      * @param value a byte buffer
      */
@@ -210,7 +216,8 @@
     }
 
     /**
-     * Creates a new byte sequence that is prefixed with specified number of zeros.
+     * 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
@@ -221,7 +228,8 @@
     }
 
     /**
-     * Creates a new byte sequence that is prefixed with specified number of ones.
+     * 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
@@ -266,6 +274,73 @@
         return bytes;
     }
 
+    private ImmutableByteSequence doBitwiseOp(ImmutableByteSequence other, BitwiseOp op) {
+        checkArgument(other != null && this.size() == other.size(),
+                      "Other sequence must be non null and with same size as this");
+        byte[] newBytes = new byte[this.size()];
+        byte[] thisBytes = this.asArray();
+        byte[] otherBytes = other.asArray();
+        for (int i = 0; i < this.size(); i++) {
+            switch (op) {
+                case AND:
+                    newBytes[i] = (byte) (thisBytes[i] & otherBytes[i]);
+                    break;
+                case OR:
+                    newBytes[i] = (byte) (thisBytes[i] | otherBytes[i]);
+                    break;
+                case XOR:
+                    newBytes[i] = (byte) (thisBytes[i] ^ otherBytes[i]);
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown bitwise operator " + op.name());
+            }
+        }
+        return ImmutableByteSequence.copyFrom(newBytes);
+    }
+
+    /**
+     * Returns a new byte sequence corresponding to the result of a bitwise AND
+     * operation between this sequence and the given other, i.e. {@code this &
+     * other}.
+     *
+     * @param other other byte sequence
+     * @return new byte sequence
+     * @throws IllegalArgumentException if other sequence is null or its size is
+     *                                  different than this sequence size
+     */
+    public ImmutableByteSequence bitwiseAnd(ImmutableByteSequence other) {
+        return doBitwiseOp(other, BitwiseOp.AND);
+    }
+
+    /**
+     * Returns a new byte sequence corresponding to the result of a bitwise OR
+     * operation between this sequence and the given other, i.e. {@code this |
+     * other}.
+     *
+     * @param other other byte sequence
+     * @return new byte sequence
+     * @throws IllegalArgumentException if other sequence is null or its size is
+     *                                  different than this sequence size
+     */
+    public ImmutableByteSequence bitwiseOr(ImmutableByteSequence other) {
+        return doBitwiseOp(other, BitwiseOp.OR);
+    }
+
+    /**
+     * Returns a new byte sequence corresponding to the result of a bitwise XOR
+     * operation between this sequence and the given other, i.e. {@code this ^
+     * other}.
+     *
+     * @param other other byte sequence
+     * @return new byte sequence
+     * @throws IllegalArgumentException if other sequence is null or its size is
+     *                                  different than this sequence size
+     */
+    public ImmutableByteSequence bitwiseXor(ImmutableByteSequence other) {
+        return doBitwiseOp(other, BitwiseOp.XOR);
+    }
+
     @Override
     public int hashCode() {
         return value.hashCode();
@@ -284,19 +359,18 @@
     }
 
     /**
-     * Returns the index of the most significant bit (MSB), assuming a bit numbering scheme of type "LSB 0", i.e. the
-     * bit numbering starts at zero for the least significant bit (LSB). The MSB index of a byte sequence of zeros will
-     * be -1.
+     * Returns the index of the most significant bit (MSB), assuming a bit
+     * numbering scheme of type "LSB 0", i.e. the bit numbering starts at zero
+     * for the least significant bit (LSB). The MSB index of a byte sequence of
+     * zeros will be -1.
      * <p>
-     * As an example, the following conditions always hold true:
-     * {@code
+     * As an example, the following conditions always hold true: {@code
      * ImmutableByteSequence.copyFrom(0).msbIndex() == -1
      * ImmutableByteSequence.copyFrom(1).msbIndex() == 0
      * ImmutableByteSequence.copyFrom(2).msbIndex() == 1
      * ImmutableByteSequence.copyFrom(3).msbIndex() == 1
      * ImmutableByteSequence.copyFrom(4).msbIndex() == 2
-     * ImmutableByteSequence.copyFrom(512).msbIndex() == 9
-     * }
+     * ImmutableByteSequence.copyFrom(512).msbIndex() == 9 }
      *
      * @return index of the MSB, -1 if the sequence has all bytes set to 0
      */
@@ -325,17 +399,45 @@
     }
 
     /**
-     * Trims or expands the given byte sequence so to fit a given bit-width. When trimming, the operations is deemed to
-     * be safe only if the trimmed bits are zero, i.e. it is safe to trim only when {@code bitWidth > msbIndex()},
-     * otherwise an exception will be thrown. When expanding, the sequence will be padded with zeros. The returned byte
-     * sequence will have minimum size to contain the given bit-width.
+     * Trims or expands a copy of this byte sequence so to fit the given
+     * bit-width. When trimming, the operations is deemed to be safe only if the
+     * trimmed bits are zero, i.e. it is safe to trim only when {@code bitWidth
+     * > msbIndex()}, otherwise an exception will be thrown. When expanding, the
+     * sequence will be padded with zeros. The returned byte sequence will have
+     * minimum size to contain the given bit-width.
+     *
+     * @param bitWidth a non-zero positive integer
+     * @return a new byte sequence
+     * @throws ByteSequenceTrimException if the byte sequence cannot be fitted
+     */
+    public ImmutableByteSequence fit(int bitWidth) throws ByteSequenceTrimException {
+        return doFit(this, bitWidth);
+    }
+
+    /**
+     * Trims or expands the given byte sequence so to fit a given bit-width.
+     * When trimming, the operations is deemed to be safe only if the trimmed
+     * bits are zero, i.e. it is safe to trim only when {@code bitWidth >
+     * msbIndex()}, otherwise an exception will be thrown. When expanding, the
+     * sequence will be padded with zeros. The returned byte sequence will have
+     * minimum size to contain the given bit-width.
      *
      * @param original a byte sequence
      * @param bitWidth a non-zero positive integer
      * @return a new byte sequence
      * @throws ByteSequenceTrimException if the byte sequence cannot be fitted
+     * @deprecated in ONOS 1.13, use {@link ImmutableByteSequence#fit(int)}
+     * instead.
      */
-    public static ImmutableByteSequence fit(ImmutableByteSequence original, int bitWidth)
+    @Deprecated
+    public static ImmutableByteSequence fit(ImmutableByteSequence original,
+                                            int bitWidth)
+            throws ByteSequenceTrimException {
+        return doFit(original, bitWidth);
+    }
+
+    private static ImmutableByteSequence doFit(ImmutableByteSequence original,
+                                               int bitWidth)
             throws ByteSequenceTrimException {
 
         checkNotNull(original, "byte sequence cannot be null");
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 76981e8..7e74328 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,6 @@
 package org.onlab.util;
 
 import com.google.common.testing.EqualsTester;
-
 import org.apache.commons.lang3.RandomUtils;
 import org.junit.Assert;
 import org.junit.Rule;
@@ -207,7 +206,7 @@
 
     private void checkIllegalFit(ImmutableByteSequence bytes, int bitWidth) {
         try {
-            ImmutableByteSequence.fit(bytes, bitWidth);
+            bytes.fit(bitWidth);
             Assert.fail(format("Except ByteSequenceTrimException due to value = %s and bitWidth %d",
                                bytes.toString(), bitWidth));
         } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
@@ -217,8 +216,8 @@
 
     private void checkLegalFit(ImmutableByteSequence bytes, int bitWidth)
             throws ImmutableByteSequence.ByteSequenceTrimException {
-        ImmutableByteSequence fitBytes = ImmutableByteSequence.fit(bytes, bitWidth);
-        ImmutableByteSequence sameBytes = ImmutableByteSequence.fit(fitBytes, bytes.size() * 8);
+        ImmutableByteSequence fitBytes = bytes.fit(bitWidth);
+        ImmutableByteSequence sameBytes = fitBytes.fit(bytes.size() * 8);
         assertThat(format("Fitted value %s (re-extended to %s) not equal to original value %s",
                           fitBytes, sameBytes, bytes),
                    sameBytes,
@@ -254,4 +253,25 @@
             checkLegalFit(bytes, msbIndex + 1);
         }
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testBitwiseOperations() {
+        Random random = new Random();
+        long long1 = random.nextLong();
+        long long2 = random.nextLong();
+
+        ImmutableByteSequence bs1 = ImmutableByteSequence.copyFrom(long1);
+        ImmutableByteSequence bs2 = ImmutableByteSequence.copyFrom(long2);
+
+        ImmutableByteSequence andBs = bs1.bitwiseAnd(bs2);
+        ImmutableByteSequence orBs = bs1.bitwiseOr(bs2);
+        ImmutableByteSequence xorBs = bs1.bitwiseXor(bs2);
+
+        assertThat("Invalid bitwise AND result",
+                   andBs.asReadOnlyBuffer().getLong(), is(long1 & long2));
+        assertThat("Invalid bitwise OR result",
+                   orBs.asReadOnlyBuffer().getLong(), is(long1 | long2));
+        assertThat("Invalid bitwise XOR result",
+                   xorBs.asReadOnlyBuffer().getLong(), is(long1 ^ long2));
+    }
+}