common.core: move saturated math to common core
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / timestamp / TmfTimestamp.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal
3 *
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
8 *
9 * Contributors:
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 *******************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.core.timestamp;
17
18 import java.nio.ByteBuffer;
19
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;
24
25 /**
26 * A generic timestamp implementation. The timestamp is represented by the tuple
27 * { value, scale, precision }.
28 *
29 * @author Francois Chouinard
30 */
31 public abstract class TmfTimestamp implements ITmfTimestamp {
32
33 /**
34 * Default implementation of the tmf timestamp. We want this to be hidden.
35 *
36 * @author Matthew Khouzam
37 *
38 */
39 private static final class Impl extends TmfTimestamp {
40
41 // ------------------------------------------------------------------------
42 // Attributes
43 // ------------------------------------------------------------------------
44
45 /**
46 * The timestamp raw value (mantissa)
47 */
48 private final long fValue;
49
50 /**
51 * The timestamp scale (magnitude)
52 */
53 private final int fScale;
54
55 // ------------------------------------------------------------------------
56 // Constructors
57 // ------------------------------------------------------------------------
58
59 /**
60 * Full constructor
61 *
62 * @param value
63 * the timestamp value
64 * @param scale
65 * the timestamp scale
66 */
67 public Impl(final long value, final int scale) {
68 fValue = value;
69 fScale = scale;
70 }
71
72 @Override
73 public long getValue() {
74 return fValue;
75 }
76
77 @Override
78 public int getScale() {
79 return fScale;
80 }
81 }
82
83 /**
84 * Create a timestamp.
85 *
86 * @param value
87 * the value in nanoseconds
88 * @return the timestamp
89 * @since 2.0
90 */
91 public static @NonNull ITmfTimestamp fromNanos(long value) {
92 return new TmfNanoTimestamp(value);
93 }
94
95 /**
96 * Create a timestamp.
97 *
98 * @param value
99 * the value in microseconds
100 * @return the timestamp
101 * @since 2.0
102 */
103 public static @NonNull ITmfTimestamp fromMicros(long value) {
104 return create(value, ITmfTimestamp.MICROSECOND_SCALE);
105 }
106
107 /**
108 * Create a timestamp.
109 *
110 * @param value
111 * the value in milliseconds
112 * @return the timestamp
113 * @since 2.0
114 */
115 public static @NonNull ITmfTimestamp fromMillis(long value) {
116 return create(value, ITmfTimestamp.MILLISECOND_SCALE);
117 }
118
119 /**
120 * Create a timestamp.
121 *
122 * @param value
123 * the value in seconds
124 * @return the timestamp
125 * @since 2.0
126 */
127 public static @NonNull ITmfTimestamp fromSeconds(long value) {
128 return new TmfSecondTimestamp(value);
129 }
130
131 /**
132 * Create a timestamp.
133 *
134 * @param bufferIn
135 * the byte buffer to read the timestamp from.
136 * @return the timestamp
137 * @since 2.0
138 */
139 public static @NonNull ITmfTimestamp create(ByteBuffer bufferIn) {
140 return create(bufferIn.getLong(), bufferIn.getInt());
141 }
142
143 /**
144 * Create a timestamp.
145 *
146 * @param value
147 * the value in time, the unit is specified by the scale
148 * @param 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
151 * (10e6)
152 * @return the timestamp
153 * @since 2.0
154 */
155 public static @NonNull ITmfTimestamp create(long value, int scale) {
156 if (scale == ITmfTimestamp.NANOSECOND_SCALE) {
157 return fromNanos(value);
158 }
159 if (scale == ITmfTimestamp.SECOND_SCALE) {
160 return fromSeconds(value);
161 }
162 if (value == 0) {
163 return ZERO;
164 }
165 return createOther(value, scale);
166 }
167
168 /**
169 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
170 *
171 * @param bufferOut
172 * the buffer to write to
173 * @param ts
174 * the timestamp to write
175 * @since 2.0
176 */
177 public static void serialize(ByteBuffer bufferOut, ITmfTimestamp ts) {
178 bufferOut.putLong(ts.getValue());
179 bufferOut.putInt(ts.getScale());
180 }
181
182 private static @NonNull ITmfTimestamp createOther(long value, int scale) {
183 return new Impl(value, scale);
184 }
185
186 // ------------------------------------------------------------------------
187 // Constants
188 // ------------------------------------------------------------------------
189
190 /**
191 * Zero - a zero time constant. The value is zero, so this allows some
192 * interesting simplifications.
193 */
194 public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp() {
195 @Override
196 public long getValue() {
197 return 0;
198 }
199
200 @Override
201 public int getScale() {
202 return 0;
203 }
204
205 @Override
206 public @NonNull ITmfTimestamp normalize(long offset, int scale) {
207 if (offset == 0) {
208 return this;
209 }
210 return create(offset, scale);
211 }
212
213 @Override
214 public int compareTo(ITmfTimestamp ts) {
215 return Long.compare(0, ts.getValue());
216 }
217 };
218
219 /**
220 * The beginning of time will be lesser than any other timestamp
221 */
222 public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp() {
223 @Override
224 public long getValue() {
225 return Long.MIN_VALUE;
226 }
227
228 @Override
229 public int getScale() {
230 return Integer.MAX_VALUE;
231 }
232
233 @Override
234 public int compareTo(ITmfTimestamp other) {
235 if (equals(other)) {
236 return 0;
237 }
238 return -1;
239 }
240
241 @Override
242 public ITmfTimestamp normalize(long offset, int scale) {
243 return this;
244 }
245
246 @Override
247 public boolean equals(Object other) {
248 return this == other;
249 }
250 };
251
252 /**
253 * The end of time will be greater than any other timestamp
254 */
255 public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp() {
256 @Override
257 public long getValue() {
258 return Long.MAX_VALUE;
259 }
260
261 @Override
262 public int getScale() {
263 return Integer.MAX_VALUE;
264 }
265
266 @Override
267 public int compareTo(ITmfTimestamp other) {
268 if (equals(other) == true) {
269 return 0;
270 }
271 return 1;
272 }
273
274 @Override
275 public ITmfTimestamp normalize(long offset, int scale) {
276 return this;
277 }
278
279 @Override
280 public boolean equals(Object other) {
281 return this == other;
282 }
283 };
284
285 // ------------------------------------------------------------------------
286 // ITmfTimestamp
287 // ------------------------------------------------------------------------
288
289 /**
290 * Scaling factors to help scale
291 *
292 * @since 2.0
293 */
294 protected static final long SCALING_FACTORS[] = new long[] {
295 1L,
296 10L,
297 100L,
298 1000L,
299 10000L,
300 100000L,
301 1000000L,
302 10000000L,
303 100000000L,
304 1000000000L,
305 10000000000L,
306 100000000000L,
307 1000000000000L,
308 10000000000000L,
309 100000000000000L,
310 1000000000000000L,
311 10000000000000000L,
312 100000000000000000L,
313 1000000000000000000L,
314 };
315
316 @Override
317 public ITmfTimestamp normalize(final long offset, final int scale) {
318
319 long value = getValue();
320
321 // Handle the trivial case
322 if (getScale() == scale && offset == 0) {
323 return this;
324 }
325
326 if (value == 0) {
327 return create(offset, scale);
328 }
329
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) {
335 value = 0;
336 } else {
337 value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
338 }
339 } else {
340 final long scalingFactor = SCALING_FACTORS[scaleDiff];
341 if (getScale() < scale) {
342 value /= scalingFactor;
343 } else {
344 value = SaturatedArithmetic.multiply(scalingFactor, value);
345 }
346 }
347 }
348
349 value = SaturatedArithmetic.add(value, offset);
350
351 return create(value, scale);
352 }
353
354 @Override
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);
360 }
361
362 @Override
363 public boolean intersects(TmfTimeRange range) {
364 if (this.compareTo(range.getStartTime()) >= 0 &&
365 this.compareTo(range.getEndTime()) <= 0) {
366 return true;
367 }
368 return false;
369 }
370
371 // ------------------------------------------------------------------------
372 // Comparable
373 // ------------------------------------------------------------------------
374
375 @Override
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
380 // compareTo()...)
381 if (BIG_BANG.equals(ts)) {
382 return 1;
383 }
384
385 if (BIG_CRUNCH.equals(ts)) {
386 return -1;
387 }
388
389 if (this == ts || isIdentical(this, ts)) {
390 return 0;
391 }
392
393 if (scale == ts.getScale()) {
394 if (ts.getValue() == Long.MIN_VALUE) {
395 return 1;
396 }
397 final long delta = SaturatedArithmetic.add(getValue(), -ts.getValue());
398 return Long.compare(delta, 0);
399 }
400 final ITmfTimestamp largerScale = (scale > ts.getScale()) ? this : ts;
401 final ITmfTimestamp smallerScale = (scale < ts.getScale()) ? this : ts;
402
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());
408 }
409 return Long.compare(nts.getValue(), 0);
410 }
411 if (smallerScale.getScale() == scale) {
412 return Long.compare(value, nts.getValue());
413 }
414 return Long.compare(nts.getValue(), smallerScale.getValue());
415 }
416
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));
419 }
420
421 private static boolean isIdentical(final ITmfTimestamp ts, final ITmfTimestamp nts) {
422 return ts.getValue() == nts.getValue() && ts.getScale() == nts.getScale();
423 }
424
425 /**
426 * Saturated addition. It will not overflow but instead clamp the result to
427 * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
428 *
429 * @param left
430 * The left long to add
431 * @param right
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>
437 * @since 2.0
438 * @deprecated use {@link SaturatedArithmetic#add(long, long)} instead
439 */
440 @Deprecated
441 protected static final long saturatedAdd(final long left, final long right) {
442 return SaturatedArithmetic.add(left, right);
443 }
444
445
446 // ------------------------------------------------------------------------
447 // Object
448 // ------------------------------------------------------------------------
449
450 @Override
451 public int hashCode() {
452 final int prime = 31;
453 int result = 1;
454 final long value = getValue();
455 result = prime * result + (int) (value ^ (value >>> 32));
456 result = prime * result + getScale();
457 return result;
458 }
459
460 @Override
461 public boolean equals(final Object other) {
462 if (this == other) {
463 return true;
464 }
465 if (other == null) {
466 return false;
467 }
468 if (!(other instanceof ITmfTimestamp)) {
469 return false;
470 }
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();
475 }
476 return (compareTo(ts) == 0);
477 }
478
479 @Override
480 public String toString() {
481 return toString(TmfTimestampFormat.getDefaulTimeFormat());
482 }
483
484 @Override
485 public String toString(final TmfTimestampFormat format) {
486 try {
487 return format.format(toNanos());
488 } catch (ArithmeticException e) {
489 return format.format(0);
490 }
491 }
492 }
This page took 0.042616 seconds and 5 git commands to generate.