Commit | Line | Data |
---|---|---|
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 | 16 | package org.eclipse.tracecompass.tmf.core.timestamp; |
8c8bf09f | 17 | |
032ecd45 MAL |
18 | import java.nio.ByteBuffer; |
19 | ||
cbf0057c GB |
20 | import org.eclipse.jdt.annotation.NonNull; |
21 | ||
8c8bf09f | 22 | /** |
9e925522 | 23 | * A generic timestamp implementation. The timestamp is represented by the tuple |
b2c971ec | 24 | * { value, scale, precision }. |
f8177ba2 | 25 | * |
b9e37ffd | 26 | * @author Francois Chouinard |
8c8bf09f | 27 | */ |
b2c971ec | 28 | public abstract class TmfTimestamp implements ITmfTimestamp { |
c61fcbab MK |
29 | |
30 | /** | |
31 | * Default implementation of the tmf timestamp. We want this to be hidden. | |
32 | * | |
33 | * @author Matthew Khouzam | |
34 | * | |
35 | */ | |
36 | private static final class Impl extends TmfTimestamp { | |
37 | ||
38 | // ------------------------------------------------------------------------ | |
39 | // Attributes | |
40 | // ------------------------------------------------------------------------ | |
41 | ||
42 | /** | |
43 | * The timestamp raw value (mantissa) | |
44 | */ | |
45 | private final long fValue; | |
46 | ||
47 | /** | |
48 | * The timestamp scale (magnitude) | |
49 | */ | |
50 | private final int fScale; | |
51 | ||
52 | // ------------------------------------------------------------------------ | |
53 | // Constructors | |
54 | // ------------------------------------------------------------------------ | |
55 | ||
56 | /** | |
57 | * Full constructor | |
58 | * | |
59 | * @param value | |
60 | * the timestamp value | |
61 | * @param scale | |
62 | * the timestamp scale | |
63 | */ | |
64 | public Impl(final long value, final int scale) { | |
65 | fValue = value; | |
66 | fScale = scale; | |
67 | } | |
68 | ||
69 | @Override | |
70 | public long getValue() { | |
71 | return fValue; | |
72 | } | |
73 | ||
74 | @Override | |
75 | public int getScale() { | |
76 | return fScale; | |
77 | } | |
78 | } | |
79 | ||
d7dbf09a | 80 | /** |
b2c971ec MK |
81 | * Create a timestamp. |
82 | * | |
83 | * @param value | |
84 | * the value in nanoseconds | |
85 | * @return the timestamp | |
86 | * @since 2.0 | |
d7dbf09a | 87 | */ |
b2c971ec MK |
88 | public static @NonNull ITmfTimestamp fromNanos(long value) { |
89 | return new TmfNanoTimestamp(value); | |
90 | } | |
d7dbf09a FC |
91 | |
92 | /** | |
b2c971ec MK |
93 | * Create a timestamp. |
94 | * | |
95 | * @param value | |
96 | * the value in microseconds | |
97 | * @return the timestamp | |
98 | * @since 2.0 | |
d7dbf09a | 99 | */ |
b2c971ec MK |
100 | public static @NonNull ITmfTimestamp fromMicros(long value) { |
101 | return create(value, ITmfTimestamp.MICROSECOND_SCALE); | |
102 | } | |
8c8bf09f ASL |
103 | |
104 | /** | |
b2c971ec MK |
105 | * Create a timestamp. |
106 | * | |
107 | * @param value | |
108 | * the value in milliseconds | |
109 | * @return the timestamp | |
110 | * @since 2.0 | |
8c8bf09f | 111 | */ |
b2c971ec MK |
112 | public static @NonNull ITmfTimestamp fromMillis(long value) { |
113 | return create(value, ITmfTimestamp.MILLISECOND_SCALE); | |
8c8bf09f ASL |
114 | } |
115 | ||
1f506a43 | 116 | /** |
b2c971ec | 117 | * Create a timestamp. |
5179fc01 | 118 | * |
065cc19b | 119 | * @param value |
b2c971ec MK |
120 | * the value in seconds |
121 | * @return the timestamp | |
122 | * @since 2.0 | |
1f506a43 | 123 | */ |
b2c971ec MK |
124 | public static @NonNull ITmfTimestamp fromSeconds(long value) { |
125 | return new TmfSecondTimestamp(value); | |
8c8bf09f ASL |
126 | } |
127 | ||
128 | /** | |
b2c971ec | 129 | * Create a timestamp. |
f8177ba2 | 130 | * |
b2c971ec MK |
131 | * @param bufferIn |
132 | * the byte buffer to read the timestamp from. | |
133 | * @return the timestamp | |
134 | * @since 2.0 | |
8c8bf09f | 135 | */ |
b2c971ec MK |
136 | public static @NonNull ITmfTimestamp create(ByteBuffer bufferIn) { |
137 | return create(bufferIn.getLong(), bufferIn.getInt()); | |
8c8bf09f ASL |
138 | } |
139 | ||
140 | /** | |
b2c971ec | 141 | * Create a timestamp. |
f8177ba2 | 142 | * |
b2c971ec MK |
143 | * @param value |
144 | * the value in time, the unit is specified by the scale | |
145 | * @param scale | |
146 | * the scale of the timestamp with respect to seconds, so a | |
147 | * nanosecond would be -9 (10e-9) and a megasecond would be 6 | |
148 | * (10e6) | |
149 | * @return the timestamp | |
150 | * @since 2.0 | |
8c8bf09f | 151 | */ |
b2c971ec MK |
152 | public static @NonNull ITmfTimestamp create(long value, int scale) { |
153 | if (scale == ITmfTimestamp.NANOSECOND_SCALE) { | |
154 | return fromNanos(value); | |
b9e37ffd | 155 | } |
b2c971ec MK |
156 | if (scale == ITmfTimestamp.SECOND_SCALE) { |
157 | return fromSeconds(value); | |
158 | } | |
c61fcbab MK |
159 | if (value == 0) { |
160 | return ZERO; | |
161 | } | |
b2c971ec | 162 | return createOther(value, scale); |
8c8bf09f | 163 | } |
e73a4ba5 GB |
164 | |
165 | /** | |
b2c971ec | 166 | * Write the time stamp to the ByteBuffer so that it can be saved to disk. |
e73a4ba5 | 167 | * |
b2c971ec MK |
168 | * @param bufferOut |
169 | * the buffer to write to | |
170 | * @param ts | |
171 | * the timestamp to write | |
172 | * @since 2.0 | |
e73a4ba5 | 173 | */ |
b2c971ec MK |
174 | public static void serialize(ByteBuffer bufferOut, ITmfTimestamp ts) { |
175 | bufferOut.putLong(ts.getValue()); | |
176 | bufferOut.putInt(ts.getScale()); | |
177 | } | |
178 | ||
179 | private static @NonNull ITmfTimestamp createOther(long value, int scale) { | |
c61fcbab | 180 | return new Impl(value, scale); |
e73a4ba5 | 181 | } |
8c8bf09f | 182 | |
5179fc01 | 183 | // ------------------------------------------------------------------------ |
b2c971ec | 184 | // Constants |
5179fc01 | 185 | // ------------------------------------------------------------------------ |
8c8bf09f | 186 | |
032ecd45 | 187 | /** |
c61fcbab MK |
188 | * Zero - a zero time constant. The value is zero, so this allows some |
189 | * interesting simplifications. | |
032ecd45 | 190 | */ |
c61fcbab MK |
191 | public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp() { |
192 | @Override | |
193 | public long getValue() { | |
194 | return 0; | |
195 | } | |
196 | ||
197 | @Override | |
198 | public int getScale() { | |
199 | return 0; | |
200 | } | |
201 | ||
202 | @Override | |
203 | public @NonNull ITmfTimestamp normalize(long offset, int scale) { | |
204 | if (offset == 0) { | |
205 | return this; | |
206 | } | |
207 | return create(offset, scale); | |
208 | } | |
209 | ||
210 | @Override | |
211 | public int compareTo(ITmfTimestamp ts) { | |
212 | return Long.compare(0, ts.getValue()); | |
213 | } | |
214 | }; | |
032ecd45 | 215 | |
b2c971ec | 216 | /** |
c61fcbab | 217 | * The beginning of time will be lesser than any other timestamp |
b2c971ec | 218 | */ |
c61fcbab MK |
219 | public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp() { |
220 | @Override | |
221 | public long getValue() { | |
222 | return Long.MIN_VALUE; | |
223 | } | |
224 | ||
225 | @Override | |
226 | public int getScale() { | |
227 | return Integer.MAX_VALUE; | |
228 | } | |
229 | ||
230 | @Override | |
231 | public int compareTo(ITmfTimestamp other) { | |
232 | if (equals(other) == true) { | |
233 | return 0; | |
234 | } | |
235 | return -1; | |
236 | } | |
237 | ||
238 | @Override | |
239 | public ITmfTimestamp normalize(long offset, int scale) { | |
240 | return this; | |
241 | } | |
242 | ||
243 | @Override | |
244 | public boolean equals(Object other) { | |
245 | return this == other; | |
246 | } | |
247 | }; | |
8c8bf09f | 248 | |
b2c971ec | 249 | /** |
c61fcbab | 250 | * The end of time will be greater than any other timestamp |
b2c971ec | 251 | */ |
c61fcbab MK |
252 | public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp() { |
253 | @Override | |
254 | public long getValue() { | |
255 | return Long.MAX_VALUE; | |
256 | } | |
257 | ||
258 | @Override | |
259 | public int getScale() { | |
260 | return Integer.MAX_VALUE; | |
261 | } | |
262 | ||
263 | @Override | |
264 | public int compareTo(ITmfTimestamp other) { | |
265 | if (equals(other) == true) { | |
266 | return 0; | |
267 | } | |
268 | return 1; | |
269 | } | |
270 | ||
271 | @Override | |
272 | public ITmfTimestamp normalize(long offset, int scale) { | |
273 | return this; | |
274 | } | |
275 | ||
276 | @Override | |
277 | public boolean equals(Object other) { | |
278 | return this == other; | |
279 | } | |
280 | }; | |
b2c971ec MK |
281 | |
282 | // ------------------------------------------------------------------------ | |
283 | // ITmfTimestamp | |
284 | // ------------------------------------------------------------------------ | |
8c8bf09f | 285 | |
b2c971ec MK |
286 | /** |
287 | * Scaling factors to help scale | |
288 | * | |
289 | * @since 2.0 | |
290 | */ | |
291 | protected static final long SCALING_FACTORS[] = new long[] { | |
9e925522 MK |
292 | 1L, |
293 | 10L, | |
294 | 100L, | |
295 | 1000L, | |
296 | 10000L, | |
297 | 100000L, | |
298 | 1000000L, | |
299 | 10000000L, | |
300 | 100000000L, | |
301 | 1000000000L, | |
302 | 10000000000L, | |
303 | 100000000000L, | |
304 | 1000000000000L, | |
305 | 10000000000000L, | |
306 | 100000000000000L, | |
307 | 1000000000000000L, | |
308 | 10000000000000000L, | |
309 | 100000000000000000L, | |
310 | 1000000000000000000L, | |
5179fc01 | 311 | }; |
4ab33d2b | 312 | |
d7dbf09a | 313 | @Override |
0316808c | 314 | public ITmfTimestamp normalize(final long offset, final int scale) { |
8c8bf09f | 315 | |
9e925522 | 316 | long value = getValue(); |
8c8bf09f | 317 | |
5179fc01 | 318 | // Handle the trivial case |
9e925522 | 319 | if (getScale() == scale && offset == 0) { |
4593bd5b | 320 | return this; |
b9e37ffd | 321 | } |
f8177ba2 | 322 | |
9e925522 | 323 | if (value == 0) { |
b2c971ec | 324 | return create(offset, scale); |
9e925522 MK |
325 | } |
326 | ||
5179fc01 | 327 | // First, scale the timestamp |
9e925522 MK |
328 | if (getScale() != scale) { |
329 | final int scaleDiff = Math.abs(getScale() - scale); | |
b2c971ec | 330 | if (scaleDiff >= SCALING_FACTORS.length) { |
9e925522 MK |
331 | if (getScale() < scale) { |
332 | value = 0; | |
333 | } else { | |
334 | value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; | |
335 | } | |
8c8bf09f | 336 | } else { |
b2c971ec | 337 | final long scalingFactor = SCALING_FACTORS[scaleDiff]; |
9e925522 MK |
338 | if (getScale() < scale) { |
339 | value /= scalingFactor; | |
340 | } else { | |
341 | value = saturatedMult(scalingFactor, value); | |
342 | } | |
8c8bf09f ASL |
343 | } |
344 | } | |
345 | ||
9e925522 | 346 | value = saturatedAdd(value, offset); |
023761c4 | 347 | |
b2c971ec | 348 | return create(value, scale); |
8c8bf09f ASL |
349 | } |
350 | ||
d7dbf09a | 351 | @Override |
065cc19b | 352 | public ITmfTimestamp getDelta(final ITmfTimestamp ts) { |
b2c971ec MK |
353 | final int scale = getScale(); |
354 | final ITmfTimestamp nts = ts.normalize(0, scale); | |
355 | final long value = getValue() - nts.getValue(); | |
356 | return new TmfTimestampDelta(value, scale); | |
065cc19b AM |
357 | } |
358 | ||
359 | @Override | |
360 | public boolean intersects(TmfTimeRange range) { | |
361 | if (this.compareTo(range.getStartTime()) >= 0 && | |
362 | this.compareTo(range.getEndTime()) <= 0) { | |
363 | return true; | |
364 | } | |
365 | return false; | |
366 | } | |
367 | ||
368 | // ------------------------------------------------------------------------ | |
369 | // Comparable | |
370 | // ------------------------------------------------------------------------ | |
023761c4 | 371 | |
065cc19b AM |
372 | @Override |
373 | public int compareTo(final ITmfTimestamp ts) { | |
b2c971ec MK |
374 | long value = getValue(); |
375 | int scale = getScale(); | |
9e925522 MK |
376 | // Check the corner cases (we can't use equals() because it uses |
377 | // compareTo()...) | |
c61fcbab | 378 | if (BIG_BANG.equals(ts)) { |
b9e37ffd FC |
379 | return 1; |
380 | } | |
c61fcbab MK |
381 | |
382 | if (BIG_CRUNCH.equals(ts)) { | |
5179fc01 | 383 | return -1; |
b9e37ffd | 384 | } |
c61fcbab MK |
385 | |
386 | if (this == ts || isIdentical(this, ts)) { | |
387 | return 0; | |
b9e37ffd | 388 | } |
5179fc01 | 389 | |
c61fcbab MK |
390 | if (scale == ts.getScale()) { |
391 | if (ts.getValue() == Long.MIN_VALUE) { | |
5179fc01 | 392 | return 1; |
b9e37ffd | 393 | } |
c61fcbab MK |
394 | final long delta = saturatedAdd(getValue(), -ts.getValue()); |
395 | return Long.compare(delta, 0); | |
396 | } | |
397 | final ITmfTimestamp largerScale = (scale > ts.getScale()) ? this : ts; | |
398 | final ITmfTimestamp smallerScale = (scale < ts.getScale()) ? this : ts; | |
399 | ||
400 | final ITmfTimestamp nts = largerScale.normalize(0, smallerScale.getScale()); | |
401 | if (hasSaturated(largerScale, nts)) { | |
402 | // We've saturated largerScale. | |
403 | if (smallerScale.getScale() == scale) { | |
404 | return Long.compare(0, nts.getValue()); | |
405 | } | |
406 | return Long.compare(nts.getValue(), 0); | |
5179fc01 | 407 | } |
c61fcbab MK |
408 | if (smallerScale.getScale() == scale) { |
409 | return Long.compare(value, nts.getValue()); | |
410 | } | |
411 | return Long.compare(nts.getValue(), smallerScale.getValue()); | |
412 | } | |
413 | ||
414 | private static boolean hasSaturated(final ITmfTimestamp ts, final ITmfTimestamp nts) { | |
415 | return (nts.getValue() == 0 && ts.getValue() != 0) || !isIdentical(ts, nts) && ((nts.getValue() == Long.MAX_VALUE) || (nts.getValue() == Long.MIN_VALUE)); | |
416 | } | |
417 | ||
418 | private static boolean isIdentical(final ITmfTimestamp ts, final ITmfTimestamp nts) { | |
419 | return ts.getValue() == nts.getValue() && ts.getScale() == nts.getScale(); | |
9e925522 MK |
420 | } |
421 | ||
422 | /** | |
423 | * Saturated multiplication. It will not overflow but instead clamp the | |
424 | * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}. | |
425 | * | |
426 | * @param left | |
427 | * The left long to multiply | |
428 | * @param right | |
429 | * The right long to multiply | |
430 | * @return The saturated multiplication result. The mathematical, not Java | |
431 | * version of Min(Max(MIN_VALUE, left*right), MAX_VALUE). | |
432 | * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic"> | |
433 | * Saturation arithmetic</a> | |
434 | */ | |
435 | private static long saturatedMult(long left, long right) { | |
436 | long retVal = left * right; | |
437 | if ((left != 0) && ((retVal / left) != right)) { | |
438 | return (sameSign(left, right) ? Long.MAX_VALUE : Long.MIN_VALUE); | |
439 | } | |
440 | return retVal; | |
441 | } | |
442 | ||
443 | /** | |
444 | * Saturated addition. It will not overflow but instead clamp the result to | |
445 | * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}. | |
446 | * | |
447 | * @param left | |
448 | * The left long to add | |
449 | * @param right | |
450 | * The right long to add | |
451 | * @return The saturated addition result. The mathematical, not Java version | |
452 | * of Min(Max(MIN_VALUE, left+right), MAX_VALUE). | |
453 | * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic"> | |
454 | * Saturation arithmetic</a> | |
455 | * @since 2.0 | |
456 | */ | |
457 | protected static final long saturatedAdd(final long left, final long right) { | |
458 | long retVal = left + right; | |
459 | if (sameSign(left, right) && !sameSign(left, retVal)) { | |
460 | if (retVal > 0) { | |
461 | return Long.MIN_VALUE; | |
462 | } | |
463 | return Long.MAX_VALUE; | |
464 | } | |
465 | return retVal; | |
466 | } | |
467 | ||
468 | private static boolean sameSign(final long left, final long right) { | |
469 | return (left ^ right) >= 0; | |
8c8bf09f ASL |
470 | } |
471 | ||
5179fc01 | 472 | // ------------------------------------------------------------------------ |
cbd4ad82 | 473 | // Object |
5179fc01 | 474 | // ------------------------------------------------------------------------ |
28b94d61 | 475 | |
8c8bf09f | 476 | @Override |
cbd4ad82 | 477 | public int hashCode() { |
5179fc01 FC |
478 | final int prime = 31; |
479 | int result = 1; | |
b2c971ec MK |
480 | final long value = getValue(); |
481 | result = prime * result + (int) (value ^ (value >>> 32)); | |
482 | result = prime * result + getScale(); | |
cbd4ad82 FC |
483 | return result; |
484 | } | |
485 | ||
5179fc01 | 486 | @Override |
085d898f | 487 | public boolean equals(final Object other) { |
b9e37ffd | 488 | if (this == other) { |
5179fc01 | 489 | return true; |
b9e37ffd FC |
490 | } |
491 | if (other == null) { | |
5179fc01 | 492 | return false; |
b9e37ffd | 493 | } |
065cc19b | 494 | if (!(other instanceof ITmfTimestamp)) { |
5179fc01 | 495 | return false; |
b9e37ffd | 496 | } |
065cc19b AM |
497 | /* We allow comparing with other types of *I*TmfTimestamp though */ |
498 | final ITmfTimestamp ts = (ITmfTimestamp) other; | |
c61fcbab MK |
499 | if (getScale() == ts.getScale()) { |
500 | return getValue() == ts.getValue(); | |
501 | } | |
065cc19b | 502 | return (compareTo(ts) == 0); |
8c8bf09f ASL |
503 | } |
504 | ||
1f506a43 FC |
505 | @Override |
506 | public String toString() { | |
f8177ba2 FC |
507 | return toString(TmfTimestampFormat.getDefaulTimeFormat()); |
508 | } | |
509 | ||
f8177ba2 FC |
510 | @Override |
511 | public String toString(final TmfTimestampFormat format) { | |
512 | try { | |
16801c72 | 513 | return format.format(toNanos()); |
9e925522 | 514 | } catch (ArithmeticException e) { |
f8177ba2 FC |
515 | return format.format(0); |
516 | } | |
ff4ed569 | 517 | } |
023761c4 | 518 | } |