/*******************************************************************************
- * Copyright (c) 2012, 2014 Ericsson
- * Copyright (c) 2010, 2011 École Polytechnique de Montréal
- * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
+ * Copyright (c) 2010, 2014 Ericsson, École Polytechnique de Montréal, and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * Contributors:
+ * Alexandre Montplaisir - Initial API and implementation
+ * Florian Wininger - Add Extension and Leaf Node
*******************************************************************************/
package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
* It extends HTNode by adding support for child nodes, and also extensions.
*
* @author Alexandre Montplaisir
- *
*/
-public class CoreNode extends HTNode {
+public final class CoreNode extends HTNode {
/** Number of bytes in a int */
private static final int SIZE_INT = 4;
* @param start
* The earliest timestamp stored in this node
*/
- protected CoreNode(HTConfig config, int seqNumber, int parentSeqNumber,
+ public CoreNode(HTConfig config, int seqNumber, int parentSeqNumber,
long start) {
super(config, seqNumber, parentSeqNumber, start);
this.nbChildren = 0;
* @param childNode
* The SHTNode object of the new child
*/
- public void linkNewChild(CoreNode childNode) {
+ public void linkNewChild(HTNode childNode) {
rwl.writeLock().lock();
try {
assert (nbChildren < getConfig().getMaxChildren());
}
@Override
- public byte getNodeType() {
- return 1;
+ public NodeType getNodeType() {
+ return NodeType.CORE;
}
@Override
/*******************************************************************************
- * Copyright (c) 2012, 2014 Ericsson
- * Copyright (c) 2010, 2011 École Polytechnique de Montréal
- * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
+ * Copyright (c) 2010, 2014 Ericsson, École Polytechnique de Montréal, and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * Contributors:
+ * Alexandre Montplaisir - Initial API and implementation
+ * Florian Wininger - Add Extension and Leaf Node
*******************************************************************************/
package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
*/
public abstract class HTNode {
+ // ------------------------------------------------------------------------
+ // Class fields
+ // ------------------------------------------------------------------------
+
+ /**
+ * The type of node
+ */
+ public static enum NodeType {
+ /**
+ * Core node, which is a "front" node, at any level of the tree except
+ * the bottom-most one. It has children, and may have extensions.
+ */
+ CORE,
+ /**
+ * Leaf node, which is a node at the last bottom level of the tree. It
+ * cannot have any children or extensions.
+ */
+ LEAF;
+
+ /**
+ * Determine a node type by reading a serialized byte.
+ *
+ * @param rep
+ * The byte representation of the node type
+ * @return The corresponding NodeType
+ * @throws IOException
+ * If the NodeType is unrecognized
+ */
+ public static NodeType fromByte(byte rep) throws IOException {
+ switch (rep) {
+ case 1:
+ return CORE;
+ case 2:
+ return LEAF;
+ default:
+ throw new IOException();
+ }
+ }
+
+ /**
+ * Get the byte representation of this node type. It can then be read
+ * with {@link #fromByte}.
+ *
+ * @return The byte matching this node type
+ */
+ public byte toByte() {
+ switch (this) {
+ case CORE:
+ return 1;
+ case LEAF:
+ return 2;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Attributes
+ // ------------------------------------------------------------------------
+
/* Configuration of the History Tree to which belongs this node */
private final HTConfig config;
buffer.flip();
/* Read the common header part */
- byte type = buffer.get();
+ byte typeByte = buffer.get();
+ NodeType type = NodeType.fromByte(typeByte);
long start = buffer.getLong();
long end = buffer.getLong();
int seqNb = buffer.getInt();
/* Now the rest of the header depends on the node type */
switch (type) {
- case 1:
+ case CORE:
/* Core nodes */
newNode = new CoreNode(config, seqNb, parentSeqNb, start);
newNode.readSpecificHeader(buffer);
break;
- // TODO implement other node types
- // case 2:
- // /* Leaf nodes */
- //
- //
- //
- // case 3:
- // /* "Claudette" (extended) nodes */
- //
+ case LEAF:
+ /* Leaf nodes */
+ newNode = new LeafNode(config, seqNb, parentSeqNb, start);
+ newNode.readSpecificHeader(buffer);
+ break;
default:
/* Unrecognized node type */
buffer.clear();
/* Write the common header part */
- buffer.put(this.getNodeType());
+ buffer.put(this.getNodeType().toByte());
buffer.putLong(nodeStart);
buffer.putLong(nodeEnd);
buffer.putInt(sequenceNumber);
/**
* Get the end time of this node.
*
- * @return The end time of this node
+ * @return The end time of this node
*/
public long getNodeEnd() {
if (this.isOnDisk) {
* The timestamp
* @return The Interval containing the information we want, or null if it
* wasn't found
- * @throws TimeRangeException If 't' is invalid
+ * @throws TimeRangeException
+ * If 't' is invalid
*/
public HTInterval getRelevantInterval(int key, long t) throws TimeRangeException {
rwl.readLock().lock();
return index;
}
-
/**
* <pre>
* 1 - byte (type)
writer.println("Node #" + sequenceNumber + ":");
/* Array of children */
- if (this.getNodeType() == 1) { /* Only Core Nodes can have children */
+ if (this.getNodeType() == NodeType.CORE) { /* Only Core Nodes can have children */
CoreNode thisNode = (CoreNode) this;
writer.print(" " + thisNode.getNbChildren() + " children");
if (thisNode.getNbChildren() >= 1) {
*
* @return The node type
*/
- public abstract byte getNodeType();
+ public abstract NodeType getNodeType();
/**
* Return the specific header size of this node. This means the size
/*******************************************************************************
- * Copyright (c) 2012, 2014 Ericsson
- * Copyright (c) 2010, 2011 École Polytechnique de Montréal
- * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
+ * Copyright (c) 2010, 2014 Ericsson, École Polytechnique de Montréal, and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * Contributors:
+ * Alexandre Montplaisir - Initial API and implementation
+ * Florian Wininger - Add Extension and Leaf Node
*******************************************************************************/
package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
import java.util.Collections;
import java.util.List;
+import org.eclipse.linuxtools.internal.tmf.core.Activator;
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateProvider;
private static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA900;
/** File format version. Increment when breaking compatibility. */
- private static final int FILE_VERSION = 3;
+ private static final int FILE_VERSION = 4;
// ------------------------------------------------------------------------
// Tree-specific configuration
private int nodeCount;
/** "Cache" to keep the active nodes in memory */
- private final List<CoreNode> latestBranch;
+ private final List<HTNode> latestBranch;
// ------------------------------------------------------------------------
// Constructors/"Destructors"
*/
public HistoryTree(HTConfig conf) throws IOException {
/*
- * Simple check to make sure we have enough place in the 0th block
- * for the tree configuration
+ * Simple check to make sure we have enough place in the 0th block for
+ * the tree configuration
*/
if (conf.getBlockSize() < TREE_HEADER_SIZE) {
throw new IllegalArgumentException();
config = conf;
treeEnd = conf.getTreeStart();
nodeCount = 0;
- latestBranch = Collections.synchronizedList(new ArrayList<CoreNode>());
+ latestBranch = Collections.synchronizedList(new ArrayList<HTNode>());
/* Prepare the IO object */
treeIO = new HT_IO(config, true);
/* Add the first node to the tree */
- CoreNode firstNode = initNewCoreNode(-1, conf.getTreeStart());
+ LeafNode firstNode = initNewLeafNode(-1, conf.getTreeStart());
latestBranch.add(firstNode);
}
* start
* @throws ClosedChannelException
*/
- private List<CoreNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
- HTNode nextChildNode;
+ private List<HTNode> buildLatestBranch(int rootNodeSeqNb) throws ClosedChannelException {
+ List<HTNode> list = new ArrayList<>();
- List<CoreNode> list = new ArrayList<>();
+ HTNode nextChildNode = treeIO.readNode(rootNodeSeqNb);
+ list.add(nextChildNode);
- nextChildNode = treeIO.readNode(rootNodeSeqNb);
- list.add((CoreNode) nextChildNode);
- while (list.get(list.size() - 1).getNbChildren() > 0) {
- nextChildNode = treeIO.readNode(list.get(list.size() - 1).getLatestChild());
- list.add((CoreNode) nextChildNode);
+ /* Follow the last branch up to the leaf */
+ while (nextChildNode.getNodeType() == HTNode.NodeType.CORE) {
+ nextChildNode = treeIO.readNode(((CoreNode) nextChildNode).getLatestChild());
+ list.add(nextChildNode);
}
return Collections.synchronizedList(list);
}
*
* @return The root node
*/
- public CoreNode getRootNode() {
+ public HTNode getRootNode() {
return latestBranch.get(0);
}
synchronized (latestBranch) {
final long splitTime = treeEnd;
- assert (indexOfNode < latestBranch.size());
+ if (indexOfNode >= latestBranch.size()) {
+ /*
+ * We need to make sure (indexOfNode - 1) doesn't get the last
+ * node in the branch, because that one is a Leaf Node.
+ */
+ throw new IllegalStateException();
+ }
/* Check if we need to add a new root node */
if (indexOfNode == 0) {
}
/* Check if we can indeed add a child to the target parent */
- if (latestBranch.get(indexOfNode - 1).getNbChildren() == config.getMaxChildren()) {
+ if (((CoreNode) latestBranch.get(indexOfNode - 1)).getNbChildren() == config.getMaxChildren()) {
/* If not, add a branch starting one level higher instead */
addSiblingNode(indexOfNode - 1);
return;
latestBranch.get(i).closeThisNode(splitTime);
treeIO.writeNode(latestBranch.get(i));
- CoreNode prevNode = latestBranch.get(i - 1);
- CoreNode newNode = initNewCoreNode(prevNode.getSequenceNumber(),
- splitTime + 1);
- prevNode.linkNewChild(newNode);
+ CoreNode prevNode = (CoreNode) latestBranch.get(i - 1);
+ HTNode newNode;
+
+ switch (latestBranch.get(i).getNodeType()) {
+ case CORE:
+ newNode = initNewCoreNode(prevNode.getSequenceNumber(), splitTime + 1);
+ break;
+ case LEAF:
+ newNode = initNewLeafNode(prevNode.getSequenceNumber(), splitTime + 1);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ prevNode.linkNewChild(newNode);
latestBranch.set(i, newNode);
}
}
private void addNewRootNode() {
final long splitTime = this.treeEnd;
- CoreNode oldRootNode = latestBranch.get(0);
+ HTNode oldRootNode = latestBranch.get(0);
CoreNode newRootNode = initNewCoreNode(-1, config.getTreeStart());
/* Tell the old root node that it isn't root anymore */
int depth = latestBranch.size();
latestBranch.clear();
latestBranch.add(newRootNode);
+
+ // Create new coreNode
for (int i = 1; i < depth + 1; i++) {
- CoreNode prevNode = latestBranch.get(i - 1);
+ CoreNode prevNode = (CoreNode) latestBranch.get(i - 1);
CoreNode newNode = initNewCoreNode(prevNode.getParentSequenceNumber(),
splitTime + 1);
prevNode.linkNewChild(newNode);
latestBranch.add(newNode);
}
+
+ // Create the new leafNode
+ CoreNode prevNode = (CoreNode) latestBranch.get(depth);
+ LeafNode newNode = initNewLeafNode(prevNode.getParentSequenceNumber(), splitTime + 1);
+ prevNode.linkNewChild(newNode);
+ latestBranch.add(newNode);
}
/**
- * Add a new empty node to the tree.
+ * Add a new empty core node to the tree.
*
* @param parentSeqNumber
* Sequence number of this node's parent
return newNode;
}
+ /**
+ * Add a new empty leaf node to the tree.
+ *
+ * @param parentSeqNumber
+ * Sequence number of this node's parent
+ * @param startTime
+ * Start time of the new node
+ * @return The newly created node
+ */
+ private LeafNode initNewLeafNode(int parentSeqNumber, long startTime) {
+ LeafNode newNode = new LeafNode(config, this.nodeCount, parentSeqNumber,
+ startTime);
+ this.nodeCount++;
+
+ /* Update the treeEnd if needed */
+ if (startTime >= this.treeEnd) {
+ this.treeEnd = startTime + 1;
+ }
+ return newNode;
+ }
+
/**
* Inner method to select the next child of the current node intersecting
* the given timestamp. Useful for moving down the tree following one
}
/*
- * Test that the childStartTimes[] array matches the real nodes' start
- * times
+ * Test that the childStartTimes[] array matches the real nodes'
+ * start times
*/
for (int i = 0; i < node.getNbChildren(); i++) {
otherNode = treeIO.readNode(node.getChild(i));
/* Only used for debugging, shouldn't be externalized */
@SuppressWarnings("nls")
private void preOrderPrint(PrintWriter writer, boolean printIntervals,
- CoreNode currentNode, int curDepth) {
+ HTNode currentNode, int curDepth) {
writer.println(currentNode.toString());
if (printIntervals) {
currentNode.debugPrintIntervals(writer);
}
- try {
- for (int i = 0; i < currentNode.getNbChildren(); i++) {
- HTNode nextNode = treeIO.readNode(currentNode.getChild(i));
- assert (nextNode instanceof CoreNode); // TODO temporary
- for (int j = 0; j < curDepth; j++) {
- writer.print(" ");
+ switch (currentNode.getNodeType()) {
+ case LEAF:
+ /* Stop if it's the leaf node */
+ return;
+
+ case CORE:
+ try {
+ final CoreNode node = (CoreNode) currentNode;
+ /* Print the extensions, if any */
+ int extension = node.getExtensionSequenceNumber();
+ while (extension != -1) {
+ HTNode nextNode = treeIO.readNode(extension);
+ preOrderPrint(writer, printIntervals, nextNode, curDepth);
+ }
+
+ /* Print the child nodes */
+ for (int i = 0; i < node.getNbChildren(); i++) {
+ HTNode nextNode = treeIO.readNode(node.getChild(i));
+ for (int j = 0; j < curDepth; j++) {
+ writer.print(" ");
+ }
+ writer.print("+-");
+ preOrderPrint(writer, printIntervals, nextNode, curDepth + 1);
}
- writer.print("+-");
- preOrderPrint(writer, printIntervals, (CoreNode) nextNode,
- curDepth + 1);
+ } catch (ClosedChannelException e) {
+ Activator.logError(e.getMessage());
}
- } catch (ClosedChannelException e) {
- e.printStackTrace();
+ break;
+
+ default:
+ break;
}
}
}
/* We start by reading the information in the root node */
- // FIXME using CoreNode for now, we'll have to redo this part to handle
- // different node types
- CoreNode currentNode = sht.getRootNode();
+ HTNode currentNode = sht.getRootNode();
currentNode.writeInfoFromNode(stateInfo, t);
/* Then we follow the branch down in the relevant children */
try {
- while (currentNode.getNbChildren() > 0) {
- currentNode = (CoreNode) sht.selectNextChild(currentNode, t);
+ while (currentNode.getNodeType() == HTNode.NodeType.CORE) {
+ currentNode = sht.selectNextChild((CoreNode) currentNode, t);
currentNode.writeInfoFromNode(stateInfo, t);
}
} catch (ClosedChannelException e) {
throw new TimeRangeException();
}
- // FIXME using CoreNode for now, we'll have to redo this part to handle
- // different node types
- CoreNode currentNode = sht.getRootNode();
+ HTNode currentNode = sht.getRootNode();
HTInterval interval = currentNode.getRelevantInterval(key, t);
try {
- while (interval == null && currentNode.getNbChildren() > 0) {
- currentNode = (CoreNode) sht.selectNextChild(currentNode, t);
+ while (interval == null && currentNode.getNodeType() == HTNode.NodeType.CORE) {
+ currentNode = sht.selectNextChild((CoreNode)currentNode, t);
interval = currentNode.getRelevantInterval(key, t);
}
} catch (ClosedChannelException e) {
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2014 É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
+ *
+ * Contributors:
+ * Florian Wininger - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A Leaf node is a last-level node of a History Tree.
+ *
+ * A leaf node cannot have children, so it extends HTNode without adding
+ * anything in particular.
+ *
+ * @author Florian Wininger
+ */
+public final class LeafNode extends HTNode {
+
+ /**
+ * Initial constructor. Use this to initialize a new EMPTY node.
+ *
+ * @param config
+ * Configuration of the History Tree
+ * @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 LeafNode(HTConfig config, int seqNumber, int parentSeqNumber,
+ long start) {
+ super(config, seqNumber, parentSeqNumber, start);
+ }
+
+ @Override
+ protected void readSpecificHeader(ByteBuffer buffer) {
+ /* No specific header part */
+ }
+
+ @Override
+ protected void writeSpecificHeader(ByteBuffer buffer) {
+ /* No specific header part */
+ }
+
+ @Override
+ public NodeType getNodeType() {
+ return NodeType.LEAF;
+ }
+
+ @Override
+ protected int getSpecificHeaderSize() {
+ /* Empty */
+ return 0;
+ }
+
+ @Override
+ public String toStringSpecific() {
+ /* Only used for debugging, shouldn't be externalized */
+ return "Leaf Node, "; //$NON-NLS-1$;
+ }
+
+}