/** * Copyright 2013 Benjamin Lerer * * 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 io.horizondb.db.btree; import io.horizondb.io.ReadableBuffer; import io.horizondb.io.files.SeekableFileDataInput; import java.io.IOException; import static org.apache.commons.lang.Validate.isTrue; import static org.apache.commons.lang.Validate.notNull; /** * @author Benjamin * */ class BlockOrganizedFileDataInput extends AbstractBlockOrganizedByteReader implements SeekableFileDataInput { private final SeekableFileDataInput input; /** * @param input * @throws IOException */ public BlockOrganizedFileDataInput(int blockSize, SeekableFileDataInput input) throws IOException { super(blockSize); notNull(input, "the input parameter must not be null."); this.input = input; if (!isFileEmpty()) { handleNewBlock(); } } /** * {@inheritDoc} */ @Override public void seek(long position) throws IOException { isTrue(position < size(), "Position greater than the current size" + "(pos: " + position + " size: " + size() + ")."); long numberOfBlocks = position / (getBlockSize() - 1); long blockPosition = numberOfBlocks * getBlockSize(); this.input.seek(blockPosition); handleNewBlock(); long realPosition = position + numberOfBlocks + 1; this.input.seek(realPosition); } public boolean seekHeader() throws IOException { int numberOfBlocks = getNumberOfCompletedBlocks(); if (numberOfBlocks == 0) { return false; } do { seekBlock(--numberOfBlocks); } while (!isHeaderBlock() && numberOfBlocks > 0); return isHeaderBlock(); } /** * {@inheritDoc} */ @Override public void close() throws IOException { this.input.close(); } /** * {@inheritDoc} */ @Override public long readableBytes() throws IOException { return size() - getPosition(); } /** * {@inheritDoc} */ @Override public long size() throws IOException { long realSize = getRealSize(); long numberOfBlocks = getNumberOfBlocks(realSize); return realSize - numberOfBlocks; } /** * {@inheritDoc} */ @Override public long getPosition() throws IOException { long realPosition = this.input.getPosition(); long numberOfBlocks = getNumberOfBlocks(realPosition); return realPosition - numberOfBlocks; } /** * Returns <code>true</code> if the file is empty, <code>false</code> otherwise. * * @return <code>true</code> if the file is empty, <code>false</code> otherwise. * @throws IOException if an I/O problem occurs. */ public boolean isFileEmpty() throws IOException { return this.input.size() == 0; } /** * Returns the number of blocks used for the specified position. * * @param position the position * @return the number of blocks used for the specified position. */ private long getNumberOfBlocks(long position) { return (long) Math.ceil(((double) position) / getBlockSize()); } /** * {@inheritDoc} */ @Override protected long getRealPosition() throws IOException { return this.input.getPosition(); } /** * {@inheritDoc} */ @Override protected byte doReadByte() throws IOException { return this.input.readByte(); } /** * {@inheritDoc} */ @Override protected void doReadBytes(byte[] bytes, int offset, int length) throws IOException { this.input.readBytes(bytes, offset, length); } /** * {@inheritDoc} */ @Override protected ReadableBuffer doSlice(int length) throws IOException { return this.input.slice(length); } /** * {@inheritDoc} */ @Override protected long getRealSize() throws IOException { return this.input.size(); } /** * @param blockNumber * @throws IOException */ private void seekBlock(long blockNumber) throws IOException { long blockPosition = (blockNumber * getBlockSize()); this.input.seek(blockPosition); handleNewBlock(); } /** * Returns the number of completed blocks. * * @return the number of completed blocks. * @throws IOException if an I/O problem occurs while retrieving the file real size. */ private int getNumberOfCompletedBlocks() throws IOException { return (int) (getRealSize() / getBlockSize()); } }