/*******************************************************************************
* 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
* 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.tracecompass.internal.statesystem.core.backend.historytree.classic;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTConfig;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.HTNode;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.ParentNode;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
/**
* 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 ParentNode {
/** 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);
}
}
@Override
public int getNbChildren() {
rwl.readLock().lock();
try {
return nbChildren;
} finally {
rwl.readLock().unlock();
}
}
@Override
public int getChild(int index) {
rwl.readLock().lock();
try {
return children[index];
} finally {
rwl.readLock().unlock();
}
}
@Override
public int getLatestChild() {
rwl.readLock().lock();
try {
return children[nbChildren - 1];
} finally {
rwl.readLock().unlock();
}
}
@Override
public long getChildStart(int index) {
rwl.readLock().lock();
try {
return childStart[index];
} 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() {
rwl.readLock().lock();
try {
return extension;
} finally {
rwl.readLock().unlock();
}
}
@Override
public void linkNewChild(HTNode childNode) {
rwl.writeLock().lock();
try {
if (nbChildren >= getConfig().getMaxChildren()) {
throw new IllegalStateException("Asked to link another child but parent already has maximum number of children"); //$NON-NLS-1$
}
children[nbChildren] = childNode.getSequenceNumber();
childStart[nbChildren] = childNode.getNodeStart();
nbChildren++;
} finally {
rwl.writeLock().unlock();
}
}
@Override
public Collection<Integer> selectNextChildren(long t) throws TimeRangeException {
if (t < getNodeStart() || (isOnDisk() && t > getNodeEnd())) {
throw new TimeRangeException("Requesting children outside the node's range: " + t); //$NON-NLS-1$
}
rwl.readLock().lock();
try {
int potentialNextSeqNb = -1;
for (int i = 0; i < nbChildren; i++) {
if (t >= childStart[i]) {
potentialNextSeqNb = children[i];
} else {
break;
}
}
if (potentialNextSeqNb == -1) {
throw new IllegalStateException("No next child node found"); //$NON-NLS-1$
}
return Collections.singleton(potentialNextSeqNb);
} finally {
rwl.readLock().unlock();
}
}
@Override
public NodeType getNodeType() {
return NodeType.CORE;
}
@Override
protected int getSpecificHeaderSize() {
int maxChildren = getConfig().getMaxChildren();
int specificSize =
Integer.BYTES /* 1x int (extension node) */
+ Integer.BYTES /* 1x int (nbChildren) */
/* MAX_NB * int ('children' table) */
+ Integer.BYTES * maxChildren
/* MAX_NB * Timevalue ('childStart' table) */
+ Long.BYTES * maxChildren;
return specificSize;
}
@Override
public String toStringSpecific() {
/* Only used for debugging, shouldn't be externalized */
return String.format("Core Node, %d children %s", //$NON-NLS-1$
nbChildren, Arrays.toString(Arrays.copyOf(children, nbChildren)));
}
}