Commit | Line | Data |
---|---|---|
8c8bf09f | 1 | /******************************************************************************* |
cbd4ad82 | 2 | * Copyright (c) 2009, 2010 Ericsson |
8c8bf09f ASL |
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: | |
1f506a43 | 10 | * Francois Chouinard - Initial API and implementation |
023761c4 | 11 | * Thomas Gatterweh - Updated scaling / synchronization |
8c8bf09f ASL |
12 | *******************************************************************************/ |
13 | ||
14 | package org.eclipse.linuxtools.tmf.event; | |
15 | ||
8c8bf09f ASL |
16 | /** |
17 | * <b><u>TmfTimestamp</u></b> | |
18 | * <p> | |
19 | * The fundamental time reference in the TMF. | |
20 | * <p> | |
21 | * It provides a generic timestamp implementation in its most basic form: | |
22 | * <ul> | |
cbd4ad82 FC |
23 | * <li>timestamp = [value] * 10**[scale] +/- [precision] |
24 | * </ul> | |
25 | * Where: | |
26 | * <ul> | |
27 | * <li>[value] is an unstructured integer value | |
28 | * <li>[scale] is the magnitude of the value wrt some application-specific | |
29 | * base unit (e.g. the second) | |
30 | * <li>[precision] indicates the error on the value (useful for comparing | |
1f506a43 | 31 | * timestamps in different scales). Default: 0. |
8c8bf09f | 32 | * </ul> |
cbd4ad82 FC |
33 | * In short: |
34 | * <ul> | |
35 | * </ul> | |
28b94d61 | 36 | * To allow synchronization of timestamps from different reference clocks, |
cbd4ad82 FC |
37 | * there is a possibility to "adjust" the timestamp by changing its scale |
38 | * (traces of different time scale) and/or by adding an offset to its value | |
39 | * (clock drift between traces). | |
8c8bf09f | 40 | * <p> |
28b94d61 FC |
41 | * Notice that the adjusted timestamp value could be negative e.g. for events |
42 | * that occurred before t0 wrt the reference clock. | |
8c8bf09f | 43 | */ |
a79913eb | 44 | public class TmfTimestamp implements Cloneable, Comparable<TmfTimestamp> { |
98029bc9 | 45 | |
cbd4ad82 | 46 | // ------------------------------------------------------------------------ |
8c8bf09f | 47 | // Attributes |
cbd4ad82 | 48 | // ------------------------------------------------------------------------ |
8c8bf09f | 49 | |
cbd4ad82 | 50 | protected long fValue; // The timestamp raw value |
28b94d61 FC |
51 | protected byte fScale; // The time scale |
52 | protected long fPrecision; // The value precision (tolerance) | |
8c8bf09f | 53 | |
cbd4ad82 | 54 | // ------------------------------------------------------------------------ |
8c8bf09f | 55 | // Constants |
cbd4ad82 | 56 | // ------------------------------------------------------------------------ |
8c8bf09f ASL |
57 | |
58 | // The beginning and end of time | |
146a887c | 59 | public static final TmfTimestamp BigBang = new TmfTimestamp(Long.MIN_VALUE, Byte.MAX_VALUE, 0); |
8c8bf09f | 60 | public static final TmfTimestamp BigCrunch = new TmfTimestamp(Long.MAX_VALUE, Byte.MAX_VALUE, 0); |
e31e01e8 | 61 | public static final TmfTimestamp Zero = new TmfTimestamp(0, (byte) 0, 0); |
8c8bf09f | 62 | |
cbd4ad82 | 63 | // ------------------------------------------------------------------------ |
8c8bf09f | 64 | // Constructors |
cbd4ad82 | 65 | // ------------------------------------------------------------------------ |
8c8bf09f ASL |
66 | |
67 | /** | |
28b94d61 | 68 | * Default constructor |
8c8bf09f ASL |
69 | */ |
70 | public TmfTimestamp() { | |
71 | this(0, (byte) 0, 0); | |
72 | } | |
73 | ||
1f506a43 | 74 | /** |
28b94d61 | 75 | * Simple constructor with value only |
1f506a43 FC |
76 | */ |
77 | public TmfTimestamp(long value) { | |
78 | this(value, (byte) 0, 0); | |
79 | } | |
80 | ||
8c8bf09f | 81 | /** |
28b94d61 | 82 | * Simple constructor with value and scale |
8c8bf09f | 83 | * |
1f506a43 FC |
84 | * @param value |
85 | * @param scale | |
8c8bf09f ASL |
86 | */ |
87 | public TmfTimestamp(long value, byte scale) { | |
88 | this(value, scale, 0); | |
89 | } | |
90 | ||
91 | /** | |
28b94d61 | 92 | * Constructor with value, scale and precision |
8c8bf09f | 93 | * |
1f506a43 FC |
94 | * @param value |
95 | * @param scale | |
96 | * @param precision | |
8c8bf09f ASL |
97 | */ |
98 | public TmfTimestamp(long value, byte scale, long precision) { | |
99 | fValue = value; | |
100 | fScale = scale; | |
101 | fPrecision = Math.abs(precision); | |
102 | } | |
103 | ||
104 | /** | |
28b94d61 | 105 | * Copy constructor |
8c8bf09f | 106 | * |
1f506a43 | 107 | * @param other |
8c8bf09f ASL |
108 | */ |
109 | public TmfTimestamp(TmfTimestamp other) { | |
cbd4ad82 FC |
110 | if (other == null) |
111 | throw new IllegalArgumentException(); | |
28b94d61 FC |
112 | fValue = other.fValue; |
113 | fScale = other.fScale; | |
114 | fPrecision = other.fPrecision; | |
8c8bf09f ASL |
115 | } |
116 | ||
cbd4ad82 | 117 | // ------------------------------------------------------------------------ |
8c8bf09f | 118 | // Accessors |
cbd4ad82 | 119 | // ------------------------------------------------------------------------ |
8c8bf09f ASL |
120 | |
121 | /** | |
28b94d61 | 122 | * @return the timestamp value |
8c8bf09f ASL |
123 | */ |
124 | public long getValue() { | |
125 | return fValue; | |
126 | } | |
127 | ||
128 | /** | |
28b94d61 | 129 | * @return the timestamp scale |
8c8bf09f ASL |
130 | */ |
131 | public byte getScale() { | |
132 | return fScale; | |
133 | } | |
134 | ||
135 | /** | |
28b94d61 | 136 | * @return the timestamp value precision |
8c8bf09f ASL |
137 | */ |
138 | public long getPrecision() { | |
139 | return fPrecision; | |
140 | } | |
141 | ||
cbd4ad82 | 142 | // ------------------------------------------------------------------------ |
8c8bf09f | 143 | // Operators |
cbd4ad82 | 144 | // ------------------------------------------------------------------------ |
4ab33d2b | 145 | |
8c8bf09f ASL |
146 | /** |
147 | * Return a shifted and scaled timestamp. | |
148 | * | |
149 | * Limitation: The scaling is limited to MAX_SCALING orders of magnitude. | |
150 | * The main reason is that the 64 bits value starts to lose any significance | |
151 | * meaning beyond that scale difference and it's not even worth the trouble | |
152 | * to switch to BigDecimal arithmetics. | |
153 | * | |
cbd4ad82 FC |
154 | * @param offset the shift value (in the same scale as newScale) |
155 | * @param newScale the new timestamp scale | |
156 | * @return the synchronized timestamp in the new scale | |
157 | * @throws ArithmeticException | |
8c8bf09f | 158 | */ |
cbd4ad82 | 159 | public TmfTimestamp synchronize(long offset, byte newScale) throws ArithmeticException { |
8c8bf09f | 160 | |
023761c4 | 161 | long newValue = fValue; |
8c8bf09f ASL |
162 | long newPrecision = fPrecision; |
163 | ||
023761c4 FC |
164 | // Handle the easy case |
165 | if (fScale == newScale && offset == 0) | |
166 | return this; | |
167 | ||
8c8bf09f ASL |
168 | // Determine the scaling factor |
169 | if (fScale != newScale) { | |
170 | int scaleDiff = Math.abs(fScale - newScale); | |
171 | // Let's try to be realistic... | |
023761c4 | 172 | if (scaleDiff >= scalingFactors.length) { |
3b38ea61 | 173 | throw new ArithmeticException("Scaling exception"); //$NON-NLS-1$ |
8c8bf09f | 174 | } |
023761c4 FC |
175 | // Adjust the timestamp |
176 | long scalingFactor = scalingFactors[scaleDiff]; | |
8c8bf09f ASL |
177 | if (newScale < fScale) { |
178 | newValue *= scalingFactor; | |
179 | newPrecision *= scalingFactor; | |
180 | } else { | |
181 | newValue /= scalingFactor; | |
182 | newPrecision /= scalingFactor; | |
183 | } | |
184 | } | |
185 | ||
023761c4 FC |
186 | if (offset < 0) { |
187 | newValue = (newValue < Long.MIN_VALUE - offset) ? Long.MIN_VALUE : newValue + offset; | |
188 | } else { | |
189 | newValue = (newValue > Long.MAX_VALUE - offset) ? Long.MAX_VALUE : newValue + offset; | |
190 | } | |
191 | ||
192 | return new TmfTimestamp(newValue, newScale, newPrecision); | |
8c8bf09f ASL |
193 | } |
194 | ||
023761c4 FC |
195 | private static final long scalingFactors[] = new long[] { |
196 | 1L, | |
197 | 10L, | |
198 | 100L, | |
199 | 1000L, | |
200 | 10000L, | |
201 | 100000L, | |
202 | 1000000L, | |
203 | 10000000L, | |
204 | 100000000L, | |
205 | 1000000000L, | |
206 | 10000000000L, | |
207 | 100000000000L, | |
208 | 1000000000000L, | |
209 | 10000000000000L, | |
210 | 100000000000000L, | |
211 | 1000000000000000L, | |
212 | 10000000000000000L, | |
213 | 100000000000000000L, | |
214 | 1000000000000000000L, | |
215 | }; | |
216 | ||
217 | private static final long scalingLimits[] = new long[] { | |
218 | Long.MAX_VALUE / 1L, | |
219 | Long.MAX_VALUE / 10L, | |
220 | Long.MAX_VALUE / 100L, | |
221 | Long.MAX_VALUE / 1000L, | |
222 | Long.MAX_VALUE / 10000L, | |
223 | Long.MAX_VALUE / 100000L, | |
224 | Long.MAX_VALUE / 1000000L, | |
225 | Long.MAX_VALUE / 10000000L, | |
226 | Long.MAX_VALUE / 100000000L, | |
227 | Long.MAX_VALUE / 1000000000L, | |
228 | Long.MAX_VALUE / 10000000000L, | |
229 | Long.MAX_VALUE / 100000000000L, | |
230 | Long.MAX_VALUE / 1000000000000L, | |
231 | Long.MAX_VALUE / 10000000000000L, | |
232 | Long.MAX_VALUE / 100000000000000L, | |
233 | Long.MAX_VALUE / 1000000000000000L, | |
234 | Long.MAX_VALUE / 10000000000000000L, | |
235 | Long.MAX_VALUE / 100000000000000000L, | |
236 | Long.MAX_VALUE / 1000000000000000000L, | |
237 | }; | |
238 | ||
239 | public static long getScalingFactor(byte scale) | |
240 | { | |
241 | return scalingFactors[scale]; | |
242 | } | |
243 | ||
8c8bf09f ASL |
244 | /** |
245 | * Compute the adjustment, in the reference scale, needed to synchronize | |
1f506a43 | 246 | * this timestamp with a reference timestamp. |
8c8bf09f | 247 | * |
cbd4ad82 FC |
248 | * @param reference the reference timestamp to synchronize with |
249 | * @param scale the scale of the adjustment | |
28b94d61 | 250 | * @return the adjustment term in the reference time scale |
cbd4ad82 | 251 | * @throws ArithmeticException |
8c8bf09f | 252 | */ |
cbd4ad82 FC |
253 | public long getAdjustment(TmfTimestamp reference, byte scale) throws ArithmeticException { |
254 | TmfTimestamp ts1 = synchronize(0, scale); | |
255 | TmfTimestamp ts2 = reference.synchronize(0, scale); | |
256 | return ts2.fValue - ts1.fValue; | |
8c8bf09f ASL |
257 | } |
258 | ||
73005152 BH |
259 | /** |
260 | * Compute the delta between two timestamps (adjusted to scale of current timestamp). | |
261 | * | |
262 | * @param reference the reference timestamp to synchronize with | |
263 | * @return the delta timestamp | |
264 | * @throws ArithmeticException | |
265 | */ | |
266 | public TmfTimestamp getDelta(TmfTimestamp other) throws ArithmeticException { | |
267 | TmfTimestamp newSecond = other; | |
268 | if ((fScale != other.fScale) || (fPrecision != other.fPrecision)) { | |
269 | newSecond = other.synchronize(0, fScale); | |
270 | } | |
271 | return new TmfTimestamp(fValue - newSecond.fValue, | |
272 | fScale, | |
273 | newSecond.fPrecision > fPrecision ? newSecond.fPrecision : fPrecision); | |
274 | } | |
275 | ||
8c8bf09f ASL |
276 | /** |
277 | * Compare with another timestamp | |
278 | * | |
cbd4ad82 FC |
279 | * @param other the other timestamp |
280 | * @param withinPrecision indicates if precision is to be take into consideration | |
281 | * @return -1: this timestamp is lower (i.e. anterior) | |
28b94d61 | 282 | * 0: timestamps are equal (within precision if requested) |
cbd4ad82 | 283 | * 1: this timestamp is higher (i.e. posterior) |
8c8bf09f ASL |
284 | */ |
285 | public int compareTo(final TmfTimestamp other, boolean withinPrecision) { | |
286 | ||
023761c4 FC |
287 | // If values have the same time scale, perform the comparison |
288 | if (fScale == other.fScale) { | |
289 | if (withinPrecision) | |
290 | return compareWithinPrecision(this.fValue, this.fPrecision, other.fValue, other.fPrecision); | |
291 | else | |
292 | return compareNoPrecision(this.fValue, other.fValue); | |
293 | } | |
8c8bf09f | 294 | |
023761c4 FC |
295 | // If values have different time scales, adjust to the finest one and |
296 | // then compare. If the scaling difference is too large, revert to | |
297 | // some heuristics. Hopefully, nobody will try to compare galactic and | |
298 | // quantic clock events... | |
299 | int scaleDiff = Math.abs(fScale - other.fScale); | |
300 | long factor, limit; | |
301 | if (scaleDiff < scalingFactors.length) { | |
302 | factor = scalingFactors[scaleDiff]; | |
303 | limit = scalingLimits[scaleDiff]; | |
304 | } else { | |
305 | factor = 0; | |
306 | limit = 0; // !!! 0 can always be scaled!!! | |
307 | } | |
f3a4c7f4 | 308 | |
023761c4 FC |
309 | if (fScale < other.fScale) { |
310 | // this has finer scale, so other should be scaled | |
311 | if (withinPrecision) | |
312 | if (other.fValue > limit || other.fValue < -limit | |
313 | || other.fPrecision > limit | |
314 | || other.fPrecision < -limit) | |
315 | return other.fValue > 0 ? -1 : +1; // other exceeds scaling limit | |
316 | else | |
317 | return compareWithinPrecision(this.fValue, this.fPrecision, | |
318 | other.fValue * factor, other.fPrecision * factor); | |
319 | else if (other.fValue > limit || other.fValue < -limit) | |
320 | return other.fValue > 0 ? -1 : +1; // other exceeds scaling limit | |
321 | else | |
322 | return compareNoPrecision(this.fValue, other.fValue * factor); | |
323 | } else { | |
324 | // other has finer scale, so this should be scaled | |
325 | if (withinPrecision) | |
326 | if (this.fValue > limit || this.fValue < -limit | |
327 | || this.fPrecision > limit || this.fPrecision < -limit) | |
328 | return this.fValue > 0 ? +1 : -1; // we exceed scaling limit | |
329 | else | |
330 | return compareWithinPrecision(this.fValue * factor, | |
331 | this.fPrecision * factor, other.fValue, | |
332 | other.fPrecision); | |
333 | else if (this.fValue > limit || this.fValue < -limit) | |
334 | return this.fValue > 0 ? +1 : -1; // we exceed scaling limit | |
335 | else | |
336 | return compareNoPrecision(this.fValue * factor, other.fValue); | |
337 | } | |
8c8bf09f ASL |
338 | } |
339 | ||
023761c4 FC |
340 | private static int compareNoPrecision(long thisValue, long otherValue) { |
341 | return (thisValue == otherValue) ? 0 : (thisValue < otherValue) ? -1 : 1; | |
342 | } | |
343 | ||
344 | private static int compareWithinPrecision(long thisValue, long thisPrecision, long otherValue, long otherPrecision) { | |
345 | if ((thisValue + thisPrecision) < (otherValue - otherPrecision)) | |
346 | return -1; | |
347 | if ((thisValue - thisPrecision) > (otherValue + otherPrecision)) | |
348 | return 1; | |
349 | return 0; | |
f3a4c7f4 FC |
350 | } |
351 | ||
cbd4ad82 FC |
352 | // ------------------------------------------------------------------------ |
353 | // Object | |
354 | // ------------------------------------------------------------------------ | |
28b94d61 | 355 | |
8c8bf09f | 356 | @Override |
cbd4ad82 FC |
357 | public int hashCode() { |
358 | int result = 17; | |
359 | result = 37 * result + (int) (fValue ^ (fValue >>> 32)); | |
360 | result = 37 * result + fScale; | |
361 | result = 37 * result + (int) (fPrecision ^ (fPrecision >>> 32)); | |
362 | return result; | |
363 | } | |
364 | ||
365 | @Override | |
8c8bf09f | 366 | public boolean equals(Object other) { |
cbd4ad82 FC |
367 | if (!(other instanceof TmfTimestamp)) |
368 | return false; | |
369 | TmfTimestamp o = (TmfTimestamp) other; | |
370 | return compareTo(o, false) == 0; | |
8c8bf09f ASL |
371 | } |
372 | ||
1f506a43 | 373 | @Override |
3b38ea61 | 374 | @SuppressWarnings("nls") |
1f506a43 | 375 | public String toString() { |
28b94d61 | 376 | return "[TmfTimestamp(" + fValue + "," + fScale + "," + fPrecision + ")]"; |
1f506a43 FC |
377 | } |
378 | ||
ff4ed569 FC |
379 | @Override |
380 | public TmfTimestamp clone() { | |
381 | TmfTimestamp clone = null; | |
382 | try { | |
383 | clone = (TmfTimestamp) super.clone(); | |
384 | clone.fValue = fValue; | |
385 | clone.fScale = fScale; | |
386 | clone.fPrecision = fPrecision; | |
387 | } catch (CloneNotSupportedException e) { | |
388 | } | |
389 | return clone; | |
390 | } | |
391 | ||
a79913eb FC |
392 | @Override |
393 | public int compareTo(TmfTimestamp o) { | |
394 | return compareTo(o, false); | |
395 | } | |
396 | ||
023761c4 | 397 | } |