/*******************************************************************************
* 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 fr.inria.linuxtools.internal.statesystem.core.backend.historytree;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A Core node is a first-level node of a History Tree which is not a leaf node.
*
* It extends HTNode by adding support for child nodes, and also extensions.
*
* @author Alexandre Montplaisir
*/
public final class CoreNode extends HTNode {
/** Number of bytes in a int */
private static final int SIZE_INT = 4;
/** Number of bytes in a long */
private static final int SIZE_LONG = 8;
/** Nb. of children this node has */
private int nbChildren;
/** Seq. numbers of the children nodes (size = MAX_NB_CHILDREN) */
private int[] children;
/** Start times of each of the children (size = MAX_NB_CHILDREN) */
private long[] childStart;
/** Seq number of this node's extension. -1 if none */
private volatile int extension = -1;
/**
* Lock used to gate the accesses to the children arrays. Meant to be a
* different lock from the one in {@link HTNode}.
*/
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false);
/**
* 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 CoreNode(HTConfig config, int seqNumber, int parentSeqNumber,
long start) {
super(config, seqNumber, parentSeqNumber, start);
this.nbChildren = 0;
int size = config.getMaxChildren();
/*
* We instantiate the two following arrays at full size right away,
* since we want to reserve that space in the node's header.
* "this.nbChildren" will tell us how many relevant entries there are in
* those tables.
*/
this.children = new int[size];
this.childStart = new long[size];
}
@Override
protected void readSpecificHeader(ByteBuffer buffer) {
int size = getConfig().getMaxChildren();
extension = buffer.getInt();
nbChildren = buffer.getInt();
children = new int[size];
for (int i = 0; i < nbChildren; i++) {
children[i] = buffer.getInt();
}
for (int i = nbChildren; i < size; i++) {
buffer.getInt();
}
this.childStart = new long[size];
for (int i = 0; i < nbChildren; i++) {
childStart[i] = buffer.getLong();
}
for (int i = nbChildren; i < size; i++) {
buffer.getLong();
}
}
@Override
protected void writeSpecificHeader(ByteBuffer buffer) {
int size = getConfig().getMaxChildren();
buffer.putInt(extension);
buffer.putInt(nbChildren);
/* Write the "children's seq number" array */
for (int i = 0; i < nbChildren; i++) {
buffer.putInt(children[i]);
}
for (int i = nbChildren; i < size; i++) {
buffer.putInt(0);
}
/* Write the "children's start times" array */
for (int i = 0; i < nbChildren; i++) {
buffer.putLong(childStart[i]);
}
for (int i = nbChildren; i < size; i++) {
buffer.putLong(0);
}
}
/**
* Return the number of child nodes this node has.
*
* @return The number of child nodes
*/
public int getNbChildren() {
rwl.readLock().lock();
int ret = nbChildren;
rwl.readLock().unlock();
return ret;
}
/**
* Get the child node corresponding to the specified index
*
* @param index The index of the child to lookup
* @return The child node
*/
public int getChild(int index) {
rwl.readLock().lock();
try {
return children[index];
} finally {
rwl.readLock().unlock();
}
}
/**
* Get the latest (right-most) child node of this node.
*
* @return The latest child node
*/
public int getLatestChild() {
rwl.readLock().lock();
try {
return children[nbChildren - 1];
} finally {
rwl.readLock().unlock();
}
}
/**
* Get the start time of the specified child node.
*
* @param index
* The index of the child node
* @return The start time of the that child node.
*/
public long getChildStart(int index) {
rwl.readLock().lock();
try {
return childStart[index];
} finally {
rwl.readLock().unlock();
}
}
/**
* Get the start time of the latest (right-most) child node.
*
* @return The start time of the latest child
*/
public long getLatestChildStart() {
rwl.readLock().lock();
try {
return childStart[nbChildren - 1];
} finally {
rwl.readLock().unlock();
}
}
/**
* Get the sequence number of the extension to this node (if there is one).
*
* @return The sequence number of the extended node. '-1' is returned if
* there is no extension node.
*/
public int getExtensionSequenceNumber() {
return extension;
}
/**
* Tell this node that it has a new child (Congrats!)
*
* @param childNode
* The SHTNode object of the new child
*/
public void linkNewChild(HTNode childNode) {
rwl.writeLock().lock();
try {
assert (nbChildren < getConfig().getMaxChildren());
children[nbChildren] = childNode.getSequenceNumber();
childStart[nbChildren] = childNode.getNodeStart();
nbChildren++;
} finally {
rwl.writeLock().unlock();
}
}
@Override
public NodeType getNodeType() {
return NodeType.CORE;
}
@Override
protected int getSpecificHeaderSize() {
int maxChildren = getConfig().getMaxChildren();
int specificSize =
SIZE_INT /* 1x int (extension node) */
+ SIZE_INT /* 1x int (nbChildren) */
/* MAX_NB * int ('children' table) */
+ SIZE_INT * maxChildren
/* MAX_NB * Timevalue ('childStart' table) */
+ SIZE_LONG * maxChildren;
return specificSize;
}
@Override
public String toStringSpecific() {
/* Only used for debugging, shouldn't be externalized */
return "Core Node, " + nbChildren + " children, "; //$NON-NLS-1$ //$NON-NLS-2$
}
}