/*
* Copyright 2011 LinkedIn, Inc
*
* 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 voldemort.store.readonly.chunk;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.log4j.Logger;
import voldemort.utils.ByteUtils;
import voldemort.utils.ClosableIterator;
abstract class DataFileChunkSetIterator<T> implements ClosableIterator<T> {
private static Logger logger = Logger.getLogger(DataFileChunkSetIterator.class);
private DataFileChunkSet dataFileChunkSet;
private int currentChunk;
private long currentOffsetInChunk;
private boolean chunksFinished;
protected boolean coalesceCollided;
// Number of k-v pairs which have collided and are in one bucket
private short tupleCount;
private ReadWriteLock fileModificationLock;
public DataFileChunkSetIterator(DataFileChunkSet dataFileChunkSet, boolean coalesceCollided) {
this(dataFileChunkSet, coalesceCollided, null);
}
/**
* Iterator over the data file chunks
*
* @param dataFileChunkSet Set of data chunk files
* @param coalesceCollided Whether we want to consider collided entries as
* one?
* @param fileModificationLock RW lock to lock before starting the iterator
*/
public DataFileChunkSetIterator(DataFileChunkSet dataFileChunkSet,
boolean coalesceCollided,
ReadWriteLock fileModificationLock) {
this.dataFileChunkSet = dataFileChunkSet;
this.currentChunk = 0;
this.currentOffsetInChunk = 0;
this.tupleCount = 0;
this.chunksFinished = false;
this.fileModificationLock = fileModificationLock;
this.coalesceCollided = coalesceCollided;
if(this.fileModificationLock != null)
this.fileModificationLock.readLock().lock();
}
public void close() {
if(this.fileModificationLock != null)
this.fileModificationLock.readLock().unlock();
}
public abstract T next();
protected DataFileChunk getCurrentChunk() {
return this.dataFileChunkSet.dataFileFor(currentChunk);
}
protected long getCurrentOffsetInChunk() {
return this.currentOffsetInChunk;
}
public boolean hasNext() {
if(chunksFinished)
return false;
// Jump till you reach the first non-zero data size file or end
if(currentOffsetInChunk >= this.dataFileChunkSet.getDataFileSize(currentChunk)) {
// Time to jump to another chunk with a non-zero size data file
currentChunk++;
currentOffsetInChunk = 0;
// Jump over all zero size data files
while(currentChunk < this.dataFileChunkSet.getNumChunks()
&& this.dataFileChunkSet.getDataFileSize(currentChunk) == 0) {
currentChunk++;
}
if(currentChunk == this.dataFileChunkSet.getNumChunks()) {
chunksFinished = true;
return false;
}
}
if(!coalesceCollided && tupleCount == 0) {
// Update the tuple count
ByteBuffer numKeyValsBuffer = ByteBuffer.allocate(ByteUtils.SIZE_OF_SHORT);
try {
getCurrentChunk().read(numKeyValsBuffer, currentOffsetInChunk);
} catch(IOException e) {
logger.error("Error while reading tuple count in iterator", e);
return false;
}
tupleCount = numKeyValsBuffer.getShort(0);
currentOffsetInChunk += ByteUtils.SIZE_OF_SHORT;
}
return true;
}
public void updateOffset(long updatedOffset) {
if(!coalesceCollided)
tupleCount--;
currentOffsetInChunk = updatedOffset;
hasNext();
}
public void remove() {
throw new UnsupportedOperationException("Cannot remove from these read-only data chunks");
}
}