| // 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; | |
| /** | |
| * 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); | |
| } | |
| /** | |
| * 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); | |
| } | |
| } | |
| /** | |
| * Clears a bit at the given index | |
| * | |
| * @param index bit index | |
| */ | |
| public void clearBit(int index) { | |
| setBit(index, false); | |
| } | |
| /** | |
| * 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; | |
| byte[] 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) { | |
| if (obj == this) { | |
| return true; | |
| } | |
| if (!(obj instanceof BitField)) { | |
| return false; | |
| } | |
| return Arrays.equals(bytes, ((BitField) obj).bytes); | |
| } | |
| /** | |
| * 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 Arrays.hashCode(bytes); | |
| } | |
| 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; | |
| } | |
| } |