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