import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTInterval;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
-import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeStub;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeClassicStub;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/* Minimal allowed blocksize */
- private static final int BLOCK_SIZE = HistoryTree.TREE_HEADER_SIZE;
+ private static final int BLOCK_SIZE = HistoryTreeClassicStub.MINIMUM_BLOCK_SIZE;
private static final HTInterval NULL_INTERVAL = new HTInterval(10, 20, 1, TmfStateValue.nullValue());
* The max number of children per node in the tree (tree config
* option)
*/
- private HistoryTreeStub setupSmallTree(int maxChildren) {
- HistoryTreeStub ht = null;
+ private HistoryTreeClassicStub setupSmallTree(int maxChildren) {
+ HistoryTreeClassicStub ht = null;
try {
File newFile = fTempFile;
assertNotNull(newFile);
maxChildren, /* Number of children */
1, /* Provider version */
1); /* Start time */
- ht = new HistoryTreeStub(config);
+ ht = new HistoryTreeClassicStub(config);
} catch (IOException e) {
fail(e.getMessage());
/**
* Setup a history tree with config MAX_CHILDREN = 3.
*/
- private HistoryTreeStub setupSmallTree() {
+ private HistoryTreeClassicStub setupSmallTree() {
return setupSmallTree(3);
}
- private static long fillValues(HistoryTree ht, TmfStateValue value, int nbValues, long start) {
+ private static long fillValues(IHistoryTree ht, TmfStateValue value, int nbValues, long start) {
for (int i = 0; i < nbValues; i++) {
ht.insertInterval(new HTInterval(start + i, start + i + 1, 1, value));
}
* greater than or equal to this to make sure the intervals go in
* the leaf node.
*/
- private static long fillNextLeafNode(HistoryTreeStub ht, long leafNodeStart) {
+ private static long fillNextLeafNode(HistoryTreeClassicStub ht, long leafNodeStart) {
int prevCount = ht.getNodeCount();
int prevDepth = ht.getDepth();
*/
@Test
public void testSequentialFill() {
- HistoryTreeStub ht = setupSmallTree();
+ HistoryTreeClassicStub ht = setupSmallTree();
HTNode node = ht.getLatestLeaf();
assertEquals(0, node.getNodeUsagePercent());
*/
@Test
public void testDepth() {
- HistoryTreeStub ht = setupSmallTree();
+ HistoryTreeClassicStub ht = setupSmallTree();
/* Fill a first node */
HTNode node = ht.getLatestLeaf();
/* Represents the start time of the current leaf node */
long start = 1;
- HistoryTreeStub ht = setupSmallTree(2);
+ HistoryTreeClassicStub ht = setupSmallTree(2);
start = fillNextLeafNode(ht, start);
List<HTNode> branch = ht.getLatestBranch();
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTInterval;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeBackend;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeBackendStub;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.backend.HistoryTreeBackendStub.HistoryTreeType;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
/**
* Test the {@link HistoryTreeBackend}-specific behavior and its interactions
- * with the {@link HistoryTree} class
+ * with the {@link IHistoryTree} classes.
*
* @author Geneviève Bastien
*/
+@RunWith(Parameterized.class)
public class HistoryTreeWithBackendTest {
/** State system ID */
/** Default block size */
private static final int BLOCK_SIZE = 4096;
+ private final HistoryTreeType fHtType;
+
+ /**
+ * @return The arrays of parameters
+ */
+ @Parameters(name = "{0}")
+ public static Iterable<Object[]> getParameters() {
+ return Arrays.asList(new Object[][] {
+ { HistoryTreeType.CLASSIC }
+ });
+ }
+
+ /**
+ * Constructor
+ *
+ * @param htType
+ * The type of history tree to use
+ */
+ public HistoryTreeWithBackendTest(HistoryTreeType htType) {
+ fHtType = htType;
+ }
+
/**
* Test the behavior of the history tree after at least a depth of 3
*/
long startTime = 1;
File historyTreeFile = NonNullUtils.checkNotNull(File.createTempFile("HistoryTreeBackendTest", ".ht"));
+ HistoryTreeBackendStub.setTreeType(fHtType);
HistoryTreeBackendStub backend = new HistoryTreeBackendStub(SSID, historyTreeFile, PROVIDER_VERSION, startTime, BLOCK_SIZE, MAX_CHILDREN);
int duration = nbAttr;
backend.insertPastState(interval.getStartTime(), interval.getEndTime(), interval.getAttribute(), interval.getStateValue());
/*
- * insert cascading intervals to fill 2 levels of history
- * tree, so that we start another branch
+ * insert cascading intervals to fill 2 levels of history tree, so
+ * that we start another branch
*/
while (backend.getHistoryTree().getDepth() < depthToRead) {
backend.insertPastState(
import java.io.IOException;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeBackend;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
/**
* Stub class for the {@link HistoryTreeBackend}. It creates a
- * {@link HistoryTreeStub} to grant access to some protected methods.
+ * {@link HistoryTreeClassicStub} to grant access to some protected methods.
*
* @author Geneviève Bastien
*/
public class HistoryTreeBackendStub extends HistoryTreeBackend {
+ private static HistoryTreeType HT_TYPE = HistoryTreeType.CLASSIC;
+
+ /**
+ * Sets the type of tree to build. Since the history tree is initialized in
+ * the parent's constructor, this stub class needs to know the type of tree
+ * to build.
+ *
+ * @param htType
+ * The type of history tree to build for this backend
+ */
+ public static void setTreeType(HistoryTreeType htType) {
+ HT_TYPE = htType;
+ }
+
+ /**
+ * Enumeration of all history tree types implemented. This will be used to
+ * create the right type of history tree
+ */
+ public enum HistoryTreeType {
+ /**
+ * The classic history tree
+ */
+ CLASSIC
+ }
+
/**
* Constructor for new history files. Use this when creating a new history
* from scratch.
}
@Override
- protected HistoryTree initializeSHT(HTConfig conf) throws IOException {
- return new HistoryTreeStub(conf);
+ protected IHistoryTree initializeSHT(HTConfig conf) throws IOException {
+ switch (HT_TYPE) {
+ case CLASSIC:
+ return new HistoryTreeClassicStub(conf);
+ default:
+ return new HistoryTreeClassicStub(conf);
+ }
}
@Override
- protected HistoryTree initializeSHT(File existingStateFile, int providerVersion) throws IOException {
- return new HistoryTreeStub(existingStateFile, providerVersion);
+ protected IHistoryTree initializeSHT(File existingStateFile, int providerVersion) throws IOException {
+ switch (HT_TYPE) {
+ case CLASSIC:
+ return new HistoryTreeClassicStub(existingStateFile, providerVersion);
+ default:
+ return new HistoryTreeClassicStub(existingStateFile, providerVersion);
+ }
}
/**
*
* @return The history tree
*/
- public HistoryTreeStub getHistoryTree() {
- return (HistoryTreeStub) super.getSHT();
+ public HistoryTreeClassicStub getHistoryTree() {
+ return (HistoryTreeClassicStub) super.getSHT();
}
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2015 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.stubs.backend;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.util.List;
+
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.CoreNode;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTreeClassic;
+import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.IHistoryTree;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Stub class to unit test the history tree. You can set the size of the
+ * interval section before using the tree, in order to fine-tune the test.
+ *
+ * Note to developers: This tree is not meant to be used with a backend. It just
+ * exposes some info from the history tree.
+ *
+ * @author Geneviève Bastien
+ */
+public class HistoryTreeClassicStub extends HistoryTreeClassic {
+
+ /**
+ * Minimum size a block of this tree should have
+ */
+ public static final int MINIMUM_BLOCK_SIZE = IHistoryTree.TREE_HEADER_SIZE;
+
+ /**
+ * Constructor for this history tree stub
+ *
+ * @param conf
+ * The config to use for this History Tree.
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public HistoryTreeClassicStub(HTConfig conf) throws IOException {
+ super(conf);
+ }
+
+ /**
+ * "Reader" constructor : instantiate a SHTree from an existing tree file on
+ * disk
+ *
+ * @param existingStateFile
+ * Path/filename of the history-file we are to open
+ * @param expProviderVersion
+ * The expected version of the state provider
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public HistoryTreeClassicStub(File existingStateFile, int expProviderVersion) throws IOException {
+ super(existingStateFile, expProviderVersion);
+ }
+
+ @Override
+ public List<HTNode> getLatestBranch() {
+ return checkNotNull(super.getLatestBranch());
+ }
+
+ /**
+ * Get the latest leaf of the tree
+ *
+ * @return The current leaf node of the tree
+ */
+ public HTNode getLatestLeaf() {
+ List<HTNode> latest = getLatestBranch();
+ return Iterables.getLast(latest);
+ }
+
+ /**
+ * Get the node from the latest branch at a given position, 0 being the root
+ * and <size of latest branch - 1> being a leaf node.
+ *
+ * @param pos
+ * The position at which to return the node
+ * @return The node at position pos
+ */
+ public HTNode getNodeAt(int pos) {
+ List<HTNode> latest = getLatestBranch();
+ return latest.get(pos);
+ }
+
+ /**
+ * Get the depth of the tree
+ *
+ * @return The depth of the tree
+ */
+ public int getDepth() {
+ return getLatestBranch().size();
+ }
+
+ private void assertChildrenIntegrity(CoreNode node) {
+ try {
+ /*
+ * Test that this node's start and end times match the start of the
+ * first child and the end of the last child, respectively
+ */
+ if (node.getNbChildren() > 0) {
+ HTNode childNode = getNode(node.getChild(0));
+ assertEquals("Equals start time of parent " + node.getSequenceNumber() + " and first child " + childNode.getSequenceNumber(),
+ node.getNodeStart(), childNode.getNodeStart());
+ if (node.isOnDisk()) {
+ childNode = getNode(node.getLatestChild());
+ assertEquals("Equals end time of parent " + node.getSequenceNumber() + " and last child " + childNode.getSequenceNumber(),
+ node.getNodeEnd(), childNode.getNodeEnd());
+ }
+ }
+
+ /*
+ * Test that the childStartTimes[] array matches the real nodes'
+ * start times
+ *
+ * Also test that children range is within the parent's range
+ */
+ for (int i = 0; i < node.getNbChildren(); i++) {
+ HTNode childNode = getNode(node.getChild(i));
+ assertEquals("Start time in parent " + node.getSequenceNumber() + " of child at index " + i,
+ childNode.getNodeStart(), node.getChildStart(i));
+ assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
+ node.getNodeStart() <= childNode.getNodeStart());
+ if (node.isOnDisk() && childNode.isOnDisk()) {
+ assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
+ childNode.getNodeEnd() <= childNode.getNodeEnd());
+ }
+ }
+
+ } catch (ClosedChannelException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ /**
+ * Debugging method to make sure all intervals contained in the given node
+ * have valid start and end times.
+ *
+ * @param node
+ * The node to check
+ */
+ private void assertNodeIntegrity(HTNode node) {
+ if (node instanceof CoreNode) {
+ assertChildrenIntegrity((CoreNode) node);
+ }
+
+ /* Check that all intervals are within the node's range */
+ // TODO: Get the intervals of a node
+
+ }
+
+ /**
+ * Check the integrity of all the nodes in the tree. Calls
+ * {@link #assertNodeIntegrity} for every node in the tree.
+ */
+ public void assertIntegrity() {
+ try {
+ for (int i = 0; i < getNodeCount(); i++) {
+ assertNodeIntegrity(getNode(i));
+ }
+ } catch (ClosedChannelException e) {
+ fail(e.getMessage());
+ }
+ }
+
+}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2015 École Polytechnique de Montréal
- *
- * 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
- *******************************************************************************/
-
-package org.eclipse.tracecompass.statesystem.core.tests.stubs.backend;
-
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
-import java.util.List;
-
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.CoreNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
-import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HistoryTree;
-
-import com.google.common.collect.Iterables;
-
-/**
- * Stub class to unit test the history tree. You can set the size of the
- * interval section before using the tree, in order to fine-tune the test.
- *
- * Note to developers: This tree is not meant to be used with a backend. It just
- * exposes some info from the history tree.
- *
- * @author Geneviève Bastien
- */
-public class HistoryTreeStub extends HistoryTree {
-
- /**
- * Constructor for this history tree stub
- *
- * @param conf
- * The config to use for this History Tree.
- * @throws IOException
- * If an error happens trying to open/write to the file
- * specified in the config
- */
- public HistoryTreeStub(HTConfig conf) throws IOException {
- super(conf);
- }
-
- /**
- * "Reader" constructor : instantiate a SHTree from an existing tree file on
- * disk
- *
- * @param existingStateFile
- * Path/filename of the history-file we are to open
- * @param expProviderVersion
- * The expected version of the state provider
- * @throws IOException
- * If an error happens reading the file
- */
- public HistoryTreeStub(File existingStateFile, int expProviderVersion) throws IOException {
- super(existingStateFile, expProviderVersion);
- }
-
- @Override
- public List<HTNode> getLatestBranch() {
- return checkNotNull(super.getLatestBranch());
- }
-
- /**
- * Get the latest leaf of the tree
- *
- * @return The current leaf node of the tree
- */
- public HTNode getLatestLeaf() {
- List<HTNode> latest = getLatestBranch();
- return Iterables.getLast(latest);
- }
-
- /**
- * Get the node from the latest branch at a given position, 0 being the root
- * and <size of latest branch - 1> being a leaf node.
- *
- * @param pos
- * The position at which to return the node
- * @return The node at position pos
- */
- public HTNode getNodeAt(int pos) {
- List<HTNode> latest = getLatestBranch();
- return latest.get(pos);
- }
-
- /**
- * Get the depth of the tree
- *
- * @return The depth of the tree
- */
- public int getDepth() {
- return getLatestBranch().size();
- }
-
- private void assertChildrenIntegrity(CoreNode node) {
- try {
- /*
- * Test that this node's start and end times match the start of the
- * first child and the end of the last child, respectively
- */
- if (node.getNbChildren() > 0) {
- HTNode childNode = getNode(node.getChild(0));
- assertEquals("Equals start time of parent " + node.getSequenceNumber() + " and first child " + childNode.getSequenceNumber(),
- node.getNodeStart(), childNode.getNodeStart());
- if (node.isOnDisk()) {
- childNode = getNode(node.getLatestChild());
- assertEquals("Equals end time of parent " + node.getSequenceNumber() + " and last child " + childNode.getSequenceNumber(),
- node.getNodeEnd(), childNode.getNodeEnd());
- }
- }
-
- /*
- * Test that the childStartTimes[] array matches the real nodes'
- * start times
- *
- * Also test that children range is within the parent's range
- */
- for (int i = 0; i < node.getNbChildren(); i++) {
- HTNode childNode = getNode(node.getChild(i));
- assertEquals("Start time in parent " + node.getSequenceNumber() + " of child at index " + i,
- childNode.getNodeStart(), node.getChildStart(i));
- assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
- node.getNodeStart() <= childNode.getNodeStart());
- if (node.isOnDisk() && childNode.isOnDisk()) {
- assertTrue("Child at index " + i + " of parent " + node.getSequenceNumber() + " has correct start time",
- childNode.getNodeEnd() <= childNode.getNodeEnd());
- }
- }
-
- } catch (ClosedChannelException e) {
- fail(e.getMessage());
- }
- }
-
- /**
- * Debugging method to make sure all intervals contained in the given node
- * have valid start and end times.
- *
- * @param node
- * The node to check
- */
- private void assertNodeIntegrity(HTNode node) {
- if (node instanceof CoreNode) {
- assertChildrenIntegrity((CoreNode) node);
- }
-
- /* Check that all intervals are within the node's range */
- // TODO: Get the intervals of a node
-
- }
-
- /**
- * Check the integrity of all the nodes in the tree. Calls
- * {@link #assertNodeIntegrity} for every node in the tree.
- */
- public void assertIntegrity() {
- try {
- for (int i = 0; i < getNodeCount(); i++) {
- assertNodeIntegrity(getNode(i));
- }
- } catch (ClosedChannelException e) {
- fail(e.getMessage());
- }
- }
-
-}
import java.io.File;
/**
- * Configuration object for the {@link HistoryTree}.
+ * Configuration object for the {@link IHistoryTree}.
*
* @author Alexandre Montplaisir
*/
* Cast to (long) is needed to make sure the result is a long too and
* doesn't get truncated
*/
- fc.position(HistoryTree.TREE_HEADER_SIZE
+ fc.position(IHistoryTree.TREE_HEADER_SIZE
+ ((long) seqNumber) * fConfig.getBlockSize());
}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
- *
- * 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:
- * Alexandre Montplaisir - Initial API and implementation
- * Florian Wininger - Add Extension and Leaf Node
- * Patrick Tasse - Add message to exceptions
- *******************************************************************************/
-
-package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.internal.statesystem.core.Activator;
-import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
-import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Meta-container for the History Tree. This structure contains all the
- * high-level data relevant to the tree.
- *
- * @author Alexandre Montplaisir
- */
-public class HistoryTree {
-
- /**
- * Size of the "tree header" in the tree-file The nodes will use this offset
- * to know where they should be in the file. This should always be a
- * multiple of 4K.
- */
- public static final int TREE_HEADER_SIZE = 4096;
-
- /**
- * The magic number for this file format. Package-private for the factory
- * class.
- */
- static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
-
- /** File format version. Increment when breaking compatibility. */
- private static final int FILE_VERSION = 7;
-
- // ------------------------------------------------------------------------
- // Tree-specific configuration
- // ------------------------------------------------------------------------
-
- /** Container for all the configuration constants */
- private final HTConfig fConfig;
-
- /** Reader/writer object */
- private final HT_IO fTreeIO;
-
- // ------------------------------------------------------------------------
- // Variable Fields (will change throughout the existence of the SHT)
- // ------------------------------------------------------------------------
-
- /** Latest timestamp found in the tree (at any given moment) */
- private long fTreeEnd;
-
- /** The total number of nodes that exists in this tree */
- private int fNodeCount;
-
- /** "Cache" to keep the active nodes in memory */
- private final @NonNull List<@NonNull HTNode> fLatestBranch;
-
- // ------------------------------------------------------------------------
- // Constructors/"Destructors"
- // ------------------------------------------------------------------------
-
- /**
- * Create a new State History from scratch, using a {@link HTConfig} object
- * for configuration.
- *
- * @param conf
- * The config to use for this History Tree.
- * @throws IOException
- * If an error happens trying to open/write to the file
- * specified in the config
- */
- public HistoryTree(HTConfig conf) throws IOException {
- /*
- * Simple check to make sure we have enough place in the 0th block for
- * the tree configuration
- */
- if (conf.getBlockSize() < TREE_HEADER_SIZE) {
- throw new IllegalArgumentException();
- }
-
- fConfig = conf;
- fTreeEnd = conf.getTreeStart();
- fNodeCount = 0;
- fLatestBranch = Collections.synchronizedList(new ArrayList<>());
-
- /* Prepare the IO object */
- fTreeIO = new HT_IO(fConfig, true);
-
- /* Add the first node to the tree */
- LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
- fLatestBranch.add(firstNode);
- }
-
- /**
- * "Reader" constructor : instantiate a SHTree from an existing tree file on
- * disk
- *
- * @param existingStateFile
- * Path/filename of the history-file we are to open
- * @param expProviderVersion
- * The expected version of the state provider
- * @throws IOException
- * If an error happens reading the file
- */
- public HistoryTree(File existingStateFile, int expProviderVersion) throws IOException {
- /*
- * Open the file ourselves, get the tree header information we need,
- * then pass on the descriptor to the TreeIO object.
- */
- int rootNodeSeqNb, res;
- int bs, maxc;
- long startTime;
-
- /* Java I/O mumbo jumbo... */
- if (!existingStateFile.exists()) {
- throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
- }
- if (existingStateFile.length() <= 0) {
- throw new IOException("Empty target file"); //$NON-NLS-1$
- }
-
- try (FileInputStream fis = new FileInputStream(existingStateFile);
- FileChannel fc = fis.getChannel();) {
-
- ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
-
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.clear();
- fc.read(buffer);
- buffer.flip();
-
- /*
- * Check the magic number to make sure we're opening the right type
- * of file
- */
- res = buffer.getInt();
- if (res != HISTORY_FILE_MAGIC_NUMBER) {
- throw new IOException("Wrong magic number"); //$NON-NLS-1$
- }
-
- res = buffer.getInt(); /* File format version number */
- if (res != FILE_VERSION) {
- throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
- }
-
- res = buffer.getInt(); /* Event handler's version number */
- if (res != expProviderVersion &&
- expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
- /*
- * The existing history was built using an event handler that
- * doesn't match the current one in the framework.
- *
- * Information could be all wrong. Instead of keeping an
- * incorrect history file, a rebuild is done.
- */
- throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
- }
-
- bs = buffer.getInt(); /* Block Size */
- maxc = buffer.getInt(); /* Max nb of children per node */
-
- fNodeCount = buffer.getInt();
- rootNodeSeqNb = buffer.getInt();
- startTime = buffer.getLong();
-
- fConfig = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
- }
-
- /*
- * FIXME We close fis here and the TreeIO will then reopen the same
- * file, not extremely elegant. But how to pass the information here to
- * the SHT otherwise?
- */
- fTreeIO = new HT_IO(fConfig, false);
-
- fLatestBranch = buildLatestBranch(rootNodeSeqNb);
- fTreeEnd = getRootNode().getNodeEnd();
-
- /*
- * Make sure the history start time we read previously is consistent
- * with was is actually in the root node.
- */
- if (startTime != getRootNode().getNodeStart()) {
- throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
- "history file, it might be corrupted."); //$NON-NLS-1$
- }
- }
-
- /**
- * Rebuild the latestBranch "cache" object by reading the nodes from disk
- * (When we are opening an existing file on disk and want to append to it,
- * for example).
- *
- * @param rootNodeSeqNb
- * The sequence number of the root node, so we know where to
- * start
- * @throws ClosedChannelException
- */
- private @NonNull List<@NonNull HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
- List<@NonNull HTNode> list = new ArrayList<>();
-
- HTNode nextChildNode = fTreeIO.readNode(rootNodeSeqNb);
- list.add(nextChildNode);
-
- /* Follow the last branch up to the leaf */
- while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
- nextChildNode = fTreeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
- list.add(nextChildNode);
- }
- return Collections.synchronizedList(list);
- }
-
- /**
- * "Save" the tree to disk. This method will cause the treeIO object to
- * commit all nodes to disk and then return the RandomAccessFile descriptor
- * so the Tree object can save its configuration into the header of the
- * file.
- *
- * @param requestedEndTime
- * The greatest timestamp present in the history tree
- */
- public void closeTree(long requestedEndTime) {
- /* This is an important operation, queries can wait */
- synchronized (fLatestBranch) {
- /*
- * Work-around the "empty branches" that get created when the root
- * node becomes full. Overwrite the tree's end time with the
- * original wanted end-time, to ensure no queries are sent into
- * those empty nodes.
- *
- * This won't be needed once extended nodes are implemented.
- */
- fTreeEnd = requestedEndTime;
-
- /* Close off the latest branch of the tree */
- for (int i = 0; i < fLatestBranch.size(); i++) {
- fLatestBranch.get(i).closeThisNode(fTreeEnd);
- fTreeIO.writeNode(fLatestBranch.get(i));
- }
-
- try (FileChannel fc = fTreeIO.getFcOut();) {
- ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.clear();
-
- /* Save the config of the tree to the header of the file */
- fc.position(0);
-
- buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
-
- buffer.putInt(FILE_VERSION);
- buffer.putInt(fConfig.getProviderVersion());
-
- buffer.putInt(fConfig.getBlockSize());
- buffer.putInt(fConfig.getMaxChildren());
-
- buffer.putInt(fNodeCount);
-
- /* root node seq. nb */
- buffer.putInt(fLatestBranch.get(0).getSequenceNumber());
-
- /* start time of this history */
- buffer.putLong(fLatestBranch.get(0).getNodeStart());
-
- buffer.flip();
- int res = fc.write(buffer);
- assert (res <= TREE_HEADER_SIZE);
- /* done writing the file header */
-
- } catch (IOException e) {
- /*
- * If we were able to write so far, there should not be any
- * problem at this point...
- */
- throw new RuntimeException("State system write error"); //$NON-NLS-1$
- }
- }
- }
-
- // ------------------------------------------------------------------------
- // Accessors
- // ------------------------------------------------------------------------
-
- /**
- * Get the start time of this tree.
- *
- * @return The start time
- */
- public long getTreeStart() {
- return fConfig.getTreeStart();
- }
-
- /**
- * Get the current end time of this tree.
- *
- * @return The end time
- */
- public long getTreeEnd() {
- return fTreeEnd;
- }
-
- /**
- * Get the number of nodes in this tree.
- *
- * @return The number of nodes
- */
- public int getNodeCount() {
- return fNodeCount;
- }
-
- /**
- * Get the current root node of this tree
- *
- * @return The root node
- */
- public HTNode getRootNode() {
- return fLatestBranch.get(0);
- }
-
- /**
- * Return the latest branch of the tree. That branch is immutable. Used for
- * unit testing and debugging.
- *
- * @return The immutable latest branch
- */
- @VisibleForTesting
- protected List<@NonNull HTNode> getLatestBranch() {
- return ImmutableList.copyOf(fLatestBranch);
- }
-
- /**
- * Read a node at sequence number
- *
- * @param seqNum
- * The sequence number of the node to read
- * @return The HTNode object
- * @throws ClosedChannelException
- * Exception thrown when reading the node
- */
- @VisibleForTesting
- protected @NonNull HTNode getNode(int seqNum) throws ClosedChannelException {
- // First, check in the latest branch if the node is there
- for (HTNode node : fLatestBranch) {
- if (node.getSequenceNumber() == seqNum) {
- return node;
- }
- }
- return fTreeIO.readNode(seqNum);
- }
-
- // ------------------------------------------------------------------------
- // HT_IO interface
- // ------------------------------------------------------------------------
-
- /**
- * Return the FileInputStream reader with which we will read an attribute
- * tree (it will be sought to the correct position).
- *
- * @return The FileInputStream indicating the file and position from which
- * the attribute tree can be read.
- */
- public FileInputStream supplyATReader() {
- return fTreeIO.supplyATReader(getNodeCount());
- }
-
- /**
- * Return the file to which we will write the attribute tree.
- *
- * @return The file to which we will write the attribute tree
- */
- public File supplyATWriterFile() {
- return fConfig.getStateFile();
- }
-
- /**
- * Return the position in the file (given by {@link #supplyATWriterFile})
- * where to start writing the attribute tree.
- *
- * @return The position in the file where to start writing
- */
- public long supplyATWriterFilePos() {
- return HistoryTree.TREE_HEADER_SIZE
- + ((long) getNodeCount() * fConfig.getBlockSize());
- }
-
- /**
- * Read a node from the tree.
- *
- * @param seqNumber
- * The sequence number of the node to read
- * @return The node
- * @throws ClosedChannelException
- * If the tree IO is unavailable
- */
- public HTNode readNode(int seqNumber) throws ClosedChannelException {
- /* Try to read the node from memory */
- synchronized (fLatestBranch) {
- for (HTNode node : fLatestBranch) {
- if (node.getSequenceNumber() == seqNumber) {
- return node;
- }
- }
- }
-
- /* Read the node from disk */
- return fTreeIO.readNode(seqNumber);
- }
-
- /**
- * Write a node object to the history file.
- *
- * @param node
- * The node to write to disk
- */
- public void writeNode(HTNode node) {
- fTreeIO.writeNode(node);
- }
-
- /**
- * Close the history file.
- */
- public void closeFile() {
- fTreeIO.closeFile();
- }
-
- /**
- * Delete the history file.
- */
- public void deleteFile() {
- fTreeIO.deleteFile();
- }
-
- // ------------------------------------------------------------------------
- // Operations
- // ------------------------------------------------------------------------
-
- /**
- * Insert an interval in the tree.
- *
- * @param interval
- * The interval to be inserted
- * @throws TimeRangeException
- * If the start of end time of the interval are invalid
- */
- public void insertInterval(HTInterval interval) throws TimeRangeException {
- if (interval.getStartTime() < fConfig.getTreeStart()) {
- throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + fConfig.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
- }
- tryInsertAtNode(interval, fLatestBranch.size() - 1);
- }
-
- /**
- * Inner method to find in which node we should add the interval.
- *
- * @param interval
- * The interval to add to the tree
- * @param indexOfNode
- * The index *in the latestBranch* where we are trying the
- * insertion
- */
- private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
- HTNode targetNode = fLatestBranch.get(indexOfNode);
-
- /* Verify if there is enough room in this node to store this interval */
- if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
- /* Nope, not enough room. Insert in a new sibling instead. */
- addSiblingNode(indexOfNode);
- tryInsertAtNode(interval, fLatestBranch.size() - 1);
- return;
- }
-
- /* Make sure the interval time range fits this node */
- if (interval.getStartTime() < targetNode.getNodeStart()) {
- /*
- * No, this interval starts before the startTime of this node. We
- * need to check recursively in parents if it can fit.
- */
- tryInsertAtNode(interval, indexOfNode - 1);
- return;
- }
-
- /*
- * Ok, there is room, and the interval fits in this time slot. Let's add
- * it.
- */
- targetNode.addInterval(interval);
-
- /* Update treeEnd if needed */
- if (interval.getEndTime() > fTreeEnd) {
- fTreeEnd = interval.getEndTime();
- }
- }
-
- /**
- * Method to add a sibling to any node in the latest branch. This will add
- * children back down to the leaf level, if needed.
- *
- * @param indexOfNode
- * The index in latestBranch where we start adding
- */
- private void addSiblingNode(int indexOfNode) {
- synchronized (fLatestBranch) {
- final long splitTime = fTreeEnd;
-
- if (indexOfNode >= fLatestBranch.size()) {
- /*
- * We need to make sure (indexOfNode - 1) doesn't get the last
- * node in the branch, because that one is a Leaf Node.
- */
- throw new IllegalStateException();
- }
-
- /* Check if we need to add a new root node */
- if (indexOfNode == 0) {
- addNewRootNode();
- return;
- }
-
- /* Check if we can indeed add a child to the target parent */
- if (((CoreNode) fLatestBranch.get(indexOfNode - 1)).getNbChildren() == fConfig.getMaxChildren()) {
- /* If not, add a branch starting one level higher instead */
- addSiblingNode(indexOfNode - 1);
- return;
- }
-
- /* Split off the new branch from the old one */
- for (int i = indexOfNode; i < fLatestBranch.size(); i++) {
- fLatestBranch.get(i).closeThisNode(splitTime);
- fTreeIO.writeNode(fLatestBranch.get(i));
-
- CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
- HTNode newNode;
-
- switch (fLatestBranch.get(i).getNodeType()) {
- case CORE:
- newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
- break;
- case LEAF:
- newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
- break;
- default:
- throw new IllegalStateException();
- }
-
- prevNode.linkNewChild(newNode);
- fLatestBranch.set(i, newNode);
- }
- }
- }
-
- /**
- * Similar to the previous method, except here we rebuild a completely new
- * latestBranch
- */
- private void addNewRootNode() {
- final long splitTime = fTreeEnd;
-
- HTNode oldRootNode = fLatestBranch.get(0);
- CoreNode newRootNode = initNewCoreNode(-1, fConfig.getTreeStart());
-
- /* Tell the old root node that it isn't root anymore */
- oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
-
- /* Close off the whole current latestBranch */
-
- for (int i = 0; i < fLatestBranch.size(); i++) {
- fLatestBranch.get(i).closeThisNode(splitTime);
- fTreeIO.writeNode(fLatestBranch.get(i));
- }
-
- /* Link the new root to its first child (the previous root node) */
- newRootNode.linkNewChild(oldRootNode);
-
- /* Rebuild a new latestBranch */
- int depth = fLatestBranch.size();
- fLatestBranch.clear();
- fLatestBranch.add(newRootNode);
-
- // Create new coreNode
- for (int i = 1; i < depth; i++) {
- CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
- CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
- splitTime + 1);
- prevNode.linkNewChild(newNode);
- fLatestBranch.add(newNode);
- }
-
- // Create the new leafNode
- CoreNode prevNode = (CoreNode) fLatestBranch.get(depth - 1);
- LeafNode newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
- prevNode.linkNewChild(newNode);
- fLatestBranch.add(newNode);
- }
-
- /**
- * Add a new empty core node to the tree.
- *
- * @param parentSeqNumber
- * Sequence number of this node's parent
- * @param startTime
- * Start time of the new node
- * @return The newly created node
- */
- private @NonNull CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
- CoreNode newNode = new CoreNode(fConfig, fNodeCount, parentSeqNumber,
- startTime);
- fNodeCount++;
- return newNode;
- }
-
- /**
- * Add a new empty leaf node to the tree.
- *
- * @param parentSeqNumber
- * Sequence number of this node's parent
- * @param startTime
- * Start time of the new node
- * @return The newly created node
- */
- private @NonNull LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
- LeafNode newNode = new LeafNode(fConfig, fNodeCount, parentSeqNumber,
- startTime);
- fNodeCount++;
- return newNode;
- }
-
- /**
- * Inner method to select the next child of the current node intersecting
- * the given timestamp. Useful for moving down the tree following one
- * branch.
- *
- * @param currentNode
- * The node on which the request is made
- * @param t
- * The timestamp to choose which child is the next one
- * @return The child node intersecting t
- * @throws ClosedChannelException
- * If the file channel was closed while we were reading the tree
- */
- public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
- assert (currentNode.getNbChildren() > 0);
- int potentialNextSeqNb = currentNode.getSequenceNumber();
-
- for (int i = 0; i < currentNode.getNbChildren(); i++) {
- if (t >= currentNode.getChildStart(i)) {
- potentialNextSeqNb = currentNode.getChild(i);
- } else {
- break;
- }
- }
-
- /*
- * Once we exit this loop, we should have found a children to follow. If
- * we didn't, there's a problem.
- */
- if (potentialNextSeqNb == currentNode.getSequenceNumber()) {
- throw new IllegalStateException("No next child node found"); //$NON-NLS-1$
- }
-
- /*
- * Since this code path is quite performance-critical, avoid iterating
- * through the whole latestBranch array if we know for sure the next
- * node has to be on disk
- */
- if (currentNode.isOnDisk()) {
- return fTreeIO.readNode(potentialNextSeqNb);
- }
- return readNode(potentialNextSeqNb);
- }
-
- /**
- * Get the current size of the history file.
- *
- * @return The history file size
- */
- public long getFileSize() {
- return fConfig.getStateFile().length();
- }
-
- // ------------------------------------------------------------------------
- // Test/debugging methods
- // ------------------------------------------------------------------------
-
- /* Only used for debugging, shouldn't be externalized */
- @SuppressWarnings("nls")
- @Override
- public String toString() {
- return "Information on the current tree:\n\n" + "Blocksize: "
- + fConfig.getBlockSize() + "\n" + "Max nb. of children per node: "
- + fConfig.getMaxChildren() + "\n" + "Number of nodes: " + fNodeCount
- + "\n" + "Depth of the tree: " + fLatestBranch.size() + "\n"
- + "Size of the treefile: " + getFileSize() + "\n"
- + "Root node has sequence number: "
- + fLatestBranch.get(0).getSequenceNumber() + "\n"
- + "'Latest leaf' has sequence number: "
- + fLatestBranch.get(fLatestBranch.size() - 1).getSequenceNumber();
- }
-
- /**
- * Start at currentNode and print the contents of all its children, in
- * pre-order. Give the root node in parameter to visit the whole tree, and
- * have a nice overview.
- */
- /* Only used for debugging, shouldn't be externalized */
- @SuppressWarnings("nls")
- private void preOrderPrint(PrintWriter writer, boolean printIntervals,
- HTNode currentNode, int curDepth, long ts) {
-
- writer.println(currentNode.toString());
- /* Print intervals only if timestamp is negative or within the range of the node */
- if (printIntervals &&
- (ts <= 0 ||
- (ts >= currentNode.getNodeStart() && ts <= currentNode.getNodeEnd()))) {
- currentNode.debugPrintIntervals(writer);
- }
-
- switch (currentNode.getNodeType()) {
- case LEAF:
- /* Stop if it's the leaf node */
- return;
-
- case CORE:
- try {
- final CoreNode node = (CoreNode) currentNode;
- /* Print the extensions, if any */
- int extension = node.getExtensionSequenceNumber();
- while (extension != -1) {
- HTNode nextNode = fTreeIO.readNode(extension);
- preOrderPrint(writer, printIntervals, nextNode, curDepth, ts);
- }
-
- /* Print the child nodes */
- for (int i = 0; i < node.getNbChildren(); i++) {
- HTNode nextNode = fTreeIO.readNode(node.getChild(i));
- for (int j = 0; j < curDepth; j++) {
- writer.print(" ");
- }
- writer.print("+-");
- preOrderPrint(writer, printIntervals, nextNode, curDepth + 1, ts);
- }
- } catch (ClosedChannelException e) {
- Activator.getDefault().logError(e.getMessage());
- }
- break;
-
- default:
- break;
- }
- }
-
- /**
- * Print out the full tree for debugging purposes
- *
- * @param writer
- * PrintWriter in which to write the output
- * @param printIntervals
- * Flag to enable full output of the interval information
- * @param ts
- * The timestamp that nodes have to intersect for intervals to be
- * printed. A negative value will print intervals for all nodes.
- * The timestamp only applies if printIntervals is true.
- */
- public void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts) {
- /* Only used for debugging, shouldn't be externalized */
-
- preOrderPrint(writer, false, fLatestBranch.get(0), 0, ts);
-
- if (printIntervals) {
- preOrderPrint(writer, true, fLatestBranch.get(0), 0, ts);
- }
- writer.println('\n');
- }
-
-}
/**
* The history tree that sits underneath.
*/
- private final @NonNull HistoryTree fSht;
+ private final @NonNull IHistoryTree fSht;
/** Indicates if the history tree construction is done */
private volatile boolean fFinishedBuilding = false;
* If there was a problem during creation
*/
@VisibleForTesting
- protected @NonNull HistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
+ protected @NonNull IHistoryTree initializeSHT(@NonNull HTConfig conf) throws IOException {
return HistoryTreeFactory.createHistoryTree(conf);
}
* If there was a problem during creation
*/
@VisibleForTesting
- protected @NonNull HistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
+ protected @NonNull IHistoryTree initializeSHT(@NonNull File existingStateFile, int providerVersion) throws IOException {
return HistoryTreeFactory.createFromFile(existingStateFile.toPath(), providerVersion);
}
*
* @return The history tree
*/
- protected final @NonNull HistoryTree getSHT() {
+ protected final @NonNull IHistoryTree getSHT() {
return fSht;
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
+ *
+ * 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:
+ * Alexandre Montplaisir - Initial API and implementation
+ * Florian Wininger - Add Extension and Leaf Node
+ * Patrick Tasse - Add message to exceptions
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.statesystem.core.Activator;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Meta-container for the History Tree. This structure contains all the
+ * high-level data relevant to the tree.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class HistoryTreeClassic implements IHistoryTree {
+
+ /**
+ * The magic number for this file format. Package-private for the factory
+ * class.
+ */
+ static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
+
+ /** File format version. Increment when breaking compatibility. */
+ private static final int FILE_VERSION = 7;
+
+ // ------------------------------------------------------------------------
+ // Tree-specific configuration
+ // ------------------------------------------------------------------------
+
+ /** Container for all the configuration constants */
+ private final HTConfig fConfig;
+
+ /** Reader/writer object */
+ private final HT_IO fTreeIO;
+
+ // ------------------------------------------------------------------------
+ // Variable Fields (will change throughout the existence of the SHT)
+ // ------------------------------------------------------------------------
+
+ /** Latest timestamp found in the tree (at any given moment) */
+ private long fTreeEnd;
+
+ /** The total number of nodes that exists in this tree */
+ private int fNodeCount;
+
+ /** "Cache" to keep the active nodes in memory */
+ private final @NonNull List<@NonNull HTNode> fLatestBranch;
+
+ // ------------------------------------------------------------------------
+ // Constructors/"Destructors"
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create a new State History from scratch, using a {@link HTConfig} object
+ * for configuration.
+ *
+ * @param conf
+ * The config to use for this History Tree.
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public HistoryTreeClassic(HTConfig conf) throws IOException {
+ /*
+ * Simple check to make sure we have enough place in the 0th block for
+ * the tree configuration
+ */
+ if (conf.getBlockSize() < TREE_HEADER_SIZE) {
+ throw new IllegalArgumentException();
+ }
+
+ fConfig = conf;
+ fTreeEnd = conf.getTreeStart();
+ fNodeCount = 0;
+ fLatestBranch = Collections.synchronizedList(new ArrayList<>());
+
+ /* Prepare the IO object */
+ fTreeIO = new HT_IO(fConfig, true);
+
+ /* Add the first node to the tree */
+ LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
+ fLatestBranch.add(firstNode);
+ }
+
+ /**
+ * "Reader" constructor : instantiate a SHTree from an existing tree file on
+ * disk
+ *
+ * @param existingStateFile
+ * Path/filename of the history-file we are to open
+ * @param expProviderVersion
+ * The expected version of the state provider
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public HistoryTreeClassic(File existingStateFile, int expProviderVersion) throws IOException {
+ /*
+ * Open the file ourselves, get the tree header information we need,
+ * then pass on the descriptor to the TreeIO object.
+ */
+ int rootNodeSeqNb, res;
+ int bs, maxc;
+ long startTime;
+
+ /* Java I/O mumbo jumbo... */
+ if (!existingStateFile.exists()) {
+ throw new IOException("Selected state file does not exist"); //$NON-NLS-1$
+ }
+ if (existingStateFile.length() <= 0) {
+ throw new IOException("Empty target file"); //$NON-NLS-1$
+ }
+
+ try (FileInputStream fis = new FileInputStream(existingStateFile);
+ FileChannel fc = fis.getChannel();) {
+
+ ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
+
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.clear();
+ fc.read(buffer);
+ buffer.flip();
+
+ /*
+ * Check the magic number to make sure we're opening the right type
+ * of file
+ */
+ res = buffer.getInt();
+ if (res != HISTORY_FILE_MAGIC_NUMBER) {
+ throw new IOException("Wrong magic number"); //$NON-NLS-1$
+ }
+
+ res = buffer.getInt(); /* File format version number */
+ if (res != FILE_VERSION) {
+ throw new IOException("Mismatching History Tree file format versions"); //$NON-NLS-1$
+ }
+
+ res = buffer.getInt(); /* Event handler's version number */
+ if (res != expProviderVersion &&
+ expProviderVersion != ITmfStateSystemBuilder.IGNORE_PROVIDER_VERSION) {
+ /*
+ * The existing history was built using an event handler that
+ * doesn't match the current one in the framework.
+ *
+ * Information could be all wrong. Instead of keeping an
+ * incorrect history file, a rebuild is done.
+ */
+ throw new IOException("Mismatching event handler versions"); //$NON-NLS-1$
+ }
+
+ bs = buffer.getInt(); /* Block Size */
+ maxc = buffer.getInt(); /* Max nb of children per node */
+
+ fNodeCount = buffer.getInt();
+ rootNodeSeqNb = buffer.getInt();
+ startTime = buffer.getLong();
+
+ fConfig = new HTConfig(existingStateFile, bs, maxc, expProviderVersion, startTime);
+ }
+
+ /*
+ * FIXME We close fis here and the TreeIO will then reopen the same
+ * file, not extremely elegant. But how to pass the information here to
+ * the SHT otherwise?
+ */
+ fTreeIO = new HT_IO(fConfig, false);
+
+ fLatestBranch = buildLatestBranch(rootNodeSeqNb);
+ fTreeEnd = getRootNode().getNodeEnd();
+
+ /*
+ * Make sure the history start time we read previously is consistent
+ * with was is actually in the root node.
+ */
+ if (startTime != getRootNode().getNodeStart()) {
+ throw new IOException("Inconsistent start times in the" + //$NON-NLS-1$
+ "history file, it might be corrupted."); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Rebuild the latestBranch "cache" object by reading the nodes from disk
+ * (When we are opening an existing file on disk and want to append to it,
+ * for example).
+ *
+ * @param rootNodeSeqNb
+ * The sequence number of the root node, so we know where to
+ * start
+ * @throws ClosedChannelException
+ */
+ private @NonNull List<@NonNull HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
+ List<@NonNull HTNode> list = new ArrayList<>();
+
+ HTNode nextChildNode = fTreeIO.readNode(rootNodeSeqNb);
+ list.add(nextChildNode);
+
+ /* Follow the last branch up to the leaf */
+ while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
+ nextChildNode = fTreeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
+ list.add(nextChildNode);
+ }
+ return Collections.synchronizedList(list);
+ }
+
+ @Override
+ public void closeTree(long requestedEndTime) {
+ /* This is an important operation, queries can wait */
+ synchronized (fLatestBranch) {
+ /*
+ * Work-around the "empty branches" that get created when the root
+ * node becomes full. Overwrite the tree's end time with the
+ * original wanted end-time, to ensure no queries are sent into
+ * those empty nodes.
+ *
+ * This won't be needed once extended nodes are implemented.
+ */
+ fTreeEnd = requestedEndTime;
+
+ /* Close off the latest branch of the tree */
+ for (int i = 0; i < fLatestBranch.size(); i++) {
+ fLatestBranch.get(i).closeThisNode(fTreeEnd);
+ fTreeIO.writeNode(fLatestBranch.get(i));
+ }
+
+ try (FileChannel fc = fTreeIO.getFcOut();) {
+ ByteBuffer buffer = ByteBuffer.allocate(TREE_HEADER_SIZE);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.clear();
+
+ /* Save the config of the tree to the header of the file */
+ fc.position(0);
+
+ buffer.putInt(HISTORY_FILE_MAGIC_NUMBER);
+
+ buffer.putInt(FILE_VERSION);
+ buffer.putInt(fConfig.getProviderVersion());
+
+ buffer.putInt(fConfig.getBlockSize());
+ buffer.putInt(fConfig.getMaxChildren());
+
+ buffer.putInt(fNodeCount);
+
+ /* root node seq. nb */
+ buffer.putInt(fLatestBranch.get(0).getSequenceNumber());
+
+ /* start time of this history */
+ buffer.putLong(fLatestBranch.get(0).getNodeStart());
+
+ buffer.flip();
+ int res = fc.write(buffer);
+ assert (res <= TREE_HEADER_SIZE);
+ /* done writing the file header */
+
+ } catch (IOException e) {
+ /*
+ * If we were able to write so far, there should not be any
+ * problem at this point...
+ */
+ throw new RuntimeException("State system write error"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Accessors
+ // ------------------------------------------------------------------------
+
+ @Override
+ public long getTreeStart() {
+ return fConfig.getTreeStart();
+ }
+
+ @Override
+ public long getTreeEnd() {
+ return fTreeEnd;
+ }
+
+ @Override
+ public int getNodeCount() {
+ return fNodeCount;
+ }
+
+ @Override
+ public HTNode getRootNode() {
+ return fLatestBranch.get(0);
+ }
+
+ /**
+ * Return the latest branch of the tree. That branch is immutable. Used for
+ * unit testing and debugging.
+ *
+ * @return The immutable latest branch
+ */
+ @VisibleForTesting
+ protected List<@NonNull HTNode> getLatestBranch() {
+ return ImmutableList.copyOf(fLatestBranch);
+ }
+
+ /**
+ * Read a node at sequence number
+ *
+ * @param seqNum
+ * The sequence number of the node to read
+ * @return The HTNode object
+ * @throws ClosedChannelException
+ * Exception thrown when reading the node
+ */
+ @VisibleForTesting
+ protected @NonNull HTNode getNode(int seqNum) throws ClosedChannelException {
+ // First, check in the latest branch if the node is there
+ for (HTNode node : fLatestBranch) {
+ if (node.getSequenceNumber() == seqNum) {
+ return node;
+ }
+ }
+ return fTreeIO.readNode(seqNum);
+ }
+
+ // ------------------------------------------------------------------------
+ // HT_IO interface
+ // ------------------------------------------------------------------------
+
+ @Override
+ public FileInputStream supplyATReader() {
+ return fTreeIO.supplyATReader(getNodeCount());
+ }
+
+ @Override
+ public File supplyATWriterFile() {
+ return fConfig.getStateFile();
+ }
+
+ @Override
+ public long supplyATWriterFilePos() {
+ return IHistoryTree.TREE_HEADER_SIZE
+ + ((long) getNodeCount() * fConfig.getBlockSize());
+ }
+
+ @Override
+ public HTNode readNode(int seqNumber) throws ClosedChannelException {
+ /* Try to read the node from memory */
+ synchronized (fLatestBranch) {
+ for (HTNode node : fLatestBranch) {
+ if (node.getSequenceNumber() == seqNumber) {
+ return node;
+ }
+ }
+ }
+
+ /* Read the node from disk */
+ return fTreeIO.readNode(seqNumber);
+ }
+
+ @Override
+ public void writeNode(HTNode node) {
+ fTreeIO.writeNode(node);
+ }
+
+ @Override
+ public void closeFile() {
+ fTreeIO.closeFile();
+ }
+
+ @Override
+ public void deleteFile() {
+ fTreeIO.deleteFile();
+ }
+
+ // ------------------------------------------------------------------------
+ // Operations
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void insertInterval(HTInterval interval) throws TimeRangeException {
+ if (interval.getStartTime() < fConfig.getTreeStart()) {
+ throw new TimeRangeException("Interval Start:" + interval.getStartTime() + ", Config Start:" + fConfig.getTreeStart()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ tryInsertAtNode(interval, fLatestBranch.size() - 1);
+ }
+
+ /**
+ * Inner method to find in which node we should add the interval.
+ *
+ * @param interval
+ * The interval to add to the tree
+ * @param indexOfNode
+ * The index *in the latestBranch* where we are trying the
+ * insertion
+ */
+ private void tryInsertAtNode(HTInterval interval, int indexOfNode) {
+ HTNode targetNode = fLatestBranch.get(indexOfNode);
+
+ /* Verify if there is enough room in this node to store this interval */
+ if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
+ /* Nope, not enough room. Insert in a new sibling instead. */
+ addSiblingNode(indexOfNode);
+ tryInsertAtNode(interval, fLatestBranch.size() - 1);
+ return;
+ }
+
+ /* Make sure the interval time range fits this node */
+ if (interval.getStartTime() < targetNode.getNodeStart()) {
+ /*
+ * No, this interval starts before the startTime of this node. We
+ * need to check recursively in parents if it can fit.
+ */
+ tryInsertAtNode(interval, indexOfNode - 1);
+ return;
+ }
+
+ /*
+ * Ok, there is room, and the interval fits in this time slot. Let's add
+ * it.
+ */
+ targetNode.addInterval(interval);
+
+ /* Update treeEnd if needed */
+ if (interval.getEndTime() > fTreeEnd) {
+ fTreeEnd = interval.getEndTime();
+ }
+ }
+
+ /**
+ * Method to add a sibling to any node in the latest branch. This will add
+ * children back down to the leaf level, if needed.
+ *
+ * @param indexOfNode
+ * The index in latestBranch where we start adding
+ */
+ private void addSiblingNode(int indexOfNode) {
+ synchronized (fLatestBranch) {
+ final long splitTime = fTreeEnd;
+
+ if (indexOfNode >= fLatestBranch.size()) {
+ /*
+ * We need to make sure (indexOfNode - 1) doesn't get the last
+ * node in the branch, because that one is a Leaf Node.
+ */
+ throw new IllegalStateException();
+ }
+
+ /* Check if we need to add a new root node */
+ if (indexOfNode == 0) {
+ addNewRootNode();
+ return;
+ }
+
+ /* Check if we can indeed add a child to the target parent */
+ if (((CoreNode) fLatestBranch.get(indexOfNode - 1)).getNbChildren() == fConfig.getMaxChildren()) {
+ /* If not, add a branch starting one level higher instead */
+ addSiblingNode(indexOfNode - 1);
+ return;
+ }
+
+ /* Split off the new branch from the old one */
+ for (int i = indexOfNode; i < fLatestBranch.size(); i++) {
+ fLatestBranch.get(i).closeThisNode(splitTime);
+ fTreeIO.writeNode(fLatestBranch.get(i));
+
+ CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
+ HTNode newNode;
+
+ switch (fLatestBranch.get(i).getNodeType()) {
+ case CORE:
+ newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
+ break;
+ case LEAF:
+ newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ prevNode.linkNewChild(newNode);
+ fLatestBranch.set(i, newNode);
+ }
+ }
+ }
+
+ /**
+ * Similar to the previous method, except here we rebuild a completely new
+ * latestBranch
+ */
+ private void addNewRootNode() {
+ final long splitTime = fTreeEnd;
+
+ HTNode oldRootNode = fLatestBranch.get(0);
+ CoreNode newRootNode = initNewCoreNode(-1, fConfig.getTreeStart());
+
+ /* Tell the old root node that it isn't root anymore */
+ oldRootNode.setParentSequenceNumber(newRootNode.getSequenceNumber());
+
+ /* Close off the whole current latestBranch */
+
+ for (int i = 0; i < fLatestBranch.size(); i++) {
+ fLatestBranch.get(i).closeThisNode(splitTime);
+ fTreeIO.writeNode(fLatestBranch.get(i));
+ }
+
+ /* Link the new root to its first child (the previous root node) */
+ newRootNode.linkNewChild(oldRootNode);
+
+ /* Rebuild a new latestBranch */
+ int depth = fLatestBranch.size();
+ fLatestBranch.clear();
+ fLatestBranch.add(newRootNode);
+
+ // Create new coreNode
+ for (int i = 1; i < depth; i++) {
+ CoreNode prevNode = (CoreNode) fLatestBranch.get(i - 1);
+ CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
+ splitTime + 1);
+ prevNode.linkNewChild(newNode);
+ fLatestBranch.add(newNode);
+ }
+
+ // Create the new leafNode
+ CoreNode prevNode = (CoreNode) fLatestBranch.get(depth - 1);
+ LeafNode newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
+ prevNode.linkNewChild(newNode);
+ fLatestBranch.add(newNode);
+ }
+
+ /**
+ * Add a new empty core node to the tree.
+ *
+ * @param parentSeqNumber
+ * Sequence number of this node's parent
+ * @param startTime
+ * Start time of the new node
+ * @return The newly created node
+ */
+ private @NonNull CoreNode initNewCoreNode(int parentSeqNumber, long startTime) {
+ CoreNode newNode = new CoreNode(fConfig, fNodeCount, parentSeqNumber,
+ startTime);
+ fNodeCount++;
+ return newNode;
+ }
+
+ /**
+ * Add a new empty leaf node to the tree.
+ *
+ * @param parentSeqNumber
+ * Sequence number of this node's parent
+ * @param startTime
+ * Start time of the new node
+ * @return The newly created node
+ */
+ private @NonNull LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
+ LeafNode newNode = new LeafNode(fConfig, fNodeCount, parentSeqNumber,
+ startTime);
+ fNodeCount++;
+ return newNode;
+ }
+
+ @Override
+ public HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException {
+ assert (currentNode.getNbChildren() > 0);
+ int potentialNextSeqNb = currentNode.getSequenceNumber();
+
+ for (int i = 0; i < currentNode.getNbChildren(); i++) {
+ if (t >= currentNode.getChildStart(i)) {
+ potentialNextSeqNb = currentNode.getChild(i);
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * Once we exit this loop, we should have found a children to follow. If
+ * we didn't, there's a problem.
+ */
+ if (potentialNextSeqNb == currentNode.getSequenceNumber()) {
+ throw new IllegalStateException("No next child node found"); //$NON-NLS-1$
+ }
+
+ /*
+ * Since this code path is quite performance-critical, avoid iterating
+ * through the whole latestBranch array if we know for sure the next
+ * node has to be on disk
+ */
+ if (currentNode.isOnDisk()) {
+ return fTreeIO.readNode(potentialNextSeqNb);
+ }
+ return readNode(potentialNextSeqNb);
+ }
+
+ @Override
+ public long getFileSize() {
+ return fConfig.getStateFile().length();
+ }
+
+ // ------------------------------------------------------------------------
+ // Test/debugging methods
+ // ------------------------------------------------------------------------
+
+ /* Only used for debugging, shouldn't be externalized */
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ return "Information on the current tree:\n\n" + "Blocksize: "
+ + fConfig.getBlockSize() + "\n" + "Max nb. of children per node: "
+ + fConfig.getMaxChildren() + "\n" + "Number of nodes: " + fNodeCount
+ + "\n" + "Depth of the tree: " + fLatestBranch.size() + "\n"
+ + "Size of the treefile: " + getFileSize() + "\n"
+ + "Root node has sequence number: "
+ + fLatestBranch.get(0).getSequenceNumber() + "\n"
+ + "'Latest leaf' has sequence number: "
+ + fLatestBranch.get(fLatestBranch.size() - 1).getSequenceNumber();
+ }
+
+ /**
+ * Start at currentNode and print the contents of all its children, in
+ * pre-order. Give the root node in parameter to visit the whole tree, and
+ * have a nice overview.
+ */
+ /* Only used for debugging, shouldn't be externalized */
+ @SuppressWarnings("nls")
+ private void preOrderPrint(PrintWriter writer, boolean printIntervals,
+ HTNode currentNode, int curDepth, long ts) {
+
+ writer.println(currentNode.toString());
+ /* Print intervals only if timestamp is negative or within the range of the node */
+ if (printIntervals &&
+ (ts <= 0 ||
+ (ts >= currentNode.getNodeStart() && ts <= currentNode.getNodeEnd()))) {
+ currentNode.debugPrintIntervals(writer);
+ }
+
+ switch (currentNode.getNodeType()) {
+ case LEAF:
+ /* Stop if it's the leaf node */
+ return;
+
+ case CORE:
+ try {
+ final CoreNode node = (CoreNode) currentNode;
+ /* Print the extensions, if any */
+ int extension = node.getExtensionSequenceNumber();
+ while (extension != -1) {
+ HTNode nextNode = fTreeIO.readNode(extension);
+ preOrderPrint(writer, printIntervals, nextNode, curDepth, ts);
+ }
+
+ /* Print the child nodes */
+ for (int i = 0; i < node.getNbChildren(); i++) {
+ HTNode nextNode = fTreeIO.readNode(node.getChild(i));
+ for (int j = 0; j < curDepth; j++) {
+ writer.print(" ");
+ }
+ writer.print("+-");
+ preOrderPrint(writer, printIntervals, nextNode, curDepth + 1, ts);
+ }
+ } catch (ClosedChannelException e) {
+ Activator.getDefault().logError(e.getMessage());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts) {
+ /* Only used for debugging, shouldn't be externalized */
+
+ preOrderPrint(writer, false, fLatestBranch.get(0), 0, ts);
+
+ if (printIntervals) {
+ preOrderPrint(writer, true, fLatestBranch.get(0), 0, ts);
+ }
+ writer.println('\n');
+ }
+
+}
* If an error happens trying to open/write to the file
* specified in the config
*/
- public static HistoryTree createHistoryTree(HTConfig conf) throws IOException {
- return new HistoryTree(conf);
+ public static IHistoryTree createHistoryTree(HTConfig conf) throws IOException {
+ return new HistoryTreeClassic(conf);
}
/**
* @throws IOException
* If an error happens reading the file
*/
- public static HistoryTree createFromFile(Path existingStateFile, int expectedProviderVersion) throws IOException {
-
+ public static IHistoryTree createFromFile(Path existingStateFile, int expectedProviderVersion) throws IOException {
/*
* Check the file exists and has a positive length. These verifications
* will also be done in the HT's constructor.
*/
int magicNumber = buffer.getInt();
switch (magicNumber) {
- case HistoryTree.HISTORY_FILE_MAGIC_NUMBER:
- return new HistoryTree(existingStateFile.toFile(), expectedProviderVersion);
+ case HistoryTreeClassic.HISTORY_FILE_MAGIC_NUMBER:
+ return new HistoryTreeClassic(existingStateFile.toFile(), expectedProviderVersion);
default:
throw new IOException("Not a known history tree file"); //$NON-NLS-1$
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+import java.nio.channels.ClosedChannelException;
+
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+
+/**
+ * Meta-container for the History Tree. This structure contains all the
+ * high-level data relevant to the tree.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface IHistoryTree {
+
+ /**
+ * Size of the "tree header" in the tree-file The nodes will use this offset
+ * to know where they should be in the file. This should always be a
+ * multiple of 4K.
+ */
+ int TREE_HEADER_SIZE = 4096;
+
+ /**
+ * "Save" the tree to disk. This method will cause the treeIO object to
+ * commit all nodes to disk and then return the RandomAccessFile descriptor
+ * so the Tree object can save its configuration into the header of the
+ * file.
+ *
+ * @param requestedEndTime
+ * The greatest timestamp present in the history tree
+ */
+ void closeTree(long requestedEndTime);
+
+ // ------------------------------------------------------------------------
+ // Accessors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get the start time of this tree.
+ *
+ * @return The start time
+ */
+ long getTreeStart();
+
+ /**
+ * Get the current end time of this tree.
+ *
+ * @return The end time
+ */
+ long getTreeEnd();
+
+ /**
+ * Get the number of nodes in this tree.
+ *
+ * @return The number of nodes
+ */
+ int getNodeCount();
+
+ /**
+ * Get the current root node of this tree
+ *
+ * @return The root node
+ */
+ HTNode getRootNode();
+
+ // ------------------------------------------------------------------------
+ // HT_IO interface
+ // ------------------------------------------------------------------------
+
+ /**
+ * Return the FileInputStream reader with which we will read an attribute
+ * tree (it will be sought to the correct position).
+ *
+ * @return The FileInputStream indicating the file and position from which
+ * the attribute tree can be read.
+ */
+ FileInputStream supplyATReader();
+
+ /**
+ * Return the file to which we will write the attribute tree.
+ *
+ * @return The file to which we will write the attribute tree
+ */
+ File supplyATWriterFile();
+
+ /**
+ * Return the position in the file (given by {@link #supplyATWriterFile})
+ * where to start writing the attribute tree.
+ *
+ * @return The position in the file where to start writing
+ */
+ long supplyATWriterFilePos();
+
+ /**
+ * Read a node from the tree.
+ *
+ * @param seqNumber
+ * The sequence number of the node to read
+ * @return The node
+ * @throws ClosedChannelException
+ * If the tree IO is unavailable
+ */
+ HTNode readNode(int seqNumber) throws ClosedChannelException;
+
+ /**
+ * Write a node object to the history file.
+ *
+ * @param node
+ * The node to write to disk
+ */
+ void writeNode(HTNode node);
+
+ /**
+ * Close the history file.
+ */
+ void closeFile();
+
+ /**
+ * Delete the history file.
+ */
+ void deleteFile();
+
+ // ------------------------------------------------------------------------
+ // Operations
+ // ------------------------------------------------------------------------
+
+ /**
+ * Insert an interval in the tree.
+ *
+ * @param interval
+ * The interval to be inserted
+ * @throws TimeRangeException
+ * If the start of end time of the interval are invalid
+ */
+ void insertInterval(HTInterval interval) throws TimeRangeException;
+
+ /**
+ * Inner method to select the next child of the current node intersecting
+ * the given timestamp. Useful for moving down the tree following one
+ * branch.
+ *
+ * @param currentNode
+ * The node on which the request is made
+ * @param t
+ * The timestamp to choose which child is the next one
+ * @return The child node intersecting t
+ * @throws ClosedChannelException
+ * If the file channel was closed while we were reading the tree
+ */
+ HTNode selectNextChild(CoreNode currentNode, long t) throws ClosedChannelException;
+
+ /**
+ * Get the current size of the history file.
+ *
+ * @return The history file size
+ */
+ long getFileSize();
+
+ /**
+ * Print out the full tree for debugging purposes
+ *
+ * @param writer
+ * PrintWriter in which to write the output
+ * @param printIntervals
+ * Flag to enable full output of the interval information
+ * @param ts
+ * The timestamp that nodes have to intersect for intervals to be
+ * printed. A negative value will print intervals for all nodes.
+ * The timestamp only applies if printIntervals is true.
+ */
+ void debugPrintFullTree(PrintWriter writer, boolean printIntervals, long ts);
+
+}