All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.numbers.arrays.BitIndexUpdatingInterval Maven / Gradle / Ivy

/*
 * 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.commons.numbers.arrays;

/**
 * An {@link UpdatingInterval} backed by a fixed size of bits.
 *
 * 

This is a specialised class to implement a reduced API similar to a * {@link java.util.BitSet}. It uses no bounds range checks and supports only * the methods required to implement the {@link UpdatingInterval} API. * *

An offset is supported to allow the fixed size to cover a range of indices starting * above 0 with the most efficient usage of storage. * *

See the BloomFilter code in Commons Collections for use of long[] data to store * bits. * * @since 1.2 */ final class BitIndexUpdatingInterval implements UpdatingInterval { /** All 64-bits bits set. */ private static final long LONG_MASK = -1L; /** A bit shift to apply to an integer to divided by 64 (2^6). */ private static final int DIVIDE_BY_64 = 6; /** Bit indexes. */ private final long[] data; /** Index offset. */ private final int offset; /** Left bound of the support. */ private int left; /** Right bound of the support. */ private int right; /** * Create an instance to store indices within the range {@code [left, right]}. * The range is not validated. * * @param left Lower bound (inclusive). * @param right Upper bound (inclusive). */ BitIndexUpdatingInterval(int left, int right) { this.offset = left; this.left = left; this.right = right; // Allocate storage to store index==right // Note: This may allow directly writing to index > right if there // is extra capacity. data = new long[getLongIndex(right - offset) + 1]; } /** * Create an instance with the range {@code [left, right]} and reusing the provided * index {@code data}. * * @param data Data. * @param offset Index offset. * @param left Lower bound (inclusive). * @param right Upper bound (inclusive). */ private BitIndexUpdatingInterval(long[] data, int offset, int left, int right) { this.data = data; this.offset = offset; this.left = left; this.right = right; } /** * Gets the filter index for the specified bit index assuming the filter is using * 64-bit longs to store bits starting at index 0. * *

The index is assumed to be positive. For a positive index the result will match * {@code bitIndex / 64}.

* *

The divide is performed using bit shifts. If the input is negative the * behavior is not defined.

* * @param bitIndex the bit index (assumed to be positive) * @return the index of the bit map in an array of bit maps. */ private static int getLongIndex(final int bitIndex) { // An integer divide by 64 is equivalent to a shift of 6 bits if the integer is // positive. // We do not explicitly check for a negative here. Instead we use a // signed shift. Any negative index will produce a negative value // by sign-extension and if used as an index into an array it will throw an // exception. return bitIndex >> DIVIDE_BY_64; } /** * Gets the filter bit mask for the specified bit index assuming the filter is using * 64-bit longs to store bits starting at index 0. The returned value is a * {@code long} with only 1 bit set. * *

The index is assumed to be positive. For a positive index the result will match * {@code 1L << (bitIndex % 64)}.

* *

If the input is negative the behavior is not defined.

* * @param bitIndex the bit index (assumed to be positive) * @return the filter bit */ private static long getLongBit(final int bitIndex) { // Bit shifts only use the first 6 bits. Thus it is not necessary to mask this // using 0x3f (63) or compute bitIndex % 64. // Note: If the index is negative the shift will be (64 - (bitIndex & 0x3f)) and // this will identify an incorrect bit. return 1L << bitIndex; } /** * Sets the bit at the specified index to {@code true}. * *

Warning: This has no range checks. * * @param bitIndex the bit index (assumed to be positive) */ void set(int bitIndex) { // WARNING: No range checks !!! final int index = bitIndex - offset; final int i = getLongIndex(index); final long m = getLongBit(index); data[i] |= m; } /** * Returns the index of the first bit that is set to {@code true} that occurs on or * after the specified starting index. * *

Warning: This has no range checks. It is assumed that {@code left <= k <= right}, * that is there is a set bit on or after {@code k}. * * @param k Index to start checking from (inclusive). * @return the index of the next set bit */ private int nextIndex(int k) { // left <= k <= right final int index = k - offset; int i = getLongIndex(index); // Mask bits after the bit index // mask = 11111000 = -1L << (index % 64) long bits = data[i] & (LONG_MASK << index); for (;;) { if (bits != 0) { //(i+1) i // | index | // | | | // 0 001010000 return i * Long.SIZE + Long.numberOfTrailingZeros(bits) + offset; } // Unsupported: the interval should contain k //if (++i == data.length) // return right + 1 bits = data[++i]; } } /** * Returns the index of the first bit that is set to {@code true} that occurs on or * before the specified starting index. * *

Warning: This has no range checks. It is assumed that {@code left <= k <= right}, * that is there is a set bit on or before {@code k}. * * @param k Index to start checking from (inclusive). * @return the index of the previous set bit */ private int previousIndex(int k) { // left <= k <= right final int index = k - offset; int i = getLongIndex(index); // Mask bits before the bit index // mask = 00011111 = -1L >>> (64 - ((index + 1) % 64)) long bits = data[i] & (LONG_MASK >>> -(index + 1)); for (;;) { if (bits != 0) { //(i+1) i // | index | // | | | // 0 001010000 return (i + 1) * Long.SIZE - Long.numberOfLeadingZeros(bits) - 1 + offset; } // Unsupported: the interval should contain k //if (i == 0) // return left - 1 bits = data[--i]; } } @Override public int left() { return left; } @Override public int right() { return right; } @Override public int updateLeft(int k) { // Assume left < k= < right return left = nextIndex(k); } @Override public int updateRight(int k) { // Assume left <= k < right return right = previousIndex(k); } @Override public UpdatingInterval splitLeft(int ka, int kb) { // Assume left < ka <= kb < right final int lower = left; left = nextIndex(kb + 1); return new BitIndexUpdatingInterval(data, offset, lower, previousIndex(ka - 1)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy