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