| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2009, 2010 Ericsson |
| 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 |
| 11 | * Thomas Gatterweh - Updated scaling / synchronization |
| 12 | *******************************************************************************/ |
| 13 | |
| 14 | package org.eclipse.linuxtools.tmf.event; |
| 15 | |
| 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> |
| 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 |
| 31 | * timestamps in different scales). Default: 0. |
| 32 | * </ul> |
| 33 | * In short: |
| 34 | * <ul> |
| 35 | * </ul> |
| 36 | * To allow synchronization of timestamps from different reference clocks, |
| 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). |
| 40 | * <p> |
| 41 | * Notice that the adjusted timestamp value could be negative e.g. for events |
| 42 | * that occurred before t0 wrt the reference clock. |
| 43 | */ |
| 44 | public class TmfTimestamp implements Cloneable, Comparable<TmfTimestamp> { |
| 45 | |
| 46 | // ------------------------------------------------------------------------ |
| 47 | // Attributes |
| 48 | // ------------------------------------------------------------------------ |
| 49 | |
| 50 | protected long fValue; // The timestamp raw value |
| 51 | protected byte fScale; // The time scale |
| 52 | protected long fPrecision; // The value precision (tolerance) |
| 53 | |
| 54 | // ------------------------------------------------------------------------ |
| 55 | // Constants |
| 56 | // ------------------------------------------------------------------------ |
| 57 | |
| 58 | // The beginning and end of time |
| 59 | public static final TmfTimestamp BigBang = new TmfTimestamp(Long.MIN_VALUE, Byte.MAX_VALUE, 0); |
| 60 | public static final TmfTimestamp BigCrunch = new TmfTimestamp(Long.MAX_VALUE, Byte.MAX_VALUE, 0); |
| 61 | public static final TmfTimestamp Zero = new TmfTimestamp(0, (byte) 0, 0); |
| 62 | |
| 63 | // ------------------------------------------------------------------------ |
| 64 | // Constructors |
| 65 | // ------------------------------------------------------------------------ |
| 66 | |
| 67 | /** |
| 68 | * Default constructor |
| 69 | */ |
| 70 | public TmfTimestamp() { |
| 71 | this(0, (byte) 0, 0); |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Simple constructor with value only |
| 76 | */ |
| 77 | public TmfTimestamp(long value) { |
| 78 | this(value, (byte) 0, 0); |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Simple constructor with value and scale |
| 83 | * |
| 84 | * @param value |
| 85 | * @param scale |
| 86 | */ |
| 87 | public TmfTimestamp(long value, byte scale) { |
| 88 | this(value, scale, 0); |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Constructor with value, scale and precision |
| 93 | * |
| 94 | * @param value |
| 95 | * @param scale |
| 96 | * @param precision |
| 97 | */ |
| 98 | public TmfTimestamp(long value, byte scale, long precision) { |
| 99 | fValue = value; |
| 100 | fScale = scale; |
| 101 | fPrecision = Math.abs(precision); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Copy constructor |
| 106 | * |
| 107 | * @param other |
| 108 | */ |
| 109 | public TmfTimestamp(TmfTimestamp other) { |
| 110 | if (other == null) |
| 111 | throw new IllegalArgumentException(); |
| 112 | fValue = other.fValue; |
| 113 | fScale = other.fScale; |
| 114 | fPrecision = other.fPrecision; |
| 115 | } |
| 116 | |
| 117 | // ------------------------------------------------------------------------ |
| 118 | // Accessors |
| 119 | // ------------------------------------------------------------------------ |
| 120 | |
| 121 | /** |
| 122 | * @return the timestamp value |
| 123 | */ |
| 124 | public long getValue() { |
| 125 | return fValue; |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * @return the timestamp scale |
| 130 | */ |
| 131 | public byte getScale() { |
| 132 | return fScale; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * @return the timestamp value precision |
| 137 | */ |
| 138 | public long getPrecision() { |
| 139 | return fPrecision; |
| 140 | } |
| 141 | |
| 142 | // ------------------------------------------------------------------------ |
| 143 | // Operators |
| 144 | // ------------------------------------------------------------------------ |
| 145 | |
| 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 | * |
| 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 |
| 158 | */ |
| 159 | public TmfTimestamp synchronize(long offset, byte newScale) throws ArithmeticException { |
| 160 | |
| 161 | long newValue = fValue; |
| 162 | long newPrecision = fPrecision; |
| 163 | |
| 164 | // Handle the easy case |
| 165 | if (fScale == newScale && offset == 0) |
| 166 | return this; |
| 167 | |
| 168 | // Determine the scaling factor |
| 169 | if (fScale != newScale) { |
| 170 | int scaleDiff = Math.abs(fScale - newScale); |
| 171 | // Let's try to be realistic... |
| 172 | if (scaleDiff >= scalingFactors.length) { |
| 173 | throw new ArithmeticException("Scaling exception"); //$NON-NLS-1$ |
| 174 | } |
| 175 | // Adjust the timestamp |
| 176 | long scalingFactor = scalingFactors[scaleDiff]; |
| 177 | if (newScale < fScale) { |
| 178 | newValue *= scalingFactor; |
| 179 | newPrecision *= scalingFactor; |
| 180 | } else { |
| 181 | newValue /= scalingFactor; |
| 182 | newPrecision /= scalingFactor; |
| 183 | } |
| 184 | } |
| 185 | |
| 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); |
| 193 | } |
| 194 | |
| 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 | |
| 244 | /** |
| 245 | * Compute the adjustment, in the reference scale, needed to synchronize |
| 246 | * this timestamp with a reference timestamp. |
| 247 | * |
| 248 | * @param reference the reference timestamp to synchronize with |
| 249 | * @param scale the scale of the adjustment |
| 250 | * @return the adjustment term in the reference time scale |
| 251 | * @throws ArithmeticException |
| 252 | */ |
| 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; |
| 257 | } |
| 258 | |
| 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 | |
| 276 | /** |
| 277 | * Compare with another timestamp |
| 278 | * |
| 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) |
| 282 | * 0: timestamps are equal (within precision if requested) |
| 283 | * 1: this timestamp is higher (i.e. posterior) |
| 284 | */ |
| 285 | public int compareTo(final TmfTimestamp other, boolean withinPrecision) { |
| 286 | |
| 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 | } |
| 294 | |
| 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 | } |
| 308 | |
| 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 | } |
| 338 | } |
| 339 | |
| 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; |
| 350 | } |
| 351 | |
| 352 | // ------------------------------------------------------------------------ |
| 353 | // Object |
| 354 | // ------------------------------------------------------------------------ |
| 355 | |
| 356 | @Override |
| 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 |
| 366 | public boolean equals(Object other) { |
| 367 | if (!(other instanceof TmfTimestamp)) |
| 368 | return false; |
| 369 | TmfTimestamp o = (TmfTimestamp) other; |
| 370 | return compareTo(o, false) == 0; |
| 371 | } |
| 372 | |
| 373 | @Override |
| 374 | @SuppressWarnings("nls") |
| 375 | public String toString() { |
| 376 | return "[TmfTimestamp(" + fValue + "," + fScale + "," + fPrecision + ")]"; |
| 377 | } |
| 378 | |
| 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 | |
| 392 | @Override |
| 393 | public int compareTo(TmfTimestamp o) { |
| 394 | return compareTo(o, false); |
| 395 | } |
| 396 | |
| 397 | } |