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