package org.apache.lucene.util.bkd;
import java.io.EOFException;
import java.io.IOException;
import org.apache.lucene.codecs.CodecUtil;
import org.apache.lucene.store.ChecksumIndexInput;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.util.BytesRef;
public final class OfflinePointReader implements PointReader {
long countLeft;
final IndexInput in;
byte[] onHeapBuffer;
int offset;
private boolean checked;
private final BKDConfig config;
private int pointsInBuffer;
private final int maxPointOnHeap;
final String name;
private final OfflinePointValue pointValue;
public OfflinePointReader(BKDConfig config, Directory tempDir, String tempFileName, long start, long length, byte[] reusableBuffer) throws IOException {
this.config = config;
if ((start + length) * config.bytesPerDoc + CodecUtil.footerLength() > tempDir.fileLength(tempFileName)) {
throw new IllegalArgumentException("requested slice is beyond the length of this file: start=" + start + " length=" + length + " bytesPerDoc=" + config.bytesPerDoc + " fileLength=" + tempDir.fileLength(tempFileName) + " tempFileName=" + tempFileName);
}
if (reusableBuffer == null) {
throw new IllegalArgumentException("[reusableBuffer] cannot be null");
}
if (reusableBuffer.length < config.bytesPerDoc) {
throw new IllegalArgumentException("Length of [reusableBuffer] must be bigger than " + config.bytesPerDoc);
}
this.maxPointOnHeap = reusableBuffer.length / config.bytesPerDoc;
if (start == 0 && length*config.bytesPerDoc == tempDir.fileLength(tempFileName) - CodecUtil.footerLength()) {
in = tempDir.openChecksumInput(tempFileName, IOContext.READONCE);
} else {
in = tempDir.openInput(tempFileName, IOContext.READONCE);
}
name = tempFileName;
long seekFP = start * config.bytesPerDoc;
in.seek(seekFP);
countLeft = length;
this.onHeapBuffer = reusableBuffer;
this.pointValue = new OfflinePointValue(config, onHeapBuffer);
}
@Override
public boolean next() throws IOException {
if (this.pointsInBuffer == 0) {
if (countLeft >= 0) {
if (countLeft == 0) {
return false;
}
}
try {
if (countLeft > maxPointOnHeap) {
in.readBytes(onHeapBuffer, 0, maxPointOnHeap * config.bytesPerDoc);
pointsInBuffer = maxPointOnHeap - 1;
countLeft -= maxPointOnHeap;
} else {
in.readBytes(onHeapBuffer, 0, (int) countLeft * config.bytesPerDoc);
pointsInBuffer = Math.toIntExact(countLeft - 1);
countLeft = 0;
}
this.offset = 0;
} catch (EOFException eofe) {
assert countLeft == -1;
return false;
}
} else {
this.pointsInBuffer--;
this.offset += config.bytesPerDoc;
}
return true;
}
@Override
public PointValue pointValue() {
pointValue.setOffset(offset);
return pointValue;
}
@Override
public void close() throws IOException {
try {
if (countLeft == 0 && in instanceof ChecksumIndexInput && checked == false) {
checked = true;
CodecUtil.checkFooter((ChecksumIndexInput) in);
}
} finally {
in.close();
}
}
static class OfflinePointValue implements PointValue {
final BytesRef packedValue;
final BytesRef packedValueDocID;
final int packedValueLength;
OfflinePointValue(BKDConfig config, byte[] value) {
this.packedValueLength = config.packedBytesLength;
this.packedValue = new BytesRef(value, 0, packedValueLength);
this.packedValueDocID = new BytesRef(value, 0, config.bytesPerDoc);
}
public void setOffset(int offset) {
packedValue.offset = offset;
packedValueDocID.offset = offset;
}
@Override
public BytesRef packedValue() {
return packedValue;
}
@Override
public int docID() {
int position = packedValueDocID.offset + packedValueLength;
return ((packedValueDocID.bytes[position] & 0xFF) << 24) | ((packedValueDocID.bytes[++position] & 0xFF) << 16)
| ((packedValueDocID.bytes[++position] & 0xFF) << 8) | (packedValueDocID.bytes[++position] & 0xFF);
}
@Override
public BytesRef packedValueDocIDBytes() {
return packedValueDocID;
}
}
}