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 static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
20 import java
.nio
.charset
.Charset
;
21 import java
.util
.Objects
;
23 import org
.eclipse
.jdt
.annotation
.NonNull
;
24 import org
.eclipse
.tracecompass
.internal
.provisional
.datastore
.core
.interval
.HTInterval
;
25 import org
.eclipse
.tracecompass
.internal
.provisional
.datastore
.core
.interval
.IHTIntervalReader
;
26 import org
.eclipse
.tracecompass
.internal
.provisional
.datastore
.core
.serialization
.ISafeByteBufferWriter
;
27 import org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
.CustomStateValue
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
34 * The interval component, which will be contained in a node of the History
37 * @author Alexandre Montplaisir
39 public final class StateSystemInterval
extends HTInterval
implements ITmfStateInterval
{
41 private static final Charset CHARSET
= Charset
.forName("UTF-8"); //$NON-NLS-1$
43 private static final String errMsg
= "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
45 /* 'Byte' equivalent for state values types */
46 private static final byte TYPE_NULL
= -1;
47 private static final byte TYPE_INTEGER
= 0;
48 private static final byte TYPE_STRING
= 1;
49 private static final byte TYPE_LONG
= 2;
50 private static final byte TYPE_DOUBLE
= 3;
51 private static final byte TYPE_CUSTOM
= 20;
53 private final int fAttribute
;
54 private final @NonNull TmfStateValue fSv
;
56 /** Number of bytes used by this interval when it is written to disk */
57 private final int fSizeOnDisk
;
60 * Standard constructor
62 * @param intervalStart
63 * Start time of the interval
65 * End time of the interval
67 * Attribute (quark) to which the state represented by this
70 * State value represented by this interval
71 * @throws TimeRangeException
72 * If the start time or end time are invalid
74 public StateSystemInterval(long intervalStart
, long intervalEnd
, int attribute
,
75 @NonNull TmfStateValue value
) throws TimeRangeException
{
76 super(intervalStart
, intervalEnd
);
78 fAttribute
= attribute
;
80 fSizeOnDisk
= computeSizeOnDisk(fSv
);
84 * Compute how much space (in bytes) an interval will take in its serialized
85 * form on disk. This is dependent on its state value.
87 private static int computeSizeOnDisk(ITmfStateValue sv
) {
89 * Minimum size is 2x long (start and end), 1x int (attribute) and 1x
92 int minSize
= Long
.BYTES
+ Long
.BYTES
+ Integer
.BYTES
+ Byte
.BYTES
;
94 switch (sv
.getType()) {
98 return (minSize
+ Integer
.BYTES
);
100 return (minSize
+ Long
.BYTES
);
102 return (minSize
+ Double
.BYTES
);
104 String str
= sv
.unboxStr();
105 int strLength
= str
.getBytes(CHARSET
).length
;
107 if (strLength
> Short
.MAX_VALUE
) {
108 throw new IllegalArgumentException("String is too long to be stored in state system: " + str
); //$NON-NLS-1$
112 * String's length + 3 (2 bytes for size, 1 byte for \0 at the end)
114 return (minSize
+ strLength
+ 3);
116 /* Length of serialized value (short) + state value */
117 return (minSize
+ Short
.BYTES
+ ((CustomStateValue
) sv
).getSerializedSize());
120 * It's very important that we know how to write the state value in
123 throw new IllegalStateException();
128 * Reader object for this interval type.
130 public static final @NonNull IHTIntervalReader
<@NonNull StateSystemInterval
> DESERIALISER
= buffer
-> {
133 /* Read the Data Section entry */
134 long intervalStart
= buffer
.getLong();
135 long intervalEnd
= buffer
.getLong();
136 int attribute
= buffer
.getInt();
138 /* Read the 'type' of the value, then react accordingly */
139 byte valueType
= buffer
.get();
143 value
= TmfStateValue
.nullValue();
147 value
= TmfStateValue
.newValueInt(buffer
.getInt());
151 /* the first short = the size to read */
152 int valueSize
= buffer
.getShort();
154 byte[] array
= new byte[valueSize
];
156 value
= TmfStateValue
.newValueString(new String(array
, CHARSET
));
158 /* Confirm the 0'ed byte at the end */
159 byte res
= buffer
.get();
161 throw new RuntimeException(errMsg
);
167 /* Go read the matching entry in the Strings section of the block */
168 value
= TmfStateValue
.newValueLong(buffer
.getLong());
172 /* Go read the matching entry in the Strings section of the block */
173 value
= TmfStateValue
.newValueDouble(buffer
.getDouble());
178 // short valueSize = buffer.getShort();
179 // ISafeByteBufferReader safeBuffer =
180 // SafeByteBufferFactory.wrapReader(buffer, valueSize);
181 value
= CustomStateValue
.readSerializedValue(buffer
);
185 /* Unknown data, better to not make anything up... */
186 throw new RuntimeException(errMsg
);
190 return new StateSystemInterval(intervalStart
, intervalEnd
, attribute
, value
);
191 } catch (TimeRangeException e
) {
192 throw new RuntimeException(errMsg
);
197 public void writeSegment(ISafeByteBufferWriter buffer
) {
198 final byte byteFromType
= getByteFromType(fSv
.getType());
200 buffer
.putLong(getStart());
201 buffer
.putLong(getEnd());
202 buffer
.putInt(fAttribute
);
203 buffer
.put(byteFromType
);
205 switch (byteFromType
) {
209 buffer
.putInt(fSv
.unboxInt());
213 String string
= fSv
.unboxStr();
214 byte[] strArray
= string
.getBytes(CHARSET
);
217 * Write the Strings entry (1st byte = size, then the bytes, then
218 * the 0). We have checked the string length at the constructor.
220 buffer
.putShort((short) strArray
.length
);
221 buffer
.put(strArray
);
222 buffer
.put((byte) 0);
227 buffer
.putLong(fSv
.unboxLong());
231 buffer
.putDouble(fSv
.unboxDouble());
235 int size
= ((CustomStateValue
) fSv
).getSerializedSize();
236 buffer
.putShort((short) size
);
237 // TODO: We send the full safe buffer to the custom value, its size
238 // should set to the necessary size only
239 ((CustomStateValue
) fSv
).serialize(buffer
);
248 // FIXME Remove these two duplicate methods
251 public long getStartTime() {
256 public long getEndTime() {
261 public int getAttribute() {
266 public ITmfStateValue
getStateValue() {
271 public boolean intersects(long timestamp
) {
273 * Need to explicitly re-define due to conflicting methods in
276 return super.intersects(timestamp
);
280 public int getSizeOnDisk() {
285 public int hashCode() {
286 return Objects
.hash(super.hashCode(), fAttribute
, fSv
);
290 public boolean equals(Object obj
) {
291 if (!super.equals(obj
)) {
295 StateSystemInterval other
= (StateSystemInterval
) checkNotNull(obj
);
296 return (fAttribute
== other
.fAttribute
297 && fSv
.equals(other
.fSv
));
301 public String
toString() {
302 /* Only for debug, should not be externalized */
303 StringBuilder sb
= new StringBuilder();
305 sb
.append(getStart());
306 sb
.append(", "); //$NON-NLS-1$
310 sb
.append(", attribute = "); //$NON-NLS-1$
311 sb
.append(fAttribute
);
313 sb
.append(", value = "); //$NON-NLS-1$
314 sb
.append(fSv
.toString());
316 return sb
.toString();
320 * Here we determine how state values "types" are written in the 8-bit field
321 * that indicates the value type in the file.
323 private static byte getByteFromType(ITmfStateValue
.Type type
) {
338 /* Should not happen if the switch is fully covered */
339 throw new IllegalStateException();