1 package org.apache.lucene.util.packed;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import org.apache.lucene.store.DataInput;
21 import org.apache.lucene.util.RamUsageEstimator;
23 import java.io.IOException;
24 import java.util.Arrays;
27 * Space optimized random access capable array of values with a fixed number of
28 * bits. For 32 bits/value and less, performance on 32 bit machines is not
29 * optimal. Consider using {@link Packed32} for such a setup.
31 * The implementation strives to avoid conditionals and expensive operations,
32 * sacrificing code clarity to achieve better performance.
35 class Packed64 extends PackedInts.ReaderImpl implements PackedInts.Mutable {
36 static final int BLOCK_SIZE = 64; // 32 = int, 64 = long
37 static final int BLOCK_BITS = 6; // The #bits representing BLOCK_SIZE
38 static final int MOD_MASK = BLOCK_SIZE - 1; // x % BLOCK_SIZE
40 private static final int ENTRY_SIZE = BLOCK_SIZE + 1;
41 private static final int FAC_BITPOS = 3;
44 * In order to make an efficient value-getter, conditionals should be
45 * avoided. A value can be positioned inside of a block, requiring shifting
46 * left or right or it can span two blocks, requiring a left-shift on the
47 * first block and a right-shift on the right block.
49 * By always shifting the first block both left and right, we get exactly
50 * the right bits. By always shifting the second block right and applying
51 * a mask, we get the right bits there. After that, we | the two bitsets.
53 private static final int[][] SHIFTS =
54 new int[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
55 //new int[BLOCK_SIZE+1][BLOCK_SIZE][BLOCK_SIZE+1];
56 private static final long[][] MASKS = new long[ENTRY_SIZE][ENTRY_SIZE];
58 static { // Generate shifts
59 for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
60 for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
61 int[] currentShifts = SHIFTS[elementBits];
62 int base = bitPos * FAC_BITPOS;
63 currentShifts[base ] = bitPos;
64 currentShifts[base + 1] = BLOCK_SIZE - elementBits;
65 if (bitPos <= BLOCK_SIZE - elementBits) { // Single block
66 currentShifts[base + 2] = 0;
67 MASKS[elementBits][bitPos] = 0;
68 } else { // Two blocks
69 int rBits = elementBits - (BLOCK_SIZE - bitPos);
70 currentShifts[base + 2] = BLOCK_SIZE - rBits;
71 MASKS[elementBits][bitPos] = ~(~0L << rBits);
78 * The setter requires more masking than the getter.
80 private static final long[][] WRITE_MASKS =
81 new long[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
83 for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
84 long elementPosMask = ~(~0L << elementBits);
85 int[] currentShifts = SHIFTS[elementBits];
86 long[] currentMasks = WRITE_MASKS[elementBits];
87 for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
88 int base = bitPos * FAC_BITPOS;
89 currentMasks[base ] =~((elementPosMask
90 << currentShifts[base + 1])
91 >>> currentShifts[base]);
92 if (bitPos <= BLOCK_SIZE - elementBits) { // Second block not used
93 currentMasks[base+1] = ~0; // Keep all bits
94 currentMasks[base+2] = 0; // Or with 0
96 currentMasks[base+1] = ~(elementPosMask
97 << currentShifts[base + 2]);
98 currentMasks[base+2] = currentShifts[base + 2] == 0 ? 0 : ~0;
105 private long[] blocks;
107 // Cached calculations
108 private int maxPos; // blocks.length * BLOCK_SIZE / elementBits - 1
109 private int[] shifts; // The shifts for the current elementBits
110 private long[] readMasks;
111 private long[] writeMasks;
114 * Creates an array with the internal structures adjusted for the given
115 * limits and initialized to 0.
116 * @param valueCount the number of elements.
117 * @param bitsPerValue the number of bits available for any given value.
119 public Packed64(int valueCount, int bitsPerValue) {
120 // TODO: Test for edge-cases (2^31 values, 63 bitsPerValue)
121 // +2 due to the avoid-conditionals-trick. The last entry is always 0
122 this(new long[(int)((long)valueCount * bitsPerValue / BLOCK_SIZE + 2)],
123 valueCount, bitsPerValue);
128 * Creates an array backed by the given blocks.
130 * Note: The blocks are used directly, so changes to the given block will
131 * affect the Packed32-structure.
132 * @param blocks used as the internal backing array. Not that the last
133 * element cannot be addressed directly.
134 * @param valueCount the number of values.
135 * @param bitsPerValue the number of bits available for any given value.
137 public Packed64(long[] blocks, int valueCount, int bitsPerValue) {
138 super(valueCount, bitsPerValue);
139 this.blocks = blocks;
144 * Creates an array with content retrieved from the given DataInput.
145 * @param in a DataInput, positioned at the start of Packed64-content.
146 * @param valueCount the number of elements.
147 * @param bitsPerValue the number of bits available for any given value.
148 * @throws java.io.IOException if the values for the backing array could not
151 public Packed64(DataInput in, int valueCount, int bitsPerValue)
153 super(valueCount, bitsPerValue);
154 int size = size(valueCount, bitsPerValue);
155 blocks = new long[size+1]; // +1 due to non-conditional tricks
156 // TODO: find a faster way to bulk-read longs...
157 for(int i=0;i<size;i++) {
158 blocks[i] = in.readLong();
163 private static int size(int valueCount, int bitsPerValue) {
164 final long totBitCount = (long) valueCount * bitsPerValue;
165 return (int)(totBitCount/64 + ((totBitCount % 64 == 0 ) ? 0:1));
168 private void updateCached() {
169 readMasks = MASKS[bitsPerValue];
170 shifts = SHIFTS[bitsPerValue];
171 writeMasks = WRITE_MASKS[bitsPerValue];
172 maxPos = (int)((((long)blocks.length) * BLOCK_SIZE / bitsPerValue) - 2);
176 * @param index the position of the value.
177 * @return the value at the given index.
179 public long get(final int index) {
180 final long majorBitPos = (long)index * bitsPerValue;
181 final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
182 final int bitPos = (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
184 final int base = bitPos * FAC_BITPOS;
185 assert elementPos < blocks.length : "elementPos: " + elementPos + "; blocks.len: " + blocks.length;
186 return ((blocks[elementPos] << shifts[base]) >>> shifts[base+1]) |
187 ((blocks[elementPos+1] >>> shifts[base+2]) & readMasks[bitPos]);
190 public void set(final int index, final long value) {
191 final long majorBitPos = (long)index * bitsPerValue;
192 final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
193 final int bitPos = (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
194 final int base = bitPos * FAC_BITPOS;
196 blocks[elementPos ] = (blocks[elementPos ] & writeMasks[base])
197 | (value << shifts[base + 1] >>> shifts[base]);
198 blocks[elementPos+1] = (blocks[elementPos+1] & writeMasks[base+1])
199 | ((value << shifts[base + 2]) & writeMasks[base+2]);
203 public String toString() {
204 return "Packed64(bitsPerValue=" + bitsPerValue + ", size="
205 + size() + ", maxPos=" + maxPos
206 + ", elements.length=" + blocks.length + ")";
209 public long ramBytesUsed() {
210 return RamUsageEstimator.NUM_BYTES_ARRAY_HEADER
211 + blocks.length * RamUsageEstimator.NUM_BYTES_LONG;
214 public void clear() {
215 Arrays.fill(blocks, 0L);