Commit | Line | Data |
---|---|---|
a52fde77 | 1 | /******************************************************************************* |
e13bd4cd | 2 | * Copyright (c) 2012, 2015 Ericsson, École Polytechnique de Montréal |
a52fde77 | 3 | * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com> |
98107b3f | 4 | * |
a52fde77 AM |
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 | |
98107b3f | 9 | * |
b0136ad6 FW |
10 | * Contributors: |
11 | * Alexandre Montplaisir - Initial API and implementation | |
12 | * Florian Wininger - Allow to change the size of a interval | |
e13bd4cd | 13 | * Patrick Tasse - Add message to exceptions |
a52fde77 AM |
14 | *******************************************************************************/ |
15 | ||
e894a508 | 16 | package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree; |
a52fde77 AM |
17 | |
18 | import java.io.IOException; | |
19 | import java.nio.ByteBuffer; | |
20 | ||
aa353506 | 21 | import org.eclipse.jdt.annotation.NonNull; |
e894a508 AM |
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; | |
a52fde77 AM |
26 | |
27 | /** | |
28 | * The interval component, which will be contained in a node of the History | |
29 | * Tree. | |
98107b3f | 30 | * |
8d47cc34 | 31 | * @author Alexandre Montplaisir |
a52fde77 | 32 | */ |
8d47cc34 | 33 | public final class HTInterval implements ITmfStateInterval, Comparable<HTInterval> { |
a52fde77 | 34 | |
b67a2540 AM |
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; | |
1cbf1a19 | 41 | private static final byte TYPE_LONG = 2; |
a3c22e8e | 42 | private static final byte TYPE_DOUBLE = 3; |
b67a2540 | 43 | |
a52fde77 AM |
44 | private final long start; |
45 | private final long end; | |
46 | private final int attribute; | |
aa353506 | 47 | private final @NonNull TmfStateValue sv; |
a52fde77 | 48 | |
59d30d83 MK |
49 | /** Number of bytes used by this interval when it is written to disk */ |
50 | private final int fSizeOnDisk; | |
a52fde77 AM |
51 | |
52 | /** | |
53 | * Standard constructor | |
98107b3f | 54 | * |
a52fde77 | 55 | * @param intervalStart |
8d47cc34 | 56 | * Start time of the interval |
a52fde77 | 57 | * @param intervalEnd |
8d47cc34 | 58 | * End time of the interval |
a52fde77 | 59 | * @param attribute |
8d47cc34 AM |
60 | * Attribute (quark) to which the state represented by this |
61 | * interval belongs | |
a52fde77 | 62 | * @param value |
8d47cc34 | 63 | * State value represented by this interval |
a52fde77 | 64 | * @throws TimeRangeException |
8d47cc34 | 65 | * If the start time or end time are invalid |
a52fde77 | 66 | */ |
8d47cc34 | 67 | public HTInterval(long intervalStart, long intervalEnd, int attribute, |
aa353506 | 68 | @NonNull TmfStateValue value) throws TimeRangeException { |
a52fde77 | 69 | if (intervalStart > intervalEnd) { |
e13bd4cd | 70 | throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$ |
a52fde77 AM |
71 | } |
72 | ||
73 | this.start = intervalStart; | |
74 | this.end = intervalEnd; | |
75 | this.attribute = attribute; | |
76 | this.sv = value; | |
59d30d83 MK |
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 | } | |
a52fde77 AM |
112 | } |
113 | ||
b4603a60 AM |
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. | |
b4603a60 AM |
119 | */ |
120 | private HTInterval(long intervalStart, long intervalEnd, int attribute, | |
aa353506 | 121 | @NonNull TmfStateValue value, int size) throws TimeRangeException { |
b4603a60 | 122 | if (intervalStart > intervalEnd) { |
e13bd4cd | 123 | throw new TimeRangeException("Start:" + intervalStart + ", End:" + intervalEnd); //$NON-NLS-1$ //$NON-NLS-2$ |
b4603a60 AM |
124 | } |
125 | ||
126 | this.start = intervalStart; | |
127 | this.end = intervalEnd; | |
128 | this.attribute = attribute; | |
129 | this.sv = value; | |
59d30d83 | 130 | this.fSizeOnDisk = size; |
b4603a60 AM |
131 | } |
132 | ||
a52fde77 | 133 | /** |
8d47cc34 | 134 | * Reader factory method. Builds the interval using an already-allocated |
a52fde77 | 135 | * ByteBuffer, which normally comes from a NIO FileChannel. |
98107b3f | 136 | * |
59d30d83 MK |
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 | * | |
a52fde77 AM |
148 | * @param buffer |
149 | * The ByteBuffer from which to read the information | |
8d47cc34 | 150 | * @return The interval object |
a52fde77 | 151 | * @throws IOException |
8d47cc34 | 152 | * If there was an error reading from the buffer |
a52fde77 | 153 | */ |
8d47cc34 | 154 | public static final HTInterval readFrom(ByteBuffer buffer) throws IOException { |
a52fde77 AM |
155 | HTInterval interval; |
156 | long intervalStart, intervalEnd; | |
157 | int attribute; | |
158 | TmfStateValue value; | |
a52fde77 AM |
159 | byte valueType; |
160 | byte array[]; | |
161 | ||
59d30d83 | 162 | int posStart = buffer.position(); |
a52fde77 AM |
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(); | |
b67a2540 | 170 | switch (valueType) { |
a52fde77 | 171 | |
b67a2540 AM |
172 | case TYPE_NULL: |
173 | value = TmfStateValue.nullValue(); | |
174 | break; | |
a52fde77 | 175 | |
b67a2540 | 176 | case TYPE_INTEGER: |
59d30d83 | 177 | value = TmfStateValue.newValueInt(buffer.getInt()); |
b67a2540 AM |
178 | break; |
179 | ||
180 | case TYPE_STRING: | |
a52fde77 | 181 | /* the first byte = the size to read */ |
59d30d83 | 182 | int valueSize = buffer.get(); |
a52fde77 | 183 | |
59d30d83 | 184 | array = new byte[valueSize]; |
a52fde77 AM |
185 | buffer.get(array); |
186 | value = TmfStateValue.newValueString(new String(array)); | |
187 | ||
188 | /* Confirm the 0'ed byte at the end */ | |
59d30d83 | 189 | byte res = buffer.get(); |
a52fde77 | 190 | if (res != 0) { |
b67a2540 | 191 | throw new IOException(errMsg); |
a52fde77 | 192 | } |
1cbf1a19 FR |
193 | break; |
194 | ||
195 | case TYPE_LONG: | |
196 | /* Go read the matching entry in the Strings section of the block */ | |
1cbf1a19 | 197 | value = TmfStateValue.newValueLong(buffer.getLong()); |
b67a2540 | 198 | break; |
a3c22e8e AM |
199 | |
200 | case TYPE_DOUBLE: | |
201 | /* Go read the matching entry in the Strings section of the block */ | |
a3c22e8e | 202 | value = TmfStateValue.newValueDouble(buffer.getDouble()); |
a3c22e8e AM |
203 | break; |
204 | ||
b67a2540 AM |
205 | default: |
206 | /* Unknown data, better to not make anything up... */ | |
207 | throw new IOException(errMsg); | |
a52fde77 AM |
208 | } |
209 | ||
210 | try { | |
59d30d83 | 211 | interval = new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart); |
a52fde77 | 212 | } catch (TimeRangeException e) { |
b67a2540 | 213 | throw new IOException(errMsg); |
a52fde77 AM |
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) | |
98107b3f | 222 | * |
59d30d83 MK |
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 | * | |
a52fde77 AM |
234 | * @param buffer |
235 | * The already-allocated ByteBuffer corresponding to a SHT Node | |
a52fde77 | 236 | */ |
59d30d83 MK |
237 | public void writeInterval(ByteBuffer buffer) { |
238 | final byte byteFromType = getByteFromType(sv.getType()); | |
239 | ||
a52fde77 AM |
240 | buffer.putLong(start); |
241 | buffer.putLong(end); | |
242 | buffer.putInt(attribute); | |
59d30d83 | 243 | buffer.put(byteFromType); |
a52fde77 | 244 | |
59d30d83 | 245 | switch (byteFromType) { |
1cbf1a19 | 246 | case TYPE_NULL: |
59d30d83 | 247 | break; |
1cbf1a19 | 248 | case TYPE_INTEGER: |
59d30d83 | 249 | buffer.putInt(sv.unboxInt()); |
9177eb74 | 250 | break; |
a52fde77 | 251 | |
1cbf1a19 | 252 | case TYPE_STRING: |
59d30d83 MK |
253 | String string = sv.unboxStr(); |
254 | byte[] strArray = string.getBytes(); | |
1cbf1a19 FR |
255 | |
256 | /* | |
59d30d83 MK |
257 | * Write the Strings entry (1st byte = size, then the bytes, then |
258 | * the 0). We have checked the string length at the constructor. | |
1cbf1a19 | 259 | */ |
59d30d83 MK |
260 | buffer.put((byte) strArray.length); |
261 | buffer.put(strArray); | |
1cbf1a19 | 262 | buffer.put((byte) 0); |
9177eb74 | 263 | break; |
1cbf1a19 FR |
264 | |
265 | case TYPE_LONG: | |
59d30d83 | 266 | buffer.putLong(sv.unboxLong()); |
9177eb74 | 267 | break; |
1cbf1a19 | 268 | |
a3c22e8e | 269 | case TYPE_DOUBLE: |
59d30d83 | 270 | buffer.putDouble(sv.unboxDouble()); |
a3c22e8e AM |
271 | break; |
272 | ||
1cbf1a19 | 273 | default: |
9177eb74 | 274 | break; |
a52fde77 | 275 | } |
a52fde77 AM |
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 | ||
a52fde77 AM |
308 | /** |
309 | * Total serialized size of this interval | |
98107b3f | 310 | * |
8d47cc34 | 311 | * @return The interval size |
a52fde77 | 312 | */ |
59d30d83 MK |
313 | public int getSizeOnDisk() { |
314 | return fSizeOnDisk; | |
a52fde77 AM |
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 | } | |
1b558482 | 331 | |
ab604305 AM |
332 | @Override |
333 | public boolean equals(Object other) { | |
cb42195c AM |
334 | if (other instanceof HTInterval && |
335 | this.compareTo((HTInterval) other) == 0) { | |
336 | return true; | |
ab604305 AM |
337 | } |
338 | return false; | |
339 | } | |
340 | ||
341 | @Override | |
342 | public int hashCode() { | |
343 | return super.hashCode(); | |
344 | } | |
1b558482 AM |
345 | |
346 | @Override | |
347 | public String toString() { | |
348 | /* Only for debug, should not be externalized */ | |
98107b3f AM |
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(); | |
1b558482 | 363 | } |
b67a2540 AM |
364 | |
365 | /** | |
59d30d83 MK |
366 | * Here we determine how state values "types" are written in the 8-bit field |
367 | * that indicates the value type in the file. | |
b67a2540 AM |
368 | */ |
369 | private static byte getByteFromType(ITmfStateValue.Type type) { | |
59d30d83 | 370 | switch (type) { |
b67a2540 AM |
371 | case NULL: |
372 | return TYPE_NULL; | |
373 | case INTEGER: | |
374 | return TYPE_INTEGER; | |
375 | case STRING: | |
376 | return TYPE_STRING; | |
1cbf1a19 FR |
377 | case LONG: |
378 | return TYPE_LONG; | |
a3c22e8e AM |
379 | case DOUBLE: |
380 | return TYPE_DOUBLE; | |
b67a2540 AM |
381 | default: |
382 | /* Should not happen if the switch is fully covered */ | |
cb42195c | 383 | throw new IllegalStateException(); |
b67a2540 AM |
384 | } |
385 | } | |
a52fde77 | 386 | } |