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;
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 // sizes of string values depend on the string itself
46 private final long start
;
47 private final long end
;
48 private final int attribute
;
49 private final TmfStateValue sv
;
52 * Size of the strings section entry used by this interval (= 0 if not used)
54 private final int stringsEntrySize
;
57 * Standard constructor
59 * @param intervalStart
63 * @throws TimeRangeException
65 HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
66 TmfStateValue value
) throws TimeRangeException
{
67 if (intervalStart
> intervalEnd
) {
68 throw new TimeRangeException();
71 this.start
= intervalStart
;
72 this.end
= intervalEnd
;
73 this.attribute
= attribute
;
75 this.stringsEntrySize
= computeStringsEntrySize();
79 * "Faster" constructor for inner use only. When we build an interval when
80 * reading it from disk (with {@link #readFrom}), we already know the size
81 * of the strings entry, so there is no need to call
82 * {@link #computeStringsEntrySize()} and do an extra copy.
84 * @param intervalStart
89 * @throws TimeRangeException
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 constructor. 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 * @throws IOException
112 final static HTInterval
readFrom(ByteBuffer buffer
) throws IOException
{
114 long intervalStart
, intervalEnd
;
117 int valueOrOffset
, valueSize
, res
;
121 /* Read the Data Section entry */
122 intervalStart
= buffer
.getLong();
123 intervalEnd
= buffer
.getLong();
124 attribute
= buffer
.getInt();
126 /* Read the 'type' of the value, then react accordingly */
127 valueType
= buffer
.get();
128 valueOrOffset
= buffer
.getInt();
132 value
= TmfStateValue
.nullValue();
133 valueSize
= NO_ENTRY_SIZE
;
137 /* "ValueOrOffset" is the straight value */
138 value
= TmfStateValue
.newValueInt(valueOrOffset
);
139 valueSize
= NO_ENTRY_SIZE
;
143 /* Go read the matching entry in the Strings section of the block */
145 buffer
.position(valueOrOffset
);
147 /* the first byte = the size to read */
148 valueSize
= buffer
.get();
151 * Careful though, 'valueSize' is the total size of the entry,
152 * including the 'size' byte at the start and end (0'ed) byte at the
153 * end. Here we want 'array' to only contain the real payload of the
156 array
= new byte[valueSize
- 2];
158 value
= TmfStateValue
.newValueString(new String(array
));
160 /* Confirm the 0'ed byte at the end */
163 throw new IOException(errMsg
);
167 * Restore the file pointer's position (so we can read the next
174 /* Go read the matching entry in the Strings section of the block */
176 buffer
.position(valueOrOffset
);
177 value
= TmfStateValue
.newValueLong(buffer
.getLong());
178 valueSize
= LONG_ENTRY_SIZE
;
181 * Restore the file pointer's position (so we can read the next
187 /* Unknown data, better to not make anything up... */
188 throw new IOException(errMsg
);
192 interval
= new HTInterval(intervalStart
, intervalEnd
, attribute
, value
, valueSize
);
193 } catch (TimeRangeException e
) {
194 throw new IOException(errMsg
);
200 * Antagonist of the previous constructor, write the Data entry
201 * corresponding to this interval in a ByteBuffer (mapped to a block in the
202 * history-file, hopefully)
205 * The already-allocated ByteBuffer corresponding to a SHT Node
206 * @param endPosOfStringEntry
207 * The initial (before calling this function for this interval)
208 * position of the Strings Entry for this node. This will change
209 * from one call to the other if we're writing String
211 * @return The size of the Strings Entry that was written, if any.
213 int writeInterval(ByteBuffer buffer
, int endPosOfStringEntry
) {
214 buffer
.putLong(start
);
216 buffer
.putInt(attribute
);
217 buffer
.put(getByteFromType(sv
.getType()));
219 switch (getByteFromType(sv
.getType())) {
223 /* We write the 'valueOffset' field as a straight value. In the case
224 * of a null value, it will be unboxed as -1 */
226 buffer
.putInt(sv
.unboxInt());
227 } catch (StateValueTypeException e
) {
229 * This should not happen, since the value told us it was of
230 * type Null or Integer (corrupted value?)
237 byte[] byteArrayToWrite
;
239 byteArrayToWrite
= sv
.unboxStr().getBytes();
240 } catch (StateValueTypeException e1
) {
241 /* Should not happen, we're in a switch/case for string type */
242 throw new RuntimeException();
245 /* we use the valueOffset as an offset. */
246 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
248 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
251 * write the Strings entry (1st byte = size, then the bytes, then the 0)
253 buffer
.put((byte) stringsEntrySize
);
254 buffer
.put(byteArrayToWrite
);
255 buffer
.put((byte) 0);
256 assert (buffer
.position() == endPosOfStringEntry
);
261 /* we use the valueOffset as an offset. */
262 buffer
.putInt(endPosOfStringEntry
- stringsEntrySize
);
264 buffer
.position(endPosOfStringEntry
- stringsEntrySize
);
267 * write the Long in the Strings section
270 buffer
.putLong(sv
.unboxLong());
271 } catch (StateValueTypeException e
) {
273 * This should not happen, since the value told us it was of
274 * type Long (corrupted value?)
278 assert (buffer
.position() == endPosOfStringEntry
);
285 return stringsEntrySize
;
289 public long getStartTime() {
294 public long getEndTime() {
299 public long getViewerEndTime() {
304 public int getAttribute() {
309 public ITmfStateValue
getStateValue() {
314 public boolean intersects(long timestamp
) {
315 if (start
<= timestamp
) {
316 if (end
>= timestamp
) {
323 int getStringsEntrySize() {
324 return stringsEntrySize
;
328 * Total serialized size of this interval
332 int getIntervalSize() {
333 return stringsEntrySize
+ HTNode
.DATA_ENTRY_SIZE
;
336 private int computeStringsEntrySize() {
337 switch(sv
.getType()) {
340 /* Those don't use the strings section at all */
341 return NO_ENTRY_SIZE
;
343 /* The value's bytes are written directly into the strings section */
344 return LONG_ENTRY_SIZE
;
347 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
348 return sv
.unboxStr().getBytes().length
+ 2;
349 } catch (StateValueTypeException e
) {
350 /* We're inside a switch/case for the string type, can't happen */
351 throw new IllegalStateException(e
);
354 /* It's very important that we know how to write the state value in
356 throw new IllegalStateException();
361 * Compare the END TIMES of different intervals. This is used to sort the
362 * intervals when we close down a node.
365 public int compareTo(HTInterval other
) {
366 if (this.end
< other
.end
) {
368 } else if (this.end
> other
.end
) {
376 public boolean equals(Object other
) {
377 if (other
instanceof HTInterval
&&
378 this.compareTo((HTInterval
) other
) == 0) {
385 public int hashCode() {
386 return super.hashCode();
390 public String
toString() {
391 /* Only for debug, should not be externalized */
392 StringBuilder sb
= new StringBuilder();
395 sb
.append(", "); //$NON-NLS-1$
399 sb
.append(", attribute = "); //$NON-NLS-1$
400 sb
.append(attribute
);
402 sb
.append(", value = "); //$NON-NLS-1$
403 sb
.append(sv
.toString());
405 return sb
.toString();
409 * Here we determine how state values "types" are written in the 8-bit
410 * field that indicates the value type in the file.
412 private static byte getByteFromType(ITmfStateValue
.Type type
) {
423 /* Should not happen if the switch is fully covered */
424 throw new IllegalStateException();