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