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
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
.CustomStateValue
;
23 import org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
.ISafeByteBufferReader
;
24 import org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
.ISafeByteBufferWriter
;
25 import org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
.SafeByteBufferFactory
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
32 * The interval component, which will be contained in a node of the History
35 * @author Alexandre Montplaisir
37 public final class HTInterval
implements ITmfStateInterval
, Comparable
<HTInterval
> {
39 private static final String errMsg
= "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
41 /* 'Byte' equivalent for state values types */
42 private static final byte TYPE_NULL
= -1;
43 private static final byte TYPE_INTEGER
= 0;
44 private static final byte TYPE_STRING
= 1;
45 private static final byte TYPE_LONG
= 2;
46 private static final byte TYPE_DOUBLE
= 3;
47 private static final byte TYPE_CUSTOM
= 20;
49 private final long start
;
50 private final long end
;
51 private final int attribute
;
52 private final @NonNull TmfStateValue sv
;
54 /** Number of bytes used by this interval when it is written to disk */
55 private final int fSizeOnDisk
;
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 @NonNull TmfStateValue value
) throws TimeRangeException
{
74 if (intervalStart
> intervalEnd
) {
75 throw new TimeRangeException("Start:" + intervalStart
+ ", End:" + intervalEnd
); //$NON-NLS-1$ //$NON-NLS-2$
78 this.start
= intervalStart
;
79 this.end
= intervalEnd
;
80 this.attribute
= attribute
;
82 this.fSizeOnDisk
= computeSizeOnDisk(sv
);
86 * Compute how much space (in bytes) an interval will take in its serialized
87 * form on disk. This is dependent on its state value.
89 private static int computeSizeOnDisk(ITmfStateValue sv
) {
91 * Minimum size is 2x long (start and end), 1x int (attribute) and 1x
94 int minSize
= Long
.BYTES
+ Long
.BYTES
+ Integer
.BYTES
+ Byte
.BYTES
;
96 switch (sv
.getType()) {
100 return (minSize
+ Integer
.BYTES
);
102 return (minSize
+ Long
.BYTES
);
104 return (minSize
+ Double
.BYTES
);
107 * String's length + 2 (1 byte for size, 1 byte for \0 at the end
109 return (minSize
+ sv
.unboxStr().getBytes().length
+ 2);
111 /* Length of serialized value (short) + state value */
112 return (minSize
+ Short
.BYTES
+ ((CustomStateValue
) sv
).getSerializedSize());
115 * It's very important that we know how to write the state value in
118 throw new IllegalStateException();
123 * "Faster" constructor for inner use only. When we build an interval when
124 * reading it from disk (with {@link #readFrom}), we already know the size
125 * of the strings entry, so there is no need to call
126 * {@link #computeStringsEntrySize()} and do an extra copy.
128 private HTInterval(long intervalStart
, long intervalEnd
, int attribute
,
129 @NonNull TmfStateValue value
, int size
) throws TimeRangeException
{
130 if (intervalStart
> intervalEnd
) {
131 throw new TimeRangeException("Start:" + intervalStart
+ ", End:" + intervalEnd
); //$NON-NLS-1$ //$NON-NLS-2$
134 this.start
= intervalStart
;
135 this.end
= intervalEnd
;
136 this.attribute
= attribute
;
138 this.fSizeOnDisk
= size
;
142 * Reader factory method. Builds the interval using an already-allocated
143 * ByteBuffer, which normally comes from a NIO FileChannel.
145 * The interval is just a start, end, attribute and value, this is the
146 * layout of the HTInterval on disk
148 * <li>start (8 bytes)</li>
149 * <li>end (8 bytes)</li>
150 * <li>attribute (4 bytes)</li>
151 * <li>sv type (1 byte)</li>
152 * <li>sv ( 0 bytes for null, 4 for int , 8 for long and double, and the
153 * length of the string +2 for strings (it's variable))</li>
157 * The ByteBuffer from which to read the information
158 * @return The interval object
159 * @throws IOException
160 * If there was an error reading from the buffer
162 public static final HTInterval
readFrom(ByteBuffer buffer
) throws IOException
{
165 int posStart
= buffer
.position();
166 /* Read the Data Section entry */
167 long intervalStart
= buffer
.getLong();
168 long intervalEnd
= buffer
.getLong();
169 int attribute
= buffer
.getInt();
171 /* Read the 'type' of the value, then react accordingly */
172 byte valueType
= buffer
.get();
176 value
= TmfStateValue
.nullValue();
180 value
= TmfStateValue
.newValueInt(buffer
.getInt());
184 /* the first byte = the size to read */
185 int valueSize
= buffer
.get();
187 byte[] array
= new byte[valueSize
];
189 value
= TmfStateValue
.newValueString(new String(array
));
191 /* Confirm the 0'ed byte at the end */
192 byte res
= buffer
.get();
194 throw new IOException(errMsg
);
200 /* Go read the matching entry in the Strings section of the block */
201 value
= TmfStateValue
.newValueLong(buffer
.getLong());
205 /* Go read the matching entry in the Strings section of the block */
206 value
= TmfStateValue
.newValueDouble(buffer
.getDouble());
210 short valueSize
= buffer
.getShort();
211 ISafeByteBufferReader safeBuffer
= SafeByteBufferFactory
.wrapReader(buffer
, valueSize
);
212 value
= CustomStateValue
.readSerializedValue(safeBuffer
);
216 /* Unknown data, better to not make anything up... */
217 throw new IOException(errMsg
);
221 return new HTInterval(intervalStart
, intervalEnd
, attribute
, value
, buffer
.position() - posStart
);
222 } catch (TimeRangeException e
) {
223 throw new IOException(errMsg
);
228 * Antagonist of the previous constructor, write the Data entry
229 * corresponding to this interval in a ByteBuffer (mapped to a block in the
230 * history-file, hopefully)
232 * The interval is just a start, end, attribute and value, this is the
233 * layout of the HTInterval on disk
235 * <li>start (8 bytes)</li>
236 * <li>end (8 bytes)</li>
237 * <li>attribute (4 bytes)</li>
238 * <li>sv type (1 byte)</li>
239 * <li>sv ( 0 bytes for null, 4 for int , 8 for long and double, and the
240 * length of the string +2 for strings (it's variable))</li>
244 * The already-allocated ByteBuffer corresponding to a SHT Node
246 public void writeInterval(ByteBuffer buffer
) {
247 final byte byteFromType
= getByteFromType(sv
.getType());
249 buffer
.putLong(start
);
251 buffer
.putInt(attribute
);
252 buffer
.put(byteFromType
);
254 switch (byteFromType
) {
258 buffer
.putInt(sv
.unboxInt());
262 String string
= sv
.unboxStr();
263 byte[] strArray
= string
.getBytes();
266 * Write the Strings entry (1st byte = size, then the bytes, then
267 * the 0). We have checked the string length at the constructor.
269 buffer
.put((byte) strArray
.length
);
270 buffer
.put(strArray
);
271 buffer
.put((byte) 0);
276 buffer
.putLong(sv
.unboxLong());
280 buffer
.putDouble(sv
.unboxDouble());
284 int size
= ((CustomStateValue
) sv
).getSerializedSize();
285 buffer
.putShort((short) size
);
286 ISafeByteBufferWriter safeBuffer
= SafeByteBufferFactory
.wrapWriter(buffer
, size
);
287 ((CustomStateValue
) sv
).serialize(safeBuffer
);
297 public long getStartTime() {
302 public long getEndTime() {
307 public int getAttribute() {
312 public ITmfStateValue
getStateValue() {
317 public boolean intersects(long timestamp
) {
318 if (start
<= timestamp
) {
319 if (end
>= timestamp
) {
327 * Total serialized size of this interval
329 * @return The interval size
331 public int getSizeOnDisk() {
336 * Compare the END TIMES of different intervals. This is used to sort the
337 * intervals when we close down a node.
340 public int compareTo(HTInterval other
) {
341 if (this.end
< other
.end
) {
343 } else if (this.end
> other
.end
) {
351 public boolean equals(Object other
) {
352 if (other
instanceof HTInterval
&&
353 this.compareTo((HTInterval
) other
) == 0) {
360 public int hashCode() {
361 return super.hashCode();
365 public String
toString() {
366 /* Only for debug, should not be externalized */
367 StringBuilder sb
= new StringBuilder();
370 sb
.append(", "); //$NON-NLS-1$
374 sb
.append(", attribute = "); //$NON-NLS-1$
375 sb
.append(attribute
);
377 sb
.append(", value = "); //$NON-NLS-1$
378 sb
.append(sv
.toString());
380 return sb
.toString();
384 * Here we determine how state values "types" are written in the 8-bit field
385 * that indicates the value type in the file.
387 private static byte getByteFromType(ITmfStateValue
.Type type
) {
402 /* Should not happen if the switch is fully covered */
403 throw new IllegalStateException();