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 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 | 28 | public 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 | } |