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