/*******************************************************************************
* Copyright (c) 2013, 2016 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc-Andre Laperle - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.core.trace.indexer;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import org.eclipse.tracecompass.internal.tmf.core.Activator;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
/**
* An array of checkpoints stored on disk. It is very efficient for searching
* checkpoints by rank (O(1))
*
* @author Marc-Andre Laperle
*/
public class FlatArray extends AbstractFileCheckpointCollection {
/**
* Typical FlatArray file name
*/
public static final String INDEX_FILE_NAME = "checkpoint_flatarray.idx"; //$NON-NLS-1$
// Cached values
private int fCheckpointSize = 0;
private ByteBuffer fByteBuffer;
/**
* Constructs a FlatArray for a given trace from scratch or from an existing
* file. When the FlatArray is created from scratch, it is populated by
* subsequent calls to {@link #insert}.
*
* @param file
* the file to use as the persistent storage
* @param trace
* the trace
*/
public FlatArray(File file, ITmfPersistentlyIndexable trace) {
super(file, trace);
fCheckpointSize = getTrace().getCheckpointSize();
fByteBuffer = ByteBuffer.allocate(fCheckpointSize);
fByteBuffer.clear();
}
/**
* Insert a checkpoint into the file-backed array
*
* @param checkpoint
* the checkpoint to insert
*/
@Override
public void insert(ITmfCheckpoint checkpoint) {
try {
CheckpointCollectionFileHeader header = getHeader();
++header.fSize;
getRandomAccessFile().seek(getRandomAccessFile().length());
fByteBuffer.clear();
checkpoint.serialize(fByteBuffer);
getRandomAccessFile().write(fByteBuffer.array());
} catch (IOException e) {
Activator.logError(MessageFormat.format(Messages.FlatArray_IOErrorWriting, getFile()), e);
}
}
/**
* Get a checkpoint from a rank
*
* @param rank
* the rank to search
* @return the checkpoint that has been found or null if not found
*/
public ITmfCheckpoint get(long rank) {
ITmfCheckpoint checkpoint = null;
try {
long pos = getHeader().getSize() + fCheckpointSize * rank;
if (getRandomAccessFile() == null) {
return null;
}
getRandomAccessFile().seek(pos);
fByteBuffer.clear();
getRandomAccessFile().read(fByteBuffer.array());
ITmfLocation location = getTrace().restoreLocation(fByteBuffer);
ITmfTimestamp timeStamp = TmfTimestamp.create(fByteBuffer);
checkpoint = new TmfCheckpoint(timeStamp, location, fByteBuffer);
} catch (IOException e) {
Activator.logError(MessageFormat.format(Messages.FlatArray_IOErrorReading, getFile()), e);
}
return checkpoint;
}
/**
* Search for a checkpoint and return the rank.
*
* @param checkpoint
* the checkpoint to search
* @return the checkpoint rank of the searched checkpoint, if it is
* contained in the index; otherwise, (-(insertion point) - 1).
*/
@Override
public long binarySearch(ITmfCheckpoint checkpoint) {
if (getHeader().fSize == 1) {
return 0;
}
long lower = 0;
long upper = getHeader().fSize - 1;
long lastMiddle = -1;
long middle = 0;
while (lower <= upper && lastMiddle != middle) {
lastMiddle = middle;
middle = (lower + upper) / 2;
ITmfCheckpoint found = get(middle);
incCacheMisses();
int compare = checkpoint.compareTo(found);
if (compare == 0) {
return middle;
}
if (compare < 0) {
upper = middle;
} else {
lower = middle + 1;
}
}
return -(lower) - 1;
}
}