tmf.core: fix timestamp normalization clamping
[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
MK
23 * A generic timestamp implementation. The timestamp is represented by the tuple
24 * { value, scale, precision }. By default, timestamps are scaled in seconds.
f8177ba2 25 *
b9e37ffd 26 * @author Francois Chouinard
8c8bf09f 27 */
4593bd5b 28public class TmfTimestamp implements ITmfTimestamp {
8c8bf09f 29
5179fc01 30 // ------------------------------------------------------------------------
8c8bf09f 31 // Constants
5179fc01 32 // ------------------------------------------------------------------------
8c8bf09f 33
d7dbf09a
FC
34 /**
35 * The beginning of time
36 */
9e925522 37 public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE);
d7dbf09a
FC
38
39 /**
40 * The end of time
41 */
9e925522 42 public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE);
085d898f 43
d7dbf09a
FC
44 /**
45 * Zero
46 */
9e925522 47 public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp(0, 0);
5179fc01
FC
48
49 // ------------------------------------------------------------------------
50 // Attributes
51 // ------------------------------------------------------------------------
8c8bf09f 52
d7dbf09a 53 /**
d96e9054 54 * The timestamp raw value (mantissa)
d7dbf09a 55 */
4593bd5b 56 private final long fValue;
d7dbf09a
FC
57
58 /**
59 * The timestamp scale (magnitude)
60 */
4593bd5b 61 private final int fScale;
d7dbf09a 62
5179fc01 63 // ------------------------------------------------------------------------
8c8bf09f 64 // Constructors
5179fc01 65 // ------------------------------------------------------------------------
8c8bf09f
ASL
66
67 /**
28b94d61 68 * Default constructor
8c8bf09f
ASL
69 */
70 public TmfTimestamp() {
065cc19b 71 this(0, ITmfTimestamp.SECOND_SCALE);
8c8bf09f
ASL
72 }
73
1f506a43 74 /**
065cc19b 75 * Simple constructor (scale = 0)
5179fc01 76 *
065cc19b
AM
77 * @param value
78 * the timestamp value
1f506a43 79 */
085d898f 80 public TmfTimestamp(final long value) {
065cc19b 81 this(value, ITmfTimestamp.SECOND_SCALE);
8c8bf09f
ASL
82 }
83
84 /**
5179fc01 85 * Full constructor
f8177ba2 86 *
065cc19b
AM
87 * @param value
88 * the timestamp value
89 * @param scale
90 * the timestamp scale
8c8bf09f 91 */
065cc19b 92 public TmfTimestamp(final long value, final int scale) {
8c8bf09f
ASL
93 fValue = value;
94 fScale = scale;
8c8bf09f
ASL
95 }
96
97 /**
28b94d61 98 * Copy constructor
f8177ba2 99 *
065cc19b
AM
100 * @param timestamp
101 * the timestamp to copy
8c8bf09f 102 */
085d898f 103 public TmfTimestamp(final ITmfTimestamp timestamp) {
b9e37ffd 104 if (timestamp == null) {
5179fc01 105 throw new IllegalArgumentException();
b9e37ffd 106 }
4df4581d 107 fValue = timestamp.getValue();
108 fScale = timestamp.getScale();
8c8bf09f 109 }
e73a4ba5
GB
110
111 /**
112 * Copies a timestamp but with a new time value
113 *
114 * @param timestamp
115 * The timestamp to copy
116 * @param newvalue
117 * The value the new timestamp will have
e73a4ba5
GB
118 */
119 public TmfTimestamp(ITmfTimestamp timestamp, long newvalue) {
120 if (timestamp == null) {
121 throw new IllegalArgumentException();
122 }
123 fValue = newvalue;
124 fScale = timestamp.getScale();
e73a4ba5 125 }
8c8bf09f 126
5179fc01
FC
127 // ------------------------------------------------------------------------
128 // ITmfTimestamp
129 // ------------------------------------------------------------------------
8c8bf09f 130
032ecd45
MAL
131 /**
132 * Construct the timestamp from the ByteBuffer.
133 *
134 * @param bufferIn
135 * the buffer to read from
032ecd45
MAL
136 */
137 public TmfTimestamp(ByteBuffer bufferIn) {
065cc19b 138 this(bufferIn.getLong(), bufferIn.getInt());
032ecd45
MAL
139 }
140
d7dbf09a 141 @Override
8c8bf09f
ASL
142 public long getValue() {
143 return fValue;
144 }
145
d7dbf09a 146 @Override
5179fc01 147 public int getScale() {
8c8bf09f
ASL
148 return fScale;
149 }
150
5179fc01 151 private static final long scalingFactors[] = new long[] {
9e925522
MK
152 1L,
153 10L,
154 100L,
155 1000L,
156 10000L,
157 100000L,
158 1000000L,
159 10000000L,
160 100000000L,
161 1000000000L,
162 10000000000L,
163 100000000000L,
164 1000000000000L,
165 10000000000000L,
166 100000000000000L,
167 1000000000000000L,
168 10000000000000000L,
169 100000000000000000L,
170 1000000000000000000L,
5179fc01 171 };
4ab33d2b 172
d7dbf09a 173 @Override
0316808c 174 public ITmfTimestamp normalize(final long offset, final int scale) {
8c8bf09f 175
9e925522 176 long value = getValue();
8c8bf09f 177
5179fc01 178 // Handle the trivial case
9e925522 179 if (getScale() == scale && offset == 0) {
4593bd5b 180 return this;
b9e37ffd 181 }
f8177ba2 182
9e925522
MK
183 // In case of big bang and big crunch just return this (no need to
184 // normalize)
e461c849
BH
185 if (this.equals(BIG_BANG) || this.equals(BIG_CRUNCH)) {
186 return this;
187 }
5179fc01 188
9e925522
MK
189 if (value == 0) {
190 return new TmfTimestamp(offset, scale);
191 }
192
5179fc01 193 // First, scale the timestamp
9e925522
MK
194 if (getScale() != scale) {
195 final int scaleDiff = Math.abs(getScale() - scale);
b9e37ffd 196 if (scaleDiff >= scalingFactors.length) {
9e925522
MK
197 if (getScale() < scale) {
198 value = 0;
199 } else {
200 value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
201 }
8c8bf09f 202 } else {
9e925522
MK
203 final long scalingFactor = scalingFactors[scaleDiff];
204 if (getScale() < scale) {
205 value /= scalingFactor;
206 } else {
207 value = saturatedMult(scalingFactor, value);
208 }
8c8bf09f
ASL
209 }
210 }
211
9e925522 212 value = saturatedAdd(value, offset);
023761c4 213
065cc19b 214 return new TmfTimestamp(value, scale);
8c8bf09f
ASL
215 }
216
d7dbf09a 217 @Override
065cc19b
AM
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);
222 }
223
224 @Override
225 public boolean intersects(TmfTimeRange range) {
226 if (this.compareTo(range.getStartTime()) >= 0 &&
227 this.compareTo(range.getEndTime()) <= 0) {
228 return true;
229 }
230 return false;
231 }
232
233 // ------------------------------------------------------------------------
234 // Comparable
235 // ------------------------------------------------------------------------
023761c4 236
065cc19b
AM
237 @Override
238 public int compareTo(final ITmfTimestamp ts) {
9e925522
MK
239 // Check the corner cases (we can't use equals() because it uses
240 // compareTo()...)
b9e37ffd
FC
241 if (ts == null) {
242 return 1;
243 }
244 if (this == ts || (fValue == ts.getValue() && fScale == ts.getScale())) {
5179fc01 245 return 0;
b9e37ffd
FC
246 }
247 if ((fValue == BIG_BANG.getValue() && fScale == BIG_BANG.getScale()) || (ts.getValue() == BIG_CRUNCH.getValue() && ts.getScale() == BIG_CRUNCH.getScale())) {
5179fc01 248 return -1;
b9e37ffd
FC
249 }
250 if ((fValue == BIG_CRUNCH.getValue() && fScale == BIG_CRUNCH.getScale()) || (ts.getValue() == BIG_BANG.getValue() && ts.getScale() == BIG_BANG.getScale())) {
5179fc01 251 return 1;
b9e37ffd 252 }
9e925522
MK
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)) {
5179fc01
FC
255 // Scaling error. We can figure it out nonetheless.
256
257 // First, look at the sign of the mantissa
085d898f 258 final long value = ts.getValue();
b9e37ffd 259 if (fValue == 0 && value == 0) {
5179fc01 260 return 0;
b9e37ffd
FC
261 }
262 if (fValue < 0 && value >= 0) {
5179fc01 263 return -1;
b9e37ffd
FC
264 }
265 if (fValue >= 0 && value < 0) {
5179fc01 266 return 1;
b9e37ffd 267 }
5179fc01
FC
268
269 // Otherwise, just compare the scales
085d898f
FC
270 final int scale = ts.getScale();
271 return (fScale > scale) ? (fValue >= 0) ? 1 : -1 : (fValue >= 0) ? -1 : 1;
5179fc01 272 }
9e925522
MK
273 final long delta = fValue - nts.getValue();
274 return Long.compare(delta, 0);
275 }
276
277 /**
278 * Saturated multiplication. It will not overflow but instead clamp the
279 * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
280 *
281 * @param left
282 * The left long to multiply
283 * @param right
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>
289 */
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);
294 }
295 return retVal;
296 }
297
298 /**
299 * Saturated addition. It will not overflow but instead clamp the result to
300 * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
301 *
302 * @param left
303 * The left long to add
304 * @param right
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>
310 * @since 2.0
311 */
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)) {
315 if (retVal > 0) {
316 return Long.MIN_VALUE;
317 }
318 return Long.MAX_VALUE;
319 }
320 return retVal;
321 }
322
323 private static boolean sameSign(final long left, final long right) {
324 return (left ^ right) >= 0;
8c8bf09f
ASL
325 }
326
5179fc01 327 // ------------------------------------------------------------------------
cbd4ad82 328 // Object
5179fc01 329 // ------------------------------------------------------------------------
28b94d61 330
8c8bf09f 331 @Override
cbd4ad82 332 public int hashCode() {
5179fc01
FC
333 final int prime = 31;
334 int result = 1;
335 result = prime * result + (int) (fValue ^ (fValue >>> 32));
336 result = prime * result + fScale;
cbd4ad82
FC
337 return result;
338 }
339
5179fc01 340 @Override
085d898f 341 public boolean equals(final Object other) {
b9e37ffd 342 if (this == other) {
5179fc01 343 return true;
b9e37ffd
FC
344 }
345 if (other == null) {
5179fc01 346 return false;
b9e37ffd 347 }
065cc19b 348 if (!(other instanceof ITmfTimestamp)) {
5179fc01 349 return false;
b9e37ffd 350 }
065cc19b
AM
351 /* We allow comparing with other types of *I*TmfTimestamp though */
352 final ITmfTimestamp ts = (ITmfTimestamp) other;
353 return (compareTo(ts) == 0);
8c8bf09f
ASL
354 }
355
1f506a43
FC
356 @Override
357 public String toString() {
f8177ba2
FC
358 return toString(TmfTimestampFormat.getDefaulTimeFormat());
359 }
360
f8177ba2
FC
361 @Override
362 public String toString(final TmfTimestampFormat format) {
363 try {
16801c72 364 return format.format(toNanos());
9e925522 365 } catch (ArithmeticException e) {
f8177ba2
FC
366 return format.format(0);
367 }
ff4ed569
FC
368 }
369
032ecd45
MAL
370 /**
371 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
9e925522
MK
372 *
373 * @param bufferOut
374 * the buffer to write to
032ecd45
MAL
375 */
376 public void serialize(ByteBuffer bufferOut) {
377 bufferOut.putLong(fValue);
378 bufferOut.putInt(fScale);
032ecd45 379 }
023761c4 380}
This page took 0.113562 seconds and 5 git commands to generate.