/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.client.keyvalue; import alluxio.exception.AlluxioException; import alluxio.util.io.BufferUtils; import alluxio.util.io.ByteIOUtils; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; import javax.annotation.concurrent.NotThreadSafe; /** * Reader that implements {@link KeyValuePartitionReader} to access a key-value file using random * access API. */ @NotThreadSafe public final class ByteBufferKeyValuePartitionReader implements KeyValuePartitionReader { private static final Logger LOG = LoggerFactory.getLogger(ByteBufferKeyValuePartitionReader.class); private Index mIndex; private PayloadReader mPayloadReader; private ByteBuffer mBuf; private int mBufferLength; /** Whether this writer is closed. */ private boolean mClosed; /** * Constructs {@link ByteBufferKeyValuePartitionReader}. * * @param fileBytes the byte buffer as underline storage to read from */ public ByteBufferKeyValuePartitionReader(ByteBuffer fileBytes) { mBuf = Preconditions.checkNotNull(fileBytes); mBufferLength = mBuf.remaining(); mIndex = createIndex(); mPayloadReader = createPayloadReader(); mClosed = false; } private Index createIndex() { int indexOffset = ByteIOUtils.readInt(mBuf, mBufferLength - 4); ByteBuffer indexBytes = BufferUtils.sliceByteBuffer(mBuf, indexOffset, mBufferLength - 4 - indexOffset); return LinearProbingIndex.loadFromByteArray(indexBytes); } private PayloadReader createPayloadReader() { return new BasePayloadReader(mBuf); } /** * {@inheritDoc} * <p> * This could be slow when value size is large, use this cautiously or {@link #get(ByteBuffer)} * which may avoid copying data. */ @Override public byte[] get(byte[] key) throws IOException { ByteBuffer valueBuffer = get(ByteBuffer.wrap(key)); if (valueBuffer == null) { return null; } return BufferUtils.newByteArrayFromByteBuffer(valueBuffer); } @Override public ByteBuffer get(ByteBuffer key) throws IOException { Preconditions.checkState(!mClosed); LOG.trace("get: key"); return mIndex.get(key, mPayloadReader); } @Override public void close() { if (mClosed) { return; } mClosed = true; } @Override public KeyValueIterator iterator() { return new KeyValueIterator() { private Iterator<ByteBuffer> mKeyIterator = mIndex.keyIterator(mPayloadReader); @Override public boolean hasNext() { return mKeyIterator.hasNext(); } @Override public KeyValuePair next() throws IOException, AlluxioException { if (!hasNext()) { throw new NoSuchElementException(); } ByteBuffer key = mKeyIterator.next(); ByteBuffer value = get(key); return new KeyValuePair(key, value); } }; } @Override public int size() throws IOException, AlluxioException { return mIndex.keyCount(); } /** * @return the {@link Index} reconstructed from the byte buffer */ public Index getIndex() { return mIndex; } /** * @return the {@link PayloadReader} for reading payloads from the byte buffer */ public PayloadReader getPayloadReader() { return mPayloadReader; } }