tmf.core: fix timestamp normalization clamping
[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
22 /**
23 * A generic timestamp implementation. The timestamp is represented by the tuple
24 * { value, scale, precision }. By default, timestamps are scaled in seconds.
25 *
26 * @author Francois Chouinard
27 */
28 public class TmfTimestamp implements ITmfTimestamp {
29
30 // ------------------------------------------------------------------------
31 // Constants
32 // ------------------------------------------------------------------------
33
34 /**
35 * The beginning of time
36 */
37 public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE);
38
39 /**
40 * The end of time
41 */
42 public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE);
43
44 /**
45 * Zero
46 */
47 public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp(0, 0);
48
49 // ------------------------------------------------------------------------
50 // Attributes
51 // ------------------------------------------------------------------------
52
53 /**
54 * The timestamp raw value (mantissa)
55 */
56 private final long fValue;
57
58 /**
59 * The timestamp scale (magnitude)
60 */
61 private final int fScale;
62
63 // ------------------------------------------------------------------------
64 // Constructors
65 // ------------------------------------------------------------------------
66
67 /**
68 * Default constructor
69 */
70 public TmfTimestamp() {
71 this(0, ITmfTimestamp.SECOND_SCALE);
72 }
73
74 /**
75 * Simple constructor (scale = 0)
76 *
77 * @param value
78 * the timestamp value
79 */
80 public TmfTimestamp(final long value) {
81 this(value, ITmfTimestamp.SECOND_SCALE);
82 }
83
84 /**
85 * Full constructor
86 *
87 * @param value
88 * the timestamp value
89 * @param scale
90 * the timestamp scale
91 */
92 public TmfTimestamp(final long value, final int scale) {
93 fValue = value;
94 fScale = scale;
95 }
96
97 /**
98 * Copy constructor
99 *
100 * @param timestamp
101 * the timestamp to copy
102 */
103 public TmfTimestamp(final ITmfTimestamp timestamp) {
104 if (timestamp == null) {
105 throw new IllegalArgumentException();
106 }
107 fValue = timestamp.getValue();
108 fScale = timestamp.getScale();
109 }
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
118 */
119 public TmfTimestamp(ITmfTimestamp timestamp, long newvalue) {
120 if (timestamp == null) {
121 throw new IllegalArgumentException();
122 }
123 fValue = newvalue;
124 fScale = timestamp.getScale();
125 }
126
127 // ------------------------------------------------------------------------
128 // ITmfTimestamp
129 // ------------------------------------------------------------------------
130
131 /**
132 * Construct the timestamp from the ByteBuffer.
133 *
134 * @param bufferIn
135 * the buffer to read from
136 */
137 public TmfTimestamp(ByteBuffer bufferIn) {
138 this(bufferIn.getLong(), bufferIn.getInt());
139 }
140
141 @Override
142 public long getValue() {
143 return fValue;
144 }
145
146 @Override
147 public int getScale() {
148 return fScale;
149 }
150
151 private static final long scalingFactors[] = new long[] {
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,
171 };
172
173 @Override
174 public ITmfTimestamp normalize(final long offset, final int scale) {
175
176 long value = getValue();
177
178 // Handle the trivial case
179 if (getScale() == scale && offset == 0) {
180 return this;
181 }
182
183 // In case of big bang and big crunch just return this (no need to
184 // normalize)
185 if (this.equals(BIG_BANG) || this.equals(BIG_CRUNCH)) {
186 return this;
187 }
188
189 if (value == 0) {
190 return new TmfTimestamp(offset, scale);
191 }
192
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) {
198 value = 0;
199 } else {
200 value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
201 }
202 } else {
203 final long scalingFactor = scalingFactors[scaleDiff];
204 if (getScale() < scale) {
205 value /= scalingFactor;
206 } else {
207 value = saturatedMult(scalingFactor, value);
208 }
209 }
210 }
211
212 value = saturatedAdd(value, offset);
213
214 return new TmfTimestamp(value, scale);
215 }
216
217 @Override
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 // ------------------------------------------------------------------------
236
237 @Override
238 public int compareTo(final ITmfTimestamp ts) {
239 // Check the corner cases (we can't use equals() because it uses
240 // compareTo()...)
241 if (ts == null) {
242 return 1;
243 }
244 if (this == ts || (fValue == ts.getValue() && fScale == ts.getScale())) {
245 return 0;
246 }
247 if ((fValue == BIG_BANG.getValue() && fScale == BIG_BANG.getScale()) || (ts.getValue() == BIG_CRUNCH.getValue() && ts.getScale() == BIG_CRUNCH.getScale())) {
248 return -1;
249 }
250 if ((fValue == BIG_CRUNCH.getValue() && fScale == BIG_CRUNCH.getScale()) || (ts.getValue() == BIG_BANG.getValue() && ts.getScale() == BIG_BANG.getScale())) {
251 return 1;
252 }
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.
256
257 // First, look at the sign of the mantissa
258 final long value = ts.getValue();
259 if (fValue == 0 && value == 0) {
260 return 0;
261 }
262 if (fValue < 0 && value >= 0) {
263 return -1;
264 }
265 if (fValue >= 0 && value < 0) {
266 return 1;
267 }
268
269 // Otherwise, just compare the scales
270 final int scale = ts.getScale();
271 return (fScale > scale) ? (fValue >= 0) ? 1 : -1 : (fValue >= 0) ? -1 : 1;
272 }
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;
325 }
326
327 // ------------------------------------------------------------------------
328 // Object
329 // ------------------------------------------------------------------------
330
331 @Override
332 public int hashCode() {
333 final int prime = 31;
334 int result = 1;
335 result = prime * result + (int) (fValue ^ (fValue >>> 32));
336 result = prime * result + fScale;
337 return result;
338 }
339
340 @Override
341 public boolean equals(final Object other) {
342 if (this == other) {
343 return true;
344 }
345 if (other == null) {
346 return false;
347 }
348 if (!(other instanceof ITmfTimestamp)) {
349 return false;
350 }
351 /* We allow comparing with other types of *I*TmfTimestamp though */
352 final ITmfTimestamp ts = (ITmfTimestamp) other;
353 return (compareTo(ts) == 0);
354 }
355
356 @Override
357 public String toString() {
358 return toString(TmfTimestampFormat.getDefaulTimeFormat());
359 }
360
361 @Override
362 public String toString(final TmfTimestampFormat format) {
363 try {
364 return format.format(toNanos());
365 } catch (ArithmeticException e) {
366 return format.format(0);
367 }
368 }
369
370 /**
371 * Write the time stamp to the ByteBuffer so that it can be saved to disk.
372 *
373 * @param bufferOut
374 * the buffer to write to
375 */
376 public void serialize(ByteBuffer bufferOut) {
377 bufferOut.putLong(fValue);
378 bufferOut.putInt(fScale);
379 }
380 }
This page took 0.039814 seconds and 5 git commands to generate.