common.core: move saturated math to common core
[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 20import org.eclipse.jdt.annotation.NonNull;
16dd744f 21import org.eclipse.tracecompass.common.core.math.SaturatedArithmetic;
dd21f749
MK
22import org.eclipse.tracecompass.internal.tmf.core.timestamp.TmfNanoTimestamp;
23import 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 31public 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}
This page took 0.151849 seconds and 5 git commands to generate.