/*******************************************************************************
- * Copyright (c) 2009 Ericsson
+ * Copyright (c) 2009, 2010 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
*
* Contributors:
* Francois Chouinard - Initial API and implementation
+ * Thomas Gatterweh - Updated scaling / synchronization
*******************************************************************************/
package org.eclipse.linuxtools.tmf.event;
-
/**
* <b><u>TmfTimestamp</u></b>
* <p>
* <p>
* It provides a generic timestamp implementation in its most basic form:
* <ul>
- * <li>an unstructured integer value
- * <li>a time scale corresponding to the magnitude of the value wrt some
- * application-specific base unit (e.g. the second)
- * <li>a precision to indicate the error on the value (useful for comparing
+ * <li>timestamp = [value] * 10**[scale] +/- [precision]
+ * </ul>
+ * Where:
+ * <ul>
+ * <li>[value] is an unstructured integer value
+ * <li>[scale] is the magnitude of the value wrt some application-specific
+ * base unit (e.g. the second)
+ * <li>[precision] indicates the error on the value (useful for comparing
* timestamps in different scales). Default: 0.
* </ul>
+ * In short:
+ * <ul>
+ * </ul>
* To allow synchronization of timestamps from different reference clocks,
- * there is a possibility to "adjust" the timestamp both by changing its scale
- * (traces of different scale) and by adding an offset to its value (clock
- * drift between traces).
+ * there is a possibility to "adjust" the timestamp by changing its scale
+ * (traces of different time scale) and/or by adding an offset to its value
+ * (clock drift between traces).
* <p>
* Notice that the adjusted timestamp value could be negative e.g. for events
* that occurred before t0 wrt the reference clock.
- * <p>
- * Finally, notice that timestamps are immutable.
*/
-public class TmfTimestamp implements Cloneable {
+public class TmfTimestamp implements Cloneable, Comparable<TmfTimestamp> {
- // ========================================================================
+ // ------------------------------------------------------------------------
// Attributes
- // ========================================================================
+ // ------------------------------------------------------------------------
- protected long fValue; // The timestamp value
+ protected long fValue; // The timestamp raw value
protected byte fScale; // The time scale
protected long fPrecision; // The value precision (tolerance)
- // ========================================================================
+ // ------------------------------------------------------------------------
// Constants
- // ========================================================================
+ // ------------------------------------------------------------------------
// The beginning and end of time
public static final TmfTimestamp BigBang = new TmfTimestamp(Long.MIN_VALUE, Byte.MAX_VALUE, 0);
public static final TmfTimestamp BigCrunch = new TmfTimestamp(Long.MAX_VALUE, Byte.MAX_VALUE, 0);
+ public static final TmfTimestamp Zero = new TmfTimestamp(0, (byte) 0, 0);
- // ========================================================================
+ // ------------------------------------------------------------------------
// Constructors
- // ========================================================================
+ // ------------------------------------------------------------------------
/**
* Default constructor
* @param other
*/
public TmfTimestamp(TmfTimestamp other) {
- assert(other != null);
+ if (other == null)
+ throw new IllegalArgumentException();
fValue = other.fValue;
fScale = other.fScale;
fPrecision = other.fPrecision;
}
- // ========================================================================
+ // ------------------------------------------------------------------------
// Accessors
- // ========================================================================
+ // ------------------------------------------------------------------------
/**
* @return the timestamp value
return fPrecision;
}
- // ========================================================================
+ // ------------------------------------------------------------------------
// Operators
- // ========================================================================
+ // ------------------------------------------------------------------------
/**
* Return a shifted and scaled timestamp.
* meaning beyond that scale difference and it's not even worth the trouble
* to switch to BigDecimal arithmetics.
*
- * @param offset
- * - the shift value (in the same scale as newScale)
- * @param newScale
- * - the new scale
- * @return The synchronized timestamp
+ * @param offset the shift value (in the same scale as newScale)
+ * @param newScale the new timestamp scale
+ * @return the synchronized timestamp in the new scale
+ * @throws ArithmeticException
*/
-
- /*
- * A java <code>long</code> has a maximum of 19 significant digits.
- * (-9,223,372,036,854,775,808 .. +9,223,372,036,854,775,807)
- *
- * It is therefore useless to try to synchronize 2 timestamps whose
- * difference in scale exceeds that value.
- */
- private static int MAX_SCALING = 19;
-
public TmfTimestamp synchronize(long offset, byte newScale) throws ArithmeticException {
+
long newValue = fValue;
long newPrecision = fPrecision;
+ // Handle the easy case
+ if (fScale == newScale && offset == 0)
+ return this;
+
// Determine the scaling factor
if (fScale != newScale) {
int scaleDiff = Math.abs(fScale - newScale);
// Let's try to be realistic...
- if (scaleDiff > MAX_SCALING) {
- throw new ArithmeticException("Scaling exception");
- }
- // Not pretty...
- long scalingFactor = 1;
- for (int i = 0; i < scaleDiff; i++) {
- scalingFactor *= 10;
+ if (scaleDiff >= scalingFactors.length) {
+ throw new ArithmeticException("Scaling exception"); //$NON-NLS-1$
}
+ // Adjust the timestamp
+ long scalingFactor = scalingFactors[scaleDiff];
if (newScale < fScale) {
newValue *= scalingFactor;
newPrecision *= scalingFactor;
}
}
- return new TmfTimestamp(newValue + offset, newScale, newPrecision);
+ if (offset < 0) {
+ newValue = (newValue < Long.MIN_VALUE - offset) ? Long.MIN_VALUE : newValue + offset;
+ } else {
+ newValue = (newValue > Long.MAX_VALUE - offset) ? Long.MAX_VALUE : newValue + offset;
+ }
+
+ return new TmfTimestamp(newValue, newScale, newPrecision);
}
+ private static final long scalingFactors[] = new long[] {
+ 1L,
+ 10L,
+ 100L,
+ 1000L,
+ 10000L,
+ 100000L,
+ 1000000L,
+ 10000000L,
+ 100000000L,
+ 1000000000L,
+ 10000000000L,
+ 100000000000L,
+ 1000000000000L,
+ 10000000000000L,
+ 100000000000000L,
+ 1000000000000000L,
+ 10000000000000000L,
+ 100000000000000000L,
+ 1000000000000000000L,
+ };
+
+ private static final long scalingLimits[] = new long[] {
+ Long.MAX_VALUE / 1L,
+ Long.MAX_VALUE / 10L,
+ Long.MAX_VALUE / 100L,
+ Long.MAX_VALUE / 1000L,
+ Long.MAX_VALUE / 10000L,
+ Long.MAX_VALUE / 100000L,
+ Long.MAX_VALUE / 1000000L,
+ Long.MAX_VALUE / 10000000L,
+ Long.MAX_VALUE / 100000000L,
+ Long.MAX_VALUE / 1000000000L,
+ Long.MAX_VALUE / 10000000000L,
+ Long.MAX_VALUE / 100000000000L,
+ Long.MAX_VALUE / 1000000000000L,
+ Long.MAX_VALUE / 10000000000000L,
+ Long.MAX_VALUE / 100000000000000L,
+ Long.MAX_VALUE / 1000000000000000L,
+ Long.MAX_VALUE / 10000000000000000L,
+ Long.MAX_VALUE / 100000000000000000L,
+ Long.MAX_VALUE / 1000000000000000000L,
+ };
+
+ public static long getScalingFactor(byte scale)
+ {
+ return scalingFactors[scale];
+ }
+
/**
* Compute the adjustment, in the reference scale, needed to synchronize
* this timestamp with a reference timestamp.
*
- * @param reference
- * - the reference timestamp to synchronize with
+ * @param reference the reference timestamp to synchronize with
+ * @param scale the scale of the adjustment
* @return the adjustment term in the reference time scale
- * @throws TmfNumericalException
+ * @throws ArithmeticException
+ */
+ public long getAdjustment(TmfTimestamp reference, byte scale) throws ArithmeticException {
+ TmfTimestamp ts1 = synchronize(0, scale);
+ TmfTimestamp ts2 = reference.synchronize(0, scale);
+ return ts2.fValue - ts1.fValue;
+ }
+
+ /**
+ * Compute the delta between two timestamps (adjusted to scale of current timestamp).
+ *
+ * @param reference the reference timestamp to synchronize with
+ * @return the delta timestamp
+ * @throws ArithmeticException
*/
- public long getAdjustment(TmfTimestamp reference) throws ArithmeticException {
- TmfTimestamp ts = synchronize(0, reference.fScale);
- return reference.fValue - ts.fValue;
+ public TmfTimestamp getDelta(TmfTimestamp other) throws ArithmeticException {
+ TmfTimestamp newSecond = other;
+ if ((fScale != other.fScale) || (fPrecision != other.fPrecision)) {
+ newSecond = other.synchronize(0, fScale);
+ }
+ return new TmfTimestamp(fValue - newSecond.fValue,
+ fScale,
+ newSecond.fPrecision > fPrecision ? newSecond.fPrecision : fPrecision);
}
/**
* Compare with another timestamp
*
- * @param other
- * - the other timestamp
- * @param withinPrecision
- * - indicates if precision is to be take into consideration
- * @return -1: this timestamp is lower
+ * @param other the other timestamp
+ * @param withinPrecision indicates if precision is to be take into consideration
+ * @return -1: this timestamp is lower (i.e. anterior)
* 0: timestamps are equal (within precision if requested)
- * 1: this timestamp is higher
- * @throws TmfNumericalException
+ * 1: this timestamp is higher (i.e. posterior)
*/
public int compareTo(final TmfTimestamp other, boolean withinPrecision) {
- // If values have the same time scale, perform the comparison
- if (fScale == other.fScale) {
- if (withinPrecision) {
- if ((fValue + fPrecision) < (other.fValue - other.fPrecision))
- return -1;
- if ((fValue - fPrecision) > (other.fValue + other.fPrecision))
- return 1;
- return 0;
- }
- return (fValue == other.fValue) ? 0 : (fValue < other.fValue) ? -1
- : 1;
- }
+ // If values have the same time scale, perform the comparison
+ if (fScale == other.fScale) {
+ if (withinPrecision)
+ return compareWithinPrecision(this.fValue, this.fPrecision, other.fValue, other.fPrecision);
+ else
+ return compareNoPrecision(this.fValue, other.fValue);
+ }
- // If values have different time scales, adjust to the finest one and
- // then compare. If the scaling difference is too large, revert to
- // some heuristics. Hopefully, nobody will try to compare galactic and
- // quantic clock events...
- byte newScale = (fScale < other.fScale) ? fScale : other.fScale;
- try {
- TmfTimestamp ts1 = this.synchronize(0, newScale);
- TmfTimestamp ts2 = other.synchronize(0, newScale);
- return ts1.compareTo(ts2, withinPrecision);
- } catch (ArithmeticException e) {
- if ((fValue == 0) || (other.fValue == 0)) {
- return (fValue == other.fValue) ? 0
- : (fValue < other.fValue) ? -1 : 1;
- }
- if ((fValue > 0) && (other.fValue > 0)) {
- return (fScale < other.fScale) ? -1 : 1;
- }
- if ((fValue < 0) && (other.fValue < 0)) {
- return (fScale > other.fScale) ? -1 : 1;
- }
- return (fValue < 0) ? -1 : 1;
- }
+ // If values have different time scales, adjust to the finest one and
+ // then compare. If the scaling difference is too large, revert to
+ // some heuristics. Hopefully, nobody will try to compare galactic and
+ // quantic clock events...
+ int scaleDiff = Math.abs(fScale - other.fScale);
+ long factor, limit;
+ if (scaleDiff < scalingFactors.length) {
+ factor = scalingFactors[scaleDiff];
+ limit = scalingLimits[scaleDiff];
+ } else {
+ factor = 0;
+ limit = 0; // !!! 0 can always be scaled!!!
+ }
+
+ if (fScale < other.fScale) {
+ // this has finer scale, so other should be scaled
+ if (withinPrecision)
+ if (other.fValue > limit || other.fValue < -limit
+ || other.fPrecision > limit
+ || other.fPrecision < -limit)
+ return other.fValue > 0 ? -1 : +1; // other exceeds scaling limit
+ else
+ return compareWithinPrecision(this.fValue, this.fPrecision,
+ other.fValue * factor, other.fPrecision * factor);
+ else if (other.fValue > limit || other.fValue < -limit)
+ return other.fValue > 0 ? -1 : +1; // other exceeds scaling limit
+ else
+ return compareNoPrecision(this.fValue, other.fValue * factor);
+ } else {
+ // other has finer scale, so this should be scaled
+ if (withinPrecision)
+ if (this.fValue > limit || this.fValue < -limit
+ || this.fPrecision > limit || this.fPrecision < -limit)
+ return this.fValue > 0 ? +1 : -1; // we exceed scaling limit
+ else
+ return compareWithinPrecision(this.fValue * factor,
+ this.fPrecision * factor, other.fValue,
+ other.fPrecision);
+ else if (this.fValue > limit || this.fValue < -limit)
+ return this.fValue > 0 ? +1 : -1; // we exceed scaling limit
+ else
+ return compareNoPrecision(this.fValue * factor, other.fValue);
+ }
}
- @Override
- public TmfTimestamp clone() {
- return new TmfTimestamp(this);
+ private static int compareNoPrecision(long thisValue, long otherValue) {
+ return (thisValue == otherValue) ? 0 : (thisValue < otherValue) ? -1 : 1;
+ }
+
+ private static int compareWithinPrecision(long thisValue, long thisPrecision, long otherValue, long otherPrecision) {
+ if ((thisValue + thisPrecision) < (otherValue - otherPrecision))
+ return -1;
+ if ((thisValue - thisPrecision) > (otherValue + otherPrecision))
+ return 1;
+ return 0;
}
+ // ------------------------------------------------------------------------
+ // Object
+ // ------------------------------------------------------------------------
+
@Override
+ public int hashCode() {
+ int result = 17;
+ result = 37 * result + (int) (fValue ^ (fValue >>> 32));
+ result = 37 * result + fScale;
+ result = 37 * result + (int) (fPrecision ^ (fPrecision >>> 32));
+ return result;
+ }
+
+ @Override
public boolean equals(Object other) {
- if (other instanceof TmfTimestamp)
- return compareTo((TmfTimestamp) other, false) == 0;
- return super.equals(other);
+ if (!(other instanceof TmfTimestamp))
+ return false;
+ TmfTimestamp o = (TmfTimestamp) other;
+ return compareTo(o, false) == 0;
}
@Override
+ @SuppressWarnings("nls")
public String toString() {
return "[TmfTimestamp(" + fValue + "," + fScale + "," + fPrecision + ")]";
}
-}
\ No newline at end of file
+ @Override
+ public TmfTimestamp clone() {
+ TmfTimestamp clone = null;
+ try {
+ clone = (TmfTimestamp) super.clone();
+ clone.fValue = fValue;
+ clone.fScale = fScale;
+ clone.fPrecision = fPrecision;
+ } catch (CloneNotSupportedException e) {
+ }
+ return clone;
+ }
+
+ @Override
+ public int compareTo(TmfTimestamp o) {
+ return compareTo(o, false);
+ }
+
+}