1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal
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
10 * Francois Chouinard - Initial API and implementation, refactoring and updates
11 * Thomas Gatterweh - Updated scaling / synchronization
12 * Geneviève Bastien - Added copy constructor with new value
13 * Alexandre Montplaisir - Removed concept of precision
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.tmf
.core
.timestamp
;
18 import java
.nio
.ByteBuffer
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.timestamp
.TmfNanoTimestamp
;
22 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.timestamp
.TmfSecondTimestamp
;
25 * A generic timestamp implementation. The timestamp is represented by the tuple
26 * { value, scale, precision }.
28 * @author Francois Chouinard
30 public abstract class TmfTimestamp
implements ITmfTimestamp
{
33 * Default implementation of the tmf timestamp. We want this to be hidden.
35 * @author Matthew Khouzam
38 private static final class Impl
extends TmfTimestamp
{
40 // ------------------------------------------------------------------------
42 // ------------------------------------------------------------------------
45 * The timestamp raw value (mantissa)
47 private final long fValue
;
50 * The timestamp scale (magnitude)
52 private final int fScale
;
54 // ------------------------------------------------------------------------
56 // ------------------------------------------------------------------------
66 public Impl(final long value
, final int scale
) {
72 public long getValue() {
77 public int getScale() {
86 * the value in nanoseconds
87 * @return the timestamp
90 public static @NonNull ITmfTimestamp
fromNanos(long value
) {
91 return new TmfNanoTimestamp(value
);
98 * the value in microseconds
99 * @return the timestamp
102 public static @NonNull ITmfTimestamp
fromMicros(long value
) {
103 return create(value
, ITmfTimestamp
.MICROSECOND_SCALE
);
107 * Create a timestamp.
110 * the value in milliseconds
111 * @return the timestamp
114 public static @NonNull ITmfTimestamp
fromMillis(long value
) {
115 return create(value
, ITmfTimestamp
.MILLISECOND_SCALE
);
119 * Create a timestamp.
122 * the value in seconds
123 * @return the timestamp
126 public static @NonNull ITmfTimestamp
fromSeconds(long value
) {
127 return new TmfSecondTimestamp(value
);
131 * Create a timestamp.
134 * the byte buffer to read the timestamp from.
135 * @return the timestamp
138 public static @NonNull ITmfTimestamp
create(ByteBuffer bufferIn
) {
139 return create(bufferIn
.getLong(), bufferIn
.getInt());
143 * Create a timestamp.
146 * the value in time, the unit is specified by the 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
151 * @return the timestamp
154 public static @NonNull ITmfTimestamp
create(long value
, int scale
) {
155 if (scale
== ITmfTimestamp
.NANOSECOND_SCALE
) {
156 return fromNanos(value
);
158 if (scale
== ITmfTimestamp
.SECOND_SCALE
) {
159 return fromSeconds(value
);
164 return createOther(value
, scale
);
168 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
171 * the buffer to write to
173 * the timestamp to write
176 public static void serialize(ByteBuffer bufferOut
, ITmfTimestamp ts
) {
177 bufferOut
.putLong(ts
.getValue());
178 bufferOut
.putInt(ts
.getScale());
181 private static @NonNull ITmfTimestamp
createOther(long value
, int scale
) {
182 return new Impl(value
, scale
);
185 // ------------------------------------------------------------------------
187 // ------------------------------------------------------------------------
190 * Zero - a zero time constant. The value is zero, so this allows some
191 * interesting simplifications.
193 public static final @NonNull ITmfTimestamp ZERO
= new TmfTimestamp() {
195 public long getValue() {
200 public int getScale() {
205 public @NonNull ITmfTimestamp
normalize(long offset
, int scale
) {
209 return create(offset
, scale
);
213 public int compareTo(ITmfTimestamp ts
) {
214 return Long
.compare(0, ts
.getValue());
219 * The beginning of time will be lesser than any other timestamp
221 public static final @NonNull ITmfTimestamp BIG_BANG
= new TmfTimestamp() {
223 public long getValue() {
224 return Long
.MIN_VALUE
;
228 public int getScale() {
229 return Integer
.MAX_VALUE
;
233 public int compareTo(ITmfTimestamp other
) {
234 if (equals(other
) == true) {
241 public ITmfTimestamp
normalize(long offset
, int scale
) {
246 public boolean equals(Object other
) {
247 return this == other
;
252 * The end of time will be greater than any other timestamp
254 public static final @NonNull ITmfTimestamp BIG_CRUNCH
= new TmfTimestamp() {
256 public long getValue() {
257 return Long
.MAX_VALUE
;
261 public int getScale() {
262 return Integer
.MAX_VALUE
;
266 public int compareTo(ITmfTimestamp other
) {
267 if (equals(other
) == true) {
274 public ITmfTimestamp
normalize(long offset
, int scale
) {
279 public boolean equals(Object other
) {
280 return this == other
;
284 // ------------------------------------------------------------------------
286 // ------------------------------------------------------------------------
289 * Scaling factors to help scale
293 protected static final long SCALING_FACTORS
[] = new long[] {
312 1000000000000000000L,
316 public ITmfTimestamp
normalize(final long offset
, final int scale
) {
318 long value
= getValue();
320 // Handle the trivial case
321 if (getScale() == scale
&& offset
== 0) {
326 return create(offset
, scale
);
329 // First, scale the timestamp
330 if (getScale() != scale
) {
331 final int scaleDiff
= Math
.abs(getScale() - scale
);
332 if (scaleDiff
>= SCALING_FACTORS
.length
) {
333 if (getScale() < scale
) {
336 value
= value
> 0 ? Long
.MAX_VALUE
: Long
.MIN_VALUE
;
339 final long scalingFactor
= SCALING_FACTORS
[scaleDiff
];
340 if (getScale() < scale
) {
341 value
/= scalingFactor
;
343 value
= saturatedMult(scalingFactor
, value
);
348 value
= saturatedAdd(value
, offset
);
350 return create(value
, scale
);
354 public ITmfTimestamp
getDelta(final ITmfTimestamp ts
) {
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
);
362 public boolean intersects(TmfTimeRange range
) {
363 if (this.compareTo(range
.getStartTime()) >= 0 &&
364 this.compareTo(range
.getEndTime()) <= 0) {
370 // ------------------------------------------------------------------------
372 // ------------------------------------------------------------------------
375 public int compareTo(final ITmfTimestamp ts
) {
376 long value
= getValue();
377 int scale
= getScale();
378 // Check the corner cases (we can't use equals() because it uses
380 if (BIG_BANG
.equals(ts
)) {
384 if (BIG_CRUNCH
.equals(ts
)) {
388 if (this == ts
|| isIdentical(this, ts
)) {
392 if (scale
== ts
.getScale()) {
393 if (ts
.getValue() == Long
.MIN_VALUE
) {
396 final long delta
= saturatedAdd(getValue(), -ts
.getValue());
397 return Long
.compare(delta
, 0);
399 final ITmfTimestamp largerScale
= (scale
> ts
.getScale()) ?
this : ts
;
400 final ITmfTimestamp smallerScale
= (scale
< ts
.getScale()) ?
this : ts
;
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());
408 return Long
.compare(nts
.getValue(), 0);
410 if (smallerScale
.getScale() == scale
) {
411 return Long
.compare(value
, nts
.getValue());
413 return Long
.compare(nts
.getValue(), smallerScale
.getValue());
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
));
420 private static boolean isIdentical(final ITmfTimestamp ts
, final ITmfTimestamp nts
) {
421 return ts
.getValue() == nts
.getValue() && ts
.getScale() == nts
.getScale();
425 * Saturated multiplication. It will not overflow but instead clamp the
426 * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
429 * The left long to multiply
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>
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
);
446 * Saturated addition. It will not overflow but instead clamp the result to
447 * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
450 * The left long to add
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>
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
)) {
463 return Long
.MIN_VALUE
;
465 return Long
.MAX_VALUE
;
470 private static boolean sameSign(final long left
, final long right
) {
471 return (left ^ right
) >= 0;
474 // ------------------------------------------------------------------------
476 // ------------------------------------------------------------------------
479 public int hashCode() {
480 final int prime
= 31;
482 final long value
= getValue();
483 result
= prime
* result
+ (int) (value ^
(value
>>> 32));
484 result
= prime
* result
+ getScale();
489 public boolean equals(final Object other
) {
496 if (!(other
instanceof ITmfTimestamp
)) {
499 /* We allow comparing with other types of *I*TmfTimestamp though */
500 final ITmfTimestamp ts
= (ITmfTimestamp
) other
;
501 if (getScale() == ts
.getScale()) {
502 return getValue() == ts
.getValue();
504 return (compareTo(ts
) == 0);
508 public String
toString() {
509 return toString(TmfTimestampFormat
.getDefaulTimeFormat());
513 public String
toString(final TmfTimestampFormat format
) {
515 return format
.format(toNanos());
516 } catch (ArithmeticException e
) {
517 return format
.format(0);