1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal
3 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
5 * All rights reserved. This program and the accompanying materials are
6 * made available under the terms of the Eclipse Public License v1.0 which
7 * accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
11 * Alexandre Montplaisir - Initial API and implementation
12 * Florian Wininger - Allow to change the size of a interval
13 * Patrick Tasse - Add message to exceptions
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
;
18 import java
.io
.IOException
;
19 import java
.nio
.ByteBuffer
;
21 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
22 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
23 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
24 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
25 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
28 * The interval component, which will be contained in a node of the History
31 * @author Alexandre Montplaisir
33 public final class HTInterval
implements ITmfStateInterval
, Comparable
<HTInterval
> {
35 private static final String errMsg
= "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
38 * Size of an entry in the data section.
41 * 16 2 x Timevalue/long (interval start + end)
44 * + 4 int (valueOffset)
47 private static final int DATA_ENTRY_SIZE
= 25;
49 /* 'Byte' equivalent for state values types */
50 private static final byte TYPE_NULL
= -1;
51 private static final byte TYPE_INTEGER
= 0;
52 private static final byte TYPE_STRING
= 1;
53 private static final byte TYPE_LONG
= 2;
54 private static final byte TYPE_DOUBLE
= 3;
56 /* String entry sizes of different state values */
57 private static final int NO_ENTRY_SIZE
= 0;
58 private static final int LONG_ENTRY_SIZE
= 8;
59 private static final int DOUBLE_ENTRY_SIZE
= 8;
60 // sizes of string values depend on the string itself
62 private final long start
;
63 private final long end
;
64 private final int attribute
;
65 private final TmfStateValue sv
;
68 * Size of the strings section entry used by this interval (= 0 if not used)
70 private final int stringsEntrySize
;
73 * Standard constructor
75 * @param intervalStart
76 * Start time of the interval
78 * End time of the interval
80 * Attribute (quark) to which the state represented by this
83 * State value represented by this interval
84 * @throws TimeRangeException
85 * If the start time or end time are invalid
87 public HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
88 TmfStateValue value
) throws TimeRangeException
{
89 if (intervalStart
> intervalEnd
) {
90 throw new TimeRangeException("Start:" + intervalStart
+ ", End:" + intervalEnd
); //$NON-NLS-1$ //$NON-NLS-2$
93 this.start
= intervalStart
;
94 this.end
= intervalEnd
;
95 this.attribute
= attribute
;
97 this.stringsEntrySize
= computeStringsEntrySize();
101 * "Faster" constructor for inner use only. When we build an interval when
102 * reading it from disk (with {@link #readFrom}), we already know the size
103 * of the strings entry, so there is no need to call
104 * {@link #computeStringsEntrySize()} and do an extra copy.
106 private HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
107 TmfStateValue value
, int size
) throws TimeRangeException
{
108 if (intervalStart
> intervalEnd
) {
109 throw new TimeRangeException("Start:" + intervalStart
+ ", End:" + intervalEnd
); //$NON-NLS-1$ //$NON-NLS-2$
112 this.start
= intervalStart
;
113 this.end
= intervalEnd
;
114 this.attribute
= attribute
;
116 this.stringsEntrySize
= size
;
120 * Reader factory method. Builds the interval using an already-allocated
121 * ByteBuffer, which normally comes from a NIO FileChannel.
124 * The ByteBuffer from which to read the information
125 * @return The interval object
126 * @throws IOException
127 * If there was an error reading from the buffer
129 public static final HTInterval
readFrom(ByteBuffer buffer
) throws IOException
{
131 long intervalStart
, intervalEnd
;
134 int valueOrOffset
, valueSize
, res
;
138 /* Read the Data Section entry */
139 intervalStart
= buffer
.getLong();
140 intervalEnd
= buffer
.getLong();
141 attribute
= buffer
.getInt();
143 /* Read the 'type' of the value, then react accordingly */
144 valueType
= buffer
.get();
145 valueOrOffset
= buffer
.getInt();
149 value
= TmfStateValue
.nullValue();
150 valueSize
= NO_ENTRY_SIZE
;
154 /* "ValueOrOffset" is the straight value */
155 value
= TmfStateValue
.newValueInt(valueOrOffset
);
156 valueSize
= NO_ENTRY_SIZE
;
160 /* Go read the matching entry in the Strings section of the block */
162 buffer
.position(valueOrOffset
);
164 /* the first byte = the size to read */
165 valueSize
= buffer
.get();
168 * Careful though, 'valueSize' is the total size of the entry,
169 * including the 'size' byte at the start and end (0'ed) byte at the
170 * end. Here we want 'array' to only contain the real payload of the
173 array
= new byte[valueSize
- 2];
175 value
= TmfStateValue
.newValueString(new String(array
));
177 /* Confirm the 0'ed byte at the end */
180 throw new IOException(errMsg
);
184 * Restore the file pointer's position (so we can read the next
191 /* Go read the matching entry in the Strings section of the block */
193 buffer
.position(valueOrOffset
);
194 value
= TmfStateValue
.newValueLong(buffer
.getLong());
195 valueSize
= LONG_ENTRY_SIZE
;
198 * Restore the file pointer's position (so we can read the next
205 /* Go read the matching entry in the Strings section of the block */
207 buffer
.position(valueOrOffset
);
208 value
= TmfStateValue
.newValueDouble(buffer
.getDouble());
209 valueSize
= DOUBLE_ENTRY_SIZE
;
212 * Restore the file pointer's position (so we can read the next
219 /* Unknown data, better to not make anything up... */
220 throw new IOException(errMsg
);
224 interval
= new HTInterval(intervalStart
, intervalEnd
, attribute
, value
, valueSize
);
225 } catch (TimeRangeException e
) {
226 throw new IOException(errMsg
);
232 * Antagonist of the previous constructor, write the Data entry
233 * corresponding to this interval in a ByteBuffer (mapped to a block in the
234 * history-file, hopefully)
237 * The already-allocated ByteBuffer corresponding to a SHT Node
238 * @param endPosOfStringEntry
239 * The initial (before calling this function for this interval)
240 * position of the Strings Entry for this node. This will change
241 * from one call to the other if we're writing String
243 * @return The size of the Strings Entry that was written, if any.
245 public int writeInterval(ByteBuffer buffer
, int endPosOfStringEntry
) {
246 buffer
.putLong(start
);
248 buffer
.putInt(attribute
);
249 buffer
.put(getByteFromType(sv
.getType()));
251 switch (getByteFromType(sv
.getType())) {
255 /* We write the 'valueOffset' field as a straight value. */
257 buffer
.putInt(sv
.unboxInt());
258 } catch (StateValueTypeException e
) {
260 * This should not happen, since the value told us it was of
261 * type Null or Integer (corrupted value?)
268 byte[] byteArrayToWrite
;
270 byteArrayToWrite
= sv
.unboxStr().getBytes();
271 } catch (StateValueTypeException e1
) {
272 /* Should not happen, we're in a switch/case for string type */
273 throw new RuntimeException();
276 /* we use the valueOffset as an offset. */
277 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
279 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
282 * write the Strings entry (1st byte = size, then the bytes, then the 0)
284 buffer
.put((byte) stringsEntrySize
);
285 buffer
.put(byteArrayToWrite
);
286 buffer
.put((byte) 0);
287 assert (buffer
.position() == endPosOfStringEntry
);
292 /* we use the valueOffset as an offset. */
293 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
295 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
298 * write the Long in the Strings section
301 buffer
.putLong(sv
.unboxLong());
302 } catch (StateValueTypeException e
) {
304 * This should not happen, since the value told us it was of
305 * type Long (corrupted value?)
309 assert (buffer
.position() == endPosOfStringEntry
);
314 /* we use the valueOffset as an offset. */
315 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
317 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
319 /* Write the Double in the Strings section */
321 buffer
.putDouble(sv
.unboxDouble());
322 } catch (StateValueTypeException e
) {
324 * This should not happen, since the value told us it was of
325 * type Double (corrupted value?)
329 if (buffer
.position() != endPosOfStringEntry
) {
330 throw new IllegalStateException();
338 return stringsEntrySize
;
342 public long getStartTime() {
347 public long getEndTime() {
352 public long getViewerEndTime() {
357 public int getAttribute() {
362 public ITmfStateValue
getStateValue() {
367 public boolean intersects(long timestamp
) {
368 if (start
<= timestamp
) {
369 if (end
>= timestamp
) {
376 int getStringsEntrySize() {
377 return stringsEntrySize
;
381 * Total serialized size of this interval
383 * @return The interval size
385 public int getIntervalSize() {
386 return stringsEntrySize
+ DATA_ENTRY_SIZE
;
389 private int computeStringsEntrySize() {
390 switch(sv
.getType()) {
393 /* Those don't use the strings section at all */
394 return NO_ENTRY_SIZE
;
396 /* The value's bytes are written directly into the strings section */
397 return LONG_ENTRY_SIZE
;
399 /* The value is also written directly into the strings section */
400 return DOUBLE_ENTRY_SIZE
;
403 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
404 return sv
.unboxStr().getBytes().length
+ 2;
405 } catch (StateValueTypeException e
) {
406 /* We're inside a switch/case for the string type, can't happen */
407 throw new IllegalStateException(e
);
410 /* It's very important that we know how to write the state value in
412 throw new IllegalStateException();
417 * Compare the END TIMES of different intervals. This is used to sort the
418 * intervals when we close down a node.
421 public int compareTo(HTInterval other
) {
422 if (this.end
< other
.end
) {
424 } else if (this.end
> other
.end
) {
432 public boolean equals(Object other
) {
433 if (other
instanceof HTInterval
&&
434 this.compareTo((HTInterval
) other
) == 0) {
441 public int hashCode() {
442 return super.hashCode();
446 public String
toString() {
447 /* Only for debug, should not be externalized */
448 StringBuilder sb
= new StringBuilder();
451 sb
.append(", "); //$NON-NLS-1$
455 sb
.append(", attribute = "); //$NON-NLS-1$
456 sb
.append(attribute
);
458 sb
.append(", value = "); //$NON-NLS-1$
459 sb
.append(sv
.toString());
461 return sb
.toString();
465 * Here we determine how state values "types" are written in the 8-bit
466 * field that indicates the value type in the file.
468 private static byte getByteFromType(ITmfStateValue
.Type type
) {
481 /* Should not happen if the switch is fully covered */
482 throw new IllegalStateException();