Support for bitwise AND/OR/XOR in ImmutableByteSequence

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

Change-Id: I826650c3fc45573c723d9d2dd8692da174d9ae08
This commit is contained in:
Carmelo Cascone 2018-04-06 23:17:04 -07:00
parent 3dca0f8fb1
commit 8a571af574
13 changed files with 172 additions and 59 deletions

View File

@ -54,7 +54,6 @@ import java.util.Optional;
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 @@ public final class PipelineInterpreterImpl extends AbstractHandlerBehaviour impl
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()));

View File

@ -58,7 +58,6 @@ import java.util.concurrent.CompletableFuture;
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 @@ public class P4RuntimeTest {
.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 @@ public class P4RuntimeTest {
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();

View File

@ -23,7 +23,6 @@ import org.onosproject.net.pi.model.PiMatchType;
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 @@ abstract class AbstractCriterionTranslator implements CriterionTranslator {
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 @@ abstract class AbstractCriterionTranslator implements CriterionTranslator {
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 @@ abstract class AbstractCriterionTranslator implements CriterionTranslator {
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;
}

View File

@ -59,7 +59,6 @@ import java.util.StringJoiner;
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 @@ final class PiFlowRuleTranslatorImpl {
"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 @@ final class PiFlowRuleTranslatorImpl {
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 @@ final class PiFlowRuleTranslatorImpl {
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:

View File

@ -65,7 +65,6 @@ import static org.hamcrest.CoreMatchers.equalTo;
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 @@ public class PiTranslatorServiceTest {
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();

View File

@ -49,7 +49,6 @@ import java.util.Optional;
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 @@ public class BasicInterpreterImpl extends AbstractHandlerBehaviour
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 @@ public class BasicInterpreterImpl extends AbstractHandlerBehaviour
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(

View File

@ -53,7 +53,6 @@ import java.util.Set;
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 @@ public class FabricInterpreter extends AbstractHandlerBehaviour
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(

View File

@ -250,7 +250,7 @@ final class FabricTreatmentInterpreter {
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

View File

@ -233,7 +233,7 @@ public class FabricInterpreterTest {
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)

View File

@ -21,14 +21,13 @@ import org.junit.Before;
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 @@ public class DefaultPacketInTest {
.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 @@ public class DefaultPacketInTest {
.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 @@ public class DefaultPacketInTest {
.addEqualityGroup(packetIn3)
.testEquals();
}
}
}

View File

@ -47,7 +47,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
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 @@ public class TableEntryEncoderTest {
.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);

View File

@ -28,8 +28,8 @@ import static java.lang.String.format;
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 @@ import static org.apache.commons.lang3.ArrayUtils.reverse;
*/
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 @@ public final class ImmutableByteSequence {
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 @@ public final class ImmutableByteSequence {
}
/**
* 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 @@ public final class ImmutableByteSequence {
}
/**
* 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 @@ public final class ImmutableByteSequence {
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 @@ public final class ImmutableByteSequence {
}
/**
* 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 @@ public final class ImmutableByteSequence {
}
/**
* 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");

View File

@ -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 @@ public class ImmutableByteSequenceTest {
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 @@ public class ImmutableByteSequenceTest {
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 @@ public class ImmutableByteSequenceTest {
checkLegalFit(bytes, msbIndex + 1);
}
}
}
@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));
}
}