1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.numbers.arrays; 18 19 /** 20 * An {@link UpdatingInterval} backed by a fixed size of bits. 21 * 22 * <p>This is a specialised class to implement a reduced API similar to a 23 * {@link java.util.BitSet}. It uses no bounds range checks and supports only 24 * the methods required to implement the {@link UpdatingInterval} API. 25 * 26 * <p>An offset is supported to allow the fixed size to cover a range of indices starting 27 * above 0 with the most efficient usage of storage. 28 * 29 * <p>See the BloomFilter code in Commons Collections for use of long[] data to store 30 * bits. 31 * 32 * @since 1.2 33 */ 34 final class BitIndexUpdatingInterval implements UpdatingInterval { 35 /** All 64-bits bits set. */ 36 private static final long LONG_MASK = -1L; 37 /** A bit shift to apply to an integer to divided by 64 (2^6). */ 38 private static final int DIVIDE_BY_64 = 6; 39 40 /** Bit indexes. */ 41 private final long[] data; 42 43 /** Index offset. */ 44 private final int offset; 45 /** Left bound of the support. */ 46 private int left; 47 /** Right bound of the support. */ 48 private int right; 49 50 /** 51 * Create an instance to store indices within the range {@code [left, right]}. 52 * The range is not validated. 53 * 54 * @param left Lower bound (inclusive). 55 * @param right Upper bound (inclusive). 56 */ 57 BitIndexUpdatingInterval(int left, int right) { 58 this.offset = left; 59 this.left = left; 60 this.right = right; 61 // Allocate storage to store index==right 62 // Note: This may allow directly writing to index > right if there 63 // is extra capacity. 64 data = new long[getLongIndex(right - offset) + 1]; 65 } 66 67 /** 68 * Create an instance with the range {@code [left, right]} and reusing the provided 69 * index {@code data}. 70 * 71 * @param data Data. 72 * @param offset Index offset. 73 * @param left Lower bound (inclusive). 74 * @param right Upper bound (inclusive). 75 */ 76 private BitIndexUpdatingInterval(long[] data, int offset, int left, int right) { 77 this.data = data; 78 this.offset = offset; 79 this.left = left; 80 this.right = right; 81 } 82 83 /** 84 * Gets the filter index for the specified bit index assuming the filter is using 85 * 64-bit longs to store bits starting at index 0. 86 * 87 * <p>The index is assumed to be positive. For a positive index the result will match 88 * {@code bitIndex / 64}.</p> 89 * 90 * <p><em>The divide is performed using bit shifts. If the input is negative the 91 * behavior is not defined.</em></p> 92 * 93 * @param bitIndex the bit index (assumed to be positive) 94 * @return the index of the bit map in an array of bit maps. 95 */ 96 private static int getLongIndex(final int bitIndex) { 97 // An integer divide by 64 is equivalent to a shift of 6 bits if the integer is 98 // positive. 99 // We do not explicitly check for a negative here. Instead we use a 100 // signed shift. Any negative index will produce a negative value 101 // by sign-extension and if used as an index into an array it will throw an 102 // exception. 103 return bitIndex >> DIVIDE_BY_64; 104 } 105 106 /** 107 * Gets the filter bit mask for the specified bit index assuming the filter is using 108 * 64-bit longs to store bits starting at index 0. The returned value is a 109 * {@code long} with only 1 bit set. 110 * 111 * <p>The index is assumed to be positive. For a positive index the result will match 112 * {@code 1L << (bitIndex % 64)}.</p> 113 * 114 * <p><em>If the input is negative the behavior is not defined.</em></p> 115 * 116 * @param bitIndex the bit index (assumed to be positive) 117 * @return the filter bit 118 */ 119 private static long getLongBit(final int bitIndex) { 120 // Bit shifts only use the first 6 bits. Thus it is not necessary to mask this 121 // using 0x3f (63) or compute bitIndex % 64. 122 // Note: If the index is negative the shift will be (64 - (bitIndex & 0x3f)) and 123 // this will identify an incorrect bit. 124 return 1L << bitIndex; 125 } 126 127 /** 128 * Sets the bit at the specified index to {@code true}. 129 * 130 * <p>Warning: This has no range checks. 131 * 132 * @param bitIndex the bit index (assumed to be positive) 133 */ 134 void set(int bitIndex) { 135 // WARNING: No range checks !!! 136 final int index = bitIndex - offset; 137 final int i = getLongIndex(index); 138 final long m = getLongBit(index); 139 data[i] |= m; 140 } 141 142 /** 143 * Returns the index of the first bit that is set to {@code true} that occurs on or 144 * after the specified starting index. 145 * 146 * <p>Warning: This has no range checks. It is assumed that {@code left <= k <= right}, 147 * that is there is a set bit on or after {@code k}. 148 * 149 * @param k Index to start checking from (inclusive). 150 * @return the index of the next set bit 151 */ 152 private int nextIndex(int k) { 153 // left <= k <= right 154 155 final int index = k - offset; 156 int i = getLongIndex(index); 157 158 // Mask bits after the bit index 159 // mask = 11111000 = -1L << (index % 64) 160 long bits = data[i] & (LONG_MASK << index); 161 for (;;) { 162 if (bits != 0) { 163 //(i+1) i 164 // | index | 165 // | | | 166 // 0 001010000 167 return i * Long.SIZE + Long.numberOfTrailingZeros(bits) + offset; 168 } 169 // Unsupported: the interval should contain k 170 //if (++i == data.length) 171 // return right + 1 172 bits = data[++i]; 173 } 174 } 175 176 /** 177 * Returns the index of the first bit that is set to {@code true} that occurs on or 178 * before the specified starting index. 179 * 180 * <p>Warning: This has no range checks. It is assumed that {@code left <= k <= right}, 181 * that is there is a set bit on or before {@code k}. 182 * 183 * @param k Index to start checking from (inclusive). 184 * @return the index of the previous set bit 185 */ 186 private int previousIndex(int k) { 187 // left <= k <= right 188 189 final int index = k - offset; 190 int i = getLongIndex(index); 191 192 // Mask bits before the bit index 193 // mask = 00011111 = -1L >>> (64 - ((index + 1) % 64)) 194 long bits = data[i] & (LONG_MASK >>> -(index + 1)); 195 for (;;) { 196 if (bits != 0) { 197 //(i+1) i 198 // | index | 199 // | | | 200 // 0 001010000 201 return (i + 1) * Long.SIZE - Long.numberOfLeadingZeros(bits) - 1 + offset; 202 } 203 // Unsupported: the interval should contain k 204 //if (i == 0) 205 // return left - 1 206 bits = data[--i]; 207 } 208 } 209 210 @Override 211 public int left() { 212 return left; 213 } 214 215 @Override 216 public int right() { 217 return right; 218 } 219 220 @Override 221 public int updateLeft(int k) { 222 // Assume left < k= < right 223 return left = nextIndex(k); 224 } 225 226 @Override 227 public int updateRight(int k) { 228 // Assume left <= k < right 229 return right = previousIndex(k); 230 } 231 232 @Override 233 public UpdatingInterval splitLeft(int ka, int kb) { 234 // Assume left < ka <= kb < right 235 final int lower = left; 236 left = nextIndex(kb + 1); 237 return new BitIndexUpdatingInterval(data, offset, lower, previousIndex(ka - 1)); 238 } 239 }