blob: 6c62b1ef518b8432083830d2a4b556a4f68f295e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.solr.util.hll;
import org.apache.solr.util.LongIterator;
/**
* A vector (array) of bits that is accessed in units ("registers") of <code>width</code>
* bits which are stored as 64bit "words" (<code>long</code>s). In this context
* a register is at most 64bits.
*/
class BitVector implements Cloneable {
// NOTE: in this context, a word is 64bits
// rather than doing division to determine how a bit index fits into 64bit
// words (i.e. longs), bit shifting is used
private static final int LOG2_BITS_PER_WORD = 6/*=>64bits*/;
private static final int BITS_PER_WORD = 1 << LOG2_BITS_PER_WORD;
private static final int BITS_PER_WORD_MASK = BITS_PER_WORD - 1;
// ditto from above but for bytes (for output)
private static final int LOG2_BITS_PER_BYTE = 3/*=>8bits*/;
public static final int BITS_PER_BYTE = 1 << LOG2_BITS_PER_BYTE;
// ========================================================================
public static final int BYTES_PER_WORD = 8/*8 bytes in a long*/;
// ************************************************************************
// 64bit words
private final long[] words;
public final long[] words() { return words; }
public final int wordCount() { return words.length; }
public final int byteCount() { return wordCount() * BYTES_PER_WORD; }
// the width of a register in bits (this cannot be more than 64 (the word size))
private final int registerWidth;
public final int registerWidth() { return registerWidth; }
private final long count;
// ------------------------------------------------------------------------
private final long registerMask;
// ========================================================================
/**
* @param width the width of each register. This cannot be negative or
* zero or greater than 63 (the signed word size).
* @param count the number of registers. This cannot be negative or zero
*/
public BitVector(final int width, final long count) {
// ceil((width * count)/BITS_PER_WORD)
this.words = new long[(int)(((width * count) + BITS_PER_WORD_MASK) >>> LOG2_BITS_PER_WORD)];
this.registerWidth = width;
this.count = count;
this.registerMask = (1L << width) - 1;
}
// ========================================================================
/**
* @param registerIndex the index of the register whose value is to be
* retrieved. This cannot be negative.
* @return the value at the specified register index
* @see #setRegister(long, long)
* @see #setMaxRegister(long, long)
*/
// NOTE: if this changes then setMaxRegister() must change
public long getRegister(final long registerIndex) {
final long bitIndex = registerIndex * registerWidth;
final int firstWordIndex = (int)(bitIndex >>> LOG2_BITS_PER_WORD)/*aka (bitIndex / BITS_PER_WORD)*/;
final int secondWordIndex = (int)((bitIndex + registerWidth - 1) >>> LOG2_BITS_PER_WORD)/*see above*/;
final int bitRemainder = (int)(bitIndex & BITS_PER_WORD_MASK)/*aka (bitIndex % BITS_PER_WORD)*/;
if(firstWordIndex == secondWordIndex)
return ((words[firstWordIndex] >>> bitRemainder) & registerMask);
/* else -- register spans words */
return (words[firstWordIndex] >>> bitRemainder)/*no need to mask since at top of word*/
| (words[secondWordIndex] << (BITS_PER_WORD - bitRemainder)) & registerMask;
}
/**
* @param registerIndex the index of the register whose value is to be set.
* This cannot be negative
* @param value the value to set in the register
* @see #getRegister(long)
* @see #setMaxRegister(long, long)
*/
// NOTE: if this changes then setMaxRegister() must change
public void setRegister(final long registerIndex, final long value) {
final long bitIndex = registerIndex * registerWidth;
final int firstWordIndex = (int)(bitIndex >>> LOG2_BITS_PER_WORD)/*aka (bitIndex / BITS_PER_WORD)*/;
final int secondWordIndex = (int)((bitIndex + registerWidth - 1) >>> LOG2_BITS_PER_WORD)/*see above*/;
final int bitRemainder = (int)(bitIndex & BITS_PER_WORD_MASK)/*aka (bitIndex % BITS_PER_WORD)*/;
final long words[] = this.words/*for convenience/performance*/;
if(firstWordIndex == secondWordIndex) {
// clear then set
words[firstWordIndex] &= ~(registerMask << bitRemainder);
words[firstWordIndex] |= (value << bitRemainder);
} else {/*register spans words*/
// clear then set each partial word
words[firstWordIndex] &= (1L << bitRemainder) - 1;
words[firstWordIndex] |= (value << bitRemainder);
words[secondWordIndex] &= ~(registerMask >>> (BITS_PER_WORD - bitRemainder));
words[secondWordIndex] |= (value >>> (BITS_PER_WORD - bitRemainder));
}
}
// ------------------------------------------------------------------------
/**
* @return a <code>LongIterator</code> for iterating starting at the register
* with index zero. This will never be <code>null</code>.
*/
public LongIterator registerIterator() {
return new LongIterator() {
final int registerWidth = BitVector.this.registerWidth;
final long[] words = BitVector.this.words;
final long registerMask = BitVector.this.registerMask;
// register setup
long registerIndex = 0;
int wordIndex = 0;
int remainingWordBits = BITS_PER_WORD;
long word = words[wordIndex];
@Override public long next() {
long register;
if(remainingWordBits >= registerWidth) {
register = word & registerMask;
// shift to the next register
word >>>= registerWidth;
remainingWordBits -= registerWidth;
} else { /*insufficient bits remaining in current word*/
wordIndex++/*move to the next word*/;
register = (word | (words[wordIndex] << remainingWordBits)) & registerMask;
// shift to the next partial register (word)
word = words[wordIndex] >>> (registerWidth - remainingWordBits);
remainingWordBits += BITS_PER_WORD - registerWidth;
}
registerIndex++;
return register;
}
@Override public boolean hasNext() {
return registerIndex < count;
}
};
}
// ------------------------------------------------------------------------
// composite accessors
/**
* Sets the value of the specified index register if and only if the specified
* value is greater than the current value in the register. This is equivalent
* to but much more performant than:<p/>
*
* <pre>vector.setRegister(index, Math.max(vector.getRegister(index), value));</pre>
*
* @param registerIndex the index of the register whose value is to be set.
* This cannot be negative
* @param value the value to set in the register if and only if this value
* is greater than the current value in the register
* @return <code>true</code> if and only if the specified value is greater
* than or equal to the current register value. <code>false</code>
* otherwise.
* @see #getRegister(long)
* @see #setRegister(long, long)
* @see java.lang.Math#max(long, long)
*/
// NOTE: if this changes then setRegister() must change
public boolean setMaxRegister(final long registerIndex, final long value) {
final long bitIndex = registerIndex * registerWidth;
final int firstWordIndex = (int)(bitIndex >>> LOG2_BITS_PER_WORD)/*aka (bitIndex / BITS_PER_WORD)*/;
final int secondWordIndex = (int)((bitIndex + registerWidth - 1) >>> LOG2_BITS_PER_WORD)/*see above*/;
final int bitRemainder = (int)(bitIndex & BITS_PER_WORD_MASK)/*aka (bitIndex % BITS_PER_WORD)*/;
// NOTE: matches getRegister()
final long registerValue;
final long words[] = this.words/*for convenience/performance*/;
if(firstWordIndex == secondWordIndex)
registerValue = ((words[firstWordIndex] >>> bitRemainder) & registerMask);
else /*register spans words*/
registerValue = (words[firstWordIndex] >>> bitRemainder)/*no need to mask since at top of word*/
| (words[secondWordIndex] << (BITS_PER_WORD - bitRemainder)) & registerMask;
// determine which is the larger and update as necessary
if(value > registerValue) {
// NOTE: matches setRegister()
if(firstWordIndex == secondWordIndex) {
// clear then set
words[firstWordIndex] &= ~(registerMask << bitRemainder);
words[firstWordIndex] |= (value << bitRemainder);
} else {/*register spans words*/
// clear then set each partial word
words[firstWordIndex] &= (1L << bitRemainder) - 1;
words[firstWordIndex] |= (value << bitRemainder);
words[secondWordIndex] &= ~(registerMask >>> (BITS_PER_WORD - bitRemainder));
words[secondWordIndex] |= (value >>> (BITS_PER_WORD - bitRemainder));
}
} /* else -- the register value is greater (or equal) so nothing needs to be done */
return (value >= registerValue);
}
// ========================================================================
/**
* Fills this bit vector with the specified bit value. This can be used to
* clear the vector by specifying <code>0</code>.
*
* @param value the value to set all bits to (only the lowest bit is used)
*/
public void fill(final long value) {
for(long i=0; i<count; i++) {
setRegister(i, value);
}
}
// ------------------------------------------------------------------------
/**
* Serializes the registers of the vector using the specified serializer.
*
* @param serializer the serializer to use. This cannot be <code>null</code>.
*/
public void getRegisterContents(final IWordSerializer serializer) {
for(final LongIterator iter = registerIterator(); iter.hasNext();) {
serializer.writeWord(iter.next());
}
}
/**
* Creates a deep copy of this vector.
*
* @see java.lang.Object#clone()
*/
@Override
public BitVector clone() {
final BitVector copy = new BitVector(registerWidth, count);
System.arraycopy(words, 0, copy.words, 0, words.length);
return copy;
}
}