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