1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 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
31 final class HTInterval
implements ITmfStateInterval
, Comparable
<HTInterval
> {
33 private static final String errMsg
= "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
35 /* 'Byte' equivalent for state values types */
36 private static final byte TYPE_NULL
= -1;
37 private static final byte TYPE_INTEGER
= 0;
38 private static final byte TYPE_STRING
= 1;
39 private static final byte TYPE_LONG
= 2;
40 private static final byte TYPE_DOUBLE
= 3;
42 /* String entry sizes of different state values */
43 private static final int NO_ENTRY_SIZE
= 0;
44 private static final int LONG_ENTRY_SIZE
= 8;
45 private static final int DOUBLE_ENTRY_SIZE
= 8;
46 // sizes of string values depend on the string itself
48 private final long start
;
49 private final long end
;
50 private final int attribute
;
51 private final TmfStateValue sv
;
54 * Size of the strings section entry used by this interval (= 0 if not used)
56 private final int stringsEntrySize
;
59 * Standard constructor
61 * @param intervalStart
65 * @throws TimeRangeException
67 HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
68 TmfStateValue value
) throws TimeRangeException
{
69 if (intervalStart
> intervalEnd
) {
70 throw new TimeRangeException();
73 this.start
= intervalStart
;
74 this.end
= intervalEnd
;
75 this.attribute
= attribute
;
77 this.stringsEntrySize
= computeStringsEntrySize();
81 * "Faster" constructor for inner use only. When we build an interval when
82 * reading it from disk (with {@link #readFrom}), we already know the size
83 * of the strings entry, so there is no need to call
84 * {@link #computeStringsEntrySize()} and do an extra copy.
86 * @param intervalStart
91 * @throws TimeRangeException
93 private HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
94 TmfStateValue value
, int size
) throws TimeRangeException
{
95 if (intervalStart
> intervalEnd
) {
96 throw new TimeRangeException();
99 this.start
= intervalStart
;
100 this.end
= intervalEnd
;
101 this.attribute
= attribute
;
103 this.stringsEntrySize
= size
;
107 * Reader constructor. Builds the interval using an already-allocated
108 * ByteBuffer, which normally comes from a NIO FileChannel.
111 * The ByteBuffer from which to read the information
112 * @throws IOException
114 final static 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 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. In the case
241 * of a null value, it will be unboxed as -1 */
243 buffer
.putInt(sv
.unboxInt());
244 } catch (StateValueTypeException e
) {
246 * This should not happen, since the value told us it was of
247 * type Null or Integer (corrupted value?)
254 byte[] byteArrayToWrite
;
256 byteArrayToWrite
= sv
.unboxStr().getBytes();
257 } catch (StateValueTypeException e1
) {
258 /* Should not happen, we're in a switch/case for string type */
259 throw new RuntimeException();
262 /* we use the valueOffset as an offset. */
263 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
265 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
268 * write the Strings entry (1st byte = size, then the bytes, then the 0)
270 buffer
.put((byte) stringsEntrySize
);
271 buffer
.put(byteArrayToWrite
);
272 buffer
.put((byte) 0);
273 assert (buffer
.position() == endPosOfStringEntry
);
278 /* we use the valueOffset as an offset. */
279 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
281 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
284 * write the Long in the Strings section
287 buffer
.putLong(sv
.unboxLong());
288 } catch (StateValueTypeException e
) {
290 * This should not happen, since the value told us it was of
291 * type Long (corrupted value?)
295 assert (buffer
.position() == endPosOfStringEntry
);
300 /* we use the valueOffset as an offset. */
301 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
303 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
305 /* Write the Double in the Strings section */
307 buffer
.putDouble(sv
.unboxDouble());
308 } catch (StateValueTypeException e
) {
310 * This should not happen, since the value told us it was of
311 * type Double (corrupted value?)
315 if (buffer
.position() != endPosOfStringEntry
) {
316 throw new IllegalStateException();
324 return stringsEntrySize
;
328 public long getStartTime() {
333 public long getEndTime() {
338 public long getViewerEndTime() {
343 public int getAttribute() {
348 public ITmfStateValue
getStateValue() {
353 public boolean intersects(long timestamp
) {
354 if (start
<= timestamp
) {
355 if (end
>= timestamp
) {
362 int getStringsEntrySize() {
363 return stringsEntrySize
;
367 * Total serialized size of this interval
371 int getIntervalSize() {
372 return stringsEntrySize
+ HTNode
.DATA_ENTRY_SIZE
;
375 private int computeStringsEntrySize() {
376 switch(sv
.getType()) {
379 /* Those don't use the strings section at all */
380 return NO_ENTRY_SIZE
;
382 /* The value's bytes are written directly into the strings section */
383 return LONG_ENTRY_SIZE
;
385 /* The value is also written directly into the strings section */
386 return DOUBLE_ENTRY_SIZE
;
389 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
390 return sv
.unboxStr().getBytes().length
+ 2;
391 } catch (StateValueTypeException e
) {
392 /* We're inside a switch/case for the string type, can't happen */
393 throw new IllegalStateException(e
);
396 /* It's very important that we know how to write the state value in
398 throw new IllegalStateException();
403 * Compare the END TIMES of different intervals. This is used to sort the
404 * intervals when we close down a node.
407 public int compareTo(HTInterval other
) {
408 if (this.end
< other
.end
) {
410 } else if (this.end
> other
.end
) {
418 public boolean equals(Object other
) {
419 if (other
instanceof HTInterval
&&
420 this.compareTo((HTInterval
) other
) == 0) {
427 public int hashCode() {
428 return super.hashCode();
432 public String
toString() {
433 /* Only for debug, should not be externalized */
434 StringBuilder sb
= new StringBuilder();
437 sb
.append(", "); //$NON-NLS-1$
441 sb
.append(", attribute = "); //$NON-NLS-1$
442 sb
.append(attribute
);
444 sb
.append(", value = "); //$NON-NLS-1$
445 sb
.append(sv
.toString());
447 return sb
.toString();
451 * Here we determine how state values "types" are written in the 8-bit
452 * field that indicates the value type in the file.
454 private static byte getByteFromType(ITmfStateValue
.Type type
) {
467 /* Should not happen if the switch is fully covered */
468 throw new IllegalStateException();