org.eclipse.tracecompass.internal.datastore.core.condition;x-internal:=true,
org.eclipse.tracecompass.internal.datastore.core.historytree;x-internal:=true,
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.condition;
+ x-friends:="org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.segmentstore.core.tests",
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"
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree;
+ x-friends:="org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.segmentstore.core.tests",
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-friends:="org.eclipse.tracecompass.statesystem.core,org.eclipse.tracecompass.statesystem.core.tests",
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+ x-friends:="org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.segmentstore.core.tests",
+ org.eclipse.tracecompass.internal.provisional.datastore.core.interval;
+ x-friends:="org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.segmentstore.core.tests",
+ org.eclipse.tracecompass.internal.provisional.datastore.core.serialization;
+ x-friends:="org.eclipse.tracecompass.statesystem.core,
+ org.eclipse.tracecompass.statesystem.core.tests,
+ org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.segmentstore.core.tests"
Import-Package: com.google.common.annotations,
com.google.common.base,
com.google.common.cache,
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
- org.eclipse.tracecompass.common.core
-Export-Package: org.eclipse.tracecompass.internal.segmentstore.core;x-internal:=true,
+ org.eclipse.tracecompass.common.core,
+ org.eclipse.tracecompass.datastore.core
+Export-Package: org.eclipse.tracecompass.internal.provisional.segmentstore.core;x-friends:="org.eclipse.tracecompass.segmentstore.core.tests",
+ org.eclipse.tracecompass.internal.segmentstore.core;x-internal:=true,
org.eclipse.tracecompass.internal.segmentstore.core.arraylist;x-friends:="org.eclipse.tracecompass.segmentstore.core.tests",
+ org.eclipse.tracecompass.internal.segmentstore.core.segmentHistoryTree;x-friends:="org.eclipse.tracecompass.segmentstore.core.tests",
org.eclipse.tracecompass.internal.segmentstore.core.treemap;x-friends:="org.eclipse.tracecompass.segmentstore.core.tests",
org.eclipse.tracecompass.segmentstore.core,
org.eclipse.tracecompass.segmentstore.core.segment.interfaces,
org.eclipse.tracecompass.segmentstore.core.treemap
-Import-Package: com.google.common.collect;version="12.0.0"
+Import-Package: com.google.common.annotations,
+ com.google.common.base,
+ com.google.common.collect
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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.segmentstore.core;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+
+/**
+ * Extension of the BasicSegment, to implement ISegment2 so that it can be
+ * stored in a history tree backend.
+ *
+ * @since 1.2
+ */
+public class BasicSegment2 extends HTInterval implements ISegment2 {
+
+ /**
+ * The factory to read an object from a buffer
+ */
+ public static final IHTIntervalReader<BasicSegment2> BASIC_SEGMENT_READ_FACTORY = buffer -> {
+ return new BasicSegment2(buffer.getLong(), buffer.getLong());
+ };
+
+ /**
+ * Constructor
+ *
+ * @param start
+ * start of the segment
+ * @param end
+ * end of the segment
+ */
+ public BasicSegment2(long start, long end) {
+ super(start, end);
+ }
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3083730702354275357L;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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.segmentstore.core;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+
+/**
+ * This is a segment that can be saved in a history tree backend
+ *
+ * Note: It should be ISegment, but that would be API breaking, so this
+ * interface was added so a segment can be both segment and history tree object.
+ *
+ * FIXME: Move that to ISegment when breaking the API
+ *
+ * @author Geneviève Bastien
+ * @since 1.2
+ */
+public interface ISegment2 extends ISegment, IHTInterval {
+
+}
--- /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.segmentstore.core;
--- /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.segmentstore.core.segmentHistoryTree;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.ISegment2;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Segment store that saves segments in a history tree.
+ *
+ * This base class is where most if not all the functionality of the segment
+ * store should be implemented. The implementation to use is
+ * {@link HistoryTreeSegmentStore} which is just a concrete class that resolves
+ * the node type for the segment store. This class can be extended in the unit
+ * tests with stub history trees and nodes to test its specific functionalities.
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ * type of {@link ISegment2}
+ */
+public class HistoryTreeSegmentStore<E extends ISegment2> implements ISegmentStore<E> {
+
+ private static final int PROVIDER_VERSION = 1;
+ // TODO: these values were taken from the state system implementation. Maybe
+ // they are not adequate for segments stores. Do some benchmarks
+ private static final int MAX_CHILDREN = 50;
+ private static final int BLOCK_SIZE = 64 * 1024;
+ /**
+ * The history tree that sits underneath.
+ */
+ private final SegmentHistoryTree<E> fSht;
+
+ /** Indicates if the history tree construction is done */
+ private volatile boolean fFinishedBuilding = false;
+
+ /**
+ * Constructor for new history files. Use this when creating a new history
+ * from scratch.
+ *
+ * @param newStateFile
+ * The filename/location where to store the state history (Should
+ * end in .ht)
+ * @param factory
+ * Factory to read history tree objects from the backend
+ * @throws IOException
+ * Thrown if we can't create the file for some reason
+ */
+ public HistoryTreeSegmentStore(Path newStateFile,
+ IHTIntervalReader<E> factory) throws IOException {
+ fSht = createHistoryTree(newStateFile, factory);
+ }
+
+
+ /**
+ * Create a history tree from an existing file
+ *
+ * @param treeFile
+ * Filename/location of the history we want to load
+ * @param intervalReader
+ * Factory to read history tree objects from the backend
+ * @return The new history tree
+ * @throws IOException
+ * If we can't read the file, if it doesn't exist, is not
+ * recognized, or if the version of the file does not match the
+ * expected providerVersion.
+ */
+ private SegmentHistoryTree<E> createHistoryTree(Path treeFile, IHTIntervalReader<@NonNull E> intervalReader) throws IOException {
+ try {
+ if (Files.exists(treeFile)) {
+ return new SegmentHistoryTree<>(NonNullUtils.checkNotNull(treeFile.toFile()), PROVIDER_VERSION, intervalReader);
+ }
+ } catch (IOException e) {
+ /**
+ * Couldn't create the history tree with this file, just fall back
+ * to a new history tree
+ */
+ }
+
+ return new SegmentHistoryTree<>(NonNullUtils.checkNotNull(treeFile.toFile()),
+ BLOCK_SIZE,
+ MAX_CHILDREN,
+ PROVIDER_VERSION,
+ 0,
+ intervalReader);
+ }
+
+ /**
+ * Get the History Tree built by this backend.
+ *
+ * @return The history tree
+ */
+ public SegmentHistoryTree<E> getSHT() {
+ return fSht;
+ }
+
+ /**
+ * Get the start time of the history tree
+ *
+ * @return the start time of the SHT
+ */
+ public long getStartTime() {
+ return getSHT().getTreeStart();
+ }
+
+ /**
+ * Get the end time of the history tree
+ *
+ * @return the end time of the SHT
+ */
+ public long getEndTime() {
+ return getSHT().getTreeEnd();
+ }
+
+ /**
+ * Tell the SHT that all segments have been inserted and to write latest
+ * branch to disk.
+ *
+ * @param endTime
+ * the time at which to close latest branch and tree
+ */
+ public void finishedBuilding(long endTime) {
+ getSHT().closeTree(endTime);
+ fFinishedBuilding = true;
+ }
+
+ /**
+ * delete the SHT files from disk
+ */
+ public void removeFiles() {
+ getSHT().deleteFile();
+ }
+
+ @Override
+ public void dispose() {
+ if (fFinishedBuilding) {
+ getSHT().closeFile();
+ } else {
+ /*
+ * The build is being interrupted, delete the file we partially
+ * built since it won't be complete, so shouldn't be re-used in the
+ * future (.deleteFile() will close the file first)
+ */
+ getSHT().deleteFile();
+ }
+ }
+
+ /**
+ * Return the size of the history tree file
+ *
+ * @return The current size of the history file in bytes
+ */
+ public long getFileSize() {
+ return getSHT().getFileSize();
+ }
+
+ @Override
+ public boolean add(E interval) {
+ getSHT().insert(interval);
+ return true;
+ }
+
+ @Override
+ public boolean addAll(@Nullable Collection<? extends E> c) {
+ if (c == null) {
+ return false;
+ }
+ c.forEach(interval -> add(interval));
+ return true;
+ }
+
+ @Override
+ public int size() {
+ return getSHT().size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getSHT().isEmpty();
+ }
+
+ @Override
+ public boolean contains(@Nullable Object o) {
+ // narrow down search when object is a segment
+ if (o instanceof ISegment) {
+ ISegment seg = (ISegment) o;
+ if (seg.getStart() < getStartTime() || seg.getEnd() > getEndTime()) {
+ /* This segment cannot be in this SegmentStore */
+ return false;
+ }
+ Iterable<@NonNull E> iterable = getIntersectingElements(seg.getStart());
+ return Iterables.contains(iterable, seg);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(@Nullable Collection<?> c) {
+ if (c == null) {
+ return false;
+ }
+ /*
+ * Check that all elements in the collection are indeed ISegments, and
+ * find their min end and max start time
+ */
+ long minEnd = Long.MAX_VALUE, maxStart = Long.MIN_VALUE;
+ for (Object o : c) {
+ if (o instanceof ISegment) {
+ ISegment seg = (ISegment) o;
+ if (seg.getStart() < getStartTime() || seg.getEnd() > getEndTime()) {
+ /* This segment cannot be in this SegmentStore */
+ return false;
+ }
+ minEnd = Math.min(minEnd, seg.getEnd());
+ maxStart = Math.max(maxStart, seg.getStart());
+ } else {
+ return false;
+ }
+ }
+ if (minEnd > maxStart) {
+ /*
+ * all segments intersect a common range, we just need to intersect
+ * a time stamp in that range
+ */
+ minEnd = maxStart;
+ }
+
+ /* Iterate through possible segments until we have found them all */
+ Iterator<@NonNull E> iterator = getIntersectingElements(minEnd, maxStart).iterator();
+ int unFound = c.size();
+ while (iterator.hasNext() && unFound > 0) {
+ E seg = iterator.next();
+ for (Object o : c) {
+ if (o.equals(seg)) {
+ unFound--;
+ }
+ }
+ }
+ return unFound == 0;
+ }
+
+ @Override
+ public @Nullable Iterator<E> iterator() {
+ return getSHT().iterator();
+ }
+
+ @Override
+ public Object @NonNull [] toArray() {
+ throw new UnsupportedOperationException("This segment store can potentially cause OutOfMemoryExceptions"); //$NON-NLS-1$
+ }
+
+ @Override
+ public <T> T @NonNull [] toArray(T @NonNull [] a) {
+ throw new UnsupportedOperationException("This segment store can potentially cause OutOfMemoryExceptions"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void clear() {
+ try {
+ getSHT().cleanFile();
+ } catch (IOException e) {
+ throw new IllegalStateException("HT segment store: couldn't clear the HT file: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public @NonNull Iterable<E> getIntersectingElements(long start, long end) {
+ return getSHT().getIntersectingElements(start, end);
+ }
+
+ @Override
+ public Iterable<E> getIntersectingElements(long start, long end, @Nullable Comparator<ISegment> order) {
+ if (order == null) {
+ return getSHT().getIntersectingElements(start, end);
+ }
+ return getSHT().getIntersectingElements(start, end, (Comparator<E>) order);
+
+ }
+}
--- /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.segmentstore.core.segmentHistoryTree;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.TimeRangeCondition;
+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.historytree.overlapping.AbstractOverlappingHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.ISegment2;
+import org.eclipse.tracecompass.internal.segmentstore.core.Activator;
+import org.eclipse.tracecompass.segmentstore.core.BasicSegment;
+import org.eclipse.tracecompass.segmentstore.core.ISegment;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Specific implementation of the history tree to save a segment store. It adds
+ * specific methods to get the elements intersecting a given time range.
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ * type of {@link ISegment}
+ */
+public class SegmentHistoryTree<E extends ISegment2> extends AbstractOverlappingHistoryTree<E, SegmentTreeNode<E>> {
+
+ private static final int HISTORY_MAGIC_NUMBER = 0x05FFC600;
+
+ /** File format version. Increment when breaking compatibility. */
+ private static final int FILE_VERSION = 1;
+
+ private static final int ITERATOR_QUEUE_SIZE = 2000;
+
+ // ------------------------------------------------------------------------
+ // Constructors/"Destructors"
+ // ------------------------------------------------------------------------
+
+ /**
+ * Create a new State History 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
+ * typed ISegment to allow access to the readSegment methods
+ * @throws IOException
+ * If an error happens trying to open/write to the file
+ * specified in the config
+ */
+ public SegmentHistoryTree(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 expProviderVersion
+ * The expected version of the state provider
+ * @param intervalReader
+ * typed ISegment to allow access to the readSegment methods
+ * @throws IOException
+ * If an error happens reading the file
+ */
+ public SegmentHistoryTree(File existingStateFile, int expProviderVersion, IHTIntervalReader<E> intervalReader) throws IOException {
+ super(existingStateFile, expProviderVersion, intervalReader);
+ }
+
+ @Override
+ protected int getMagicNumber() {
+ return HISTORY_MAGIC_NUMBER;
+ }
+
+ @Override
+ protected int getFileVersion() {
+ return FILE_VERSION;
+ }
+
+ @Override
+ protected @NonNull IHTNodeFactory<E, SegmentTreeNode<E>> getNodeFactory() {
+ // This cannot be defined statically because of the generic and because
+ // this method is called from the constructor of the abstract class,
+ // assigning it in a final field in the constructor generates a NPE. So
+ // we return the method directly here.
+ return (t, b, m, seq, p, start) -> new SegmentTreeNode<>(t, b, m, seq, p, start);
+ }
+
+ // ------------------------------------------
+ // Segment store specific methods
+ // ------------------------------------------
+
+ /**
+ * Get the number of elements in this history tree
+ *
+ * @return The number of elements in this tree
+ */
+ public int size() {
+ SegmentTreeNode<E> node;
+ long total = 0;
+
+ try {
+ // Add the number of intervals of each node
+ for (int seq = 0; seq < getNodeCount(); seq++) {
+ node = readNode(seq);
+ total += node.getNumIntervals();
+ }
+ } catch (ClosedChannelException e) {
+ Activator.instance().logError(e.getMessage(), e);
+ return 0;
+ }
+ return (int) total;
+ }
+
+ /**
+ * Return whether the tree is empty or not
+ *
+ * @return <code>true</code> if the tree is empty
+ */
+ public boolean isEmpty() {
+ Iterator<E> it = iterator();
+ if (it == null) {
+ return true;
+ }
+ return !it.hasNext();
+ }
+
+ // ------------------------------------------
+ // Iterators
+ // ------------------------------------------
+
+ /**
+ * Get an iterator for the elements of this tree. It uses
+ * {@link #getIntersectingElements(long, long)} for the full duration of the
+ * tree.
+ *
+ * @return An iterator for this tree
+ */
+ public @Nullable Iterator<E> iterator() {
+ return getIntersectingElements(getTreeStart(), getTreeEnd()).iterator();
+ }
+
+ /**
+ * Return an iterator for a range. It will iterate through the nodes
+ * top-down and visit only the nodes that contain segments within this range
+ * and for each of those nodes, it gets the segments that intersect the
+ * range
+ *
+ * @param start
+ * The start of the range
+ * @param end
+ * The end of the range
+ * @return The iterable
+ */
+ public Iterable<@NonNull E> getIntersectingElements(final long start, final long end) {
+ final TimeRangeCondition rc = TimeRangeCondition.forContinuousRange(start, end);
+ return new Iterable<E>() {
+
+ @Override
+ public Iterator<@NonNull E> iterator() {
+ return new Iterator<E>() {
+
+ private boolean started = false;
+ private Deque<Integer> queue = new LinkedList<>();
+ private Deque<E> intersecting = new LinkedList<>();
+
+ @Override
+ public @NonNull E next() {
+ hasNext();
+ return NonNullUtils.checkNotNull(intersecting.removeFirst());
+ }
+
+ @Override
+ public boolean hasNext() {
+ /* Iteration has not started yet */
+ if (!started) {
+ queue.add(getRootNode().getSequenceNumber());
+ started = true;
+ }
+
+ /*
+ * Need to read nodes until either we find more segments
+ * or iterate over all segments
+ */
+ while (intersecting.isEmpty() && !queue.isEmpty()) {
+ SegmentTreeNode<E> currentNode;
+ try {
+ currentNode = readNode(queue.pop());
+ } catch (ClosedChannelException e) {
+ Activator.instance().logError(e.getMessage(), e);
+ return false;
+ }
+ if (currentNode.getNodeType() == IHTNode.NodeType.CORE) {
+ queue.addAll(currentNode.selectNextChildren(rc));
+ }
+ intersecting.addAll(currentNode.getMatchingIntervals(rc, interval -> true));
+ }
+
+ /* Return if we have found segments */
+ return !intersecting.isEmpty();
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Return an iterator for a range where segments are sorted
+ *
+ * @param start
+ * The start of the range
+ * @param end
+ * The end of the range
+ * @param order
+ * The comparator to sort elements with
+ * @return The iterable
+ */
+ public Iterable<@NonNull E> getIntersectingElements(long start, long end, Comparator<@NonNull E> order) {
+ final TimeRangeCondition rc = TimeRangeCondition.forContinuousRange(start, end);
+ return new Iterable<E>() {
+
+ @Override
+ public Iterator<@NonNull E> iterator() {
+ return new Iterator<E>() {
+
+ private boolean started = false;
+ private PriorityQueue<SegmentTreeNode.Tuple<E>> queue = new PriorityQueue<>(getNodeCount(),
+ Comparator.comparing(SegmentTreeNode.Tuple<E>::getSegment, order));
+
+ private PriorityQueue<E> intersecting = new PriorityQueue<>(ITERATOR_QUEUE_SIZE, order);
+
+ @Override
+ public @NonNull E next() {
+ if (hasNext()) {
+ return NonNullUtils.checkNotNull(intersecting.remove());
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public boolean hasNext() {
+ /* Iteration has not started yet */
+ if (!started) {
+ SegmentTreeNode<E> rootNode = getRootNode();
+
+ /*
+ * Add the root node with any segment for the tuple,
+ * it will always be read.
+ */
+ queue.add(new SegmentTreeNode.Tuple(new BasicSegment(0,0), rootNode.getSequenceNumber()));
+
+ started = true;
+ }
+
+ /*
+ * Need to read nodes until either we find more segments
+ * or iterate over all nodes
+ */
+ while (!queue.isEmpty() && (intersecting.isEmpty()
+ || order.compare(intersecting.element(), queue.peek().getSegment()) > 0)) {
+ SegmentTreeNode<E> currentNode;
+ try {
+ currentNode = readNode(queue.poll().getSequenceNumber());
+ } catch (ClosedChannelException e) {
+ Activator.instance().logError(e.getMessage(), e);
+ return false;
+ }
+ if (currentNode.getNodeType() == HTNode.NodeType.CORE) {
+ queue.addAll(((SegmentTreeNode) currentNode).selectNextChildren(rc, order));
+ }
+ intersecting.addAll(currentNode.getMatchingIntervals(rc, interval -> true));
+ }
+
+ /* Return if we have found segments */
+ return !intersecting.isEmpty();
+ }
+ };
+ }
+ };
+ }
+
+ // ------------------------------------------------------------------------
+ // Test-specific methods
+ // ------------------------------------------------------------------------
+
+ @Override
+ @VisibleForTesting
+ protected boolean verifyChildrenSpecific(SegmentTreeNode<E> parent, int index, SegmentTreeNode<E> child) {
+ return (parent.getMaxStart(index) >= child.getMaxStart()
+ && parent.getMinEnd(index) <= child.getMinEnd()
+ && parent.getLongest(index) >= child.getLongest()
+ && parent.getShortest(index) <= child.getShortest());
+ }
+
+ @Override
+ @VisibleForTesting
+ protected boolean verifyIntersectingChildren(SegmentTreeNode<E> parent, SegmentTreeNode<E> child) {
+ int childSequence = child.getSequenceNumber();
+ for (long t = parent.getNodeStart(); t < parent.getNodeEnd(); t++) {
+ TimeRangeCondition rc = TimeRangeCondition.singleton(t);
+ boolean shouldBeInCollection = (rc.intersects(child.getNodeStart(), child.getNodeEnd()));
+ Collection<Integer> nextChildren = parent.selectNextChildren(rc);
+ if (shouldBeInCollection != nextChildren.contains(childSequence)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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.segmentstore.core.segmentHistoryTree;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+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.TimeRangeCondition;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingNode;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.BasicSegment2;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.ISegment2;
+import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
+
+/**
+ * The history tree node class for segment history tree. This is an extension of
+ * the {@link OverlappingNode} class and keeps more information about the
+ * children of a node, that will help for sorting segments, one of the specific
+ * functionalities of the segment stores.
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ * type of {@link ISegment2}
+ */
+public class SegmentTreeNode<E extends ISegment2> extends OverlappingNode<E> {
+
+ // These values represent the values for the current node only, not its
+ // children
+ private long fMaxStart = 0;
+ private long fMinEnd = Long.MAX_VALUE;
+ private long fShortest = Long.MAX_VALUE;
+ private long fLongest = 0;
+
+ /**
+ * 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 SegmentTreeNode(NodeType type, int blockSize, int maxChildren, int seqNumber, int parentSeqNumber, long start) {
+ super(type, blockSize, maxChildren, seqNumber, parentSeqNumber, start);
+ fMaxStart = start;
+ }
+
+ /**
+ * Adds the data concerning the segment nodes, max start/min end and
+ * durations
+ *
+ * @param <E> type of {@link ISegment2}
+ */
+ protected static class OverlappingSegmentCoreData<E extends ISegment2> extends OverlappingExtraData {
+
+ // These values cover the full subtrees of the child nodes
+ // Max start of an interval
+ private final long[] fChildMaxStart;
+ // minimum end of an interval
+ private final long[] fChildMinEnd;
+ // minimum length
+ private final long[] fMinLength;
+ // maximum length
+ private final long[] fMaxLength;
+
+ /**
+ * Segment history tree node data constructor
+ *
+ * @param node
+ * The node containing this extra data.
+ */
+ public OverlappingSegmentCoreData(SegmentTreeNode<?> node) {
+ super(node);
+ int size = getNode().getMaxChildren();
+ /*
+ * We instantiate the 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.
+ */
+ fChildMaxStart = new long[size];
+ fChildMinEnd = new long[size];
+ fMinLength = new long[size];
+ fMaxLength = new long[size];
+ for (int i = 0; i < size; i++) {
+ fChildMaxStart[i] = 0;
+ fChildMinEnd[i] = Long.MAX_VALUE;
+ fMinLength[i] = Long.MAX_VALUE;
+ fMaxLength[i] = Long.MIN_VALUE;
+ }
+ }
+
+ @Override
+ protected SegmentTreeNode<?> getNode() {
+ /* Type enforced by constructor */
+ return (SegmentTreeNode<?>) super.getNode();
+ }
+
+ @Override
+ public void readSpecificHeader(@NonNull ByteBuffer buffer) {
+ super.readSpecificHeader(buffer);
+ int size = getNode().getMaxChildren();
+
+ for (int i = 0; i < size; i++) {
+ fChildMaxStart[i] = buffer.getLong();
+ fChildMinEnd[i] = buffer.getLong();
+ fMinLength[i] = buffer.getLong();
+ fMaxLength[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(fChildMaxStart[i]);
+ buffer.putLong(fChildMinEnd[i]);
+ buffer.putLong(fMinLength[i]);
+ buffer.putLong(fMaxLength[i]);
+ }
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ @Override
+ protected int getSpecificHeaderSize() {
+ int maxChildren = getNode().getMaxChildren();
+ int specificSize = super.getSpecificHeaderSize();
+ /*
+ * MAX_NB * Timevalue (max starts, min ends, min length, max length
+ * table)
+ */
+ specificSize += 4 * Long.BYTES * maxChildren;
+
+ return specificSize;
+ }
+
+ @Override
+ public void linkNewChild(IHTNode<?> childNode) {
+ // The child node should be a SegmentTreeNode
+ if (!(childNode instanceof SegmentTreeNode)) {
+ throw new IllegalArgumentException("Adding a node that is not an segment tree node to an segment tree!"); //$NON-NLS-1$
+ }
+ getNode().takeWriteLock();
+ try {
+
+ super.linkNewChild(childNode);
+ final int childIndex = getNbChildren() - 1;
+
+ // The child node should be a SegmentTreeNode, but in case it
+ // isn't, just return here
+ if (!(childNode instanceof SegmentTreeNode<?>)) {
+ return;
+ }
+
+ SegmentTreeNode<E> segmentNode = (SegmentTreeNode<E>) childNode;
+ // The child node may already have segments, so we update
+ // child's data with what is already in there
+ updateChild(segmentNode, childIndex);
+
+ // Add a listener on the child node to update its data in the
+ // children's arrays
+ segmentNode.addListener((node, endtime) -> updateChild((SegmentTreeNode<E>) node, childIndex));
+
+ } finally {
+ getNode().releaseWriteLock();
+ }
+ }
+
+ private void updateChild(SegmentTreeNode<E> child, int childIndex) {
+ fChildMaxStart[childIndex] = child.getMaxStart();
+ fChildMinEnd[childIndex] = child.getMinEnd();
+ fMinLength[childIndex] = child.getShortest();
+ fMaxLength[childIndex] = child.getLongest();
+ // The child node's extra data applies to the child and its subtree,
+ // so also update with the child's children
+ for (int i = 0; i < child.getNbChildren(); i++) {
+ fChildMaxStart[childIndex] = Math.max(fChildMaxStart[childIndex], child.getMaxStart(i));
+ fChildMinEnd[childIndex] = Math.min(fChildMinEnd[childIndex], child.getMinEnd(i));
+ fMinLength[childIndex] = Math.min(fMinLength[childIndex], child.getShortest(i));
+ fMaxLength[childIndex] = Math.max(fMaxLength[childIndex], child.getLongest(i));
+ }
+ }
+
+ /* Make sure it is visible to the enclosing class */
+ @Override
+ protected Collection<Integer> selectNextIndices(TimeRangeCondition rc) {
+ return super.selectNextIndices(rc);
+ }
+
+ /**
+ * Get the maximum start value of a child and its subtree
+ *
+ * @param index
+ * The child index
+ * @return The maximum start value of the child at index and its subtree
+ */
+ public long getMaxStart(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 fChildMaxStart[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ /**
+ * Get the minimum end value of a child and its subtree
+ *
+ * @param index
+ * The child index
+ * @return The minimum end value of the child at index and its subtree
+ */
+ public long getMinEnd(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 fChildMinEnd[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ /**
+ * Get the shortest element length of a child and its subtree
+ *
+ * @param index
+ * The child index
+ * @return The shortest length of the child at index and its subtree
+ */
+ public long getShortest(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 fMinLength[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ /**
+ * Get the longest element length of a child and its subtree
+ *
+ * @param index
+ * The child index
+ * @return The longest length of the child at index and its subtree
+ */
+ public long getLongest(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 fMaxLength[index];
+ } finally {
+ getNode().releaseReadLock();
+ }
+ }
+
+ /**
+ * Get a segment from a comparator that correspond whose field wrt to
+ * the comparator is the smallest value of the child node.
+ *
+ * @param index
+ * The index of the child node
+ * @param order
+ * The comparator with which to sort segments
+ * @return A segment whose value for the field that correspond to the
+ * comparator is the least value of the child node
+ */
+ public ISegment2 getIndex(int index, Comparator<E> order) {
+ if (order.equals(SegmentComparators.INTERVAL_START_COMPARATOR)) {
+ return new BasicSegment2(getChildStart(index), getChildStart(index));
+ } else if (order.equals(SegmentComparators.INTERVAL_START_COMPARATOR.reversed())) {
+ return new BasicSegment2(fChildMaxStart[index], fChildMaxStart[index]);
+ } else if (order.equals(SegmentComparators.INTERVAL_END_COMPARATOR)) {
+ return new BasicSegment2(fChildMinEnd[index], fChildMinEnd[index]);
+ } else if (order.equals(SegmentComparators.INTERVAL_END_COMPARATOR.reversed())) {
+ return new BasicSegment2(getChildEnd(index), getChildEnd(index));
+ } else if (order.equals(SegmentComparators.INTERVAL_LENGTH_COMPARATOR)) {
+ /*
+ * Do a math.max to prevent overflow, especially in case of
+ * empty nodes where min length can be Long.MAX_VALUE
+ */
+ return new BasicSegment2(getChildStart(index), getChildStart(index) + fMinLength[index]);
+ } else if (order.equals(SegmentComparators.INTERVAL_LENGTH_COMPARATOR.reversed())) {
+ /*
+ * Do a math.max to prevent overflow, though it should not
+ * happen in this case
+ */
+ return new BasicSegment2(getChildStart(index), Math.max(getChildStart(index) + fMaxLength[index], getChildStart(index)));
+ }
+ // TODO: Don't know what to do with other comparators yet
+ return new BasicSegment2(getChild(index), getChild(index));
+ }
+
+ }
+
+ @Override
+ protected @Nullable OverlappingSegmentCoreData<E> createNodeExtraData(final NodeType type) {
+ if (type == NodeType.CORE) {
+ return new OverlappingSegmentCoreData<>(this);
+ }
+ return null;
+ }
+
+ /**
+ * Get the number of intervals in this node
+ *
+ * @return The number of intervals
+ */
+ public int getNumIntervals() {
+ return getIntervals().size();
+ }
+
+ @Override
+ public void add(E newInterval) {
+ super.add(newInterval);
+ updateBoundaries(newInterval);
+ }
+
+ /**
+ * Get the maximum start time of an interval in this node
+ *
+ * @return the latest start time of this node's intervals
+ */
+ public long getMaxStart() {
+ return fMaxStart;
+ }
+
+ /**
+ * Get the earliest end time of an interval in this node
+ *
+ * @return the earliest end time of this node's intervals
+ */
+ public long getMinEnd() {
+ return fMinEnd;
+ }
+
+ /**
+ * Get the shortest duration of the intervals of this node
+ *
+ * @return the shortest duration of this node's intervals
+ */
+ public long getShortest() {
+ return fShortest;
+ }
+
+ /**
+ * Get the longest duration of the intervals of this node
+ *
+ * @return the longest duration of this node's intervals
+ */
+ public long getLongest() {
+ return fLongest;
+ }
+
+ /**
+ * Get the children intersecting the given time range, and return the least
+ * element for each child wrt to the comparator
+ *
+ * @param range
+ * The range condition (start, end times) of the children
+ * @param order
+ * The comparator to use to compare segments in child nodes
+ * @return For each intersecting child, a Tuple with the segment with the
+ * least value for the comparator
+ */
+ public Set<Tuple<ISegment2>> selectNextChildren(TimeRangeCondition range, Comparator<E> order) {
+ OverlappingSegmentCoreData<E> extraData = getCoreNodeData();
+ if (extraData != null) {
+ Set<Tuple<ISegment2>> set = new HashSet<>();
+ for (Integer index : extraData.selectNextIndices(range)) {
+ set.add(new Tuple<>(extraData.getIndex(index, order), extraData.getChild(index)));
+ }
+ return set;
+ }
+ return Collections.emptySet();
+ }
+
+ @Override
+ protected @Nullable OverlappingSegmentCoreData<E> getCoreNodeData() {
+ return (OverlappingSegmentCoreData<E>) super.getCoreNodeData();
+ }
+
+ /**
+ * Get the maximum start value of a child subtree of this node
+ *
+ * @param index
+ * The index of the child subtree
+ * @return The child subtree's maximal start value
+ */
+ protected long getMaxStart(int index) {
+ OverlappingSegmentCoreData<E> extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getMaxStart(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the minimum end value of a child subtree of this node
+ *
+ * @param index
+ * The index of the child subtree
+ * @return The child subtree's minimum end value
+ */
+ protected long getMinEnd(int index) {
+ OverlappingSegmentCoreData<E> extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getMinEnd(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the length of the shortest element of a child subtree of this node
+ *
+ * @param index
+ * The index of the child subtree
+ * @return The child subtree's shortest element length
+ */
+ protected long getShortest(int index) {
+ OverlappingSegmentCoreData<E> extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getShortest(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the length of the longest element of a child subtree of this node
+ *
+ * @param index
+ * The index of the child subtree
+ * @return The child subtree's longest element length
+ */
+ protected long getLongest(int index) {
+ OverlappingSegmentCoreData<E> extraData = getCoreNodeData();
+ if (extraData != null) {
+ return extraData.getLongest(index);
+ }
+ throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+ }
+
+ /**
+ * Class to store the node sequence numbers and Index element for
+ * sortedIterators PriorityQueue
+ */
+ static class Tuple<E> {
+ private final E fSegment;
+ private final int fNodeSequenceNumber;
+
+ /**
+ * @param segment
+ * segment to use to order nodes in PriorityQueue
+ * @param nodeSequenceNumber
+ * sequence number of the node we want to read
+ */
+ public Tuple(E segment, int nodeSequenceNumber) {
+ fSegment = segment;
+ fNodeSequenceNumber = nodeSequenceNumber;
+ }
+
+ /**
+ * @return the element to compare or <code>null</code> if this node is
+ * empty
+ */
+ public E getSegment() {
+ return fSegment;
+ }
+
+ /**
+ * @return the sequence number to read the node
+ */
+ public int getSequenceNumber() {
+ return fNodeSequenceNumber;
+ }
+ }
+
+ /**
+ * Get the tuples of minimal segments and sequence number for this node. If
+ * the current node is empty, <code>null</code> is returned.
+ *
+ * @param order
+ * Comparator for which we need the key
+ * @return Tuple to sort nodes in iterator, <code>null</code> if not is
+ * empty
+ */
+ @Nullable Tuple<E> key(Comparator<@NonNull E> order) {
+ if (isEmpty()) {
+ return null;
+ }
+ return new Tuple<>(Collections.min(this.getIntervals(), order), getSequenceNumber());
+ }
+
+ private void updateBoundaries(E segment) {
+ fMaxStart = Math.max(fMaxStart, segment.getStart());
+ fMinEnd = Math.min(fMinEnd, segment.getEnd());
+ fShortest = Math.min(fShortest, segment.getLength());
+ fLongest = Math.max(fLongest, segment.getLength());
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 É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.segmentstore.core.segmentHistoryTree;
* @since 1.1
*/
default Iterable<E> iterator(Comparator<ISegment> order){
- return getIntersectingElements(Long.MIN_VALUE, Long.MAX_VALUE, order);
+ return getIntersectingElements(0, Long.MAX_VALUE, order);
}
/**
package org.eclipse.tracecompass.segmentstore.core;
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.ISegment2;
import org.eclipse.tracecompass.internal.segmentstore.core.arraylist.ArrayListStore;
import org.eclipse.tracecompass.internal.segmentstore.core.arraylist.LazyArrayListStore;
+import org.eclipse.tracecompass.internal.segmentstore.core.segmentHistoryTree.HistoryTreeSegmentStore;
import org.eclipse.tracecompass.internal.segmentstore.core.treemap.TreeMapStore;
/**
return new LazyArrayListStore<>(array);
}
+ /**
+ * SegmentStore factory method that creates a segment store on disk
+ *
+ * @param segmentFile
+ * The file where to store the segments
+ * @param segmentReader
+ * The factory to read the segments from a safe byte buffer
+ *
+ * @return an {@link ISegmentStore}
+ * @throws IOException
+ * Exceptions when creating the segment store
+ * @since 1.2
+ */
+ public static <E extends ISegment2> ISegmentStore<E> createOnDiskSegmentStore(Path segmentFile, IHTIntervalReader<E> segmentReader) throws IOException {
+ return new HistoryTreeSegmentStore<>(segmentFile, segmentReader);
+ }
+
private static Set<@NonNull SegmentStoreType> getListOfFlags(SegmentStoreType... segmentTypes) {
Set<@NonNull SegmentStoreType> segments = new HashSet<>();
for(@Nullable SegmentStoreType segmentType : segmentTypes ) {