blob: 512fa36a26115d80ce9591dde38d87b2cb933eb0 [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.lucene.util.packed;
import static org.apache.lucene.util.BitUtil.zigZagDecode;
import static org.apache.lucene.util.packed.AbstractBlockPackedWriter.BPV_SHIFT;
import static org.apache.lucene.util.packed.AbstractBlockPackedWriter.MAX_BLOCK_SIZE;
import static org.apache.lucene.util.packed.AbstractBlockPackedWriter.MIN_BLOCK_SIZE;
import static org.apache.lucene.util.packed.AbstractBlockPackedWriter.MIN_VALUE_EQUALS_0;
import static org.apache.lucene.util.packed.PackedInts.checkBlockSize;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.util.LongsRef;
/**
* Reader for sequences of longs written with {@link BlockPackedWriter}.
* @see BlockPackedWriter
* @lucene.internal
*/
public final class BlockPackedReaderIterator {
// same as DataInput.readVLong but supports negative values
static long readVLong(DataInput in) throws IOException {
byte b = in.readByte();
if (b >= 0) return b;
long i = b & 0x7FL;
b = in.readByte();
i |= (b & 0x7FL) << 7;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 14;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 21;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 28;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 35;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 42;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0x7FL) << 49;
if (b >= 0) return i;
b = in.readByte();
i |= (b & 0xFFL) << 56;
return i;
}
DataInput in;
final int packedIntsVersion;
long valueCount;
final int blockSize;
final long[] values;
final LongsRef valuesRef;
byte[] blocks;
int off;
long ord;
/** Sole constructor.
* @param blockSize the number of values of a block, must be equal to the
* block size of the {@link BlockPackedWriter} which has
* been used to write the stream
*/
public BlockPackedReaderIterator(DataInput in, int packedIntsVersion, int blockSize, long valueCount) {
checkBlockSize(blockSize, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE);
this.packedIntsVersion = packedIntsVersion;
this.blockSize = blockSize;
this.values = new long[blockSize];
this.valuesRef = new LongsRef(this.values, 0, 0);
reset(in, valueCount);
}
/** Reset the current reader to wrap a stream of <code>valueCount</code>
* values contained in <code>in</code>. The block size remains unchanged. */
public void reset(DataInput in, long valueCount) {
this.in = in;
assert valueCount >= 0;
this.valueCount = valueCount;
off = blockSize;
ord = 0;
}
/** Skip exactly <code>count</code> values. */
public void skip(long count) throws IOException {
assert count >= 0;
if (ord + count > valueCount || ord + count < 0) {
throw new EOFException();
}
// 1. skip buffered values
final int skipBuffer = (int) Math.min(count, blockSize - off);
off += skipBuffer;
ord += skipBuffer;
count -= skipBuffer;
if (count == 0L) {
return;
}
// 2. skip as many blocks as necessary
assert off == blockSize;
while (count >= blockSize) {
final int token = in.readByte() & 0xFF;
final int bitsPerValue = token >>> BPV_SHIFT;
if (bitsPerValue > 64) {
throw new IOException("Corrupted");
}
if ((token & MIN_VALUE_EQUALS_0) == 0) {
readVLong(in);
}
final long blockBytes = PackedInts.Format.PACKED.byteCount(packedIntsVersion, blockSize, bitsPerValue);
skipBytes(blockBytes);
ord += blockSize;
count -= blockSize;
}
if (count == 0L) {
return;
}
// 3. skip last values
assert count < blockSize;
refill();
ord += count;
off += count;
}
private void skipBytes(long count) throws IOException {
if (in instanceof IndexInput) {
final IndexInput iin = (IndexInput) in;
iin.seek(iin.getFilePointer() + count);
} else {
if (blocks == null) {
blocks = new byte[blockSize];
}
long skipped = 0;
while (skipped < count) {
final int toSkip = (int) Math.min(blocks.length, count - skipped);
in.readBytes(blocks, 0, toSkip);
skipped += toSkip;
}
}
}
/** Read the next value. */
public long next() throws IOException {
if (ord == valueCount) {
throw new EOFException();
}
if (off == blockSize) {
refill();
}
final long value = values[off++];
++ord;
return value;
}
/** Read between <tt>1</tt> and <code>count</code> values. */
public LongsRef next(int count) throws IOException {
assert count > 0;
if (ord == valueCount) {
throw new EOFException();
}
if (off == blockSize) {
refill();
}
count = Math.min(count, blockSize - off);
count = (int) Math.min(count, valueCount - ord);
valuesRef.offset = off;
valuesRef.length = count;
off += count;
ord += count;
return valuesRef;
}
private void refill() throws IOException {
final int token = in.readByte() & 0xFF;
final boolean minEquals0 = (token & MIN_VALUE_EQUALS_0) != 0;
final int bitsPerValue = token >>> BPV_SHIFT;
if (bitsPerValue > 64) {
throw new IOException("Corrupted");
}
final long minValue = minEquals0 ? 0L : zigZagDecode(1L + readVLong(in));
assert minEquals0 || minValue != 0;
if (bitsPerValue == 0) {
Arrays.fill(values, minValue);
} else {
final PackedInts.Decoder decoder = PackedInts.getDecoder(PackedInts.Format.PACKED, packedIntsVersion, bitsPerValue);
final int iterations = blockSize / decoder.byteValueCount();
final int blocksSize = iterations * decoder.byteBlockCount();
if (blocks == null || blocks.length < blocksSize) {
blocks = new byte[blocksSize];
}
final int valueCount = (int) Math.min(this.valueCount - ord, blockSize);
final int blocksCount = (int) PackedInts.Format.PACKED.byteCount(packedIntsVersion, valueCount, bitsPerValue);
in.readBytes(blocks, 0, blocksCount);
decoder.decode(blocks, 0, values, 0, iterations);
if (minValue != 0) {
for (int i = 0; i < valueCount; ++i) {
values[i] += minValue;
}
}
}
off = 0;
}
/** Return the offset of the next value to read. */
public long ord() {
return ord;
}
}