BitIndexUpdatingInterval.java
- /*
- * 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>The index is assumed to be positive. For a positive index the result will match
- * {@code bitIndex / 64}.</p>
- *
- * <p><em>The divide is performed using bit shifts. If the input is negative the
- * behavior is not defined.</em></p>
- *
- * @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.
- *
- * <p>The index is assumed to be positive. For a positive index the result will match
- * {@code 1L << (bitIndex % 64)}.</p>
- *
- * <p><em>If the input is negative the behavior is not defined.</em></p>
- *
- * @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}.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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));
- }
- }