/*
* 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.codecs.lucene84;
import java.io.IOException;
import java.util.Arrays;
import org.apache.lucene.codecs.MultiLevelSkipListReader;
import org.apache.lucene.store.IndexInput;
Implements the skip list reader for block postings format
that stores positions and payloads.
Although this skipper uses MultiLevelSkipListReader as an interface,
its definition of skip position will be a little different.
For example, when skipInterval = blockSize = 3, df = 2*skipInterval = 6,
0 1 2 3 4 5
d d d d d d (posting list)
^ ^ (skip point in MultiLeveSkipWriter)
^ (skip point in Lucene84SkipWriter)
In this case, MultiLevelSkipListReader will use the last document as a skip point,
while Lucene84SkipReader should assume no skip point will comes.
If we use the interface directly in Lucene84SkipReader, it may silly try to read
another skip data after the only skip point is loaded.
To illustrate this, we can call skipTo(d[5]), since skip point d[3] has smaller docId,
and numSkipped+blockSize== df, the MultiLevelSkipListReader will assume the skip list
isn't exhausted yet, and try to load a non-existed skip point
Therefore, we'll trim df before passing it to the interface. see trim(int)
/**
* Implements the skip list reader for block postings format
* that stores positions and payloads.
*
* Although this skipper uses MultiLevelSkipListReader as an interface,
* its definition of skip position will be a little different.
*
* For example, when skipInterval = blockSize = 3, df = 2*skipInterval = 6,
*
* 0 1 2 3 4 5
* d d d d d d (posting list)
* ^ ^ (skip point in MultiLeveSkipWriter)
* ^ (skip point in Lucene84SkipWriter)
*
* In this case, MultiLevelSkipListReader will use the last document as a skip point,
* while Lucene84SkipReader should assume no skip point will comes.
*
* If we use the interface directly in Lucene84SkipReader, it may silly try to read
* another skip data after the only skip point is loaded.
*
* To illustrate this, we can call skipTo(d[5]), since skip point d[3] has smaller docId,
* and numSkipped+blockSize== df, the MultiLevelSkipListReader will assume the skip list
* isn't exhausted yet, and try to load a non-existed skip point
*
* Therefore, we'll trim df before passing it to the interface. see trim(int)
*
*/
class Lucene84SkipReader extends MultiLevelSkipListReader {
private long docPointer[];
private long posPointer[];
private long payPointer[];
private int posBufferUpto[];
private int payloadByteUpto[];
private long lastPosPointer;
private long lastPayPointer;
private int lastPayloadByteUpto;
private long lastDocPointer;
private int lastPosBufferUpto;
public Lucene84SkipReader(
IndexInput skipStream, int maxSkipLevels,
boolean hasPos, boolean hasOffsets, boolean hasPayloads) {
super(skipStream, maxSkipLevels, ForUtil.BLOCK_SIZE, 8);
docPointer = new long[maxSkipLevels];
if (hasPos) {
posPointer = new long[maxSkipLevels];
posBufferUpto = new int[maxSkipLevels];
if (hasPayloads) {
payloadByteUpto = new int[maxSkipLevels];
} else {
payloadByteUpto = null;
}
if (hasOffsets || hasPayloads) {
payPointer = new long[maxSkipLevels];
} else {
payPointer = null;
}
} else {
posPointer = null;
}
}
Trim original docFreq to tell skipReader read proper number of skip points.
Since our definition in Lucene84Skip* is a little different from MultiLevelSkip*
This trimmed docFreq will prevent skipReader from:
1. silly reading a non-existed skip point after the last block boundary
2. moving into the vInt block
/**
* Trim original docFreq to tell skipReader read proper number of skip points.
*
* Since our definition in Lucene84Skip* is a little different from MultiLevelSkip*
* This trimmed docFreq will prevent skipReader from:
* 1. silly reading a non-existed skip point after the last block boundary
* 2. moving into the vInt block
*
*/
protected int trim(int df) {
return df % ForUtil.BLOCK_SIZE == 0? df - 1: df;
}
public void init(long skipPointer, long docBasePointer, long posBasePointer, long payBasePointer, int df) throws IOException {
super.init(skipPointer, trim(df));
lastDocPointer = docBasePointer;
lastPosPointer = posBasePointer;
lastPayPointer = payBasePointer;
Arrays.fill(docPointer, docBasePointer);
if (posPointer != null) {
Arrays.fill(posPointer, posBasePointer);
if (payPointer != null) {
Arrays.fill(payPointer, payBasePointer);
}
} else {
assert posBasePointer == 0;
}
}
Returns the doc pointer of the doc to which the last call of MultiLevelSkipListReader.skipTo(int)
has skipped. /** Returns the doc pointer of the doc to which the last call of
* {@link MultiLevelSkipListReader#skipTo(int)} has skipped. */
public long getDocPointer() {
return lastDocPointer;
}
public long getPosPointer() {
return lastPosPointer;
}
public int getPosBufferUpto() {
return lastPosBufferUpto;
}
public long getPayPointer() {
return lastPayPointer;
}
public int getPayloadByteUpto() {
return lastPayloadByteUpto;
}
public int getNextSkipDoc() {
return skipDoc[0];
}
@Override
protected void seekChild(int level) throws IOException {
super.seekChild(level);
docPointer[level] = lastDocPointer;
if (posPointer != null) {
posPointer[level] = lastPosPointer;
posBufferUpto[level] = lastPosBufferUpto;
if (payloadByteUpto != null) {
payloadByteUpto[level] = lastPayloadByteUpto;
}
if (payPointer != null) {
payPointer[level] = lastPayPointer;
}
}
}
@Override
protected void setLastSkipData(int level) {
super.setLastSkipData(level);
lastDocPointer = docPointer[level];
if (posPointer != null) {
lastPosPointer = posPointer[level];
lastPosBufferUpto = posBufferUpto[level];
if (payPointer != null) {
lastPayPointer = payPointer[level];
}
if (payloadByteUpto != null) {
lastPayloadByteUpto = payloadByteUpto[level];
}
}
}
@Override
protected int readSkipData(int level, IndexInput skipStream) throws IOException {
int delta = skipStream.readVInt();
docPointer[level] += skipStream.readVLong();
if (posPointer != null) {
posPointer[level] += skipStream.readVLong();
posBufferUpto[level] = skipStream.readVInt();
if (payloadByteUpto != null) {
payloadByteUpto[level] = skipStream.readVInt();
}
if (payPointer != null) {
payPointer[level] += skipStream.readVLong();
}
}
readImpacts(level, skipStream);
return delta;
}
// The default impl skips impacts
protected void readImpacts(int level, IndexInput skipStream) throws IOException {
skipStream.skipBytes(skipStream.readVInt());
}
}