1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
6 * All rights reserved. This program and the accompanying materials are
7 * made available under the terms of the Eclipse Public License v1.0 which
8 * accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.historytree
;
15 import java
.io
.IOException
;
16 import java
.nio
.ByteBuffer
;
18 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
19 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
20 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
21 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
25 * The interval component, which will be contained in a node of the History
28 * @author Alexandre Montplaisir
30 public final class HTInterval
implements ITmfStateInterval
, Comparable
<HTInterval
> {
32 private static final String errMsg
= "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
34 /* 'Byte' equivalent for state values types */
35 private static final byte TYPE_NULL
= -1;
36 private static final byte TYPE_INTEGER
= 0;
37 private static final byte TYPE_STRING
= 1;
38 private static final byte TYPE_LONG
= 2;
39 private static final byte TYPE_DOUBLE
= 3;
41 /* String entry sizes of different state values */
42 private static final int NO_ENTRY_SIZE
= 0;
43 private static final int LONG_ENTRY_SIZE
= 8;
44 private static final int DOUBLE_ENTRY_SIZE
= 8;
45 // sizes of string values depend on the string itself
47 private final long start
;
48 private final long end
;
49 private final int attribute
;
50 private final TmfStateValue sv
;
53 * Size of the strings section entry used by this interval (= 0 if not used)
55 private final int stringsEntrySize
;
58 * Standard constructor
60 * @param intervalStart
61 * Start time of the interval
63 * End time of the interval
65 * Attribute (quark) to which the state represented by this
68 * State value represented by this interval
69 * @throws TimeRangeException
70 * If the start time or end time are invalid
72 public HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
73 TmfStateValue value
) throws TimeRangeException
{
74 if (intervalStart
> intervalEnd
) {
75 throw new TimeRangeException();
78 this.start
= intervalStart
;
79 this.end
= intervalEnd
;
80 this.attribute
= attribute
;
82 this.stringsEntrySize
= computeStringsEntrySize();
86 * "Faster" constructor for inner use only. When we build an interval when
87 * reading it from disk (with {@link #readFrom}), we already know the size
88 * of the strings entry, so there is no need to call
89 * {@link #computeStringsEntrySize()} and do an extra copy.
91 private HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
92 TmfStateValue value
, int size
) throws TimeRangeException
{
93 if (intervalStart
> intervalEnd
) {
94 throw new TimeRangeException();
97 this.start
= intervalStart
;
98 this.end
= intervalEnd
;
99 this.attribute
= attribute
;
101 this.stringsEntrySize
= size
;
105 * Reader factory method. Builds the interval using an already-allocated
106 * ByteBuffer, which normally comes from a NIO FileChannel.
109 * The ByteBuffer from which to read the information
110 * @return The interval object
111 * @throws IOException
112 * If there was an error reading from the buffer
114 public static final HTInterval
readFrom(ByteBuffer buffer
) throws IOException
{
116 long intervalStart
, intervalEnd
;
119 int valueOrOffset
, valueSize
, res
;
123 /* Read the Data Section entry */
124 intervalStart
= buffer
.getLong();
125 intervalEnd
= buffer
.getLong();
126 attribute
= buffer
.getInt();
128 /* Read the 'type' of the value, then react accordingly */
129 valueType
= buffer
.get();
130 valueOrOffset
= buffer
.getInt();
134 value
= TmfStateValue
.nullValue();
135 valueSize
= NO_ENTRY_SIZE
;
139 /* "ValueOrOffset" is the straight value */
140 value
= TmfStateValue
.newValueInt(valueOrOffset
);
141 valueSize
= NO_ENTRY_SIZE
;
145 /* Go read the matching entry in the Strings section of the block */
147 buffer
.position(valueOrOffset
);
149 /* the first byte = the size to read */
150 valueSize
= buffer
.get();
153 * Careful though, 'valueSize' is the total size of the entry,
154 * including the 'size' byte at the start and end (0'ed) byte at the
155 * end. Here we want 'array' to only contain the real payload of the
158 array
= new byte[valueSize
- 2];
160 value
= TmfStateValue
.newValueString(new String(array
));
162 /* Confirm the 0'ed byte at the end */
165 throw new IOException(errMsg
);
169 * Restore the file pointer's position (so we can read the next
176 /* Go read the matching entry in the Strings section of the block */
178 buffer
.position(valueOrOffset
);
179 value
= TmfStateValue
.newValueLong(buffer
.getLong());
180 valueSize
= LONG_ENTRY_SIZE
;
183 * Restore the file pointer's position (so we can read the next
190 /* Go read the matching entry in the Strings section of the block */
192 buffer
.position(valueOrOffset
);
193 value
= TmfStateValue
.newValueDouble(buffer
.getDouble());
194 valueSize
= DOUBLE_ENTRY_SIZE
;
197 * Restore the file pointer's position (so we can read the next
204 /* Unknown data, better to not make anything up... */
205 throw new IOException(errMsg
);
209 interval
= new HTInterval(intervalStart
, intervalEnd
, attribute
, value
, valueSize
);
210 } catch (TimeRangeException e
) {
211 throw new IOException(errMsg
);
217 * Antagonist of the previous constructor, write the Data entry
218 * corresponding to this interval in a ByteBuffer (mapped to a block in the
219 * history-file, hopefully)
222 * The already-allocated ByteBuffer corresponding to a SHT Node
223 * @param endPosOfStringEntry
224 * The initial (before calling this function for this interval)
225 * position of the Strings Entry for this node. This will change
226 * from one call to the other if we're writing String
228 * @return The size of the Strings Entry that was written, if any.
230 public int writeInterval(ByteBuffer buffer
, int endPosOfStringEntry
) {
231 buffer
.putLong(start
);
233 buffer
.putInt(attribute
);
234 buffer
.put(getByteFromType(sv
.getType()));
236 switch (getByteFromType(sv
.getType())) {
240 /* We write the 'valueOffset' field as a straight value. */
242 buffer
.putInt(sv
.unboxInt());
243 } catch (StateValueTypeException e
) {
245 * This should not happen, since the value told us it was of
246 * type Null or Integer (corrupted value?)
253 byte[] byteArrayToWrite
;
255 byteArrayToWrite
= sv
.unboxStr().getBytes();
256 } catch (StateValueTypeException e1
) {
257 /* Should not happen, we're in a switch/case for string type */
258 throw new RuntimeException();
261 /* we use the valueOffset as an offset. */
262 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
264 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
267 * write the Strings entry (1st byte = size, then the bytes, then the 0)
269 buffer
.put((byte) stringsEntrySize
);
270 buffer
.put(byteArrayToWrite
);
271 buffer
.put((byte) 0);
272 assert (buffer
.position() == endPosOfStringEntry
);
277 /* we use the valueOffset as an offset. */
278 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
280 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
283 * write the Long in the Strings section
286 buffer
.putLong(sv
.unboxLong());
287 } catch (StateValueTypeException e
) {
289 * This should not happen, since the value told us it was of
290 * type Long (corrupted value?)
294 assert (buffer
.position() == endPosOfStringEntry
);
299 /* we use the valueOffset as an offset. */
300 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
302 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
304 /* Write the Double in the Strings section */
306 buffer
.putDouble(sv
.unboxDouble());
307 } catch (StateValueTypeException e
) {
309 * This should not happen, since the value told us it was of
310 * type Double (corrupted value?)
314 if (buffer
.position() != endPosOfStringEntry
) {
315 throw new IllegalStateException();
323 return stringsEntrySize
;
327 public long getStartTime() {
332 public long getEndTime() {
337 public long getViewerEndTime() {
342 public int getAttribute() {
347 public ITmfStateValue
getStateValue() {
352 public boolean intersects(long timestamp
) {
353 if (start
<= timestamp
) {
354 if (end
>= timestamp
) {
361 int getStringsEntrySize() {
362 return stringsEntrySize
;
366 * Total serialized size of this interval
368 * @return The interval size
370 public int getIntervalSize() {
371 return stringsEntrySize
+ HTNode
.DATA_ENTRY_SIZE
;
374 private int computeStringsEntrySize() {
375 switch(sv
.getType()) {
378 /* Those don't use the strings section at all */
379 return NO_ENTRY_SIZE
;
381 /* The value's bytes are written directly into the strings section */
382 return LONG_ENTRY_SIZE
;
384 /* The value is also written directly into the strings section */
385 return DOUBLE_ENTRY_SIZE
;
388 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
389 return sv
.unboxStr().getBytes().length
+ 2;
390 } catch (StateValueTypeException e
) {
391 /* We're inside a switch/case for the string type, can't happen */
392 throw new IllegalStateException(e
);
395 /* It's very important that we know how to write the state value in
397 throw new IllegalStateException();
402 * Compare the END TIMES of different intervals. This is used to sort the
403 * intervals when we close down a node.
406 public int compareTo(HTInterval other
) {
407 if (this.end
< other
.end
) {
409 } else if (this.end
> other
.end
) {
417 public boolean equals(Object other
) {
418 if (other
instanceof HTInterval
&&
419 this.compareTo((HTInterval
) other
) == 0) {
426 public int hashCode() {
427 return super.hashCode();
431 public String
toString() {
432 /* Only for debug, should not be externalized */
433 StringBuilder sb
= new StringBuilder();
436 sb
.append(", "); //$NON-NLS-1$
440 sb
.append(", attribute = "); //$NON-NLS-1$
441 sb
.append(attribute
);
443 sb
.append(", value = "); //$NON-NLS-1$
444 sb
.append(sv
.toString());
446 return sb
.toString();
450 * Here we determine how state values "types" are written in the 8-bit
451 * field that indicates the value type in the file.
453 private static byte getByteFromType(ITmfStateValue
.Type type
) {
466 /* Should not happen if the switch is fully covered */
467 throw new IllegalStateException();