/**
* Copyright 2014 National University of Ireland, Galway.
*
* This file is part of the SIREn project. Project and contact information:
*
* https://github.com/rdelbru/SIREn
*
* Licensed 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.sindice.siren.index.codecs.block;
import java.io.Closeable;
import java.io.IOException;
import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.IndexInput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract API to decode a block-based posting format.
*
* <p>
*
* This class is safe for concurrent file access. This is achieved by cloning
* the underlying {@link IndexInput} in every {@link BlockReader}.
*/
public abstract class BlockIndexInput implements Closeable {
protected final IndexInput in;
protected static final Logger logger = LoggerFactory.getLogger(BlockIndexInput.class);
public BlockIndexInput(final IndexInput in) throws IOException {
this.in = in;
}
public void close() throws IOException {
in.close();
}
public Index index() throws IOException {
return new Index();
}
/**
* This class stores the file pointer of a {@link DataInput}.
*/
public class Index {
private long fp;
public void read(final DataInput indexIn, final boolean absolute) throws IOException {
if (absolute) {
fp = indexIn.readVLong();
}
else {
fp += indexIn.readVLong();
}
// logger.debug("Read index {}", fp);
}
public void seek(final BlockIndexInput.BlockReader other) throws IOException {
other.seek(fp);
}
public void set(final BlockIndexInput.Index other) {
final Index idx = other;
fp = idx.fp;
}
@Override
public Object clone() {
final Index other = new Index();
other.fp = fp;
return other;
}
@Override
public String toString() {
return "fp=" + fp;
}
}
/**
* Create a new {@link BlockReader} associated to the {@link BlockIndexInput}.
*
* <p>
*
* Subclasses must create a clone of the {@link BlockIndexInput#in} to ensure
* safe concurrent file access.
*/
public abstract BlockReader getBlockReader();
/**
* Abstraction over the reader of the blocks of the postings file.
*
* <p>
*
* The abstraction provides an interface to iterate over the blocks.
* Subclasses must implement an interface to iterate over the data within
* a block.
*/
protected abstract class BlockReader {
private boolean seekPending = false;
private long pendingFP = 0;
private long lastBlockFP = -1;
/**
* Each block reader should have their own clone of the {@link IndexInput}
*/
protected final IndexInput in;
protected BlockReader(final IndexInput in) {
this.in = in;
}
/**
* Init reader
*/
public void init() {
seekPending = false;
pendingFP = 0;
lastBlockFP = -1;
this.initBlock();
}
/**
* Move to the next block and decode block header
*/
public void nextBlock() throws IOException {
if (!seekPending) {
this.skipData();
}
this.maybeSeek();
this.initBlock();
this.readHeader();
}
/**
* Init reader for new block
*/
protected abstract void initBlock();
public abstract boolean isExhausted();
/**
* Read and decode block header
*/
protected abstract void readHeader() throws IOException;
/**
* Skip remaining data in the block and advance input stream pointer.
*/
protected abstract void skipData() throws IOException;
public void seek(final long fp) {
// logger.debug("Set pending seek to {}", fp);
pendingFP = fp;
seekPending = true;
}
/**
* Seek block if needed. Return true if a seek has been performed.
*/
private boolean maybeSeek() throws IOException {
if (seekPending) {
if (pendingFP != lastBlockFP) {
// logger.debug("Seek to {}", pendingFP);
in.seek(pendingFP);
lastBlockFP = pendingFP;
seekPending = false;
return true;
}
seekPending = false;
}
return false;
}
/**
* Compute the minimum size of a buffer based on the required size and
* the decompression window size.
*/
protected int getMinimumBufferSize(final int bufferSize, final int windowSize) {
return (int) Math.ceil((float) bufferSize / (float) windowSize) * windowSize;
}
}
}