/** * 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.IndexOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Abstract API to encode a block-based posting format. * * <p> * * This class is not thread-safe and not safe for concurrent file access. It * must not be used to encode multiple blocks concurrently. In the current * implementation of Lucene, terms are processed sequentially during the * creation of a new index segment. It is ensured that (1) one instance of this * class is always used by one single thread, and (2) one instance of this class * is always encoding postings one term at a time. */ public abstract class BlockIndexOutput implements Closeable { protected final IndexOutput out; protected static final Logger logger = LoggerFactory.getLogger(BlockIndexOutput.class); public BlockIndexOutput(final IndexOutput out) { this.out = out; } /** * Instantiates a new block index. */ public Index index() throws IOException { return new Index(); } /** * This class stores the file pointer of an {@link IndexOutput}. */ public class Index { long fp; long lastFP; public void mark() throws IOException { fp = out.getFilePointer(); } public void copyFrom(final BlockIndexOutput.Index other, final boolean copyLast) throws IOException { final Index idx = other; fp = idx.fp; if (copyLast) { lastFP = fp; } } public void write(final IndexOutput indexOut, final boolean absolute) throws IOException { // logger.debug("Write index at {}", fp); if (absolute) { indexOut.writeVLong(fp); } else { indexOut.writeVLong(fp - lastFP); } lastFP = fp; } @Override public String toString() { return "fp=" + fp; } } public void close() throws IOException { out.close(); } /** * Create a new {@link BlockWriter} associated to this * {@link BlockIndexOutput}. * * <p> * * You should ensure to flush all {@link BlockWriter} before closing the * {@link BlockIndexOutput}. * * <p> * * More than one {@link BlockWriter} can be instantiated by a * {@link BlockIndexOutput}. Usually one writer is instantiated for each term. */ public abstract BlockWriter getBlockWriter(); /** * Abstraction over the writer of the blocks of the postings file. * * <p> * * The abstraction provides an interface to write and flush blocks. Subclasses * must implement the encoding of the block header and the encoding of * the block data. */ protected abstract class BlockWriter { /** * Flush of pending data block to the output file. */ public void flush() throws IOException { // Flush only if the block is non empty if (!this.isEmpty()) { this.writeBlock(); } } /** * Write data block to the output file with the following sequence of * operations: * <ul> * <li> Compress the data * <li> Write block header (as header can depend on statistic computed * from data compression) * <li> Write compressed data block * <li> Reset writer for new block * </ul> */ protected void writeBlock() throws IOException { this.compress(); this.writeHeader(); this.writeData(); this.initBlock(); } public abstract boolean isEmpty(); public abstract boolean isFull(); /** * Compress the data block */ protected abstract void compress(); /** * Write block header to the output file */ protected abstract void writeHeader() throws IOException; /** * Write compressed data block to the output file */ protected abstract void writeData() throws IOException; /** * Init writer for new block */ protected abstract void initBlock(); /** * Compute the minimum size of a buffer based on the required size and * the compression window size. */ protected int getMinimumBufferSize(final int bufferSize, final int windowSize) { return (int) Math.ceil((float) bufferSize / (float) windowSize) * windowSize; } } }