ss: History trees can define their own node types
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HTNode.java
index f8d1bc1ed6e736aaba436e5a81c989ea86cda74a..9cada2d4f4e989f8ea981490187fd168b34ba40e 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2015 Ericsson, École Polytechnique de Montréal, and others
+ * Copyright (c) 2010, 2016 Ericsson, École Polytechnique de Montréal, and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -24,10 +24,13 @@ import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
 
+import com.google.common.collect.Iterables;
+
 /**
  * The base class for all the types of nodes that go in the History Tree.
  *
@@ -101,7 +104,10 @@ public abstract class HTNode {
      *  1 - byte (done or not)
      * </pre>
      */
-    private static final int COMMON_HEADER_SIZE = 34;
+    private static final int COMMON_HEADER_SIZE = Byte.BYTES
+            + 2 * Long.BYTES
+            + 4 * Integer.BYTES
+            + Byte.BYTES;
 
     // ------------------------------------------------------------------------
     // Attributes
@@ -118,9 +124,6 @@ public abstract class HTNode {
     private final int fSequenceNumber;
     private int fParentSequenceNumber; /* = -1 if this node is the root node */
 
-    /* Where the Strings section begins (from the start of the node */
-    private int fStringSectionOffset;
-
     /* Sum of bytes of all intervals in the node */
     private int fSizeOfIntervalSection;
 
@@ -151,7 +154,6 @@ public abstract class HTNode {
         fSequenceNumber = seqNumber;
         fParentSequenceNumber = parentSeqNumber;
 
-        fStringSectionOffset = config.getBlockSize();
         fSizeOfIntervalSection = 0;
         fIsOnDisk = false;
         fIntervals = new ArrayList<>();
@@ -166,11 +168,13 @@ public abstract class HTNode {
      * @param fc
      *            FileChannel to the history file, ALREADY SEEKED at the start
      *            of the node.
+     * @param nodeFactory
+     *            The factory to create the nodes for this tree
      * @return The node object
      * @throws IOException
      *             If there was an error reading from the file channel
      */
-    public static final HTNode readNode(HTConfig config, FileChannel fc)
+    public static final @NonNull HTNode readNode(HTConfig config, FileChannel fc, IHistoryTree.IHTNodeFactory nodeFactory)
             throws IOException {
         HTNode newNode = null;
         int res, i;
@@ -190,20 +194,19 @@ public abstract class HTNode {
         int seqNb = buffer.getInt();
         int parentSeqNb = buffer.getInt();
         int intervalCount = buffer.getInt();
-        int stringSectionOffset = buffer.getInt();
         buffer.get(); // TODO Used to be "isDone", to be removed from the header
 
         /* Now the rest of the header depends on the node type */
         switch (type) {
         case CORE:
             /* Core nodes */
-            newNode = new CoreNode(config, seqNb, parentSeqNb, start);
+            newNode = nodeFactory.createCoreNode(config, seqNb, parentSeqNb, start);
             newNode.readSpecificHeader(buffer);
             break;
 
         case LEAF:
             /* Leaf nodes */
-            newNode = new LeafNode(config, seqNb, parentSeqNb, start);
+            newNode = nodeFactory.createLeafNode(config, seqNb, parentSeqNb, start);
             newNode.readSpecificHeader(buffer);
             break;
 
@@ -219,12 +222,11 @@ public abstract class HTNode {
         for (i = 0; i < intervalCount; i++) {
             HTInterval interval = HTInterval.readFrom(buffer);
             newNode.fIntervals.add(interval);
-            newNode.fSizeOfIntervalSection += interval.getIntervalSize();
+            newNode.fSizeOfIntervalSection += interval.getSizeOnDisk();
         }
 
         /* Assign the node's other information we have read previously */
         newNode.fNodeEnd = end;
-        newNode.fStringSectionOffset = stringSectionOffset;
         newNode.fIsOnDisk = true;
 
         return newNode;
@@ -247,7 +249,6 @@ public abstract class HTNode {
         fRwl.readLock().lock();
         try {
             final int blockSize = fConfig.getBlockSize();
-            int curStringsEntryEndPos = blockSize;
 
             ByteBuffer buffer = ByteBuffer.allocate(blockSize);
             buffer.order(ByteOrder.LITTLE_ENDIAN);
@@ -260,41 +261,27 @@ public abstract class HTNode {
             buffer.putInt(fSequenceNumber);
             buffer.putInt(fParentSequenceNumber);
             buffer.putInt(fIntervals.size());
-            buffer.putInt(fStringSectionOffset);
             buffer.put((byte) 1); // TODO Used to be "isDone", to be removed from header
 
             /* Now call the inner method to write the specific header part */
             writeSpecificHeader(buffer);
 
             /* Back to us, we write the intervals */
-            for (HTInterval interval : fIntervals) {
-                int size = interval.writeInterval(buffer, curStringsEntryEndPos);
-                curStringsEntryEndPos -= size;
-            }
+            fIntervals.forEach(i -> i.writeInterval(buffer));
 
             /*
-             * Write padding between the end of the Data section and the start
-             * of the Strings section (needed to fill the node in case there is
-             * no Strings section)
+             * Fill the rest with zeros
              */
-            while (buffer.position() < fStringSectionOffset) {
+            while (buffer.position() < blockSize) {
                 buffer.put((byte) 0);
             }
 
-            /*
-             * If the offsets were right, the size of the Strings section should
-             * be == to the expected size
-             */
-            assert (curStringsEntryEndPos == fStringSectionOffset);
-
             /* Finally, write everything in the Buffer to disk */
-
-            // if we don't do this, flip() will lose what's after.
-            buffer.position(blockSize);
-
             buffer.flip();
             int res = fc.write(buffer);
-            assert (res == blockSize);
+            if (res != blockSize) {
+                throw new IllegalStateException("Wrong size of block written: Actual: " + res + ", Expected: " + blockSize); //$NON-NLS-1$ //$NON-NLS-2$
+            }
 
         } finally {
             fRwl.readLock().unlock();
@@ -384,7 +371,7 @@ public abstract class HTNode {
         fRwl.writeLock().lock();
         try {
             /* Just in case, should be checked before even calling this function */
-            assert (newInterval.getIntervalSize() <= getNodeFreeSpace());
+            assert (newInterval.getSizeOnDisk() <= getNodeFreeSpace());
 
             /* Find the insert position to keep the list sorted */
             int index = fIntervals.size();
@@ -393,10 +380,8 @@ public abstract class HTNode {
             }
 
             fIntervals.add(index, newInterval);
-            fSizeOfIntervalSection += newInterval.getIntervalSize();
+            fSizeOfIntervalSection += newInterval.getSizeOnDisk();
 
-            /* Update the in-node offset "pointer" */
-            fStringSectionOffset -= (newInterval.getStringsEntrySize());
         } finally {
             fRwl.writeLock().unlock();
         }
@@ -412,7 +397,13 @@ public abstract class HTNode {
     public void closeThisNode(long endtime) {
         fRwl.writeLock().lock();
         try {
-            assert (endtime >= fNodeStart);
+            /**
+             * FIXME: was assert (endtime >= fNodeStart); but that exception
+             * is reached with an empty node that has start time endtime + 1
+             */
+//            if (endtime < fNodeStart) {
+//                throw new IllegalArgumentException("Endtime " + endtime + " cannot be lower than start time " + fNodeStart);
+//            }
 
             if (!fIntervals.isEmpty()) {
                 /*
@@ -420,7 +411,9 @@ public abstract class HTNode {
                  * EndTime > the one requested. Only need to check the last one
                  * since they are sorted
                  */
-                assert (endtime >= fIntervals.get(fIntervals.size() - 1).getEndTime());
+                if (endtime < Iterables.getLast(fIntervals).getEndTime()) {
+                    throw new IllegalArgumentException("Closing end time should be greater than or equal to the end time of the intervals of this node"); //$NON-NLS-1$
+                }
             }
 
             fNodeEnd = endtime;
@@ -457,7 +450,7 @@ public abstract class HTNode {
                  * null anyway).
                  */
                 ITmfStateInterval interval = fIntervals.get(i);
-                if (interval.getStartTime() <= t &&
+                if (t >= interval.getStartTime() &&
                         interval.getAttribute() < stateInfo.size()) {
                     stateInfo.set(interval.getAttribute(), interval);
                 }
@@ -522,25 +515,17 @@ public abstract class HTNode {
              */
             index = -index - 1;
 
-        }
-
-        /* Sometimes binarySearch yields weird stuff... */
-        if (index < 0) {
-            index = 0;
-        }
-        if (index >= fIntervals.size()) {
-            index = fIntervals.size() - 1;
-        }
-
-        /*
-         * Another API quirkiness, the returned index is the one of the *last*
-         * element of a series of equal endtimes, which happens sometimes. We
-         * want the *first* element of such a series, to read through them
-         * again.
-         */
-        while (index > 0
-                && fIntervals.get(index - 1).compareTo(fIntervals.get(index)) == 0) {
-            index--;
+        } else {
+            /*
+             * Another API quirkiness, the returned index is the one of the *last*
+             * element of a series of equal endtimes, which happens sometimes. We
+             * want the *first* element of such a series, to read through them
+             * again.
+             */
+            while (index > 0
+                    && fIntervals.get(index - 1).compareTo(fIntervals.get(index)) == 0) {
+                index--;
+            }
         }
 
         return index;
@@ -570,7 +555,7 @@ public abstract class HTNode {
      */
     public int getNodeFreeSpace() {
         fRwl.readLock().lock();
-        int ret = fStringSectionOffset - getDataSectionEndOffset();
+        int ret = fConfig.getBlockSize() - getDataSectionEndOffset();
         fRwl.readLock().unlock();
 
         return ret;
@@ -605,18 +590,14 @@ public abstract class HTNode {
     @Override
     public String toString() {
         /* Only used for debugging, shouldn't be externalized */
-        StringBuffer buf = new StringBuffer("Node #" + fSequenceNumber + ", ");
-        buf.append(toStringSpecific());
-        buf.append(fIntervals.size() + " intervals (" + getNodeUsagePercent()
-                + "% used), ");
-
-        buf.append("[" + fNodeStart + " - ");
-        if (fIsOnDisk) {
-            buf = buf.append("" + fNodeEnd + "]");
-        } else {
-            buf = buf.append("...]");
-        }
-        return buf.toString();
+        return String.format("Node #%d, %s, %s, %d intervals (%d%% used), [%d - %s]",
+                fSequenceNumber,
+                (fParentSequenceNumber == -1) ? "Root" : "Parent #" + fParentSequenceNumber,
+                toStringSpecific(),
+                fIntervals.size(),
+                getNodeUsagePercent(),
+                fNodeStart,
+                (fIsOnDisk || fNodeEnd != 0) ? fNodeEnd : "...");
     }
 
     /**
@@ -628,11 +609,11 @@ public abstract class HTNode {
     @SuppressWarnings("nls")
     public void debugPrintIntervals(PrintWriter writer) {
         /* Only used for debugging, shouldn't be externalized */
-        writer.println("Node #" + fSequenceNumber + ":");
+        writer.println("Intervals for node #" + fSequenceNumber + ":");
 
         /* Array of children */
-        if (getNodeType() == NodeType.CORE) { /* Only Core Nodes can have children */
-            CoreNode thisNode = (CoreNode) this;
+        if (getNodeType() != NodeType.LEAF) { /* Only Core Nodes can have children */
+            ParentNode thisNode = (ParentNode) this;
             writer.print("  " + thisNode.getNbChildren() + " children");
             if (thisNode.getNbChildren() >= 1) {
                 writer.print(": [ " + thisNode.getChild(0));
This page took 0.040659 seconds and 5 git commands to generate.