/** * 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 java.io.IOException; import java.nio.ByteOrder; /** * Slice of a <code>BlockOrganizedByteReader</code>. */ final class BlockOrganizedReadableBuffer extends AbstractBlockOrganizedByteReader implements ReadableBuffer { /** * The decorated buffer. */ private ReadableBuffer buffer; /** * The decorated buffer size. */ private int bufferLength; /** * The initial prefix of the next block. */ private byte initialCurrentBlockPrefix; /** * The initial position of the next block. */ private long initialNextBlockPosition; /** * The buffer length. */ private int length; /** * Creates a <code>BlockOrganizedByteReaderSlice</code> with blocks of the specified size. * * @param blockSize the block size. */ public BlockOrganizedReadableBuffer(int blockSize) { super(blockSize); } /** * {@inheritDoc} */ @Override public BlockOrganizedReadableBuffer order(ByteOrder order) { super.order(order); return this; } /** * Decorates the specified reader. * * @param buffer the buffer to decorate. * @param nextBlockPosition the position of the next block. * @param currentBlockPrefix the prefix of the current block. * @param length the length of the slice. */ public void decorate(ReadableBuffer buffer, long nextBlockPosition, byte currentBlockPrefix, int length) { this.buffer = buffer; this.bufferLength = buffer.readableBytes(); this.initialCurrentBlockPrefix = currentBlockPrefix; this.initialNextBlockPosition = nextBlockPosition; this.length = length; setCurrentBlockPrefix(currentBlockPrefix); setNextBlockPosition(nextBlockPosition); } @Override public BlockOrganizedReadableBuffer duplicate() { BlockOrganizedReadableBuffer duplicate = new BlockOrganizedReadableBuffer(getBlockSize()); duplicate.decorate(this.buffer.duplicate(), this.initialNextBlockPosition, this.initialCurrentBlockPrefix, this.length); return duplicate; } /** * {@inheritDoc} */ @Override public int readableBytes() { return this.length - readerIndex(); } /** * {@inheritDoc} */ @Override public byte getByte(int index) { return this.buffer.getByte(index + blockNumber(index)); } /** * {@inheritDoc} */ @Override public BlockOrganizedReadableBuffer getBytes(int index, byte[] array) { return getBytes(index, array, 0, array.length); } /** * {@inheritDoc} */ @Override public final short getShort(int index) { return getEndianness().getShort(this, index); } /** * {@inheritDoc} */ @Override public final int getUnsignedShort(int index) { return getEndianness().getUnsignedShort(this, index); } /** * {@inheritDoc} */ @Override public final int getInt(int index) { return getEndianness().getInt(this, index); } /** * {@inheritDoc} */ @Override public final long getUnsignedInt(int index) { return getEndianness().getUnsignedInt(this, index); } /** * {@inheritDoc} */ @Override public final long getLong(int index) { return getEndianness().getLong(this, index); } /** * {@inheritDoc} */ @Override public BlockOrganizedReadableBuffer getBytes(int index, byte[] array, int off, int len) { int blockNumber = blockNumber(index); int position = index + blockNumber; int remaining = len; int arrayOffset = off; long nextBlockStart = this.initialNextBlockPosition + (blockNumber * getBlockSize()); while (remaining > 0) { if (position < nextBlockStart) { int toCopy = (int) Math.min(remaining, nextBlockStart - position); this.buffer.getBytes(position, array, arrayOffset, toCopy); arrayOffset += toCopy; remaining -= toCopy; position += toCopy; } position++; nextBlockStart += getBlockSize(); } return this; } /** * {@inheritDoc} */ @Override public BlockOrganizedReadableBuffer readerIndex(int readerIndex) { if (readerIndex < this.initialNextBlockPosition) { this.buffer.readerIndex(readerIndex); setCurrentBlockPrefix(this.initialCurrentBlockPrefix); setNextBlockPosition(this.initialNextBlockPosition); } else { int numberOfBlocks = 1 + ((int) (readerIndex + 1 - this.initialNextBlockPosition) / getBlockSize()); setCurrentBlockPrefix(this.initialCurrentBlockPrefix); setNextBlockPosition(this.initialNextBlockPosition + (numberOfBlocks * getBlockSize())); this.buffer.readerIndex(readerIndex + numberOfBlocks); } return this; } /** * {@inheritDoc} */ @Override public ReadableBuffer slice(int index, int length) throws IOException { int readerIndex = readerIndex(); readerIndex(index); ReadableBuffer slice = slice(length); readerIndex(readerIndex); return slice; } /** * {@inheritDoc} */ @Override public int readerIndex() { int readerIndex = this.buffer.readerIndex(); if (readerIndex <= this.initialNextBlockPosition) { return readerIndex; } int numberOfBlocks = 1 + ((int) (readerIndex - 1 - this.initialNextBlockPosition) / getBlockSize()); return readerIndex - numberOfBlocks; } /** * {@inheritDoc} */ @Override public boolean canBeMergedWith(ReadableBuffer buffer) { return false; } /** * {@inheritDoc} */ @Override public void mergeWith(ReadableBuffer buffer) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override protected long getRealPosition() throws IOException { return this.buffer.readerIndex(); } /** * {@inheritDoc} */ @Override protected byte doReadByte() throws IOException { return this.buffer.readByte(); } /** * {@inheritDoc} */ @Override protected void doReadBytes(byte[] bytes, int offset, int length) throws IOException { this.buffer.readBytes(bytes, offset, length); } /** * {@inheritDoc} */ @Override protected long getRealSize() throws IOException { return this.bufferLength; } /** * {@inheritDoc} */ @Override protected ReadableBuffer doSlice(int length) throws IOException { return this.buffer.slice(length); } /** * Returns the block number in which is located the specified index. * * @param index the index. * @return the block number in which is located the specified index. */ private int blockNumber(int index) { if (index < this.initialNextBlockPosition) { return 0; } return 1 + ((int) (index + 1 - this.initialNextBlockPosition) / getBlockSize()); } }