// Copyright 2016 The Bazel Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.testing.coverage; import java.util.Arrays; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; /** * Abstracts bit field operations. */ public class BitField { private byte[] bytes; private static final int[] BIT_COUNT_LOOKUP = new int[256]; static { BIT_COUNT_LOOKUP[0] = 0; BIT_COUNT_LOOKUP[1] = 1; for (int i = 2; i < 256; i += 2) { int count = BIT_COUNT_LOOKUP[i / 2]; BIT_COUNT_LOOKUP[i] = count; BIT_COUNT_LOOKUP[i + 1] = count + 1; } } public BitField() { this(new byte[0]); } public BitField(byte[] bytes) { this.bytes = bytes.clone(); } /** * Returns a copy of the underlying byte array. * * @return byte array copy */ public byte[] getBytes() { return bytes.clone(); } /** * Sets or clears a bit at the given index. * * @param index bit index */ public void setBit(int index) { setBit(index, true); } /** * Clears a bit at the given index * * @param index bit index */ public void clearBit(int index) { setBit(index, false); } /** * Sets or clears a bit at the given index. * * @param index bit index */ private void setBit(int index, boolean isSet) { int byteIndex = index / 8; int newByteSize = byteIndex + 1; if (bytes.length < newByteSize) { bytes = Arrays.copyOf(bytes, newByteSize); } int bitIndex = index % 8; int mask = 1 << bitIndex; if (isSet) { bytes[byteIndex] = (byte) (bytes[byteIndex] | mask); } else { bytes[byteIndex] = (byte) (bytes[byteIndex] & ~mask); } } /** * Checks whether a bit at the given index is set. * * @param index bit index * @return true if set, false otherwise */ public boolean isBitSet(int index) { int byteIndex = index / 8; if (byteIndex >= bytes.length) { return false; } int bitIndex = index % 8; int mask = 1 << bitIndex; return (bytes[byteIndex] & mask) != 0; } /** Performs a non-destructive bit-wise "and" of this bit field with another one. */ public BitField and(BitField other) { int size = Math.min(bytes.length, other.bytes.length); byte[] result = new byte[size]; for (int i = 0; i < size; i++) { result[i] = (byte) (bytes[i] & other.bytes[i]); } return new BitField(result); } /** * Performs a non-destructive bit-wise merge of this bit field and another one. * * @param other the other bit field * @return this bit field */ public BitField or(BitField other) { byte[] largerArray, smallerArray; if (bytes.length < other.bytes.length) { largerArray = other.bytes; smallerArray = bytes; } else { largerArray = bytes; smallerArray = other.bytes; } // Start out with a copy of the larger of the two arrays. byte[] result = Arrays.copyOf(largerArray, largerArray.length); for (int i = 0; i < smallerArray.length; i++) { result[i] |= smallerArray[i]; } return new BitField(result); } /** * Compares two bit fields for equality. * * @param obj another object * @return true if the other object is a bit field with the same bits set */ @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } /** * Compare a BitField object with an array of bytes * * @param other a byte array to compare to * @return true if the underlying byte array is equal to the given byte array */ public boolean equals(byte[] other) { return Arrays.equals(bytes, other); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } public int countBitsSet() { int count = 0; for (byte b : bytes) { // JAVA doesn't have the concept of unsigned byte; need to & with 255 // to avoid exception of IndexOutOfBoundException when b < 0. count += BIT_COUNT_LOOKUP[0xFF & b]; } return count; } public BitField not() { byte[] invertedBytes = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) { invertedBytes[i] = Integer.valueOf(~bytes[i]).byteValue(); } return new BitField(invertedBytes); } public int sizeInBits() { return bytes.length * 8; } public boolean any() { for (int i = 0; i < bytes.length; i++) { if (bytes[i] != (byte) 0) { return true; } } return false; } }