segStore: Add a history tree segment store
authorLoic Prieur-Drevon <loic.prieurdrevon@gmail.com>
Thu, 1 Dec 2016 01:38:19 +0000 (20:38 -0500)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Fri, 7 Apr 2017 15:01:28 +0000 (11:01 -0400)
Change-Id: I9bf5cfffa7aa15aa6048b30f2e17446406aba891
Signed-off-by: Loïc Prieur-Drevon <loic.prieurdrevon@gmail.com>
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/75358
Reviewed-by: Hudson CI
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
statesystem/org.eclipse.tracecompass.datastore.core/META-INF/MANIFEST.MF
statesystem/org.eclipse.tracecompass.segmentstore.core/META-INF/MANIFEST.MF
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/BasicSegment2.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/ISegment2.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/package-info.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/HistoryTreeSegmentStore.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentHistoryTree.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentTreeNode.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/package-info.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/segmentstore/core/ISegmentStore.java
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/segmentstore/core/SegmentStoreFactory.java

index ac403ae469ad4df2c12a14516ab4098a5b2c0a73..1d4c40c412c000f8754a818609605a31ff99d531 100644 (file)
@@ -14,13 +14,25 @@ Export-Package: org.eclipse.tracecompass.internal.datastore.core;x-internal:=tru
  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,
index a235473e1c2592f680e7a54f0a3418e791a2c189..d5ed0e11fc54002174b7e96ee8d57aa3257a802e 100644 (file)
@@ -10,11 +10,16 @@ Bundle-ActivationPolicy: lazy
 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
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/BasicSegment2.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/BasicSegment2.java
new file mode 100644 (file)
index 0000000..0dc26be
--- /dev/null
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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;
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/ISegment2.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/ISegment2.java
new file mode 100644 (file)
index 0000000..33ca744
--- /dev/null
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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 {
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/package-info.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/provisional/segmentstore/core/package-info.java
new file mode 100644 (file)
index 0000000..79f786c
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * 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;
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/HistoryTreeSegmentStore.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/HistoryTreeSegmentStore.java
new file mode 100644 (file)
index 0000000..65e5916
--- /dev/null
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * 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);
+
+    }
+}
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentHistoryTree.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentHistoryTree.java
new file mode 100644 (file)
index 0000000..6a0f554
--- /dev/null
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ * 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;
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentTreeNode.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/SegmentTreeNode.java
new file mode 100644 (file)
index 0000000..bd58fcd
--- /dev/null
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * 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());
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/package-info.java b/statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/internal/segmentstore/core/segmentHistoryTree/package-info.java
new file mode 100644 (file)
index 0000000..ff4ee75
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * 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;
index c5779732bc43538149c80152874565e9c490358f..13f047061c64e5d4a85626b1c5faa183e872268d 100644 (file)
@@ -51,7 +51,7 @@ public interface ISegmentStore<E extends ISegment> extends Collection<E> {
      * @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);
     }
 
     /**
index fe9d40fed1334d31a3bffb9a6af04517cb6c3f4b..7c983eca251e6ca18b79dbd73e4b62850ff7cf45 100644 (file)
 
 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;
 
 /**
@@ -113,6 +118,23 @@ public final class SegmentStoreFactory<E> {
         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 ) {
This page took 0.046595 seconds and 5 git commands to generate.