ss: Add support for custom state values
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / statesystem / core / backend / historytree / HTInterval.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal
3 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
4 *
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
9 *
10 * Contributors:
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 *******************************************************************************/
15
16 package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;
17
18 import java.io.IOException;
19 import java.nio.ByteBuffer;
20
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;
30
31 /**
32 * The interval component, which will be contained in a node of the History
33 * Tree.
34 *
35 * @author Alexandre Montplaisir
36 */
37 public final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
38
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;
45 private static final byte TYPE_LONG = 2;
46 private static final byte TYPE_DOUBLE = 3;
47 private static final byte TYPE_CUSTOM = 20;
48
49 private final long start;
50 private final long end;
51 private final int attribute;
52 private final @NonNull TmfStateValue sv;
53
54 /** Number of bytes used by this interval when it is written to disk */
55 private final int fSizeOnDisk;
56
57 /**
58 * Standard constructor
59 *
60 * @param intervalStart
61 * Start time of the interval
62 * @param intervalEnd
63 * End time of the interval
64 * @param attribute
65 * Attribute (quark) to which the state represented by this
66 * interval belongs
67 * @param value
68 * State value represented by this interval
69 * @throws TimeRangeException
70 * If the start time or end time are invalid
71 */
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$
76 }
77
78 this.start = intervalStart;
79 this.end = intervalEnd;
80 this.attribute = attribute;
81 this.sv = value;
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);
110 case CUSTOM:
111 /* Length of serialized value (short) + state value */
112 return (minSize + Short.BYTES + ((CustomStateValue) sv).getSerializedSize());
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 }
120 }
121
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.
127 */
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$
132 }
133
134 this.start = intervalStart;
135 this.end = intervalEnd;
136 this.attribute = attribute;
137 this.sv = value;
138 this.fSizeOnDisk = size;
139 }
140
141 /**
142 * Reader factory method. Builds the interval using an already-allocated
143 * ByteBuffer, which normally comes from a NIO FileChannel.
144 *
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 *
156 * @param buffer
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
161 */
162 public static final HTInterval readFrom(ByteBuffer buffer) throws IOException {
163 TmfStateValue value;
164
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();
170
171 /* Read the 'type' of the value, then react accordingly */
172 byte valueType = buffer.get();
173 switch (valueType) {
174
175 case TYPE_NULL:
176 value = TmfStateValue.nullValue();
177 break;
178
179 case TYPE_INTEGER:
180 value = TmfStateValue.newValueInt(buffer.getInt());
181 break;
182
183 case TYPE_STRING: {
184 /* the first byte = the size to read */
185 int valueSize = buffer.get();
186
187 byte[] array = new byte[valueSize];
188 buffer.get(array);
189 value = TmfStateValue.newValueString(new String(array));
190
191 /* Confirm the 0'ed byte at the end */
192 byte res = buffer.get();
193 if (res != 0) {
194 throw new IOException(errMsg);
195 }
196 break;
197 }
198
199 case TYPE_LONG:
200 /* Go read the matching entry in the Strings section of the block */
201 value = TmfStateValue.newValueLong(buffer.getLong());
202 break;
203
204 case TYPE_DOUBLE:
205 /* Go read the matching entry in the Strings section of the block */
206 value = TmfStateValue.newValueDouble(buffer.getDouble());
207 break;
208
209 case TYPE_CUSTOM: {
210 short valueSize = buffer.getShort();
211 ISafeByteBufferReader safeBuffer = SafeByteBufferFactory.wrapReader(buffer, valueSize);
212 value = CustomStateValue.readSerializedValue(safeBuffer);
213 break;
214 }
215 default:
216 /* Unknown data, better to not make anything up... */
217 throw new IOException(errMsg);
218 }
219
220 try {
221 return new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart);
222 } catch (TimeRangeException e) {
223 throw new IOException(errMsg);
224 }
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)
231 *
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 *
243 * @param buffer
244 * The already-allocated ByteBuffer corresponding to a SHT Node
245 */
246 public void writeInterval(ByteBuffer buffer) {
247 final byte byteFromType = getByteFromType(sv.getType());
248
249 buffer.putLong(start);
250 buffer.putLong(end);
251 buffer.putInt(attribute);
252 buffer.put(byteFromType);
253
254 switch (byteFromType) {
255 case TYPE_NULL:
256 break;
257 case TYPE_INTEGER:
258 buffer.putInt(sv.unboxInt());
259 break;
260
261 case TYPE_STRING: {
262 String string = sv.unboxStr();
263 byte[] strArray = string.getBytes();
264
265 /*
266 * Write the Strings entry (1st byte = size, then the bytes, then
267 * the 0). We have checked the string length at the constructor.
268 */
269 buffer.put((byte) strArray.length);
270 buffer.put(strArray);
271 buffer.put((byte) 0);
272 break;
273 }
274
275 case TYPE_LONG:
276 buffer.putLong(sv.unboxLong());
277 break;
278
279 case TYPE_DOUBLE:
280 buffer.putDouble(sv.unboxDouble());
281 break;
282
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
291 default:
292 break;
293 }
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
326 /**
327 * Total serialized size of this interval
328 *
329 * @return The interval size
330 */
331 public int getSizeOnDisk() {
332 return fSizeOnDisk;
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 }
349
350 @Override
351 public boolean equals(Object other) {
352 if (other instanceof HTInterval &&
353 this.compareTo((HTInterval) other) == 0) {
354 return true;
355 }
356 return false;
357 }
358
359 @Override
360 public int hashCode() {
361 return super.hashCode();
362 }
363
364 @Override
365 public String toString() {
366 /* Only for debug, should not be externalized */
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();
381 }
382
383 /**
384 * Here we determine how state values "types" are written in the 8-bit field
385 * that indicates the value type in the file.
386 */
387 private static byte getByteFromType(ITmfStateValue.Type type) {
388 switch (type) {
389 case NULL:
390 return TYPE_NULL;
391 case INTEGER:
392 return TYPE_INTEGER;
393 case STRING:
394 return TYPE_STRING;
395 case LONG:
396 return TYPE_LONG;
397 case DOUBLE:
398 return TYPE_DOUBLE;
399 case CUSTOM:
400 return TYPE_CUSTOM;
401 default:
402 /* Should not happen if the switch is fully covered */
403 throw new IllegalStateException();
404 }
405 }
406 }
This page took 0.043287 seconds and 5 git commands to generate.