lttng.ust: Properly null-check the debug info state system
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HTInterval.java
CommitLineData
a52fde77 1/*******************************************************************************
e13bd4cd 2 * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal
a52fde77 3 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
98107b3f 4 *
a52fde77
AM
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
98107b3f 9 *
b0136ad6
FW
10 * Contributors:
11 * Alexandre Montplaisir - Initial API and implementation
12 * Florian Wininger - Allow to change the size of a interval
e13bd4cd 13 * Patrick Tasse - Add message to exceptions
a52fde77
AM
14 *******************************************************************************/
15
e894a508 16package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
a52fde77
AM
17
18import java.io.IOException;
19import java.nio.ByteBuffer;
20
aa353506 21import org.eclipse.jdt.annotation.NonNull;
d69a6555
GB
22import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.CustomStateValue;
23import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferReader;
24import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
25import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.SafeByteBufferFactory;
e894a508
AM
26import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
27import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
28import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
29import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
a52fde77
AM
30
31/**
32 * The interval component, which will be contained in a node of the History
33 * Tree.
98107b3f 34 *
8d47cc34 35 * @author Alexandre Montplaisir
a52fde77 36 */
8d47cc34 37public final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
a52fde77 38
b67a2540
AM
39 private static final String errMsg = "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
40
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;
1cbf1a19 45 private static final byte TYPE_LONG = 2;
a3c22e8e 46 private static final byte TYPE_DOUBLE = 3;
7ef85bff 47 private static final byte TYPE_CUSTOM = 20;
b67a2540 48
a52fde77
AM
49 private final long start;
50 private final long end;
51 private final int attribute;
aa353506 52 private final @NonNull TmfStateValue sv;
a52fde77 53
59d30d83
MK
54 /** Number of bytes used by this interval when it is written to disk */
55 private final int fSizeOnDisk;
a52fde77
AM
56
57 /**
58 * Standard constructor
98107b3f 59 *
a52fde77 60 * @param intervalStart
8d47cc34 61 * Start time of the interval
a52fde77 62 * @param intervalEnd
8d47cc34 63 * End time of the interval
a52fde77 64 * @param attribute
8d47cc34
AM
65 * Attribute (quark) to which the state represented by this
66 * interval belongs
a52fde77 67 * @param value
8d47cc34 68 * State value represented by this interval
a52fde77 69 * @throws TimeRangeException
8d47cc34 70 * If the start time or end time are invalid
a52fde77 71 */
8d47cc34 72 public HTInterval(long intervalStart, long intervalEnd, int attribute,
aa353506 73 @NonNull TmfStateValue value) throws TimeRangeException {
a52fde77 74 if (intervalStart > intervalEnd) {
e13bd4cd 75 throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$
a52fde77
AM
76 }
77
78 this.start = intervalStart;
79 this.end = intervalEnd;
80 this.attribute = attribute;
81 this.sv = value;
59d30d83
MK
82 this.fSizeOnDisk = computeSizeOnDisk(sv);
83 }
84
85 /**
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.
88 */
89 private static int computeSizeOnDisk(ITmfStateValue sv) {
90 /*
91 * Minimum size is 2x long (start and end), 1x int (attribute) and 1x
92 * byte (value type).
93 */
94 int minSize = Long.BYTES + Long.BYTES + Integer.BYTES + Byte.BYTES;
95
96 switch (sv.getType()) {
97 case NULL:
98 return minSize;
99 case INTEGER:
100 return (minSize + Integer.BYTES);
101 case LONG:
102 return (minSize + Long.BYTES);
103 case DOUBLE:
104 return (minSize + Double.BYTES);
105 case STRING:
106 /*
107 * String's length + 2 (1 byte for size, 1 byte for \0 at the end
108 */
109 return (minSize + sv.unboxStr().getBytes().length + 2);
7ef85bff 110 case CUSTOM:
d69a6555
GB
111 /* Length of serialized value (short) + state value */
112 return (minSize + Short.BYTES + ((CustomStateValue) sv).getSerializedSize());
59d30d83
MK
113 default:
114 /*
115 * It's very important that we know how to write the state value in
116 * the file!!
117 */
118 throw new IllegalStateException();
119 }
a52fde77
AM
120 }
121
b4603a60
AM
122 /**
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.
b4603a60
AM
127 */
128 private HTInterval(long intervalStart, long intervalEnd, int attribute,
aa353506 129 @NonNull TmfStateValue value, int size) throws TimeRangeException {
b4603a60 130 if (intervalStart > intervalEnd) {
e13bd4cd 131 throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$
b4603a60
AM
132 }
133
134 this.start = intervalStart;
135 this.end = intervalEnd;
136 this.attribute = attribute;
137 this.sv = value;
59d30d83 138 this.fSizeOnDisk = size;
b4603a60
AM
139 }
140
a52fde77 141 /**
8d47cc34 142 * Reader factory method. Builds the interval using an already-allocated
a52fde77 143 * ByteBuffer, which normally comes from a NIO FileChannel.
98107b3f 144 *
59d30d83
MK
145 * The interval is just a start, end, attribute and value, this is the
146 * layout of the HTInterval on disk
147 * <ul>
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>
154 * </ul>
155 *
a52fde77
AM
156 * @param buffer
157 * The ByteBuffer from which to read the information
8d47cc34 158 * @return The interval object
a52fde77 159 * @throws IOException
8d47cc34 160 * If there was an error reading from the buffer
a52fde77 161 */
8d47cc34 162 public static final HTInterval readFrom(ByteBuffer buffer) throws IOException {
a52fde77 163 TmfStateValue value;
a52fde77 164
59d30d83 165 int posStart = buffer.position();
a52fde77 166 /* Read the Data Section entry */
d69a6555
GB
167 long intervalStart = buffer.getLong();
168 long intervalEnd = buffer.getLong();
169 int attribute = buffer.getInt();
a52fde77
AM
170
171 /* Read the 'type' of the value, then react accordingly */
d69a6555 172 byte valueType = buffer.get();
b67a2540 173 switch (valueType) {
a52fde77 174
b67a2540
AM
175 case TYPE_NULL:
176 value = TmfStateValue.nullValue();
177 break;
a52fde77 178
b67a2540 179 case TYPE_INTEGER:
59d30d83 180 value = TmfStateValue.newValueInt(buffer.getInt());
b67a2540
AM
181 break;
182
d69a6555 183 case TYPE_STRING: {
a52fde77 184 /* the first byte = the size to read */
59d30d83 185 int valueSize = buffer.get();
a52fde77 186
d69a6555 187 byte[] array = new byte[valueSize];
a52fde77
AM
188 buffer.get(array);
189 value = TmfStateValue.newValueString(new String(array));
190
191 /* Confirm the 0'ed byte at the end */
59d30d83 192 byte res = buffer.get();
a52fde77 193 if (res != 0) {
b67a2540 194 throw new IOException(errMsg);
a52fde77 195 }
1cbf1a19 196 break;
d69a6555 197 }
1cbf1a19
FR
198
199 case TYPE_LONG:
200 /* Go read the matching entry in the Strings section of the block */
1cbf1a19 201 value = TmfStateValue.newValueLong(buffer.getLong());
b67a2540 202 break;
a3c22e8e
AM
203
204 case TYPE_DOUBLE:
205 /* Go read the matching entry in the Strings section of the block */
a3c22e8e 206 value = TmfStateValue.newValueDouble(buffer.getDouble());
a3c22e8e
AM
207 break;
208
d69a6555
GB
209 case TYPE_CUSTOM: {
210 short valueSize = buffer.getShort();
211 ISafeByteBufferReader safeBuffer = SafeByteBufferFactory.wrapReader(buffer, valueSize);
212 value = CustomStateValue.readSerializedValue(safeBuffer);
213 break;
214 }
b67a2540
AM
215 default:
216 /* Unknown data, better to not make anything up... */
217 throw new IOException(errMsg);
a52fde77
AM
218 }
219
220 try {
d69a6555 221 return new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart);
a52fde77 222 } catch (TimeRangeException e) {
b67a2540 223 throw new IOException(errMsg);
a52fde77 224 }
a52fde77
AM
225 }
226
227 /**
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)
98107b3f 231 *
59d30d83
MK
232 * The interval is just a start, end, attribute and value, this is the
233 * layout of the HTInterval on disk
234 * <ul>
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>
241 * </ul>
242 *
a52fde77
AM
243 * @param buffer
244 * The already-allocated ByteBuffer corresponding to a SHT Node
a52fde77 245 */
59d30d83
MK
246 public void writeInterval(ByteBuffer buffer) {
247 final byte byteFromType = getByteFromType(sv.getType());
248
a52fde77
AM
249 buffer.putLong(start);
250 buffer.putLong(end);
251 buffer.putInt(attribute);
59d30d83 252 buffer.put(byteFromType);
a52fde77 253
59d30d83 254 switch (byteFromType) {
1cbf1a19 255 case TYPE_NULL:
59d30d83 256 break;
1cbf1a19 257 case TYPE_INTEGER:
59d30d83 258 buffer.putInt(sv.unboxInt());
9177eb74 259 break;
a52fde77 260
d69a6555 261 case TYPE_STRING: {
59d30d83
MK
262 String string = sv.unboxStr();
263 byte[] strArray = string.getBytes();
1cbf1a19
FR
264
265 /*
59d30d83
MK
266 * Write the Strings entry (1st byte = size, then the bytes, then
267 * the 0). We have checked the string length at the constructor.
1cbf1a19 268 */
59d30d83
MK
269 buffer.put((byte) strArray.length);
270 buffer.put(strArray);
1cbf1a19 271 buffer.put((byte) 0);
9177eb74 272 break;
d69a6555 273 }
1cbf1a19
FR
274
275 case TYPE_LONG:
59d30d83 276 buffer.putLong(sv.unboxLong());
9177eb74 277 break;
1cbf1a19 278
a3c22e8e 279 case TYPE_DOUBLE:
59d30d83 280 buffer.putDouble(sv.unboxDouble());
a3c22e8e
AM
281 break;
282
d69a6555
GB
283 case TYPE_CUSTOM: {
284 int size = ((CustomStateValue) sv).getSerializedSize();
285 buffer.putShort((short) size);
286 ISafeByteBufferWriter safeBuffer = SafeByteBufferFactory.wrapWriter(buffer, size);
287 ((CustomStateValue) sv).serialize(safeBuffer);
288 break;
289 }
290
1cbf1a19 291 default:
9177eb74 292 break;
a52fde77 293 }
a52fde77
AM
294 }
295
296 @Override
297 public long getStartTime() {
298 return start;
299 }
300
301 @Override
302 public long getEndTime() {
303 return end;
304 }
305
306 @Override
307 public int getAttribute() {
308 return attribute;
309 }
310
311 @Override
312 public ITmfStateValue getStateValue() {
313 return sv;
314 }
315
316 @Override
317 public boolean intersects(long timestamp) {
318 if (start <= timestamp) {
319 if (end >= timestamp) {
320 return true;
321 }
322 }
323 return false;
324 }
325
a52fde77
AM
326 /**
327 * Total serialized size of this interval
98107b3f 328 *
8d47cc34 329 * @return The interval size
a52fde77 330 */
59d30d83
MK
331 public int getSizeOnDisk() {
332 return fSizeOnDisk;
a52fde77
AM
333 }
334
335 /**
336 * Compare the END TIMES of different intervals. This is used to sort the
337 * intervals when we close down a node.
338 */
339 @Override
340 public int compareTo(HTInterval other) {
341 if (this.end < other.end) {
342 return -1;
343 } else if (this.end > other.end) {
344 return 1;
345 } else {
346 return 0;
347 }
348 }
1b558482 349
ab604305
AM
350 @Override
351 public boolean equals(Object other) {
cb42195c
AM
352 if (other instanceof HTInterval &&
353 this.compareTo((HTInterval) other) == 0) {
354 return true;
ab604305
AM
355 }
356 return false;
357 }
358
359 @Override
360 public int hashCode() {
361 return super.hashCode();
362 }
1b558482
AM
363
364 @Override
365 public String toString() {
366 /* Only for debug, should not be externalized */
98107b3f
AM
367 StringBuilder sb = new StringBuilder();
368 sb.append('[');
369 sb.append(start);
370 sb.append(", "); //$NON-NLS-1$
371 sb.append(end);
372 sb.append(']');
373
374 sb.append(", attribute = "); //$NON-NLS-1$
375 sb.append(attribute);
376
377 sb.append(", value = "); //$NON-NLS-1$
378 sb.append(sv.toString());
379
380 return sb.toString();
1b558482 381 }
b67a2540
AM
382
383 /**
59d30d83
MK
384 * Here we determine how state values "types" are written in the 8-bit field
385 * that indicates the value type in the file.
b67a2540
AM
386 */
387 private static byte getByteFromType(ITmfStateValue.Type type) {
59d30d83 388 switch (type) {
b67a2540
AM
389 case NULL:
390 return TYPE_NULL;
391 case INTEGER:
392 return TYPE_INTEGER;
393 case STRING:
394 return TYPE_STRING;
1cbf1a19
FR
395 case LONG:
396 return TYPE_LONG;
a3c22e8e
AM
397 case DOUBLE:
398 return TYPE_DOUBLE;
7ef85bff
GB
399 case CUSTOM:
400 return TYPE_CUSTOM;
b67a2540
AM
401 default:
402 /* Should not happen if the switch is fully covered */
cb42195c 403 throw new IllegalStateException();
b67a2540
AM
404 }
405 }
a52fde77 406}
This page took 0.11247 seconds and 5 git commands to generate.