/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.collect; import com.oracle.max.cri.intrinsics.*; import com.sun.max.lang.*; /** * A {@link ByteArrayBitMap} instance is a view on a fixed size bit map encoded in a byte array. The underlying byte * array may contain more than one bit map. In this case, each bit map occupies a range of the byte array that is disjoint from * every other encoded bit map. */ public final class ByteArrayBitMap implements Cloneable { /** * The byte array encoding the bit map. */ private byte[] bytes; /** * The offset in {@link #bytes} at which this bit map's bits start. */ private int offset; /** * The number of bytes in {@link #bytes} reserved for this bit map's bits. */ private int size; public ByteArrayBitMap(int numberOfBits) { size = computeBitMapSize(numberOfBits); bytes = new byte[size]; offset = 0; } public ByteArrayBitMap(byte[] bytes, int offset, int size) { this.bytes = bytes; this.offset = offset; this.size = size; } public ByteArrayBitMap(byte[] bytes) { this(bytes, 0, bytes.length); } /** * Returns a hash code value for this bit set. * * Note that the hash code values change if the set of bits is altered. * * @return a hash code value for this bit set. */ @Override public int hashCode() { long h = 1234; for (int i = size + offset; --i >= 0;) { h ^= bytes[i] * (i + 1); } return (int) ((h >> 32) ^ h); } /** * Compares this object against a given object. The result is {@code true} if and only if {@code other} is not * {@code null} and is a {@code ByteArrayBitMap} object that has exactly the same set of bits set as this bit set. * * @param other the object to compare with * @return {@code true} if the objects are the same; {@code false} otherwise. */ @Override public boolean equals(Object other) { if (other instanceof ByteArrayBitMap) { final ByteArrayBitMap bm = (ByteArrayBitMap) other; if (size == bm.size) { for (int i = 0; i < size; ++i) { if (bytes[offset + i] != bm.bytes[bm.offset + i]) { return false; } } return true; } } return false; } /** * Gets the underlying bytes array storing the bits of this bit map. */ public byte[] bytes() { return bytes; } /** * Gets the index in the underlying bytes array of the first byte used by this bit map. */ public int offset() { return offset; } /** * Gets the number of bytes used by this bit map. */ public int size() { return size; } /** * Gets the number of bits set in this bit map. */ public int cardinality() { int cardinality = 0; final int end = offset + size; for (int i = offset; i < end; ++i) { final byte b = bytes[i]; if (b != 0) { cardinality += Integer.bitCount(b & 0xff); } } return cardinality; } /** * Gets the number of bits that can be encoded in this bit map. */ public int width() { return size * Bytes.WIDTH; } public void setBytes(byte[] bytes) { this.bytes = bytes; } public void setOffset(int offset) { assert offset < bytes.length; this.offset = offset; } public void setIndex(int index) { setOffset(index * size); } public void setSize(int size) { this.size = size; } /** * Adjusts this map to the next logical bit map encoded in the underlying byte array. That is, this bit map's * {@linkplain #offset() offset} is updated to {@code this.size() + this.offset()}. */ public void next() { offset = offset + size; } /** * Sets a bit in this map. * * @throws IndexOutOfBoundsException if {@code bitIndex < 0 || bitIndex >= this.size() * 8} */ public void set(int bitIndex) throws IndexOutOfBoundsException { set(bytes, offset, size, bitIndex); } public static void set(byte[] bytes, int offset, int size, int bitIndex) throws IndexOutOfBoundsException { final int byteIndex = byteIndexFor(offset, size, bitIndex); // set the relevant bit final byte bit = (byte) (1 << (bitIndex % Bytes.WIDTH)); bytes[byteIndex] |= bit; } // calculate the index of the relevant byte in the map private int byteIndexFor(int bitIndex) throws IndexOutOfBoundsException { return byteIndexFor(offset, size, bitIndex); } // calculate the index of the relevant byte in the map private static int byteIndexFor(int offset, int size, int bitIndex) throws IndexOutOfBoundsException { final int relativeByteIndex = UnsignedMath.divide(bitIndex, Bytes.WIDTH); if (relativeByteIndex >= size) { throw new IndexOutOfBoundsException("Bit index: " + bitIndex + ", Width: " + (size * Bytes.WIDTH)); } return offset + relativeByteIndex; } /** * Clears a bit in this map. * * @throws IndexOutOfBoundsException if {@code bitIndex < 0 || bitIndex >= this.size() * 8} */ public void clear(int bitIndex) throws IndexOutOfBoundsException { final int byteIndex = byteIndexFor(bitIndex); // clear the relevant bit final byte bit = (byte) (1 << (bitIndex % Bytes.WIDTH)); bytes[byteIndex] &= ~bit; } /** * Determines if a bit is set in this map. * * @return true if bit {@code bitIndex} is set, false otherwise * @throws IndexOutOfBoundsException if {@code bitIndex < 0 || bitIndex >= this.size() * 8} */ public boolean isSet(int bitIndex) throws IndexOutOfBoundsException { return isSet(bytes, offset, size, bitIndex); } /** * Determines if a bit is set in a given bit map. * * @param bytes a byte array encoding a bit map * @param offset the index in {@code bytes} of the first byte used by the bit map * @param size the number of bytes used by the bit map * @return true if bit {@code bitIndex} is set, false otherwise * @throws IndexOutOfBoundsException if {@code bitIndex < 0 || bitIndex >= this.size() * 8} */ public static boolean isSet(byte[] bytes, int offset, int size, int bitIndex) throws IndexOutOfBoundsException { final int byteIndex = byteIndexFor(offset, size, bitIndex); final byte bit = (byte) (1 << (bitIndex % Bytes.WIDTH)); return (bytes[byteIndex] & bit) != 0; } /** * Returns the index of the first set bit that occurs on or after the specified starting index. If no such bit * exists then -1 is returned. * <p> * To iterate over the set bits in a {@code BitMap}, use the following loop: * * <pre> * for (int i = bitMap.nextSetBit(0); i >= 0; i = bitMap.nextSetBit(i + 1)) { * // operate on index i here * } * </pre> * * @param fromIndex the index to start checking from (inclusive) * @return the index of the next set bit * @throws IndexOutOfBoundsException if the specified index is negative. */ public int nextSetBit(int fromIndex) { if (fromIndex < 0) { throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); } int byteIndex = offset + UnsignedMath.divide(fromIndex, Bytes.WIDTH); final int end = offset + size; if (byteIndex >= end) { return -1; } final int fromBitIndex = fromIndex % Bytes.WIDTH; byte bite = (byte) (bytes[byteIndex] & (0xFF << fromBitIndex)); while (true) { if (bite != 0) { final int result = ((byteIndex - offset) * Bytes.WIDTH) + Bytes.numberOfTrailingZeros(bite); return result; } if (++byteIndex == end) { return -1; } bite = bytes[byteIndex]; } } @Override public String toString() { final int numBits = size * Bytes.WIDTH; final StringBuilder buffer = new StringBuilder(8 * numBits + 2); String separator = ""; buffer.append('['); for (int i = 0; i < numBits; i++) { if (isSet(i)) { buffer.append(separator); separator = ", "; buffer.append(i); } } buffer.append(']'); return buffer.toString(); } /** * Computes the minimum number of bytes required to encode a bit map with a given number of bits. */ public static int computeBitMapSize(int numberOfBits) { return UnsignedMath.divide(Ints.roundUnsignedUpByPowerOfTwo(numberOfBits, Bytes.WIDTH), Bytes.WIDTH); } }