Mercurial > hg > truffle
view graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiBitMap.java @ 4378:0c632ed89063
Removed scripting proxies (and therefore support for running igv on java 5 or below).
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Fri, 27 Jan 2012 23:31:28 +0100 |
parents | aaac4894175c |
children |
line wrap: on
line source
/* * Copyright (c) 2009, 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.oracle.max.cri.ci; import java.io.*; import java.util.*; /** * Implements a bitmap that stores a single bit for a range of integers (0-n). */ public final class CiBitMap implements Serializable { private static final long serialVersionUID = 2471441272241401105L; private static final int ADDRESS_BITS_PER_WORD = 6; private static final int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD; private static final int BIT_INDEX_MASK = BITS_PER_WORD - 1; public static final int DEFAULT_LENGTH = BITS_PER_WORD; public static int roundUpLength(int length) { return ((length + (BITS_PER_WORD - 1)) >> ADDRESS_BITS_PER_WORD) << ADDRESS_BITS_PER_WORD; } private int size; private long low; private long[] extra; /** * Constructs a new bit map with the {@linkplain #DEFAULT_LENGTH default length}. */ public CiBitMap() { this(DEFAULT_LENGTH); } /** * Constructs a new bit map from a byte array encoded bit map. * * @param bitmap the bit map to convert */ public CiBitMap(byte[] bitmap) { this(bitmap, 0, bitmap.length); } /** * Constructs a copy of the given bit map. * * @param bitmap the bit map to copy. */ public CiBitMap(CiBitMap bitmap) { this.size = bitmap.size; this.low = bitmap.low; if (bitmap.extra != null) { this.extra = Arrays.copyOf(bitmap.extra, bitmap.extra.length); } } /** * Constructs a new bit map from a byte array encoded bit map. * * @param arr the byte array containing the bit map to convert * @param off the byte index in {@code arr} at which the bit map starts * @param numberOfBytes the number of bytes worth of bits to copy from {@code arr} */ public CiBitMap(byte[] arr, int off, int numberOfBytes) { this(numberOfBytes * 8); int byteIndex = off; int end = off + numberOfBytes; assert end <= arr.length; while (byteIndex < end && (byteIndex - off) < 8) { long bite = (long) arr[byteIndex] & 0xff; low |= bite << ((byteIndex - off) * 8); byteIndex++; } if (byteIndex < end) { assert (byteIndex - off) == 8; int remBytes = end - byteIndex; int remWords = (remBytes + 7) / 8; for (int word = 0; word < remWords; word++) { long w = 0L; for (int i = 0; i < 8 && byteIndex < end; i++) { long bite = (long) arr[byteIndex] & 0xff; w |= bite << (i * 8); byteIndex++; } extra[word] = w; } } } /** * Converts a {@code long} to a {@link CiBitMap}. */ public static CiBitMap fromLong(long bitmap) { CiBitMap bm = new CiBitMap(64); bm.low = bitmap; return bm; } /** * Constructs a new bit map with the specified length. * * @param length the length of the bitmap */ public CiBitMap(int length) { assert length >= 0; this.size = length; if (length > BITS_PER_WORD) { extra = new long[length >> ADDRESS_BITS_PER_WORD]; } } /** * Sets the bit at the specified index. * * @param i the index of the bit to set */ public void set(int i) { if (checkIndex(i) < BITS_PER_WORD) { low |= 1L << i; } else { int pos = wordIndex(i); int index = bitInWord(i); extra[pos] |= 1L << index; } } /** * Grows this bitmap to a new size, appending necessary zero bits. * * @param newLength the new length of the bitmap */ public void grow(int newLength) { if (newLength > size) { // grow this bitmap to the new length int newSize = newLength >> ADDRESS_BITS_PER_WORD; if (newLength > 0) { if (extra == null) { // extra just needs to be allocated now extra = new long[newSize]; } else { if (extra.length < newSize) { // extra needs to be copied long[] newExtra = new long[newSize]; for (int i = 0; i < extra.length; i++) { newExtra[i] = extra[i]; } extra = newExtra; } else { // nothing to do, extra is already the right size } } } size = newLength; } } private static int bitInWord(int i) { return i & BIT_INDEX_MASK; } private static int wordIndex(int i) { return (i >> ADDRESS_BITS_PER_WORD) - 1; } /** * Clears the bit at the specified index. * @param i the index of the bit to clear */ public void clear(int i) { if (checkIndex(i) < BITS_PER_WORD) { low &= ~(1L << i); } else { int pos = wordIndex(i); int index = bitInWord(i); extra[pos] &= ~(1L << index); } } /** * Sets all the bits in this bitmap. */ public void setAll() { low = -1; if (extra != null) { for (int i = 0; i < extra.length; i++) { extra[i] = -1; } } } /** * Clears all the bits in this bitmap. */ public void clearAll() { low = 0; if (extra != null) { for (int i = 0; i < extra.length; i++) { extra[i] = 0; } } } /** * Gets the value of the bit at the specified index. * * @param i the index of the bit to get * @return {@code true} if the bit at the specified position is {@code 1} */ public boolean get(int i) { if (checkIndex(i) < BITS_PER_WORD) { return ((low >> i) & 1) != 0; } int pos = wordIndex(i); int index = bitInWord(i); long bits = extra[pos]; return ((bits >> index) & 1) != 0; } /** * Gets the value of the bit at the specified index, returning {@code false} if the * bitmap does not cover the specified index. * * @param i the index of the bit to get * @return {@code true} if the bit at the specified position is {@code 1} */ public boolean getDefault(int i) { if (i < 0 || i >= size) { return false; } if (i < BITS_PER_WORD) { return ((low >> i) & 1) != 0; } int pos = wordIndex(i); int index = bitInWord(i); long bits = extra[pos]; return ((bits >> index) & 1) != 0; } /** * Performs the union operation on this bitmap with the specified bitmap. That is, all bits set in either of the two * bitmaps will be set in this bitmap following this operation. * * @param other the other bitmap for the union operation */ public void setUnion(CiBitMap other) { low |= other.low; if (extra != null && other.extra != null) { for (int i = 0; i < extra.length && i < other.extra.length; i++) { extra[i] |= other.extra[i]; } } } /** * Performs the union operation on this bitmap with the specified bitmap. That is, a bit is set in this * bitmap if and only if it is set in both this bitmap and the specified bitmap. * * @param other the other bitmap for this operation * @return {@code true} if any bits were cleared as a result of this operation */ public boolean setIntersect(CiBitMap other) { boolean same = true; long intx = low & other.low; if (low != intx) { same = false; low = intx; } long[] oxtra = other.extra; if (extra != null && oxtra != null) { for (int i = 0; i < extra.length; i++) { long a = extra[i]; if (i < oxtra.length) { // zero bits out of this map long ax = a & oxtra[i]; if (a != ax) { same = false; extra[i] = ax; } } else { // this bitmap is larger than the specified bitmap; zero remaining bits if (a != 0) { same = false; extra[i] = 0; } } } } return !same; } /** * Gets the number of addressable bits in this bitmap. * * @return the size of this bitmap */ public int size() { return size; } private int checkIndex(int i) { if (i < 0 || i >= size) { throw new IndexOutOfBoundsException(); } return i; } public void setFrom(CiBitMap other) { assert this.size == other.size : "must have same size"; low = other.low; if (extra != null) { for (int i = 0; i < extra.length; i++) { extra[i] = other.extra[i]; } } } public void setDifference(CiBitMap other) { assert this.size == other.size : "must have same size"; low &= ~other.low; if (extra != null) { for (int i = 0; i < extra.length; i++) { extra[i] &= ~other.extra[i]; } } } public boolean isSame(CiBitMap other) { if (this.size != other.size || this.low != other.low) { return false; } if (extra != null) { for (int i = 0; i < extra.length; i++) { if (extra[i] != other.extra[i]) { return false; } } } return true; } /** * Returns the index of the first set bit that occurs on or after a specified start 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 lowest set bit between {@code [fromIndex .. size())} or -1 if there is no set bit in this range * @throws IndexOutOfBoundsException if the specified index is negative. */ public int nextSetBit(int fromIndex) { return nextSetBit(fromIndex, size()); } /** * Returns the index of the first set bit that occurs on or after a specified start index * and before a specified end 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, bitMap.size()); i >= 0; i = bitMap.nextSetBit(i + 1, bitMap.size())) { * // operate on index i here * } * </pre> * * @param fromIndex the index to start checking from (inclusive) * @param toIndex the index at which to stop checking (exclusive) * @return the index of the lowest set bit between {@code [fromIndex .. toIndex)} or -1 if there is no set bit in this range * @throws IndexOutOfBoundsException if the specified index is negative. */ public int nextSetBit(int fromIndex, int toIndex) { assert fromIndex <= size() : "index out of bounds"; assert toIndex <= size() : "index out of bounds"; assert fromIndex <= toIndex : "fromIndex > toIndex"; if (fromIndex == toIndex) { return -1; } int fromWordIndex = wordIndex(fromIndex); int toWordIndex = wordIndex(toIndex - 1) + 1; int resultIndex = fromIndex; // check bits including and to the left_ of offset's position int pos = bitInWord(resultIndex); long res = map(fromWordIndex) >> pos; if (res != 0) { resultIndex += Long.numberOfTrailingZeros(res); assert resultIndex >= fromIndex && resultIndex < toIndex : "just checking"; if (resultIndex < toIndex) { return resultIndex; } return -1; } // skip over all word length 0-bit runs for (fromWordIndex++; fromWordIndex < toWordIndex; fromWordIndex++) { res = map(fromWordIndex); if (res != 0) { // found a 1, return the offset resultIndex = bitIndex(fromWordIndex) + Long.numberOfTrailingZeros(res); assert resultIndex >= fromIndex : "just checking"; if (resultIndex < toIndex) { return resultIndex; } return -1; } } return -1; } private static int bitIndex(int index) { return (index + 1) << ADDRESS_BITS_PER_WORD; } private long map(int index) { if (index == -1) { return low; } return extra[index]; } private static boolean allZeros(int start, long[] arr) { for (int i = start; i < arr.length; i++) { if (arr[i] != 0) { return false; } } return true; } /** * Compares this object against the specified object. * The result is {@code true} if and only if {@code obj} is * not {@code null} and is a {@code CiBitMap} object that has * exactly the same set of bits set to {@code true} as this bit * set. * * @param obj the object to compare with. */ @Override public boolean equals(Object obj) { if (obj instanceof CiBitMap) { CiBitMap bm = (CiBitMap) obj; if (bm.low == low) { if (bm.extra == null) { if (extra == null) { // Common case return true; } return allZeros(0, extra); } if (extra == null) { return allZeros(0, bm.extra); } // both 'extra' array non null: int i = 0; int length = Math.min(extra.length, bm.extra.length); while (i < length) { if (extra[i] != bm.extra[i]) { return false; } i++; } if (extra.length > bm.extra.length) { return allZeros(length, extra); } if (extra.length < bm.extra.length) { return allZeros(length, bm.extra); } return true; } } return false; } @Override public int hashCode() { return (int) low ^ size; } /** * Returns a string representation of this bit map * that is the same as the string returned by {@link BitSet#toString()} * for a bit set with the same bits set as this bit map. */ @Override public String toString() { StringBuilder sb = new StringBuilder(size * 2); sb.append('{'); int bit = nextSetBit(0); if (bit != -1) { sb.append(bit); for (bit = nextSetBit(bit + 1); bit >= 0; bit = nextSetBit(bit + 1)) { sb.append(", ").append(bit); } } sb.append('}'); return sb.toString(); } public static int highestOneBitIndex(long value) { int bit = Long.numberOfTrailingZeros(Long.highestOneBit(value)); if (bit == 64) { return -1; } return bit; } /** * Returns the number of bits set to {@code true} in this bit map. */ public int cardinality() { int sum = Long.bitCount(low); if (extra != null) { for (long word : extra) { sum += Long.bitCount(word); } } return sum; } /** * Returns the "logical size" of this bit map: the index of * the highest set bit in the bit map plus one. Returns zero * if the bit map contains no set bits. * * @return the logical size of this bit map */ public int length() { if (extra != null) { for (int i = extra.length - 1; i >= 0; i--) { if (extra[i] != 0) { return (highestOneBitIndex(extra[i]) + ((i + 1) * 64)) + 1; } } } return highestOneBitIndex(low) + 1; } /** * Returns a string representation of this bit map with every set bit represented as {@code '1'} * and every unset bit represented as {@code '0'}. The first character in the returned string represents * bit 0 in this bit map. * * @param length the number of bits represented in the returned string. If {@code length < 0 || length > size()}, * then the value of {@link #length()} is used. */ public String toBinaryString() { int length = length(); if (length == 0) { return ""; } StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; ++i) { sb.append(get(i) ? '1' : '0'); } return sb.toString(); } static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Returns a string representation of this bit map in hex. */ public String toHexString() { if (size == 0) { return ""; } int hexSize = CiUtil.align(this.size, 4); StringBuilder sb = new StringBuilder(hexSize / 4); for (int i = 0; i < hexSize; i += 4) { int nibble = get(i) ? 1 : 0; if (get(i + 1)) { nibble |= 2; } if (get(i + 2)) { nibble |= 4; } if (get(i + 3)) { nibble |= 8; } sb.append(hexDigits[nibble]); } return sb.toString(); } public CiBitMap copy() { CiBitMap n = new CiBitMap(BITS_PER_WORD); n.low = low; if (extra != null) { n.extra = Arrays.copyOf(extra, extra.length); } n.size = size; return n; } /** * Copies this bit map into a given byte array. * * @param arr the destination * @param off the byte index in {@code arr} at which to start writing * @param numberOfBytes the number of bytes worth of bits to copy from this bit map. * The number of bits copied is {@code numberOfBytes * 8}. If {@code numberOfBytes} * is -1, then {@code ((size() + 7) / 8)} is used instead. * @return the number of bytes written to {@code arr} */ public int copyTo(byte[] arr, int off, int numberOfBytes) { for (int i = 0; i < numberOfBytes; ++i) { long word = low; int byteInWord; if (i >= 8) { int wordIndex = (i - 8) / 8; word = extra[wordIndex]; byteInWord = i & 0x7; } else { byteInWord = i; } assert byteInWord < 8; byte b = (byte) (word >> (byteInWord * 8)); arr[off + i] = b; } return numberOfBytes; } /** * Converts this bit map to a byte array. The length of the returned * byte array is {@code ((size() + 7) / 8)}. */ public byte[] toByteArray() { byte[] arr = new byte[(size + 7) / 8]; copyTo(arr, 0, arr.length); return arr; } /** * Converts this bit map to a long. * * @throws IllegalArgumentException if {@code (size() > 64)} */ public long toLong() { if (size > 64) { throw new IllegalArgumentException("bit map of size " + size + " cannot be converted to long"); } return low; } }