70a27d0d0573a109104dc7af24e1edadbe47252e
[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. In the case
241 * of a null value, it will be unboxed as -1 */
242 try {
243 buffer.putInt(sv.unboxInt());
244 } catch (StateValueTypeException e) {
245 /*
246 * This should not happen, since the value told us it was of
247 * type Null or Integer (corrupted value?)
248 */
249 e.printStackTrace();
250 }
251 break;
252
253 case TYPE_STRING:
254 byte[] byteArrayToWrite;
255 try {
256 byteArrayToWrite = sv.unboxStr().getBytes();
257 } catch (StateValueTypeException e1) {
258 /* Should not happen, we're in a switch/case for string type */
259 throw new RuntimeException();
260 }
261
262 /* we use the valueOffset as an offset. */
263 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
264 buffer.mark();
265 buffer.position(endPosOfStringEntry - stringsEntrySize);
266
267 /*
268 * write the Strings entry (1st byte = size, then the bytes, then the 0)
269 */
270 buffer.put((byte) stringsEntrySize);
271 buffer.put(byteArrayToWrite);
272 buffer.put((byte) 0);
273 assert (buffer.position() == endPosOfStringEntry);
274 buffer.reset();
275 break;
276
277 case TYPE_LONG:
278 /* we use the valueOffset as an offset. */
279 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
280 buffer.mark();
281 buffer.position(endPosOfStringEntry - stringsEntrySize);
282
283 /*
284 * write the Long in the Strings section
285 */
286 try {
287 buffer.putLong(sv.unboxLong());
288 } catch (StateValueTypeException e) {
289 /*
290 * This should not happen, since the value told us it was of
291 * type Long (corrupted value?)
292 */
293 e.printStackTrace();
294 }
295 assert (buffer.position() == endPosOfStringEntry);
296 buffer.reset();
297 break;
298
299 case TYPE_DOUBLE:
300 /* we use the valueOffset as an offset. */
301 buffer.putInt(endPosOfStringEntry - stringsEntrySize);
302 buffer.mark();
303 buffer.position(endPosOfStringEntry - stringsEntrySize);
304
305 /* Write the Double in the Strings section */
306 try {
307 buffer.putDouble(sv.unboxDouble());
308 } catch (StateValueTypeException e) {
309 /*
310 * This should not happen, since the value told us it was of
311 * type Double (corrupted value?)
312 */
313 e.printStackTrace();
314 }
315 if (buffer.position() != endPosOfStringEntry) {
316 throw new IllegalStateException();
317 }
318 buffer.reset();
319 break;
320
321 default:
322 break;
323 }
324 return stringsEntrySize;
325 }
326
327 @Override
328 public long getStartTime() {
329 return start;
330 }
331
332 @Override
333 public long getEndTime() {
334 return end;
335 }
336
337 @Override
338 public long getViewerEndTime() {
339 return end + 1;
340 }
341
342 @Override
343 public int getAttribute() {
344 return attribute;
345 }
346
347 @Override
348 public ITmfStateValue getStateValue() {
349 return sv;
350 }
351
352 @Override
353 public boolean intersects(long timestamp) {
354 if (start <= timestamp) {
355 if (end >= timestamp) {
356 return true;
357 }
358 }
359 return false;
360 }
361
362 int getStringsEntrySize() {
363 return stringsEntrySize;
364 }
365
366 /**
367 * Total serialized size of this interval
368 *
369 * @return
370 */
371 int getIntervalSize() {
372 return stringsEntrySize + HTNode.DATA_ENTRY_SIZE;
373 }
374
375 private int computeStringsEntrySize() {
376 switch(sv.getType()) {
377 case NULL:
378 case INTEGER:
379 /* Those don't use the strings section at all */
380 return NO_ENTRY_SIZE;
381 case LONG:
382 /* The value's bytes are written directly into the strings section */
383 return LONG_ENTRY_SIZE;
384 case DOUBLE:
385 /* The value is also written directly into the strings section */
386 return DOUBLE_ENTRY_SIZE;
387 case STRING:
388 try {
389 /* String's length + 2 (1 byte for size, 1 byte for \0 at the end */
390 return sv.unboxStr().getBytes().length + 2;
391 } catch (StateValueTypeException e) {
392 /* We're inside a switch/case for the string type, can't happen */
393 throw new IllegalStateException(e);
394 }
395 default:
396 /* It's very important that we know how to write the state value in
397 * the file!! */
398 throw new IllegalStateException();
399 }
400 }
401
402 /**
403 * Compare the END TIMES of different intervals. This is used to sort the
404 * intervals when we close down a node.
405 */
406 @Override
407 public int compareTo(HTInterval other) {
408 if (this.end < other.end) {
409 return -1;
410 } else if (this.end > other.end) {
411 return 1;
412 } else {
413 return 0;
414 }
415 }
416
417 @Override
418 public boolean equals(Object other) {
419 if (other instanceof HTInterval &&
420 this.compareTo((HTInterval) other) == 0) {
421 return true;
422 }
423 return false;
424 }
425
426 @Override
427 public int hashCode() {
428 return super.hashCode();
429 }
430
431 @Override
432 public String toString() {
433 /* Only for debug, should not be externalized */
434 StringBuilder sb = new StringBuilder();
435 sb.append('[');
436 sb.append(start);
437 sb.append(", "); //$NON-NLS-1$
438 sb.append(end);
439 sb.append(']');
440
441 sb.append(", attribute = "); //$NON-NLS-1$
442 sb.append(attribute);
443
444 sb.append(", value = "); //$NON-NLS-1$
445 sb.append(sv.toString());
446
447 return sb.toString();
448 }
449
450 /**
451 * Here we determine how state values "types" are written in the 8-bit
452 * field that indicates the value type in the file.
453 */
454 private static byte getByteFromType(ITmfStateValue.Type type) {
455 switch(type) {
456 case NULL:
457 return TYPE_NULL;
458 case INTEGER:
459 return TYPE_INTEGER;
460 case STRING:
461 return TYPE_STRING;
462 case LONG:
463 return TYPE_LONG;
464 case DOUBLE:
465 return TYPE_DOUBLE;
466 default:
467 /* Should not happen if the switch is fully covered */
468 throw new IllegalStateException();
469 }
470 }
471 }
This page took 0.040891 seconds and 4 git commands to generate.