org.eclipse.tracecompass.internal.datastore.core.serialization;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.datastore.core.condition;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.datastore.core.historytree;x-internal:=true,
- org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true,
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;x-internal:=true
Import-Package: com.google.common.collect
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HistoryTreeStub;
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode.NodeType;
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic.ClassicHistoryTreeStub;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTreeStub;
import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
import org.junit.Test;
HTNode.COMMON_HEADER_SIZE + Integer.BYTES + Integer.BYTES * NB_CHILDREN + Long.BYTES * NB_CHILDREN,
ClassicHistoryTreeStub.CLASSIC_NODE_FACTORY,
HtTestUtils.READ_FACTORY,
- HTNodeTest.BASE_OBJ_FACTORY },
-
+ HTNodeTest.BASE_OBJ_FACTORY
+ },
+ { "Overlapping core node",
+ HTNode.COMMON_HEADER_SIZE + Integer.BYTES + Integer.BYTES * NB_CHILDREN + 2 * Long.BYTES * NB_CHILDREN,
+ OverlappingHistoryTreeStub.OVERLAPPING_NODE_FACTORY,
+ HtTestUtils.READ_FACTORY,
+ HTNodeTest.BASE_OBJ_FACTORY
+ },
});
}
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode;
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode.NodeType;
import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic.ClassicHistoryTreeStub;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTreeStub;
import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
HTNode.COMMON_HEADER_SIZE,
ClassicHistoryTreeStub.CLASSIC_NODE_FACTORY,
HtTestUtils.READ_FACTORY,
- BASE_OBJ_FACTORY },
+ BASE_OBJ_FACTORY
+ },
+ { "Overlapping leaf node",
+ HTNode.COMMON_HEADER_SIZE,
+ OverlappingHistoryTreeStub.OVERLAPPING_NODE_FACTORY,
+ HtTestUtils.READ_FACTORY,
+ BASE_OBJ_FACTORY
+ },
});
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTreeTestBase;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HtTestUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.junit.Test;
+
+/**
+ * Test the overlapping history tree. This base class keeps the parameter such
+ * that any tree implementing the overlapping history tree can extend this test
+ * method
+ *
+ * @author Geneviève Bastien
+ * @param <E>
+ * Type of interval
+ * @param <N>
+ * Type of nodes in the tree
+ */
+public abstract class AbstractOverlappingHistoryTreeTestBase<E extends IHTInterval, N extends OverlappingNode<E>>
+ extends AbstractHistoryTreeTestBase<E, N> {
+
+ @Override
+ protected abstract AbstractOverlappingHistoryTree<E, N> createHistoryTree(File stateHistoryFile,
+ int blockSize,
+ int maxChildren,
+ int providerVersion,
+ long treeStart) throws IOException;
+
+ @Override
+ protected abstract AbstractOverlappingHistoryTree<E, N> createHistoryTree(
+ File existingStateFile,
+ int expectedProviderVersion) throws IOException;
+
+ /**
+ * Test that the children start and end times are as expected
+ */
+ @Test
+ public void testChildrenTimes() {
+ AbstractOverlappingHistoryTree<E, N> ht = (AbstractOverlappingHistoryTree<E, N>) setupSmallTree();
+
+ /* Fill a first node */
+ OverlappingNode<E> node = ht.getLatestLeaf();
+ long time = fillValues(ht, node.getNodeFreeSpace(), 1);
+
+ /* Add elements that should add a sibling to the node */
+ assertEquals(1, ht.getNodeCount());
+ assertEquals(1, ht.getDepth());
+ ht.insert(createInterval(time, time + 1));
+
+ assertEquals(3, ht.getNodeCount());
+ assertEquals(2, ht.getDepth());
+
+ // The first node should have been closed, so let's check that the
+ // parent has the right start and end end time
+ OverlappingNode<E> parent = ht.getLatestNode(0);
+ assertEquals(time, node.getNodeEnd());
+ assertEquals(time, parent.getChildEnd(0));
+ assertEquals(node.getNodeStart(), parent.getChildStart(0));
+
+ /* Fill the latest leaf node (2nd child) */
+ node = ht.getLatestLeaf();
+ time += 1;
+ time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+ /*
+ * Add an element that should add another sibling to the previous nodes
+ */
+ ht.insert(createInterval(time, time + 1));
+ assertEquals(4, ht.getNodeCount());
+ assertEquals(2, ht.getDepth());
+
+ // The second node should have been closed, so let's check that the
+ // parent has the right start and end time
+ assertEquals(time, node.getNodeEnd());
+ assertEquals(time, parent.getChildEnd(1));
+ assertEquals(node.getNodeStart(), parent.getChildStart(1));
+
+ /* Fill the latest leaf node (3rd and last child) */
+ node = ht.getLatestLeaf();
+ time += 1;
+ time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+ /* The new node created here should generate a new branch */
+ ht.insert(createInterval(time, time + 1));
+ assertEquals(7, ht.getNodeCount());
+ assertEquals(3, ht.getDepth());
+
+ // The third node and previous parent should have been closed, so let's
+ // check that the parent has the right start and end time
+ assertEquals(time, node.getNodeEnd());
+ assertEquals(time, parent.getChildEnd(2));
+ assertEquals(node.getNodeStart(), parent.getChildStart(2));
+
+ // Now verify the higher level
+ node = parent;
+ parent = ht.getLatestNode(0);
+ assertEquals(time, node.getNodeEnd());
+ assertEquals(time, parent.getChildEnd(0));
+
+ // Assert the integrity of the tree
+ ht.closeTree(ht.getTreeEnd());
+ time = ht.getTreeEnd();
+
+ /* The tree is closed, the last branch should have their end time set */
+ // leaf level
+ assertEquals(time, ht.getLatestNode(2).getNodeEnd());
+ // Second level
+ assertEquals(time, ht.getLatestNode(1).getNodeEnd());
+ assertEquals(time, ht.getLatestNode(1).getChildEnd(0));
+ // Head level
+ assertEquals(time, parent.getNodeEnd());
+ assertEquals(time, parent.getChildEnd(1));
+
+ HtTestUtils.assertTreeIntegrity(ht);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTreeTestBase;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HtTestUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
+import org.junit.Test;
+
+/**
+ * Tests for the concrete {@link OverlappingHistoryTree} class.
+ *
+ * @author Geneviève Bastien
+ */
+public class OverlappingHistoryTreeTest
+ extends AbstractHistoryTreeTestBase<HTInterval, OverlappingNode<HTInterval>> {
+
+ private static final HTInterval DEFAULT_OBJECT = new HTInterval(0, 0);
+
+ @Override
+ protected OverlappingHistoryTreeStub createHistoryTree(File stateHistoryFile,
+ int blockSize,
+ int maxChildren,
+ int providerVersion,
+ long treeStart) throws IOException {
+
+ return new OverlappingHistoryTreeStub(stateHistoryFile,
+ blockSize,
+ maxChildren,
+ providerVersion,
+ treeStart);
+ }
+
+ @Override
+ protected OverlappingHistoryTreeStub createHistoryTree(@NonNull File existingStateFile,
+ int expectedProviderVersion) throws IOException {
+ return new OverlappingHistoryTreeStub(existingStateFile, expectedProviderVersion);
+ }
+
+ @Override
+ protected HTInterval createInterval(long start, long end) {
+ return new HTInterval(start, end);
+ }
+
+ @Override
+ protected long fillValues(
+ AbstractHistoryTree<HTInterval, OverlappingNode<HTInterval>> ht,
+ int fillSize, long start) {
+
+ int nbValues = fillSize / DEFAULT_OBJECT.getSizeOnDisk();
+ for (int i = 0; i < nbValues; i++) {
+ ht.insert(new HTInterval(start + i, start + i + 1));
+ }
+ return start + nbValues;
+ }
+
+ /**
+ * Test insertions at different moments
+ */
+ @Test
+ public void testInsertions() {
+ long start = 10L;
+ final OverlappingHistoryTreeStub ht = (OverlappingHistoryTreeStub) setupSmallTree(3, start);
+
+ /* Fill a first node */
+ OverlappingNode<HTInterval> node = ht.getLatestLeaf();
+ long time = fillValues(ht, node.getNodeFreeSpace(), start);
+
+ /*
+ * Add an element that starts before the last end time and make sure it
+ * is added to the next node
+ */
+ long lastStart = time - 10;
+ assertEquals(1, ht.getNodeCount());
+ assertEquals(1, ht.getDepth());
+ ht.insert(createInterval(lastStart, time + 1));
+
+ assertEquals(3, ht.getNodeCount());
+ assertEquals(2, ht.getDepth());
+ assertEquals(1, ht.getLastInsertionLocation());
+
+ /*
+ * Add an element that starts before the node start and make sure it is
+ * added in the parent
+ */
+ ht.insert(createInterval(lastStart - 5, time + 1));
+ assertEquals(0, ht.getLastInsertionLocation());
+
+ /* Fill the latest leaf node (2nd child) */
+ node = ht.getLatestLeaf();
+ time += 1;
+ time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+ /*
+ * Add an interval that should add another sibling to the previous
+ * nodes, but that starts before last node. The sibling should not start
+ * before previous one, so the element will be added at the top
+ */
+ ht.insert(createInterval(lastStart - 5, time + 1));
+ assertEquals(4, ht.getNodeCount());
+ assertEquals(2, ht.getDepth());
+ assertEquals(0, ht.getLastInsertionLocation());
+
+ OverlappingNode<HTInterval> newNode = ht.getLatestLeaf();
+ assertTrue(node.getSequenceNumber() != newNode.getSequenceNumber());
+ assertEquals(lastStart, newNode.getNodeStart());
+
+ // Fill the last node with values in the past and make sure there are no
+ // new nodes
+ node = ht.getLatestLeaf();
+ time = fillValues(ht, node.getNodeFreeSpace(), node.getNodeStart());
+ assertEquals(4, ht.getNodeCount());
+ assertEquals(2, ht.getDepth());
+
+ // Add an interval that will create a new branch
+ ht.insert(createInterval(time, time + 1));
+ assertEquals(7, ht.getNodeCount());
+ assertEquals(3, ht.getDepth());
+ assertEquals(2, ht.getLastInsertionLocation());
+
+ // Fill the last leaf node
+ node = ht.getLatestLeaf();
+ time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+ // Add an element that starts at the beginning and make sure it is
+ // inserted at the top level.
+ ht.insert(createInterval(start + 1, time));
+ assertEquals(0, ht.getLastInsertionLocation());
+
+ // An element with the correct start/end should create a sibling now
+ ht.insert(createInterval(time, time + 1));
+ assertEquals(8, ht.getNodeCount());
+
+ // Close the tree and assert integrity
+ ht.closeTree(ht.getTreeEnd());
+
+ HtTestUtils.assertTreeIntegrity(ht);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
+
+/**
+ * A stub for the overlapping history tree, specifying the object type to
+ * {@link HTInterval}.
+ *
+ * @author Geneviève Bastien
+ */
+public class OverlappingHistoryTreeStub extends OverlappingHistoryTree<HTInterval> {
+
+ /**
+ * A factory to create leaf and core nodes based on the BaseHtObject object
+ */
+ public static final IHTNodeFactory<HTInterval, OverlappingNode<HTInterval>> OVERLAPPING_NODE_FACTORY =
+ (t, b, m, seq, p, start) -> new OverlappingNode<>(t, b, m, seq, p, start);
+
+ private int fLastInsertionIndex;
+
+ /**
+ * Create a new Overlapping History Tree test stub from scratch, specifying
+ * all configuration parameters.
+ *
+ * @param stateHistoryFile
+ * The name of the history file
+ * @param blockSize
+ * The size of each "block" on disk in bytes. One node will
+ * always fit in one block. It should be at least 4096.
+ * @param maxChildren
+ * The maximum number of children allowed per core (non-leaf)
+ * node.
+ * @param providerVersion
+ * The version of the state provider. If a file already exists,
+ * and their versions match, the history file will not be rebuilt
+ * uselessly.
+ * @param treeStart
+ * The start time of the history
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public OverlappingHistoryTreeStub(File stateHistoryFile,
+ int blockSize,
+ int maxChildren,
+ int providerVersion,
+ long treeStart) throws IOException {
+
+ super(stateHistoryFile,
+ blockSize,
+ maxChildren,
+ providerVersion,
+ treeStart,
+ HTInterval.INTERVAL_READER);
+ }
+
+ /**
+ * "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 expectedProviderVersion
+ * The expected version of the state provider
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public OverlappingHistoryTreeStub(File existingStateFile,
+ int expectedProviderVersion) throws IOException {
+ super(existingStateFile, expectedProviderVersion, HTInterval.INTERVAL_READER);
+ }
+
+ @Override
+ protected IHTNodeFactory<HTInterval, OverlappingNode<HTInterval>> getNodeFactory() {
+ return OVERLAPPING_NODE_FACTORY;
+ }
+
+ @Override
+ protected void informInsertingAtDepth(int depth) {
+ fLastInsertionIndex = depth;
+ }
+
+ /**
+ * Get the index in the current branch where the last element was inserted
+ *
+ * @return The index in the branch of the last insertion
+ */
+ public int getLastInsertionLocation() {
+ return fLastInsertionIndex;
+ }
+
+ // ------------------------------------------------------------------------
+ // Re-exported methods (to be visible for the tests in same package)
+ // ------------------------------------------------------------------------
+
+ @Override
+ protected OverlappingNode<HTInterval> getLatestNode(int depth) {
+ return super.getLatestNode(depth);
+ }
+
+ @Override
+ protected int getDepth() {
+ return super.getDepth();
+ }
+
+ @Override
+ protected OverlappingNode<HTInterval> getLatestLeaf() {
+ return super.getLatestLeaf();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
org.eclipse.tracecompass.internal.provisional.datastore.core.exceptions,
org.eclipse.tracecompass.internal.provisional.datastore.core.historytree;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true,
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.datastore.core.interval;x-internal:=true,
org.eclipse.tracecompass.internal.provisional.datastore.core.serialization;x-friends:="org.eclipse.tracecompass.statesystem.core,org.eclipse.tracecompass.statesystem.core.tests"
Import-Package: com.google.common.annotations,
* The depth at which to get the node
* @return The node at depth
*/
- protected final N getLatestNode(int depth) {
+ protected N getLatestNode(int depth) {
if (depth > fLatestBranch.size()) {
throw new IndexOutOfBoundsException("Trying to get latest node too deep"); //$NON-NLS-1$
}
*/
protected final void tryInsertAtNode(E interval, int depth) {
N targetNode = getLatestBranch().get(depth);
+ informInsertingAtDepth(depth);
/* Verify if there is enough room in this node to store this interval */
if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
updateEndTime(interval);
}
+ /**
+ * Informs the tree that the insertion is requested at a given depth. When
+ * this is called, the element is not yet inserted, but the last call to
+ * this for an element will represent the depth at which is was really
+ * inserted. By default, this method does nothing and should not be
+ * necessary for concrete implementations, but it can be used by unit tests
+ * to check to position of insertion of elements.
+ *
+ * @param depth
+ * The depth at which the last insertion was done
+ */
+ @VisibleForTesting
+ protected void informInsertingAtDepth(int depth) {
+
+ }
+
/**
* Get the start time of the new node of the branch that will be added
* starting at depth.
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.RangeCondition;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Overlapping history tree, where children node's ranges are allowed to overlap
+ * and children will start at the time of the next interval to insert or at
+ * minimum at the time of its sibling node
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ * The type of objects that will be saved in the tree
+ * @param <N> The type of node used by this tree
+ */
+public abstract class AbstractOverlappingHistoryTree<E extends IHTInterval, N extends OverlappingNode<E>>
+ extends AbstractHistoryTree<E, N> {
+
+ // ------------------------------------------------------------------------
+ // Constructors/"Destructors"
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create a new Overlapping History Tree from scratch, specifying all
+ * configuration parameters.
+ *
+ * @param stateHistoryFile
+ * The name of the history file
+ * @param blockSize
+ * The size of each "block" on disk in bytes. One node will
+ * always fit in one block. It should be at least 4096.
+ * @param maxChildren
+ * The maximum number of children allowed per core (non-leaf)
+ * node.
+ * @param providerVersion
+ * The version of the state provider. If a file already exists,
+ * and their versions match, the history file will not be rebuilt
+ * uselessly.
+ * @param treeStart
+ * The start time of the history
+ * @param intervalReader
+ * The factory to create new tree data elements when reading from
+ * the disk
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public AbstractOverlappingHistoryTree(File stateHistoryFile,
+ int blockSize,
+ int maxChildren,
+ int providerVersion,
+ long treeStart,
+ IHTIntervalReader<E> intervalReader) throws IOException {
+
+ super(stateHistoryFile,
+ blockSize,
+ maxChildren,
+ providerVersion,
+ treeStart,
+ intervalReader);
+ }
+
+ /**
+ * "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 expectedProviderVersion
+ * The expected version of the state provider
+ * @param objectReader
+ * The factory used to read segments from the history tree
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public AbstractOverlappingHistoryTree(File existingStateFile,
+ int expectedProviderVersion,
+ IHTIntervalReader<E> objectReader) throws IOException {
+ super(existingStateFile, expectedProviderVersion, objectReader);
+ }
+
+ @Override
+ protected long getNewBranchStart(int depth, E interval) {
+ // The node starts at the time of the interval to add, but should not
+ // start before the previous sibling
+ // TODO: Do some benchmark to see if this Math.max is efficient enough
+ // as opposed to just interval.getStart which would require to start the
+ // new branch higher up in the tree
+ return Math.max(interval.getStart(), getLatestNode(depth).getNodeStart());
+ }
+
+ // ------------------------------------------------------------------------
+ // Test-specific methods
+ // ------------------------------------------------------------------------
+
+ @Override
+ @VisibleForTesting
+ protected N getLatestLeaf() {
+ return super.getLatestLeaf();
+ }
+
+ @Override
+ @VisibleForTesting
+ protected int getDepth() {
+ return super.getDepth();
+ }
+
+ @Override
+ @VisibleForTesting
+ protected N getLatestNode(int depth) {
+ return super.getLatestNode(depth);
+ }
+
+ @Override
+ @VisibleForTesting
+ protected boolean verifyChildrenSpecific(N parent, int index, N child) {
+ if (parent.getChildStart(index) == child.getNodeStart()
+ && parent.getChildEnd(index) == child.getNodeEnd()) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ @VisibleForTesting
+ protected boolean verifyIntersectingChildren(N parent, N child) {
+ int childSequence = child.getSequenceNumber();
+ boolean shouldBeInCollection;
+ Collection<Integer> nextChildren;
+ for (long t = parent.getNodeStart(); t < parent.getNodeEnd(); t++) {
+ RangeCondition<Long> timeCondition = RangeCondition.singleton(t);
+ shouldBeInCollection = (timeCondition.intersects(child.getNodeStart(), child.getNodeEnd()));
+ nextChildren = parent.selectNextChildren(timeCondition);
+ if (shouldBeInCollection != nextChildren.contains(childSequence)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+
+/**
+ * Basic implementation of {@link AbstractOverlappingHistoryTree} that can
+ * be used directly by clients.
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ * The type of objects that will be saved in the tree
+ */
+public class OverlappingHistoryTree<E extends IHTInterval>
+ extends AbstractOverlappingHistoryTree<E, OverlappingNode<E>> {
+
+ /**
+ * The magic number for this file format.
+ */
+ public static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA800;
+
+ /** File format version. Increment when breaking compatibility. */
+ private static final int FILE_VERSION = 1;
+
+ private final IHTNodeFactory<E, OverlappingNode<E>> fNodeFactory =
+ (t, b, m, seq, p, start) -> new OverlappingNode<>(t, b, m, seq, p, start);
+
+ // ------------------------------------------------------------------------
+ // Constructors/"Destructors"
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create a new Overlapping History Tree from scratch, specifying all
+ * configuration parameters.
+ *
+ * @param stateHistoryFile
+ * The name of the history file
+ * @param blockSize
+ * The size of each "block" on disk in bytes. One node will
+ * always fit in one block. It should be at least 4096.
+ * @param maxChildren
+ * The maximum number of children allowed per core (non-leaf)
+ * node.
+ * @param providerVersion
+ * The version of the state provider. If a file already exists,
+ * and their versions match, the history file will not be rebuilt
+ * uselessly.
+ * @param treeStart
+ * The start time of the history
+ * @param intervalReader
+ * The factory to create new tree data elements when reading from
+ * the disk
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public OverlappingHistoryTree(File stateHistoryFile,
+ int blockSize,
+ int maxChildren,
+ int providerVersion,
+ long treeStart,
+ IHTIntervalReader<E> intervalReader) throws IOException {
+
+ super(stateHistoryFile,
+ blockSize,
+ maxChildren,
+ providerVersion,
+ treeStart,
+ intervalReader);
+ }
+
+ /**
+ * "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 expectedProviderVersion
+ * The expected version of the state provider
+ * @param objectReader
+ * The factory used to read segments from the history tree
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public OverlappingHistoryTree(File existingStateFile,
+ int expectedProviderVersion,
+ IHTIntervalReader<E> objectReader) throws IOException {
+ super(existingStateFile, expectedProviderVersion, objectReader);
+ }
+
+ @Override
+ protected int getMagicNumber() {
+ return HISTORY_FILE_MAGIC_NUMBER;
+ }
+
+ @Override
+ protected int getFileVersion() {
+ return FILE_VERSION;
+ }
+
+ @Override
+ protected IHTNodeFactory<E, OverlappingNode<E>> getNodeFactory() {
+ return fNodeFactory;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.RangeCondition;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.exceptions.RangeException;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HTNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * History tree node for overlapping history tree. Those nodes keep both the
+ * child node's start and end times
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ *
+ * @param <E>
+ * The type of objects that will be saved in the tree
+ */
+public class OverlappingNode<E extends IHTInterval> extends HTNode<E> {
+
+ /**
+ * Listeners for when nodes are closed, to update the child node's
+ * information.
+ */
+ private final Set<NodeClosedListener> fListeners = new HashSet<>();
+
+ /**
+ * Listener to node close operations. This can be used by parent nodes to
+ * update their children data when a node is closed.
+ */
+ @FunctionalInterface
+ protected static interface NodeClosedListener {
+
+ /**
+ * A node was just closed
+ *
+ * @param node
+ * The node that was just closed
+ * @param endtime
+ * The end time of the node
+ */
+ void nodeClosed(OverlappingNode<?> node, long endtime);
+ }
+
+ /**
+ * Adds the data concerning the segment nodes, start/end of children
+ */
+ protected static class OverlappingExtraData extends CoreNodeData {
+
+ private final long[] fChildStart;
+ private final long[] fChildEnd;
+
+ /**
+ * Node data constructor
+ *
+ * @param node
+ * The node containing this extra data.
+ */
+ public OverlappingExtraData(OverlappingNode<?> node) {
+ super(node);
+ int size = node.getMaxChildren();
+ /*
+ * * We instantiate the two following arrays at full size right
+ * away, since we want to reserve that space in the node's header.
+ * "this.nbChildren" will tell us how many relevant entries there
+ * are in those tables.
+ */
+ fChildStart = new long[size];
+ fChildEnd = new long[size];
+ for (int i = 0; i < size; i++) {
+ fChildStart[i] = 0;
+ fChildEnd[i] = Long.MAX_VALUE;
+ }
+ }
+
+ @Override
+ protected OverlappingNode<?> getNode() {
+ /* Type enforced by constructor */
+ return (OverlappingNode<?>) super.getNode();
+ }
+
+ @Override
+ public void readSpecificHeader(@NonNull ByteBuffer buffer) {
+ super.readSpecificHeader(buffer);
+ int size = getNode().getMaxChildren();
+
+ for (int i = 0; i < size; i++) {
+ fChildStart[i] = buffer.getLong();
+ fChildEnd[i] = buffer.getLong();
+ }
+ }
+
+ @Override
+ protected void writeSpecificHeader(@NonNull ByteBuffer buffer) {
+ getNode().takeReadLock();
+ try {
+ super.writeSpecificHeader(buffer);
+
+ int size = getNode().getMaxChildren();
+
+ /*
+ * Write the children array
+ */
+ for (int i = 0; i < size; i++) {
+ buffer.putLong(fChildStart[i]);
+ buffer.putLong(fChildEnd[i]);
+ }
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ @Override
+ protected int getSpecificHeaderSize() {
+ int maxChildren = getNode().getMaxChildren();
+ int specificSize = super.getSpecificHeaderSize();
+ /*
+ * MAX_NB * child start and child end arrays
+ */
+ specificSize += 2 * Long.BYTES * maxChildren;
+
+ return specificSize;
+ }
+
+ @Override
+ public void linkNewChild(IHTNode<?> childNode) {
+ if (!(childNode instanceof OverlappingNode<?>)) {
+ throw new IllegalArgumentException("Adding a node that is not an overlapping node to an overlapping tree!"); //$NON-NLS-1$
+ }
+
+ getNode().takeWriteLock();
+ try {
+ super.linkNewChild(childNode);
+ final int childIndex = getNbChildren() - 1;
+
+ // We do not know the end time at this point, add a listener to
+ // update child end when the node is closed
+ ((OverlappingNode<?>) childNode).addListener((node, endtime) -> {
+ // FIXME On who are we calling getNode() and fChildEnd here?
+ getNode().takeWriteLock();
+ try {
+ fChildEnd[childIndex] = endtime;
+ } finally {
+ getNode().releaseWriteLock();
+ }
+ });
+
+ fChildStart[childIndex] = childNode.getNodeStart();
+ // The child may already be closed
+ if (childNode.isOnDisk()) {
+ fChildEnd[childIndex] = childNode.getNodeEnd();
+ }
+
+ } finally {
+ getNode().releaseWriteLock();
+ }
+ }
+
+ @Override
+ protected Collection<Integer> selectNextIndices(RangeCondition<Long> rc) {
+ OverlappingNode<?> node = getNode();
+
+ if (rc.min() < node.getNodeStart()
+ || (node.isOnDisk() && rc.max() > node.getNodeEnd())) {
+ throw new RangeException("Requesting children outside the node's range: " + rc.toString()); //$NON-NLS-1$
+ }
+
+ node.takeReadLock();
+ try {
+ List<Integer> childList = new ArrayList<>();
+ for (int i = 0; i < getNbChildren(); i++) {
+ if (rc.intersects(fChildStart[i], fChildEnd[i])) {
+ childList.add(i);
+ }
+ }
+ return childList;
+
+ } finally {
+ node.releaseReadLock();
+ }
+
+ }
+
+ /**
+ * Get the start value of a child
+ *
+ * @param index
+ * The child index
+ * @return The start value
+ */
+ public long getChildStart(int index) {
+ getNode().takeReadLock();
+ try {
+ if (index >= getNbChildren()) {
+ throw new IndexOutOfBoundsException("The child at index " + index + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return fChildStart[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ /**
+ * Get the start value of a child
+ *
+ * @param index
+ * The child index
+ * @return The start value
+ */
+ public long getChildEnd(int index) {
+ getNode().takeReadLock();
+ try {
+ if (index >= getNbChildren()) {
+ throw new IndexOutOfBoundsException("The child at index " + index + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return fChildEnd[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), fChildStart, fChildEnd);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!super.equals(obj)) {
+ return false;
+ }
+ /* super.equals already checks for null / same class */
+ OverlappingExtraData other = (OverlappingExtraData) checkNotNull(obj);
+ return (Arrays.equals(fChildStart, other.fChildStart)
+ && Arrays.equals(fChildEnd, other.fChildEnd));
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param type
+ * The type of this node
+ * @param blockSize
+ * The size (in bytes) of a serialized node on disk
+ * @param maxChildren
+ * The maximum allowed number of children per node
+ * @param seqNumber
+ * The (unique) sequence number assigned to this particular node
+ * @param parentSeqNumber
+ * The sequence number of this node's parent node
+ * @param start
+ * The earliest timestamp stored in this node
+ */
+ public OverlappingNode(NodeType type, int blockSize, int maxChildren,
+ int seqNumber, int parentSeqNumber, long start) {
+ super(type, blockSize, maxChildren, seqNumber, parentSeqNumber, start);
+ }
+
+ @Override
+ protected @Nullable OverlappingExtraData createNodeExtraData(final NodeType type) {
+ if (type == NodeType.CORE) {
+ return new OverlappingExtraData(this);
+ }
+ return null;
+ }
+
+ @Override
+ public void add(E newInterval) {
+ super.add(newInterval);
+ }
+
+ @Override
+ public void closeThisNode(long endtime) {
+ super.closeThisNode(endtime);
+ fListeners.forEach(l -> l.nodeClosed(this, endtime));
+ }
+
+ /**
+ * The listener for this node
+ *
+ * @param listener
+ * The update listener
+ */
+ protected void addListener(NodeClosedListener listener) {
+ fListeners.add(listener);
+ }
+
+ @Override
+ protected @Nullable OverlappingExtraData getCoreNodeData() {
+ return (OverlappingExtraData) super.getCoreNodeData();
+ }
+
+ /**
+ * Get the start value of a child of this node
+ *
+ * @param index
+ * The index of the node to get the child start
+ * @return The child start value
+ */
+ @VisibleForTesting
+ long getChildStart(int index) {
+ OverlappingExtraData extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getChildStart(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the end value of a child of this node
+ *
+ * @param index
+ * The index of the node to get the child end
+ * @return The child end value
+ */
+ @VisibleForTesting
+ long getChildEnd(int index) {
+ OverlappingExtraData extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getChildEnd(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2017 É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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;