Merge branch 'master' into lttng-luna
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / internal / tmf / core / statesystem / backends / historytree / HTInterval.java
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>
5 *
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
10 *
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree;
14
15 import java.io.IOException;
16 import java.nio.ByteBuffer;
17
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;
23
24 /**
25 * The interval component, which will be contained in a node of the History
26 * Tree.
27 *
28 * @author alexmont
29 *
30 */
31 final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
32
33 private static final String errMsg = "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
34
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;
40 private static final byte TYPE_DOUBLE = 3;
41
42 /* String entry sizes of different state values */
43 private static final int NO_ENTRY_SIZE = 0;
44 private static final int LONG_ENTRY_SIZE = 8;
45 private static final int DOUBLE_ENTRY_SIZE = 8;
46 // sizes of string values depend on the string itself
47
48 private final long start;
49 private final long end;
50 private final int attribute;
51 private final TmfStateValue sv;
52
53 /*
54 * Size of the strings section entry used by this interval (= 0 if not used)
55 */
56 private final int stringsEntrySize;
57
58 /**
59 * Standard constructor
60 *
61 * @param intervalStart
62 * @param intervalEnd
63 * @param attribute
64 * @param value
65 * @throws TimeRangeException
66 */
67 HTInterval(long intervalStart, long intervalEnd, int attribute,
68 TmfStateValue value) throws TimeRangeException {
69 if (intervalStart > intervalEnd) {
70 throw new TimeRangeException();
71 }
72
73 this.start = intervalStart;
74 this.end = intervalEnd;
75 this.attribute = attribute;
76 this.sv = value;
77 this.stringsEntrySize = computeStringsEntrySize();
78 }
79
80 /**
81 * "Faster" constructor for inner use only. When we build an interval when
82 * reading it from disk (with {@link #readFrom}), we already know the size
83 * of the strings entry, so there is no need to call
84 * {@link #computeStringsEntrySize()} and do an extra copy.
85 *
86 * @param intervalStart
87 * @param intervalEnd
88 * @param attribute
89 * @param value
90 * @param size
91 * @throws TimeRangeException
92 */
93 private HTInterval(long intervalStart, long intervalEnd, int attribute,
94 TmfStateValue value, int size) throws TimeRangeException {
95 if (intervalStart > intervalEnd) {
96 throw new TimeRangeException();
97 }
98
99 this.start = intervalStart;
100 this.end = intervalEnd;
101 this.attribute = attribute;
102 this.sv = value;
103 this.stringsEntrySize = size;
104 }
105
106 /**
107 * Reader constructor. Builds the interval using an already-allocated
108 * ByteBuffer, which normally comes from a NIO FileChannel.
109 *
110 * @param buffer
111 * The ByteBuffer from which to read the information
112 * @throws IOException
113 */
114 final static HTInterval readFrom(ByteBuffer buffer) throws IOException {
115 HTInterval interval;
116 long intervalStart, intervalEnd;
117 int attribute;
118 TmfStateValue value;
119 int valueOrOffset, valueSize, res;
120 byte valueType;
121 byte array[];
122
123 /* Read the Data Section entry */
124 intervalStart = buffer.getLong();
125 intervalEnd = buffer.getLong();
126 attribute = buffer.getInt();
127
128 /* Read the 'type' of the value, then react accordingly */
129 valueType = buffer.get();
130 valueOrOffset = buffer.getInt();
131 switch (valueType) {
132
133 case TYPE_NULL:
134 value = TmfStateValue.nullValue();
135 valueSize = NO_ENTRY_SIZE;
136 break;
137
138 case TYPE_INTEGER:
139 /* "ValueOrOffset" is the straight value */
140 value = TmfStateValue.newValueInt(valueOrOffset);
141 valueSize = NO_ENTRY_SIZE;
142 break;
143
144 case TYPE_STRING:
145 /* Go read the matching entry in the Strings section of the block */
146 buffer.mark();
147 buffer.position(valueOrOffset);
148
149 /* the first byte = the size to read */
150 valueSize = buffer.get();
151
152 /*
153 * Careful though, 'valueSize' is the total size of the entry,
154 * including the 'size' byte at the start and end (0'ed) byte at the
155 * end. Here we want 'array' to only contain the real payload of the
156 * value.
157 */
158 array = new byte[valueSize - 2];
159 buffer.get(array);
160 value = TmfStateValue.newValueString(new String(array));
161
162 /* Confirm the 0'ed byte at the end */
163 res = buffer.get();
164 if (res != 0) {
165 throw new IOException(errMsg);
166 }
167
168 /*
169 * Restore the file pointer's position (so we can read the next
170 * interval)
171 */
172 buffer.reset();
173 break;
174
175 case TYPE_LONG:
176 /* Go read the matching entry in the Strings section of the block */
177 buffer.mark();
178 buffer.position(valueOrOffset);
179 value = TmfStateValue.newValueLong(buffer.getLong());
180 valueSize = LONG_ENTRY_SIZE;
181
182 /*
183 * Restore the file pointer's position (so we can read the next
184 * interval)
185 */
186 buffer.reset();
187 break;
188
189 case TYPE_DOUBLE:
190 /* Go read the matching entry in the Strings section of the block */
191 buffer.mark();
192 buffer.position(valueOrOffset);
193 value = TmfStateValue.newValueDouble(buffer.getDouble());
194 valueSize = DOUBLE_ENTRY_SIZE;
195
196 /*
197 * Restore the file pointer's position (so we can read the next
198 * interval)
199 */
200 buffer.reset();
201 break;
202
203 default:
204 /* Unknown data, better to not make anything up... */
205 throw new IOException(errMsg);
206 }
207
208 try {
209 interval = new HTInterval(intervalStart, intervalEnd, attribute, value, valueSize);
210 } catch (TimeRangeException e) {
211 throw new IOException(errMsg);
212 }
213 return interval;
214 }
215
216 /**
217 * Antagonist of the previous constructor, write the Data entry
218 * corresponding to this interval in a ByteBuffer (mapped to a block in the
219 * history-file, hopefully)
220 *
221 * @param buffer
222 * The already-allocated ByteBuffer corresponding to a SHT Node
223 * @param endPosOfStringEntry
224 * The initial (before calling this function for this interval)
225 * position of the Strings Entry for this node. This will change
226 * from one call to the other if we're writing String
227 * StateValues.
228 * @return The size of the Strings Entry that was written, if any.
229 */
230 int writeInterval(ByteBuffer buffer, int endPosOfStringEntry) {
231 buffer.putLong(start);
232 buffer.putLong(end);
233 buffer.putInt(attribute);
234 buffer.put(getByteFromType(sv.getType()));
235
236 switch (getByteFromType(sv.getType())) {
237
238 case TYPE_NULL:
239 case TYPE_INTEGER:
240 /* We write the 'valueOffset' field as a straight value. */
241 try {
242 buffer.putInt(sv.unboxInt());
243 } catch (StateValueTypeException e) {
244 /*
245 * This should not happen, since the value told us it was of
246 * type Null or Integer (corrupted value?)
247 */
248 e.printStackTrace();
249 }
250 break;
251
252 case TYPE_STRING:
253 byte[] byteArrayToWrite;
254 try {
255 byteArrayToWrite = sv.unboxStr().getBytes();
256 } catch (StateValueTypeException e1) {
257 /* Should not happen, we're in a switch/case for string type */
258 throw new RuntimeException();
259 }
260
261 /* we use the valueOffset as an offset. */
262 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
263 buffer.mark();
264 buffer.position(endPosOfStringEntry - stringsEntrySize);
265
266 /*
267 * write the Strings entry (1st byte = size, then the bytes, then the 0)
268 */
269 buffer.put((byte) stringsEntrySize);
270 buffer.put(byteArrayToWrite);
271 buffer.put((byte) 0);
272 assert (buffer.position() == endPosOfStringEntry);
273 buffer.reset();
274 break;
275
276 case TYPE_LONG:
277 /* we use the valueOffset as an offset. */
278 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
279 buffer.mark();
280 buffer.position(endPosOfStringEntry - stringsEntrySize);
281
282 /*
283 * write the Long in the Strings section
284 */
285 try {
286 buffer.putLong(sv.unboxLong());
287 } catch (StateValueTypeException e) {
288 /*
289 * This should not happen, since the value told us it was of
290 * type Long (corrupted value?)
291 */
292 e.printStackTrace();
293 }
294 assert (buffer.position() == endPosOfStringEntry);
295 buffer.reset();
296 break;
297
298 case TYPE_DOUBLE:
299 /* we use the valueOffset as an offset. */
300 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
301 buffer.mark();
302 buffer.position(endPosOfStringEntry - stringsEntrySize);
303
304 /* Write the Double in the Strings section */
305 try {
306 buffer.putDouble(sv.unboxDouble());
307 } catch (StateValueTypeException e) {
308 /*
309 * This should not happen, since the value told us it was of
310 * type Double (corrupted value?)
311 */
312 e.printStackTrace();
313 }
314 if (buffer.position() != endPosOfStringEntry) {
315 throw new IllegalStateException();
316 }
317 buffer.reset();
318 break;
319
320 default:
321 break;
322 }
323 return stringsEntrySize;
324 }
325
326 @Override
327 public long getStartTime() {
328 return start;
329 }
330
331 @Override
332 public long getEndTime() {
333 return end;
334 }
335
336 @Override
337 public long getViewerEndTime() {
338 return end + 1;
339 }
340
341 @Override
342 public int getAttribute() {
343 return attribute;
344 }
345
346 @Override
347 public ITmfStateValue getStateValue() {
348 return sv;
349 }
350
351 @Override
352 public boolean intersects(long timestamp) {
353 if (start <= timestamp) {
354 if (end >= timestamp) {
355 return true;
356 }
357 }
358 return false;
359 }
360
361 int getStringsEntrySize() {
362 return stringsEntrySize;
363 }
364
365 /**
366 * Total serialized size of this interval
367 *
368 * @return
369 */
370 int getIntervalSize() {
371 return stringsEntrySize + HTNode.DATA_ENTRY_SIZE;
372 }
373
374 private int computeStringsEntrySize() {
375 switch(sv.getType()) {
376 case NULL:
377 case INTEGER:
378 /* Those don't use the strings section at all */
379 return NO_ENTRY_SIZE;
380 case LONG:
381 /* The value's bytes are written directly into the strings section */
382 return LONG_ENTRY_SIZE;
383 case DOUBLE:
384 /* The value is also written directly into the strings section */
385 return DOUBLE_ENTRY_SIZE;
386 case STRING:
387 try {
388 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
389 return sv.unboxStr().getBytes().length + 2;
390 } catch (StateValueTypeException e) {
391 /* We're inside a switch/case for the string type, can't happen */
392 throw new IllegalStateException(e);
393 }
394 default:
395 /* It's very important that we know how to write the state value in
396 * the file!! */
397 throw new IllegalStateException();
398 }
399 }
400
401 /**
402 * Compare the END TIMES of different intervals. This is used to sort the
403 * intervals when we close down a node.
404 */
405 @Override
406 public int compareTo(HTInterval other) {
407 if (this.end < other.end) {
408 return -1;
409 } else if (this.end > other.end) {
410 return 1;
411 } else {
412 return 0;
413 }
414 }
415
416 @Override
417 public boolean equals(Object other) {
418 if (other instanceof HTInterval &&
419 this.compareTo((HTInterval) other) == 0) {
420 return true;
421 }
422 return false;
423 }
424
425 @Override
426 public int hashCode() {
427 return super.hashCode();
428 }
429
430 @Override
431 public String toString() {
432 /* Only for debug, should not be externalized */
433 StringBuilder sb = new StringBuilder();
434 sb.append('[');
435 sb.append(start);
436 sb.append(", "); //$NON-NLS-1$
437 sb.append(end);
438 sb.append(']');
439
440 sb.append(", attribute = "); //$NON-NLS-1$
441 sb.append(attribute);
442
443 sb.append(", value = "); //$NON-NLS-1$
444 sb.append(sv.toString());
445
446 return sb.toString();
447 }
448
449 /**
450 * Here we determine how state values "types" are written in the 8-bit
451 * field that indicates the value type in the file.
452 */
453 private static byte getByteFromType(ITmfStateValue.Type type) {
454 switch(type) {
455 case NULL:
456 return TYPE_NULL;
457 case INTEGER:
458 return TYPE_INTEGER;
459 case STRING:
460 return TYPE_STRING;
461 case LONG:
462 return TYPE_LONG;
463 case DOUBLE:
464 return TYPE_DOUBLE;
465 default:
466 /* Should not happen if the switch is fully covered */
467 throw new IllegalStateException();
468 }
469 }
470 }
This page took 0.058322 seconds and 6 git commands to generate.