/*******************************************************************************
- * Copyright (c) 2012, 2013 Ericsson
- * Copyright (c) 2010, 2011 École Polytechnique de Montréal
+ * Copyright (c) 2012, 2014 Ericsson, École Polytechnique de Montréal
* Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
*
* All rights reserved. This program and the accompanying materials are
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * Contributors:
+ * Alexandre Montplaisir - Initial API and implementation
+ * Florian Wininger - Allow to change the size of a interval
*******************************************************************************/
package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
* The interval component, which will be contained in a node of the History
* Tree.
*
- * @author alexmont
- *
+ * @author Alexandre Montplaisir
*/
-final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
+public final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
private static final String errMsg = "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
+ /**
+ * Size of an entry in the data section.
+ *
+ * <pre>
+ * 16 2 x Timevalue/long (interval start + end)
+ * + 4 int (key)
+ * + 1 byte (type)
+ * + 4 int (valueOffset)
+ * </pre>
+ */
+ private static final int DATA_ENTRY_SIZE = 25;
+
/* 'Byte' equivalent for state values types */
private static final byte TYPE_NULL = -1;
private static final byte TYPE_INTEGER = 0;
private static final byte TYPE_STRING = 1;
private static final byte TYPE_LONG = 2;
+ private static final byte TYPE_DOUBLE = 3;
+
+ /* String entry sizes of different state values */
+ private static final int NO_ENTRY_SIZE = 0;
+ private static final int LONG_ENTRY_SIZE = 8;
+ private static final int DOUBLE_ENTRY_SIZE = 8;
+ // sizes of string values depend on the string itself
private final long start;
private final long end;
* Standard constructor
*
* @param intervalStart
+ * Start time of the interval
* @param intervalEnd
+ * End time of the interval
* @param attribute
+ * Attribute (quark) to which the state represented by this
+ * interval belongs
* @param value
+ * State value represented by this interval
* @throws TimeRangeException
+ * If the start time or end time are invalid
*/
- HTInterval(long intervalStart, long intervalEnd, int attribute,
+ public HTInterval(long intervalStart, long intervalEnd, int attribute,
TmfStateValue value) throws TimeRangeException {
if (intervalStart > intervalEnd) {
throw new TimeRangeException();
}
/**
- * Reader constructor. Builds the interval using an already-allocated
+ * "Faster" constructor for inner use only. When we build an interval when
+ * reading it from disk (with {@link #readFrom}), we already know the size
+ * of the strings entry, so there is no need to call
+ * {@link #computeStringsEntrySize()} and do an extra copy.
+ */
+ private HTInterval(long intervalStart, long intervalEnd, int attribute,
+ TmfStateValue value, int size) throws TimeRangeException {
+ if (intervalStart > intervalEnd) {
+ throw new TimeRangeException();
+ }
+
+ this.start = intervalStart;
+ this.end = intervalEnd;
+ this.attribute = attribute;
+ this.sv = value;
+ this.stringsEntrySize = size;
+ }
+
+ /**
+ * Reader factory method. Builds the interval using an already-allocated
* ByteBuffer, which normally comes from a NIO FileChannel.
*
* @param buffer
* The ByteBuffer from which to read the information
+ * @return The interval object
* @throws IOException
+ * If there was an error reading from the buffer
*/
- final static HTInterval readFrom(ByteBuffer buffer) throws IOException {
+ public static final HTInterval readFrom(ByteBuffer buffer) throws IOException {
HTInterval interval;
long intervalStart, intervalEnd;
int attribute;
case TYPE_NULL:
value = TmfStateValue.nullValue();
+ valueSize = NO_ENTRY_SIZE;
break;
case TYPE_INTEGER:
/* "ValueOrOffset" is the straight value */
value = TmfStateValue.newValueInt(valueOrOffset);
+ valueSize = NO_ENTRY_SIZE;
break;
case TYPE_STRING:
buffer.mark();
buffer.position(valueOrOffset);
value = TmfStateValue.newValueLong(buffer.getLong());
+ valueSize = LONG_ENTRY_SIZE;
+
+ /*
+ * Restore the file pointer's position (so we can read the next
+ * interval)
+ */
+ buffer.reset();
+ break;
+
+ case TYPE_DOUBLE:
+ /* Go read the matching entry in the Strings section of the block */
+ buffer.mark();
+ buffer.position(valueOrOffset);
+ value = TmfStateValue.newValueDouble(buffer.getDouble());
+ valueSize = DOUBLE_ENTRY_SIZE;
/*
* Restore the file pointer's position (so we can read the next
*/
buffer.reset();
break;
+
default:
/* Unknown data, better to not make anything up... */
throw new IOException(errMsg);
}
try {
- interval = new HTInterval(intervalStart, intervalEnd, attribute, value);
+ interval = new HTInterval(intervalStart, intervalEnd, attribute, value, valueSize);
} catch (TimeRangeException e) {
throw new IOException(errMsg);
}
* StateValues.
* @return The size of the Strings Entry that was written, if any.
*/
- int writeInterval(ByteBuffer buffer, int endPosOfStringEntry) {
+ public int writeInterval(ByteBuffer buffer, int endPosOfStringEntry) {
buffer.putLong(start);
buffer.putLong(end);
buffer.putInt(attribute);
case TYPE_NULL:
case TYPE_INTEGER:
- /* We write the 'valueOffset' field as a straight value. In the case
- * of a null value, it will be unboxed as -1 */
+ /* We write the 'valueOffset' field as a straight value. */
try {
buffer.putInt(sv.unboxInt());
} catch (StateValueTypeException e) {
buffer.reset();
break;
+ case TYPE_DOUBLE:
+ /* we use the valueOffset as an offset. */
+ buffer.putInt(endPosOfStringEntry - stringsEntrySize);
+ buffer.mark();
+ buffer.position(endPosOfStringEntry - stringsEntrySize);
+
+ /* Write the Double in the Strings section */
+ try {
+ buffer.putDouble(sv.unboxDouble());
+ } catch (StateValueTypeException e) {
+ /*
+ * This should not happen, since the value told us it was of
+ * type Double (corrupted value?)
+ */
+ e.printStackTrace();
+ }
+ if (buffer.position() != endPosOfStringEntry) {
+ throw new IllegalStateException();
+ }
+ buffer.reset();
+ break;
+
default:
break;
}
/**
* Total serialized size of this interval
*
- * @return
+ * @return The interval size
*/
- int getIntervalSize() {
- return stringsEntrySize + HTNode.getDataEntrySize();
+ public int getIntervalSize() {
+ return stringsEntrySize + DATA_ENTRY_SIZE;
}
private int computeStringsEntrySize() {
case NULL:
case INTEGER:
/* Those don't use the strings section at all */
- return 0;
+ return NO_ENTRY_SIZE;
case LONG:
/* The value's bytes are written directly into the strings section */
- return 8;
+ return LONG_ENTRY_SIZE;
+ case DOUBLE:
+ /* The value is also written directly into the strings section */
+ return DOUBLE_ENTRY_SIZE;
case STRING:
try {
/* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
return sv.unboxStr().getBytes().length + 2;
} catch (StateValueTypeException e) {
/* We're inside a switch/case for the string type, can't happen */
- throw new RuntimeException();
+ throw new IllegalStateException(e);
}
default:
/* It's very important that we know how to write the state value in
* the file!! */
- throw new RuntimeException();
+ throw new IllegalStateException();
}
}
@Override
public boolean equals(Object other) {
- if (other instanceof HTInterval) {
- if (this.compareTo((HTInterval) other) == 0) {
- return true;
- }
+ if (other instanceof HTInterval &&
+ this.compareTo((HTInterval) other) == 0) {
+ return true;
}
return false;
}
return TYPE_STRING;
case LONG:
return TYPE_LONG;
+ case DOUBLE:
+ return TYPE_DOUBLE;
default:
/* Should not happen if the switch is fully covered */
- throw new RuntimeException();
+ throw new IllegalStateException();
}
}
}