Commit | Line | Data |
---|---|---|
8c8bf09f | 1 | /******************************************************************************* |
065cc19b | 2 | * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal |
f8177ba2 | 3 | * |
8c8bf09f ASL |
4 | * All rights reserved. This program and the accompanying materials are |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
f8177ba2 | 8 | * |
8c8bf09f | 9 | * Contributors: |
065cc19b | 10 | * Francois Chouinard - Initial API and implementation, refactoring and updates |
d5efe032 | 11 | * Thomas Gatterweh - Updated scaling / synchronization |
065cc19b AM |
12 | * Geneviève Bastien - Added copy constructor with new value |
13 | * Alexandre Montplaisir - Removed concept of precision | |
8c8bf09f ASL |
14 | *******************************************************************************/ |
15 | ||
2bdf0193 | 16 | package org.eclipse.tracecompass.tmf.core.timestamp; |
8c8bf09f | 17 | |
032ecd45 MAL |
18 | import java.nio.ByteBuffer; |
19 | ||
cbf0057c | 20 | import org.eclipse.jdt.annotation.NonNull; |
16dd744f | 21 | import org.eclipse.tracecompass.common.core.math.SaturatedArithmetic; |
dd21f749 MK |
22 | import org.eclipse.tracecompass.internal.tmf.core.timestamp.TmfNanoTimestamp; |
23 | import org.eclipse.tracecompass.internal.tmf.core.timestamp.TmfSecondTimestamp; | |
cbf0057c | 24 | |
8c8bf09f | 25 | /** |
9e925522 | 26 | * A generic timestamp implementation. The timestamp is represented by the tuple |
b2c971ec | 27 | * { value, scale, precision }. |
f8177ba2 | 28 | * |
b9e37ffd | 29 | * @author Francois Chouinard |
8c8bf09f | 30 | */ |
b2c971ec | 31 | public abstract class TmfTimestamp implements ITmfTimestamp { |
c61fcbab MK |
32 | |
33 | /** | |
34 | * Default implementation of the tmf timestamp. We want this to be hidden. | |
35 | * | |
36 | * @author Matthew Khouzam | |
37 | * | |
38 | */ | |
39 | private static final class Impl extends TmfTimestamp { | |
40 | ||
41 | // ------------------------------------------------------------------------ | |
42 | // Attributes | |
43 | // ------------------------------------------------------------------------ | |
44 | ||
45 | /** | |
46 | * The timestamp raw value (mantissa) | |
47 | */ | |
48 | private final long fValue; | |
49 | ||
50 | /** | |
51 | * The timestamp scale (magnitude) | |
52 | */ | |
53 | private final int fScale; | |
54 | ||
55 | // ------------------------------------------------------------------------ | |
56 | // Constructors | |
57 | // ------------------------------------------------------------------------ | |
58 | ||
59 | /** | |
60 | * Full constructor | |
61 | * | |
62 | * @param value | |
63 | * the timestamp value | |
64 | * @param scale | |
65 | * the timestamp scale | |
66 | */ | |
67 | public Impl(final long value, final int scale) { | |
68 | fValue = value; | |
69 | fScale = scale; | |
70 | } | |
71 | ||
72 | @Override | |
73 | public long getValue() { | |
74 | return fValue; | |
75 | } | |
76 | ||
77 | @Override | |
78 | public int getScale() { | |
79 | return fScale; | |
80 | } | |
81 | } | |
82 | ||
d7dbf09a | 83 | /** |
b2c971ec MK |
84 | * Create a timestamp. |
85 | * | |
86 | * @param value | |
87 | * the value in nanoseconds | |
88 | * @return the timestamp | |
89 | * @since 2.0 | |
d7dbf09a | 90 | */ |
b2c971ec MK |
91 | public static @NonNull ITmfTimestamp fromNanos(long value) { |
92 | return new TmfNanoTimestamp(value); | |
93 | } | |
d7dbf09a FC |
94 | |
95 | /** | |
b2c971ec MK |
96 | * Create a timestamp. |
97 | * | |
98 | * @param value | |
99 | * the value in microseconds | |
100 | * @return the timestamp | |
101 | * @since 2.0 | |
d7dbf09a | 102 | */ |
b2c971ec MK |
103 | public static @NonNull ITmfTimestamp fromMicros(long value) { |
104 | return create(value, ITmfTimestamp.MICROSECOND_SCALE); | |
105 | } | |
8c8bf09f ASL |
106 | |
107 | /** | |
b2c971ec MK |
108 | * Create a timestamp. |
109 | * | |
110 | * @param value | |
111 | * the value in milliseconds | |
112 | * @return the timestamp | |
113 | * @since 2.0 | |
8c8bf09f | 114 | */ |
b2c971ec MK |
115 | public static @NonNull ITmfTimestamp fromMillis(long value) { |
116 | return create(value, ITmfTimestamp.MILLISECOND_SCALE); | |
8c8bf09f ASL |
117 | } |
118 | ||
1f506a43 | 119 | /** |
b2c971ec | 120 | * Create a timestamp. |
5179fc01 | 121 | * |
065cc19b | 122 | * @param value |
b2c971ec MK |
123 | * the value in seconds |
124 | * @return the timestamp | |
125 | * @since 2.0 | |
1f506a43 | 126 | */ |
b2c971ec MK |
127 | public static @NonNull ITmfTimestamp fromSeconds(long value) { |
128 | return new TmfSecondTimestamp(value); | |
8c8bf09f ASL |
129 | } |
130 | ||
131 | /** | |
b2c971ec | 132 | * Create a timestamp. |
f8177ba2 | 133 | * |
b2c971ec MK |
134 | * @param bufferIn |
135 | * the byte buffer to read the timestamp from. | |
136 | * @return the timestamp | |
137 | * @since 2.0 | |
8c8bf09f | 138 | */ |
b2c971ec MK |
139 | public static @NonNull ITmfTimestamp create(ByteBuffer bufferIn) { |
140 | return create(bufferIn.getLong(), bufferIn.getInt()); | |
8c8bf09f ASL |
141 | } |
142 | ||
143 | /** | |
b2c971ec | 144 | * Create a timestamp. |
f8177ba2 | 145 | * |
b2c971ec MK |
146 | * @param value |
147 | * the value in time, the unit is specified by the scale | |
148 | * @param scale | |
149 | * the scale of the timestamp with respect to seconds, so a | |
150 | * nanosecond would be -9 (10e-9) and a megasecond would be 6 | |
151 | * (10e6) | |
152 | * @return the timestamp | |
153 | * @since 2.0 | |
8c8bf09f | 154 | */ |
b2c971ec MK |
155 | public static @NonNull ITmfTimestamp create(long value, int scale) { |
156 | if (scale == ITmfTimestamp.NANOSECOND_SCALE) { | |
157 | return fromNanos(value); | |
b9e37ffd | 158 | } |
b2c971ec MK |
159 | if (scale == ITmfTimestamp.SECOND_SCALE) { |
160 | return fromSeconds(value); | |
161 | } | |
c61fcbab MK |
162 | if (value == 0) { |
163 | return ZERO; | |
164 | } | |
b2c971ec | 165 | return createOther(value, scale); |
8c8bf09f | 166 | } |
e73a4ba5 GB |
167 | |
168 | /** | |
b2c971ec | 169 | * Write the time stamp to the ByteBuffer so that it can be saved to disk. |
e73a4ba5 | 170 | * |
b2c971ec MK |
171 | * @param bufferOut |
172 | * the buffer to write to | |
173 | * @param ts | |
174 | * the timestamp to write | |
175 | * @since 2.0 | |
e73a4ba5 | 176 | */ |
b2c971ec MK |
177 | public static void serialize(ByteBuffer bufferOut, ITmfTimestamp ts) { |
178 | bufferOut.putLong(ts.getValue()); | |
179 | bufferOut.putInt(ts.getScale()); | |
180 | } | |
181 | ||
182 | private static @NonNull ITmfTimestamp createOther(long value, int scale) { | |
c61fcbab | 183 | return new Impl(value, scale); |
e73a4ba5 | 184 | } |
8c8bf09f | 185 | |
5179fc01 | 186 | // ------------------------------------------------------------------------ |
b2c971ec | 187 | // Constants |
5179fc01 | 188 | // ------------------------------------------------------------------------ |
8c8bf09f | 189 | |
032ecd45 | 190 | /** |
c61fcbab MK |
191 | * Zero - a zero time constant. The value is zero, so this allows some |
192 | * interesting simplifications. | |
032ecd45 | 193 | */ |
c61fcbab MK |
194 | public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp() { |
195 | @Override | |
196 | public long getValue() { | |
197 | return 0; | |
198 | } | |
199 | ||
200 | @Override | |
201 | public int getScale() { | |
202 | return 0; | |
203 | } | |
204 | ||
205 | @Override | |
206 | public @NonNull ITmfTimestamp normalize(long offset, int scale) { | |
207 | if (offset == 0) { | |
208 | return this; | |
209 | } | |
210 | return create(offset, scale); | |
211 | } | |
212 | ||
213 | @Override | |
214 | public int compareTo(ITmfTimestamp ts) { | |
215 | return Long.compare(0, ts.getValue()); | |
216 | } | |
217 | }; | |
032ecd45 | 218 | |
b2c971ec | 219 | /** |
c61fcbab | 220 | * The beginning of time will be lesser than any other timestamp |
b2c971ec | 221 | */ |
c61fcbab MK |
222 | public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp() { |
223 | @Override | |
224 | public long getValue() { | |
225 | return Long.MIN_VALUE; | |
226 | } | |
227 | ||
228 | @Override | |
229 | public int getScale() { | |
230 | return Integer.MAX_VALUE; | |
231 | } | |
232 | ||
233 | @Override | |
234 | public int compareTo(ITmfTimestamp other) { | |
59d8646c | 235 | if (equals(other)) { |
c61fcbab MK |
236 | return 0; |
237 | } | |
238 | return -1; | |
239 | } | |
240 | ||
241 | @Override | |
242 | public ITmfTimestamp normalize(long offset, int scale) { | |
243 | return this; | |
244 | } | |
245 | ||
246 | @Override | |
247 | public boolean equals(Object other) { | |
248 | return this == other; | |
249 | } | |
250 | }; | |
8c8bf09f | 251 | |
b2c971ec | 252 | /** |
c61fcbab | 253 | * The end of time will be greater than any other timestamp |
b2c971ec | 254 | */ |
c61fcbab MK |
255 | public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp() { |
256 | @Override | |
257 | public long getValue() { | |
258 | return Long.MAX_VALUE; | |
259 | } | |
260 | ||
261 | @Override | |
262 | public int getScale() { | |
263 | return Integer.MAX_VALUE; | |
264 | } | |
265 | ||
266 | @Override | |
267 | public int compareTo(ITmfTimestamp other) { | |
268 | if (equals(other) == true) { | |
269 | return 0; | |
270 | } | |
271 | return 1; | |
272 | } | |
273 | ||
274 | @Override | |
275 | public ITmfTimestamp normalize(long offset, int scale) { | |
276 | return this; | |
277 | } | |
278 | ||
279 | @Override | |
280 | public boolean equals(Object other) { | |
281 | return this == other; | |
282 | } | |
283 | }; | |
b2c971ec MK |
284 | |
285 | // ------------------------------------------------------------------------ | |
286 | // ITmfTimestamp | |
287 | // ------------------------------------------------------------------------ | |
8c8bf09f | 288 | |
b2c971ec MK |
289 | /** |
290 | * Scaling factors to help scale | |
291 | * | |
292 | * @since 2.0 | |
293 | */ | |
294 | protected static final long SCALING_FACTORS[] = new long[] { | |
9e925522 MK |
295 | 1L, |
296 | 10L, | |
297 | 100L, | |
298 | 1000L, | |
299 | 10000L, | |
300 | 100000L, | |
301 | 1000000L, | |
302 | 10000000L, | |
303 | 100000000L, | |
304 | 1000000000L, | |
305 | 10000000000L, | |
306 | 100000000000L, | |
307 | 1000000000000L, | |
308 | 10000000000000L, | |
309 | 100000000000000L, | |
310 | 1000000000000000L, | |
311 | 10000000000000000L, | |
312 | 100000000000000000L, | |
313 | 1000000000000000000L, | |
5179fc01 | 314 | }; |
4ab33d2b | 315 | |
d7dbf09a | 316 | @Override |
0316808c | 317 | public ITmfTimestamp normalize(final long offset, final int scale) { |
8c8bf09f | 318 | |
9e925522 | 319 | long value = getValue(); |
8c8bf09f | 320 | |
5179fc01 | 321 | // Handle the trivial case |
9e925522 | 322 | if (getScale() == scale && offset == 0) { |
4593bd5b | 323 | return this; |
b9e37ffd | 324 | } |
f8177ba2 | 325 | |
9e925522 | 326 | if (value == 0) { |
b2c971ec | 327 | return create(offset, scale); |
9e925522 MK |
328 | } |
329 | ||
5179fc01 | 330 | // First, scale the timestamp |
9e925522 MK |
331 | if (getScale() != scale) { |
332 | final int scaleDiff = Math.abs(getScale() - scale); | |
b2c971ec | 333 | if (scaleDiff >= SCALING_FACTORS.length) { |
9e925522 MK |
334 | if (getScale() < scale) { |
335 | value = 0; | |
336 | } else { | |
337 | value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; | |
338 | } | |
8c8bf09f | 339 | } else { |
b2c971ec | 340 | final long scalingFactor = SCALING_FACTORS[scaleDiff]; |
9e925522 MK |
341 | if (getScale() < scale) { |
342 | value /= scalingFactor; | |
343 | } else { | |
16dd744f | 344 | value = SaturatedArithmetic.multiply(scalingFactor, value); |
9e925522 | 345 | } |
8c8bf09f ASL |
346 | } |
347 | } | |
348 | ||
16dd744f | 349 | value = SaturatedArithmetic.add(value, offset); |
023761c4 | 350 | |
b2c971ec | 351 | return create(value, scale); |
8c8bf09f ASL |
352 | } |
353 | ||
d7dbf09a | 354 | @Override |
065cc19b | 355 | public ITmfTimestamp getDelta(final ITmfTimestamp ts) { |
b2c971ec MK |
356 | final int scale = getScale(); |
357 | final ITmfTimestamp nts = ts.normalize(0, scale); | |
358 | final long value = getValue() - nts.getValue(); | |
359 | return new TmfTimestampDelta(value, scale); | |
065cc19b AM |
360 | } |
361 | ||
362 | @Override | |
363 | public boolean intersects(TmfTimeRange range) { | |
364 | if (this.compareTo(range.getStartTime()) >= 0 && | |
365 | this.compareTo(range.getEndTime()) <= 0) { | |
366 | return true; | |
367 | } | |
368 | return false; | |
369 | } | |
370 | ||
371 | // ------------------------------------------------------------------------ | |
372 | // Comparable | |
373 | // ------------------------------------------------------------------------ | |
023761c4 | 374 | |
065cc19b AM |
375 | @Override |
376 | public int compareTo(final ITmfTimestamp ts) { | |
b2c971ec MK |
377 | long value = getValue(); |
378 | int scale = getScale(); | |
9e925522 MK |
379 | // Check the corner cases (we can't use equals() because it uses |
380 | // compareTo()...) | |
c61fcbab | 381 | if (BIG_BANG.equals(ts)) { |
b9e37ffd FC |
382 | return 1; |
383 | } | |
c61fcbab MK |
384 | |
385 | if (BIG_CRUNCH.equals(ts)) { | |
5179fc01 | 386 | return -1; |
b9e37ffd | 387 | } |
c61fcbab MK |
388 | |
389 | if (this == ts || isIdentical(this, ts)) { | |
390 | return 0; | |
b9e37ffd | 391 | } |
5179fc01 | 392 | |
c61fcbab MK |
393 | if (scale == ts.getScale()) { |
394 | if (ts.getValue() == Long.MIN_VALUE) { | |
5179fc01 | 395 | return 1; |
b9e37ffd | 396 | } |
16dd744f | 397 | final long delta = SaturatedArithmetic.add(getValue(), -ts.getValue()); |
c61fcbab MK |
398 | return Long.compare(delta, 0); |
399 | } | |
400 | final ITmfTimestamp largerScale = (scale > ts.getScale()) ? this : ts; | |
401 | final ITmfTimestamp smallerScale = (scale < ts.getScale()) ? this : ts; | |
402 | ||
403 | final ITmfTimestamp nts = largerScale.normalize(0, smallerScale.getScale()); | |
404 | if (hasSaturated(largerScale, nts)) { | |
405 | // We've saturated largerScale. | |
406 | if (smallerScale.getScale() == scale) { | |
407 | return Long.compare(0, nts.getValue()); | |
408 | } | |
409 | return Long.compare(nts.getValue(), 0); | |
5179fc01 | 410 | } |
c61fcbab MK |
411 | if (smallerScale.getScale() == scale) { |
412 | return Long.compare(value, nts.getValue()); | |
413 | } | |
414 | return Long.compare(nts.getValue(), smallerScale.getValue()); | |
415 | } | |
416 | ||
417 | private static boolean hasSaturated(final ITmfTimestamp ts, final ITmfTimestamp nts) { | |
418 | return (nts.getValue() == 0 && ts.getValue() != 0) || !isIdentical(ts, nts) && ((nts.getValue() == Long.MAX_VALUE) || (nts.getValue() == Long.MIN_VALUE)); | |
419 | } | |
420 | ||
421 | private static boolean isIdentical(final ITmfTimestamp ts, final ITmfTimestamp nts) { | |
422 | return ts.getValue() == nts.getValue() && ts.getScale() == nts.getScale(); | |
9e925522 MK |
423 | } |
424 | ||
9e925522 MK |
425 | /** |
426 | * Saturated addition. It will not overflow but instead clamp the result to | |
427 | * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}. | |
428 | * | |
429 | * @param left | |
430 | * The left long to add | |
431 | * @param right | |
432 | * The right long to add | |
433 | * @return The saturated addition result. The mathematical, not Java version | |
434 | * of Min(Max(MIN_VALUE, left+right), MAX_VALUE). | |
435 | * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic"> | |
436 | * Saturation arithmetic</a> | |
437 | * @since 2.0 | |
16dd744f | 438 | * @deprecated use {@link SaturatedArithmetic#add(long, long)} instead |
9e925522 | 439 | */ |
16dd744f | 440 | @Deprecated |
9e925522 | 441 | protected static final long saturatedAdd(final long left, final long right) { |
16dd744f | 442 | return SaturatedArithmetic.add(left, right); |
9e925522 MK |
443 | } |
444 | ||
8c8bf09f | 445 | |
5179fc01 | 446 | // ------------------------------------------------------------------------ |
cbd4ad82 | 447 | // Object |
5179fc01 | 448 | // ------------------------------------------------------------------------ |
28b94d61 | 449 | |
8c8bf09f | 450 | @Override |
cbd4ad82 | 451 | public int hashCode() { |
5179fc01 FC |
452 | final int prime = 31; |
453 | int result = 1; | |
b2c971ec MK |
454 | final long value = getValue(); |
455 | result = prime * result + (int) (value ^ (value >>> 32)); | |
456 | result = prime * result + getScale(); | |
cbd4ad82 FC |
457 | return result; |
458 | } | |
459 | ||
5179fc01 | 460 | @Override |
085d898f | 461 | public boolean equals(final Object other) { |
b9e37ffd | 462 | if (this == other) { |
5179fc01 | 463 | return true; |
b9e37ffd FC |
464 | } |
465 | if (other == null) { | |
5179fc01 | 466 | return false; |
b9e37ffd | 467 | } |
065cc19b | 468 | if (!(other instanceof ITmfTimestamp)) { |
5179fc01 | 469 | return false; |
b9e37ffd | 470 | } |
065cc19b AM |
471 | /* We allow comparing with other types of *I*TmfTimestamp though */ |
472 | final ITmfTimestamp ts = (ITmfTimestamp) other; | |
c61fcbab MK |
473 | if (getScale() == ts.getScale()) { |
474 | return getValue() == ts.getValue(); | |
475 | } | |
065cc19b | 476 | return (compareTo(ts) == 0); |
8c8bf09f ASL |
477 | } |
478 | ||
1f506a43 FC |
479 | @Override |
480 | public String toString() { | |
f8177ba2 FC |
481 | return toString(TmfTimestampFormat.getDefaulTimeFormat()); |
482 | } | |
483 | ||
f8177ba2 FC |
484 | @Override |
485 | public String toString(final TmfTimestampFormat format) { | |
486 | try { | |
16801c72 | 487 | return format.format(toNanos()); |
9e925522 | 488 | } catch (ArithmeticException e) { |
f8177ba2 FC |
489 | return format.format(0); |
490 | } | |
ff4ed569 | 491 | } |
023761c4 | 492 | } |