44eada2b9cc8c2b76d099b91d88a03577f500335
[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.statesystem.core.exceptions.TimeRangeException;
23 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
24 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
25 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
26
27 /**
28 * The interval component, which will be contained in a node of the History
29 * Tree.
30 *
31 * @author Alexandre Montplaisir
32 */
33 public final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> {
34
35 private static final String errMsg = "Invalid interval data. Maybe your file is corrupt?"; //$NON-NLS-1$
36
37 /* 'Byte' equivalent for state values types */
38 private static final byte TYPE_NULL = -1;
39 private static final byte TYPE_INTEGER = 0;
40 private static final byte TYPE_STRING = 1;
41 private static final byte TYPE_LONG = 2;
42 private static final byte TYPE_DOUBLE = 3;
43 private static final byte TYPE_CUSTOM = 20;
44
45 private final long start;
46 private final long end;
47 private final int attribute;
48 private final @NonNull TmfStateValue sv;
49
50 /** Number of bytes used by this interval when it is written to disk */
51 private final int fSizeOnDisk;
52
53 /**
54 * Standard constructor
55 *
56 * @param intervalStart
57 * Start time of the interval
58 * @param intervalEnd
59 * End time of the interval
60 * @param attribute
61 * Attribute (quark) to which the state represented by this
62 * interval belongs
63 * @param value
64 * State value represented by this interval
65 * @throws TimeRangeException
66 * If the start time or end time are invalid
67 */
68 public HTInterval(long intervalStart, long intervalEnd, int attribute,
69 @NonNull TmfStateValue value) throws TimeRangeException {
70 if (intervalStart > intervalEnd) {
71 throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$
72 }
73
74 this.start = intervalStart;
75 this.end = intervalEnd;
76 this.attribute = attribute;
77 this.sv = value;
78 this.fSizeOnDisk = computeSizeOnDisk(sv);
79 }
80
81 /**
82 * Compute how much space (in bytes) an interval will take in its serialized
83 * form on disk. This is dependent on its state value.
84 */
85 private static int computeSizeOnDisk(ITmfStateValue sv) {
86 /*
87 * Minimum size is 2x long (start and end), 1x int (attribute) and 1x
88 * byte (value type).
89 */
90 int minSize = Long.BYTES + Long.BYTES + Integer.BYTES + Byte.BYTES;
91
92 switch (sv.getType()) {
93 case NULL:
94 return minSize;
95 case INTEGER:
96 return (minSize + Integer.BYTES);
97 case LONG:
98 return (minSize + Long.BYTES);
99 case DOUBLE:
100 return (minSize + Double.BYTES);
101 case STRING:
102 /*
103 * String's length + 2 (1 byte for size, 1 byte for \0 at the end
104 */
105 return (minSize + sv.unboxStr().getBytes().length + 2);
106 case CUSTOM:
107 default:
108 /*
109 * It's very important that we know how to write the state value in
110 * the file!!
111 */
112 throw new IllegalStateException();
113 }
114 }
115
116 /**
117 * "Faster" constructor for inner use only. When we build an interval when
118 * reading it from disk (with {@link #readFrom}), we already know the size
119 * of the strings entry, so there is no need to call
120 * {@link #computeStringsEntrySize()} and do an extra copy.
121 */
122 private HTInterval(long intervalStart, long intervalEnd, int attribute,
123 @NonNull TmfStateValue value, int size) throws TimeRangeException {
124 if (intervalStart > intervalEnd) {
125 throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$
126 }
127
128 this.start = intervalStart;
129 this.end = intervalEnd;
130 this.attribute = attribute;
131 this.sv = value;
132 this.fSizeOnDisk = size;
133 }
134
135 /**
136 * Reader factory method. Builds the interval using an already-allocated
137 * ByteBuffer, which normally comes from a NIO FileChannel.
138 *
139 * The interval is just a start, end, attribute and value, this is the
140 * layout of the HTInterval on disk
141 * <ul>
142 * <li>start (8 bytes)</li>
143 * <li>end (8 bytes)</li>
144 * <li>attribute (4 bytes)</li>
145 * <li>sv type (1 byte)</li>
146 * <li>sv ( 0 bytes for null, 4 for int , 8 for long and double, and the
147 * length of the string +2 for strings (it's variable))</li>
148 * </ul>
149 *
150 * @param buffer
151 * The ByteBuffer from which to read the information
152 * @return The interval object
153 * @throws IOException
154 * If there was an error reading from the buffer
155 */
156 public static final HTInterval readFrom(ByteBuffer buffer) throws IOException {
157 HTInterval interval;
158 long intervalStart, intervalEnd;
159 int attribute;
160 TmfStateValue value;
161 byte valueType;
162 byte array[];
163
164 int posStart = buffer.position();
165 /* Read the Data Section entry */
166 intervalStart = buffer.getLong();
167 intervalEnd = buffer.getLong();
168 attribute = buffer.getInt();
169
170 /* Read the 'type' of the value, then react accordingly */
171 valueType = buffer.get();
172 switch (valueType) {
173
174 case TYPE_NULL:
175 value = TmfStateValue.nullValue();
176 break;
177
178 case TYPE_INTEGER:
179 value = TmfStateValue.newValueInt(buffer.getInt());
180 break;
181
182 case TYPE_STRING:
183 /* the first byte = the size to read */
184 int valueSize = buffer.get();
185
186 array = new byte[valueSize];
187 buffer.get(array);
188 value = TmfStateValue.newValueString(new String(array));
189
190 /* Confirm the 0'ed byte at the end */
191 byte res = buffer.get();
192 if (res != 0) {
193 throw new IOException(errMsg);
194 }
195 break;
196
197 case TYPE_LONG:
198 /* Go read the matching entry in the Strings section of the block */
199 value = TmfStateValue.newValueLong(buffer.getLong());
200 break;
201
202 case TYPE_DOUBLE:
203 /* Go read the matching entry in the Strings section of the block */
204 value = TmfStateValue.newValueDouble(buffer.getDouble());
205 break;
206
207 case TYPE_CUSTOM:
208 default:
209 /* Unknown data, better to not make anything up... */
210 throw new IOException(errMsg);
211 }
212
213 try {
214 interval = new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart);
215 } catch (TimeRangeException e) {
216 throw new IOException(errMsg);
217 }
218 return interval;
219 }
220
221 /**
222 * Antagonist of the previous constructor, write the Data entry
223 * corresponding to this interval in a ByteBuffer (mapped to a block in the
224 * history-file, hopefully)
225 *
226 * The interval is just a start, end, attribute and value, this is the
227 * layout of the HTInterval on disk
228 * <ul>
229 * <li>start (8 bytes)</li>
230 * <li>end (8 bytes)</li>
231 * <li>attribute (4 bytes)</li>
232 * <li>sv type (1 byte)</li>
233 * <li>sv ( 0 bytes for null, 4 for int , 8 for long and double, and the
234 * length of the string +2 for strings (it's variable))</li>
235 * </ul>
236 *
237 * @param buffer
238 * The already-allocated ByteBuffer corresponding to a SHT Node
239 */
240 public void writeInterval(ByteBuffer buffer) {
241 final byte byteFromType = getByteFromType(sv.getType());
242
243 buffer.putLong(start);
244 buffer.putLong(end);
245 buffer.putInt(attribute);
246 buffer.put(byteFromType);
247
248 switch (byteFromType) {
249 case TYPE_NULL:
250 break;
251 case TYPE_INTEGER:
252 buffer.putInt(sv.unboxInt());
253 break;
254
255 case TYPE_STRING:
256 String string = sv.unboxStr();
257 byte[] strArray = string.getBytes();
258
259 /*
260 * Write the Strings entry (1st byte = size, then the bytes, then
261 * the 0). We have checked the string length at the constructor.
262 */
263 buffer.put((byte) strArray.length);
264 buffer.put(strArray);
265 buffer.put((byte) 0);
266 break;
267
268 case TYPE_LONG:
269 buffer.putLong(sv.unboxLong());
270 break;
271
272 case TYPE_DOUBLE:
273 buffer.putDouble(sv.unboxDouble());
274 break;
275
276 default:
277 break;
278 }
279 }
280
281 @Override
282 public long getStartTime() {
283 return start;
284 }
285
286 @Override
287 public long getEndTime() {
288 return end;
289 }
290
291 @Override
292 public int getAttribute() {
293 return attribute;
294 }
295
296 @Override
297 public ITmfStateValue getStateValue() {
298 return sv;
299 }
300
301 @Override
302 public boolean intersects(long timestamp) {
303 if (start <= timestamp) {
304 if (end >= timestamp) {
305 return true;
306 }
307 }
308 return false;
309 }
310
311 /**
312 * Total serialized size of this interval
313 *
314 * @return The interval size
315 */
316 public int getSizeOnDisk() {
317 return fSizeOnDisk;
318 }
319
320 /**
321 * Compare the END TIMES of different intervals. This is used to sort the
322 * intervals when we close down a node.
323 */
324 @Override
325 public int compareTo(HTInterval other) {
326 if (this.end < other.end) {
327 return -1;
328 } else if (this.end > other.end) {
329 return 1;
330 } else {
331 return 0;
332 }
333 }
334
335 @Override
336 public boolean equals(Object other) {
337 if (other instanceof HTInterval &&
338 this.compareTo((HTInterval) other) == 0) {
339 return true;
340 }
341 return false;
342 }
343
344 @Override
345 public int hashCode() {
346 return super.hashCode();
347 }
348
349 @Override
350 public String toString() {
351 /* Only for debug, should not be externalized */
352 StringBuilder sb = new StringBuilder();
353 sb.append('[');
354 sb.append(start);
355 sb.append(", "); //$NON-NLS-1$
356 sb.append(end);
357 sb.append(']');
358
359 sb.append(", attribute = "); //$NON-NLS-1$
360 sb.append(attribute);
361
362 sb.append(", value = "); //$NON-NLS-1$
363 sb.append(sv.toString());
364
365 return sb.toString();
366 }
367
368 /**
369 * Here we determine how state values "types" are written in the 8-bit field
370 * that indicates the value type in the file.
371 */
372 private static byte getByteFromType(ITmfStateValue.Type type) {
373 switch (type) {
374 case NULL:
375 return TYPE_NULL;
376 case INTEGER:
377 return TYPE_INTEGER;
378 case STRING:
379 return TYPE_STRING;
380 case LONG:
381 return TYPE_LONG;
382 case DOUBLE:
383 return TYPE_DOUBLE;
384 case CUSTOM:
385 return TYPE_CUSTOM;
386 default:
387 /* Should not happen if the switch is fully covered */
388 throw new IllegalStateException();
389 }
390 }
391 }
This page took 0.039618 seconds and 4 git commands to generate.