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
.common
.core
.math
.SaturatedArithmetic
;
22 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.timestamp
.TmfNanoTimestamp
;
23 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.timestamp
.TmfSecondTimestamp
;
26 * A generic timestamp implementation. The timestamp is represented by the tuple
27 * { value, scale, precision }.
29 * @author Francois Chouinard
31 public abstract class TmfTimestamp
implements ITmfTimestamp
{
34 * Default implementation of the tmf timestamp. We want this to be hidden.
36 * @author Matthew Khouzam
39 private static final class Impl
extends TmfTimestamp
{
41 // ------------------------------------------------------------------------
43 // ------------------------------------------------------------------------
46 * The timestamp raw value (mantissa)
48 private final long fValue
;
51 * The timestamp scale (magnitude)
53 private final int fScale
;
55 // ------------------------------------------------------------------------
57 // ------------------------------------------------------------------------
67 public Impl(final long value
, final int scale
) {
73 public long getValue() {
78 public int getScale() {
87 * the value in nanoseconds
88 * @return the timestamp
91 public static @NonNull ITmfTimestamp
fromNanos(long value
) {
92 return new TmfNanoTimestamp(value
);
99 * the value in microseconds
100 * @return the timestamp
103 public static @NonNull ITmfTimestamp
fromMicros(long value
) {
104 return create(value
, ITmfTimestamp
.MICROSECOND_SCALE
);
108 * Create a timestamp.
111 * the value in milliseconds
112 * @return the timestamp
115 public static @NonNull ITmfTimestamp
fromMillis(long value
) {
116 return create(value
, ITmfTimestamp
.MILLISECOND_SCALE
);
120 * Create a timestamp.
123 * the value in seconds
124 * @return the timestamp
127 public static @NonNull ITmfTimestamp
fromSeconds(long value
) {
128 return new TmfSecondTimestamp(value
);
132 * Create a timestamp.
135 * the byte buffer to read the timestamp from.
136 * @return the timestamp
139 public static @NonNull ITmfTimestamp
create(ByteBuffer bufferIn
) {
140 return create(bufferIn
.getLong(), bufferIn
.getInt());
144 * Create a timestamp.
147 * the value in time, the unit is specified by the 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
152 * @return the timestamp
155 public static @NonNull ITmfTimestamp
create(long value
, int scale
) {
156 if (scale
== ITmfTimestamp
.NANOSECOND_SCALE
) {
157 return fromNanos(value
);
159 if (scale
== ITmfTimestamp
.SECOND_SCALE
) {
160 return fromSeconds(value
);
165 return createOther(value
, scale
);
169 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
172 * the buffer to write to
174 * the timestamp to write
177 public static void serialize(ByteBuffer bufferOut
, ITmfTimestamp ts
) {
178 bufferOut
.putLong(ts
.getValue());
179 bufferOut
.putInt(ts
.getScale());
182 private static @NonNull ITmfTimestamp
createOther(long value
, int scale
) {
183 return new Impl(value
, scale
);
186 // ------------------------------------------------------------------------
188 // ------------------------------------------------------------------------
191 * Zero - a zero time constant. The value is zero, so this allows some
192 * interesting simplifications.
194 public static final @NonNull ITmfTimestamp ZERO
= new TmfTimestamp() {
196 public long getValue() {
201 public int getScale() {
206 public @NonNull ITmfTimestamp
normalize(long offset
, int scale
) {
210 return create(offset
, scale
);
214 public int compareTo(ITmfTimestamp ts
) {
215 return Long
.compare(0, ts
.getValue());
220 * The beginning of time will be lesser than any other timestamp
222 public static final @NonNull ITmfTimestamp BIG_BANG
= new TmfTimestamp() {
224 public long getValue() {
225 return Long
.MIN_VALUE
;
229 public int getScale() {
230 return Integer
.MAX_VALUE
;
234 public int compareTo(ITmfTimestamp other
) {
242 public ITmfTimestamp
normalize(long offset
, int scale
) {
247 public boolean equals(Object other
) {
248 return this == other
;
253 * The end of time will be greater than any other timestamp
255 public static final @NonNull ITmfTimestamp BIG_CRUNCH
= new TmfTimestamp() {
257 public long getValue() {
258 return Long
.MAX_VALUE
;
262 public int getScale() {
263 return Integer
.MAX_VALUE
;
267 public int compareTo(ITmfTimestamp other
) {
268 if (equals(other
) == true) {
275 public ITmfTimestamp
normalize(long offset
, int scale
) {
280 public boolean equals(Object other
) {
281 return this == other
;
285 // ------------------------------------------------------------------------
287 // ------------------------------------------------------------------------
290 * Scaling factors to help scale
294 protected static final long SCALING_FACTORS
[] = new long[] {
313 1000000000000000000L,
317 public ITmfTimestamp
normalize(final long offset
, final int scale
) {
319 long value
= getValue();
321 // Handle the trivial case
322 if (getScale() == scale
&& offset
== 0) {
327 return create(offset
, scale
);
330 // First, scale the timestamp
331 if (getScale() != scale
) {
332 final int scaleDiff
= Math
.abs(getScale() - scale
);
333 if (scaleDiff
>= SCALING_FACTORS
.length
) {
334 if (getScale() < scale
) {
337 value
= value
> 0 ? Long
.MAX_VALUE
: Long
.MIN_VALUE
;
340 final long scalingFactor
= SCALING_FACTORS
[scaleDiff
];
341 if (getScale() < scale
) {
342 value
/= scalingFactor
;
344 value
= SaturatedArithmetic
.multiply(scalingFactor
, value
);
349 value
= SaturatedArithmetic
.add(value
, offset
);
351 return create(value
, scale
);
355 public ITmfTimestamp
getDelta(final ITmfTimestamp ts
) {
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
);
363 public boolean intersects(TmfTimeRange range
) {
364 if (this.compareTo(range
.getStartTime()) >= 0 &&
365 this.compareTo(range
.getEndTime()) <= 0) {
371 // ------------------------------------------------------------------------
373 // ------------------------------------------------------------------------
376 public int compareTo(final ITmfTimestamp ts
) {
377 long value
= getValue();
378 int scale
= getScale();
379 // Check the corner cases (we can't use equals() because it uses
381 if (BIG_BANG
.equals(ts
)) {
385 if (BIG_CRUNCH
.equals(ts
)) {
389 if (this == ts
|| isIdentical(this, ts
)) {
393 if (scale
== ts
.getScale()) {
394 if (ts
.getValue() == Long
.MIN_VALUE
) {
397 final long delta
= SaturatedArithmetic
.add(getValue(), -ts
.getValue());
398 return Long
.compare(delta
, 0);
400 final ITmfTimestamp largerScale
= (scale
> ts
.getScale()) ?
this : ts
;
401 final ITmfTimestamp smallerScale
= (scale
< ts
.getScale()) ?
this : ts
;
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());
409 return Long
.compare(nts
.getValue(), 0);
411 if (smallerScale
.getScale() == scale
) {
412 return Long
.compare(value
, nts
.getValue());
414 return Long
.compare(nts
.getValue(), smallerScale
.getValue());
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
));
421 private static boolean isIdentical(final ITmfTimestamp ts
, final ITmfTimestamp nts
) {
422 return ts
.getValue() == nts
.getValue() && ts
.getScale() == nts
.getScale();
426 * Saturated addition. It will not overflow but instead clamp the result to
427 * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
430 * The left long to add
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>
438 * @deprecated use {@link SaturatedArithmetic#add(long, long)} instead
441 protected static final long saturatedAdd(final long left
, final long right
) {
442 return SaturatedArithmetic
.add(left
, right
);
446 // ------------------------------------------------------------------------
448 // ------------------------------------------------------------------------
451 public int hashCode() {
452 final int prime
= 31;
454 final long value
= getValue();
455 result
= prime
* result
+ (int) (value ^
(value
>>> 32));
456 result
= prime
* result
+ getScale();
461 public boolean equals(final Object other
) {
468 if (!(other
instanceof ITmfTimestamp
)) {
471 /* We allow comparing with other types of *I*TmfTimestamp though */
472 final ITmfTimestamp ts
= (ITmfTimestamp
) other
;
473 if (getScale() == ts
.getScale()) {
474 return getValue() == ts
.getValue();
476 return (compareTo(ts
) == 0);
480 public String
toString() {
481 return toString(TmfTimestampFormat
.getDefaulTimeFormat());
485 public String
toString(final TmfTimestampFormat format
) {
487 return format
.format(toNanos());
488 } catch (ArithmeticException e
) {
489 return format
.format(0);