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