import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
// ------------------------------------------------------------------------
private final ITmfTimestamp ts0 = new TmfTimestamp();
- private final ITmfTimestamp ts1 = new TmfTimestamp(12345, 0);
+ private final ITmfTimestamp ts1 = new TmfTimestamp(12345, 0);
private final ITmfTimestamp ts2 = new TmfTimestamp(12345, -1);
- private final ITmfTimestamp ts3 = new TmfTimestamp(12345, 2);
+ private final ITmfTimestamp ts3 = new TmfTimestamp(12345, 2);
private final ITmfTimestamp ts4 = new TmfTimestamp(12345, -3);
private final ITmfTimestamp ts5 = new TmfTimestamp(12345, -6);
private final ITmfTimestamp ts6 = new TmfTimestamp(12345, -9);
private final ITmfTimestamp ts7 = new TmfTimestamp(-12345, -3);
private final ITmfTimestamp ts8 = new TmfTimestamp(-12345, -6);
private final ITmfTimestamp ts9 = new TmfTimestamp(-12345, -9);
+ private final ITmfTimestamp ts10 = new TmfTimestamp(Long.MAX_VALUE / 100, -6);
+ private final ITmfTimestamp ts11 = new TmfTimestamp(Long.MIN_VALUE / 100, -6);
// ------------------------------------------------------------------------
// Constructors
assertEquals("getscale", 2, copy.getScale());
}
- @Test(expected=IllegalArgumentException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testCopyNullConstructor() {
new TmfTimestamp((TmfTimestamp) null);
}
final int MAX_SCALE_DIFF = 19;
// Test below limit
- try {
- ts1.normalize(0, +MAX_SCALE_DIFF - 1);
- ts1.normalize(0, -MAX_SCALE_DIFF + 1);
- } catch (final ArithmeticException e) {
- fail("normalize: scale error");
- }
+ assertEquals(Long.MAX_VALUE, ts1.normalize(0, -MAX_SCALE_DIFF + 1).getValue());
+ assertEquals(0, ts1.normalize(0, +MAX_SCALE_DIFF - 1).getValue());
// Test at limit
- try {
- ts1.normalize(0, +MAX_SCALE_DIFF);
- fail("normalize: scale error");
- ts1.normalize(0, -MAX_SCALE_DIFF);
- fail("normalize: scale error");
- } catch (final ArithmeticException e) {
- }
+ assertEquals(Long.MAX_VALUE, ts1.normalize(0, -MAX_SCALE_DIFF).getValue());
+ assertEquals(0, ts1.normalize(0, +MAX_SCALE_DIFF).getValue());
// Test over limit
- try {
- ts1.normalize(0, +MAX_SCALE_DIFF + 1);
- fail("normalize: scale error");
- ts1.normalize(0, -MAX_SCALE_DIFF - 1);
- fail("normalize: scale error");
- } catch (final ArithmeticException e) {
- }
+ assertEquals(Long.MAX_VALUE, ts1.normalize(0, -MAX_SCALE_DIFF - 1).getValue());
+ assertEquals(0, ts1.normalize(0, +MAX_SCALE_DIFF - 1).getValue());
}
@Test
assertEquals("getscale", SCALE, ts.getScale());
}
+ @Test
+ public void testToNanos() {
+ assertEquals(12345000000000L, ts1.toNanos());
+ assertEquals(-12345, ts9.toNanos());
+ assertEquals(Long.MAX_VALUE, ts10.toNanos());
+ assertEquals(Long.MIN_VALUE, ts11.toNanos());
+ }
+
// ------------------------------------------------------------------------
// compareTo
// ------------------------------------------------------------------------
final ITmfTimestamp ts0b = new TmfTimestamp(0, Integer.MAX_VALUE);
final ITmfTimestamp ts0c = new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE);
- assertTrue("compareTo", ts0a.compareTo(ts0b) == 1);
- assertTrue("compareTo", ts0a.compareTo(ts0c) == -1);
+ assertEquals("compareTo", 1, ts0a.compareTo(ts0b));
+ assertEquals("compareTo", -1, ts0a.compareTo(ts0c));
- assertTrue("compareTo", ts0b.compareTo(ts0a) == -1);
- assertTrue("compareTo", ts0b.compareTo(ts0c) == -1);
+ assertEquals("compareTo", -1, ts0b.compareTo(ts0a));
+ assertEquals("compareTo", -1, ts0b.compareTo(ts0c));
- assertTrue("compareTo", ts0c.compareTo(ts0a) == 1);
- assertTrue("compareTo", ts0c.compareTo(ts0b) == 1);
+ assertEquals("compareTo", 1, ts0c.compareTo(ts0a));
+ assertEquals("compareTo", 1, ts0c.compareTo(ts0b));
}
@Test
final ITmfTimestamp ts0b = new TmfTimestamp(0, Integer.MAX_VALUE);
final ITmfTimestamp ts0c = new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE);
- assertTrue("compareTo", ts0a.compareTo(ts0b) == -1);
- assertTrue("compareTo", ts0a.compareTo(ts0c) == 1);
+ assertEquals("compareTo", -1, ts0a.compareTo(ts0b));
+ assertEquals("compareTo", 1, ts0a.compareTo(ts0c));
- assertTrue("compareTo", ts0b.compareTo(ts0a) == 1);
- assertTrue("compareTo", ts0b.compareTo(ts0c) == 1);
+ assertEquals("compareTo", 1, ts0b.compareTo(ts0a));
+ assertEquals("compareTo", 1, ts0b.compareTo(ts0c));
- assertTrue("compareTo", ts0c.compareTo(ts0a) == -1);
- assertTrue("compareTo", ts0c.compareTo(ts0b) == -1);
+ assertEquals("compareTo", -1, ts0c.compareTo(ts0a));
+ assertEquals("compareTo", -1, ts0c.compareTo(ts0b));
}
@Test
public void testCompareToCornerCases4() {
- assertTrue("compareTo", ts0.compareTo(null) == 1);
+ assertEquals("compareTo", 1, ts0.compareTo(null));
}
@Test
final ITmfTimestamp t3 = new TmfTimestamp(1100, 0);
final ITmfTimestamp t4 = new TmfTimestamp(1000, 0);
- assertTrue(t1.compareTo(t1) == 0);
+ assertEquals(0, t1.compareTo(t1));
assertTrue("CompareTo", t1.compareTo(t2) < 0);
assertTrue("CompareTo", t1.compareTo(t3) < 0);
final ITmfTimestamp t3 = new TmfTimestamp(1, 100);
final ITmfTimestamp t4 = new TmfTimestamp(1000, -100);
- assertTrue("CompareTo", t1.compareTo(t2) < 0);
+ assertEquals("CompareTo", -1, t1.compareTo(t2));
assertTrue("CompareTo", t1.compareTo(t3) < 0);
assertTrue("CompareTo", t1.compareTo(t4) < 0);
final ITmfTimestamp ts0a = new TmfTimestamp(0, Integer.MAX_VALUE);
final ITmfTimestamp ts0b = new TmfTimestamp(1, Integer.MAX_VALUE);
- assertTrue("CompareTo", ts0a.compareTo(ts0) == 0);
- assertTrue("CompareTo", ts0.compareTo(ts0a) == 0);
+ assertEquals("CompareTo", 0, ts0a.compareTo(ts0));
+ assertEquals("CompareTo", 0, ts0.compareTo(ts0a));
- assertTrue("CompareTo", ts0b.compareTo(ts0) == 1);
- assertTrue("CompareTo", ts0.compareTo(ts0b) == -1);
+ assertEquals("CompareTo", 1, ts0b.compareTo(ts0));
+ assertEquals("CompareTo", -1, ts0.compareTo(ts0b));
}
// ------------------------------------------------------------------------
import org.eclipse.jdt.annotation.NonNull;
/**
- * A generic timestamp implementation. The timestamp is represented by the
- * tuple { value, scale, precision }. By default, timestamps are scaled in
- * seconds.
+ * A generic timestamp implementation. The timestamp is represented by the tuple
+ * { value, scale, precision }. By default, timestamps are scaled in seconds.
*
* @author Francois Chouinard
*/
/**
* The beginning of time
*/
- public static final @NonNull ITmfTimestamp BIG_BANG =
- new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE);
+ public static final @NonNull ITmfTimestamp BIG_BANG = new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE);
/**
* The end of time
*/
- public static final @NonNull ITmfTimestamp BIG_CRUNCH =
- new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE);
+ public static final @NonNull ITmfTimestamp BIG_CRUNCH = new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE);
/**
* Zero
*/
- public static final @NonNull ITmfTimestamp ZERO =
- new TmfTimestamp(0, 0);
+ public static final @NonNull ITmfTimestamp ZERO = new TmfTimestamp(0, 0);
// ------------------------------------------------------------------------
// Attributes
}
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,
+ 1L,
+ 10L,
+ 100L,
+ 1000L,
+ 10000L,
+ 100000L,
+ 1000000L,
+ 10000000L,
+ 100000000L,
+ 1000000000L,
+ 10000000000L,
+ 100000000000L,
+ 1000000000000L,
+ 10000000000000L,
+ 100000000000000L,
+ 1000000000000000L,
+ 10000000000000000L,
+ 100000000000000000L,
+ 1000000000000000000L,
};
@Override
public ITmfTimestamp normalize(final long offset, final int scale) {
- long value = fValue;
+ long value = getValue();
// Handle the trivial case
- if (fScale == scale && offset == 0) {
+ if (getScale() == scale && offset == 0) {
return this;
}
- // In case of big bang and big crunch just return this (no need to normalize)
+ // In case of big bang and big crunch just return this (no need to
+ // normalize)
if (this.equals(BIG_BANG) || this.equals(BIG_CRUNCH)) {
return this;
}
+ if (value == 0) {
+ return new TmfTimestamp(offset, scale);
+ }
+
// First, scale the timestamp
- if (fScale != scale) {
- final int scaleDiff = Math.abs(fScale - scale);
+ if (getScale() != scale) {
+ final int scaleDiff = Math.abs(getScale() - scale);
if (scaleDiff >= scalingFactors.length) {
- throw new ArithmeticException("Scaling exception"); //$NON-NLS-1$
- }
-
- final long scalingFactor = scalingFactors[scaleDiff];
- if (scale < fScale) {
- value *= scalingFactor;
+ if (getScale() < scale) {
+ value = 0;
+ } else {
+ value = value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
+ }
} else {
- value /= scalingFactor;
+ final long scalingFactor = scalingFactors[scaleDiff];
+ if (getScale() < scale) {
+ value /= scalingFactor;
+ } else {
+ value = saturatedMult(scalingFactor, value);
+ }
}
}
- // Then, apply the offset
- if (offset < 0) {
- value = (value < Long.MIN_VALUE - offset) ? Long.MIN_VALUE : value + offset;
- } else {
- value = (value > Long.MAX_VALUE - offset) ? Long.MAX_VALUE : value + offset;
- }
+ value = saturatedAdd(value, offset);
return new TmfTimestamp(value, scale);
}
@Override
public int compareTo(final ITmfTimestamp ts) {
- // Check the corner cases (we can't use equals() because it uses compareTo()...)
+ // Check the corner cases (we can't use equals() because it uses
+ // compareTo()...)
if (ts == null) {
return 1;
}
if ((fValue == BIG_CRUNCH.getValue() && fScale == BIG_CRUNCH.getScale()) || (ts.getValue() == BIG_BANG.getValue() && ts.getScale() == BIG_BANG.getScale())) {
return 1;
}
-
- try {
- final ITmfTimestamp nts = ts.normalize(0, fScale);
- final long delta = fValue - nts.getValue();
- return Long.compare(delta, 0);
- }
- catch (final ArithmeticException e) {
+ final ITmfTimestamp nts = ts.normalize(0, fScale);
+ 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)) {
// Scaling error. We can figure it out nonetheless.
// First, look at the sign of the mantissa
final int scale = ts.getScale();
return (fScale > scale) ? (fValue >= 0) ? 1 : -1 : (fValue >= 0) ? -1 : 1;
}
+ final long delta = fValue - nts.getValue();
+ return Long.compare(delta, 0);
+ }
+
+ /**
+ * Saturated multiplication. It will not overflow but instead clamp the
+ * result to {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
+ *
+ * @param left
+ * The left long to multiply
+ * @param right
+ * The right long to multiply
+ * @return The saturated multiplication result. The mathematical, not Java
+ * version of Min(Max(MIN_VALUE, left*right), MAX_VALUE).
+ * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
+ * Saturation arithmetic</a>
+ */
+ private static long saturatedMult(long left, long right) {
+ long retVal = left * right;
+ if ((left != 0) && ((retVal / left) != right)) {
+ return (sameSign(left, right) ? Long.MAX_VALUE : Long.MIN_VALUE);
+ }
+ return retVal;
+ }
+
+ /**
+ * Saturated addition. It will not overflow but instead clamp the result to
+ * {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
+ *
+ * @param left
+ * The left long to add
+ * @param right
+ * The right long to add
+ * @return The saturated addition result. The mathematical, not Java version
+ * of Min(Max(MIN_VALUE, left+right), MAX_VALUE).
+ * @see <a href="http://en.wikipedia.org/wiki/Saturation_arithmetic">
+ * Saturation arithmetic</a>
+ * @since 2.0
+ */
+ protected static final long saturatedAdd(final long left, final long right) {
+ long retVal = left + right;
+ if (sameSign(left, right) && !sameSign(left, retVal)) {
+ if (retVal > 0) {
+ return Long.MIN_VALUE;
+ }
+ return Long.MAX_VALUE;
+ }
+ return retVal;
+ }
+
+ private static boolean sameSign(final long left, final long right) {
+ return (left ^ right) >= 0;
}
// ------------------------------------------------------------------------
public String toString(final TmfTimestampFormat format) {
try {
return format.format(toNanos());
- }
- catch (ArithmeticException e) {
+ } catch (ArithmeticException e) {
return format.format(0);
}
}
/**
* Write the time stamp to the ByteBuffer so that it can be saved to disk.
- * @param bufferOut the buffer to write to
+ *
+ * @param bufferOut
+ * the buffer to write to
*/
public void serialize(ByteBuffer bufferOut) {
bufferOut.putLong(fValue);