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
;
23 * A generic timestamp implementation. The timestamp is represented by the tuple
24 * { value, scale, precision }. By default, timestamps are scaled in seconds.
26 * @author Francois Chouinard
28 public class TmfTimestamp
implements ITmfTimestamp
{
30 // ------------------------------------------------------------------------
32 // ------------------------------------------------------------------------
35 * The beginning of time
37 public static final @NonNull ITmfTimestamp BIG_BANG
= new TmfTimestamp(Long
.MIN_VALUE
, Integer
.MAX_VALUE
);
42 public static final @NonNull ITmfTimestamp BIG_CRUNCH
= new TmfTimestamp(Long
.MAX_VALUE
, Integer
.MAX_VALUE
);
47 public static final @NonNull ITmfTimestamp ZERO
= new TmfTimestamp(0, 0);
49 // ------------------------------------------------------------------------
51 // ------------------------------------------------------------------------
54 * The timestamp raw value (mantissa)
56 private final long fValue
;
59 * The timestamp scale (magnitude)
61 private final int fScale
;
63 // ------------------------------------------------------------------------
65 // ------------------------------------------------------------------------
70 public TmfTimestamp() {
71 this(0, ITmfTimestamp
.SECOND_SCALE
);
75 * Simple constructor (scale = 0)
80 public TmfTimestamp(final long value
) {
81 this(value
, ITmfTimestamp
.SECOND_SCALE
);
92 public TmfTimestamp(final long value
, final int scale
) {
101 * the timestamp to copy
103 public TmfTimestamp(final ITmfTimestamp timestamp
) {
104 if (timestamp
== null) {
105 throw new IllegalArgumentException();
107 fValue
= timestamp
.getValue();
108 fScale
= timestamp
.getScale();
112 * Copies a timestamp but with a new time value
115 * The timestamp to copy
117 * The value the new timestamp will have
119 public TmfTimestamp(ITmfTimestamp timestamp
, long newvalue
) {
120 if (timestamp
== null) {
121 throw new IllegalArgumentException();
124 fScale
= timestamp
.getScale();
127 // ------------------------------------------------------------------------
129 // ------------------------------------------------------------------------
132 * Construct the timestamp from the ByteBuffer.
135 * the buffer to read from
137 public TmfTimestamp(ByteBuffer bufferIn
) {
138 this(bufferIn
.getLong(), bufferIn
.getInt());
142 public long getValue() {
147 public int getScale() {
151 private static final long scalingFactors
[] = new long[] {
170 1000000000000000000L,
174 public ITmfTimestamp
normalize(final long offset
, final int scale
) {
176 long value
= getValue();
178 // Handle the trivial case
179 if (getScale() == scale
&& offset
== 0) {
183 // In case of big bang and big crunch just return this (no need to
185 if (this.equals(BIG_BANG
) || this.equals(BIG_CRUNCH
)) {
190 return new TmfTimestamp(offset
, scale
);
193 // First, scale the timestamp
194 if (getScale() != scale
) {
195 final int scaleDiff
= Math
.abs(getScale() - scale
);
196 if (scaleDiff
>= scalingFactors
.length
) {
197 if (getScale() < scale
) {
200 value
= value
> 0 ? Long
.MAX_VALUE
: Long
.MIN_VALUE
;
203 final long scalingFactor
= scalingFactors
[scaleDiff
];
204 if (getScale() < scale
) {
205 value
/= scalingFactor
;
207 value
= saturatedMult(scalingFactor
, value
);
212 value
= saturatedAdd(value
, offset
);
214 return new TmfTimestamp(value
, scale
);
218 public ITmfTimestamp
getDelta(final ITmfTimestamp ts
) {
219 final ITmfTimestamp nts
= ts
.normalize(0, fScale
);
220 final long value
= fValue
- nts
.getValue();
221 return new TmfTimestampDelta(value
, fScale
);
225 public boolean intersects(TmfTimeRange range
) {
226 if (this.compareTo(range
.getStartTime()) >= 0 &&
227 this.compareTo(range
.getEndTime()) <= 0) {
233 // ------------------------------------------------------------------------
235 // ------------------------------------------------------------------------
238 public int compareTo(final ITmfTimestamp ts
) {
239 // Check the corner cases (we can't use equals() because it uses
244 if (this == ts
|| (fValue
== ts
.getValue() && fScale
== ts
.getScale())) {
247 if ((fValue
== BIG_BANG
.getValue() && fScale
== BIG_BANG
.getScale()) || (ts
.getValue() == BIG_CRUNCH
.getValue() && ts
.getScale() == BIG_CRUNCH
.getScale())) {
250 if ((fValue
== BIG_CRUNCH
.getValue() && fScale
== BIG_CRUNCH
.getScale()) || (ts
.getValue() == BIG_BANG
.getValue() && ts
.getScale() == BIG_BANG
.getScale())) {
253 final ITmfTimestamp nts
= ts
.normalize(0, fScale
);
254 if ((nts
.getValue() == 0 && ts
.getValue() != 0) || (ts
.getValue() != Long
.MAX_VALUE
&& nts
.getValue() == Long
.MAX_VALUE
) || (ts
.getValue() != Long
.MIN_VALUE
&& nts
.getValue() == Long
.MIN_VALUE
)) {
255 // Scaling error. We can figure it out nonetheless.
257 // First, look at the sign of the mantissa
258 final long value
= ts
.getValue();
259 if (fValue
== 0 && value
== 0) {
262 if (fValue
< 0 && value
>= 0) {
265 if (fValue
>= 0 && value
< 0) {
269 // Otherwise, just compare the scales
270 final int scale
= ts
.getScale();
271 return (fScale
> scale
) ?
(fValue
>= 0) ?
1 : -1 : (fValue
>= 0) ?
-1 : 1;
273 final long delta
= fValue
- nts
.getValue();
274 return Long
.compare(delta
, 0);
278 * Saturated multiplication. It will not overflow but instead clamp the
279 * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
282 * The left long to multiply
284 * The right long to multiply
285 * @return The saturated multiplication result. The mathematical, not Java
286 * version of Min(Max(MIN_VALUE, left*right), MAX_VALUE).
287 * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
288 * Saturation arithmetic</a>
290 private static long saturatedMult(long left
, long right
) {
291 long retVal
= left
* right
;
292 if ((left
!= 0) && ((retVal
/ left
) != right
)) {
293 return (sameSign(left
, right
) ? Long
.MAX_VALUE
: Long
.MIN_VALUE
);
299 * Saturated addition. It will not overflow but instead clamp the result to
300 * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
303 * The left long to add
305 * The right long to add
306 * @return The saturated addition result. The mathematical, not Java version
307 * of Min(Max(MIN_VALUE, left+right), MAX_VALUE).
308 * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
309 * Saturation arithmetic</a>
312 protected static final long saturatedAdd(final long left
, final long right
) {
313 long retVal
= left
+ right
;
314 if (sameSign(left
, right
) && !sameSign(left
, retVal
)) {
316 return Long
.MIN_VALUE
;
318 return Long
.MAX_VALUE
;
323 private static boolean sameSign(final long left
, final long right
) {
324 return (left ^ right
) >= 0;
327 // ------------------------------------------------------------------------
329 // ------------------------------------------------------------------------
332 public int hashCode() {
333 final int prime
= 31;
335 result
= prime
* result
+ (int) (fValue ^
(fValue
>>> 32));
336 result
= prime
* result
+ fScale
;
341 public boolean equals(final Object other
) {
348 if (!(other
instanceof ITmfTimestamp
)) {
351 /* We allow comparing with other types of *I*TmfTimestamp though */
352 final ITmfTimestamp ts
= (ITmfTimestamp
) other
;
353 return (compareTo(ts
) == 0);
357 public String
toString() {
358 return toString(TmfTimestampFormat
.getDefaulTimeFormat());
362 public String
toString(final TmfTimestampFormat format
) {
364 return format
.format(toNanos());
365 } catch (ArithmeticException e
) {
366 return format
.format(0);
371 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
374 * the buffer to write to
376 public void serialize(ByteBuffer bufferOut
) {
377 bufferOut
.putLong(fValue
);
378 bufferOut
.putInt(fScale
);