/******************************************************************************* * Copyright (c) 2009, 2013 Ericsson, École Polytechnique de Montréal * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Francois Chouinard - Initial API and implementation * Thomas Gatterweh - Updated scaling / synchronization * Francois Chouinard - Refactoring to align with TMF Event Model 1.0 * Francois Chouinard - Implement augmented interface * Geneviève Bastien - Added copy constructor with new value *******************************************************************************/ package fr.inria.linuxtools.tmf.core.timestamp; import java.nio.ByteBuffer; /** * A generic timestamp implementation. The timestamp is represented by the * tuple { value, scale, precision }. By default, timestamps are scaled in * seconds. * * @author Francois Chouinard * @version 1.1 * @since 2.0 */ public class TmfTimestamp implements ITmfTimestamp { // ------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------ /** * The beginning of time */ public static final ITmfTimestamp BIG_BANG = new TmfTimestamp(Long.MIN_VALUE, Integer.MAX_VALUE, 0); /** * The end of time */ public static final ITmfTimestamp BIG_CRUNCH = new TmfTimestamp(Long.MAX_VALUE, Integer.MAX_VALUE, 0); /** * A more practical definition of "beginning of time" */ public static final ITmfTimestamp PROJECT_IS_FUNDED = BIG_BANG; /** * A more practical definition of "end of time" */ public static final ITmfTimestamp PROJECT_IS_CANNED = BIG_CRUNCH; /** * Zero */ public static final ITmfTimestamp ZERO = new TmfTimestamp(0, 0, 0); // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ /** * The timestamp raw value (mantissa) */ private final long fValue; /** * The timestamp scale (magnitude) */ private final int fScale; /** * The value precision (tolerance) */ private final int fPrecision; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Default constructor */ public TmfTimestamp() { this(0, ITmfTimestamp.SECOND_SCALE, 0); } /** * Simple constructor (scale = precision = 0) * * @param value the timestamp value */ public TmfTimestamp(final long value) { this(value, ITmfTimestamp.SECOND_SCALE, 0); } /** * Simple constructor (precision = 0) * * @param value the timestamp value * @param scale the timestamp scale */ public TmfTimestamp(final long value, final int scale) { this(value, scale, 0); } /** * Full constructor * * @param value the timestamp value * @param scale the timestamp scale * @param precision the timestamp precision */ public TmfTimestamp(final long value, final int scale, final int precision) { fValue = value; fScale = scale; fPrecision = Math.abs(precision); } /** * Copy constructor * * @param timestamp the timestamp to copy */ public TmfTimestamp(final ITmfTimestamp timestamp) { if (timestamp == null) { throw new IllegalArgumentException(); } fValue = timestamp.getValue(); fScale = timestamp.getScale(); fPrecision = timestamp.getPrecision(); } /** * Copies a timestamp but with a new time value * * @param timestamp * The timestamp to copy * @param newvalue * The value the new timestamp will have * @since 3.0 */ public TmfTimestamp(ITmfTimestamp timestamp, long newvalue) { if (timestamp == null) { throw new IllegalArgumentException(); } fValue = newvalue; fScale = timestamp.getScale(); fPrecision = timestamp.getPrecision(); } // ------------------------------------------------------------------------ // ITmfTimestamp // ------------------------------------------------------------------------ /** * Construct the timestamp from the ByteBuffer. * * @param bufferIn * the buffer to read from * * @since 3.0 */ public TmfTimestamp(ByteBuffer bufferIn) { this(bufferIn.getLong(), bufferIn.getInt(), bufferIn.getInt()); } @Override public long getValue() { return fValue; } @Override public int getScale() { return fScale; } @Override public int getPrecision() { return fPrecision; } 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, }; @Override public ITmfTimestamp normalize(final long offset, final int scale) { long value = fValue; int precision = fPrecision; // Handle the trivial case if (fScale == scale && offset == 0) { return this; } // 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; } // First, scale the timestamp if (fScale != scale) { final int scaleDiff = Math.abs(fScale - scale); if (scaleDiff >= scalingFactors.length) { throw new ArithmeticException("Scaling exception"); //$NON-NLS-1$ } final long scalingFactor = scalingFactors[scaleDiff]; if (scale < fScale) { value *= scalingFactor; precision *= scalingFactor; } else { value /= scalingFactor; precision /= scalingFactor; } } // 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; } return new TmfTimestamp(value, scale, precision); } @Override public int compareTo(final ITmfTimestamp ts, final boolean withinPrecision) { // Check the corner cases (we can't use equals() because it uses compareTo()...) if (ts == null) { return 1; } if (this == ts || (fValue == ts.getValue() && fScale == ts.getScale())) { return 0; } if ((fValue == BIG_BANG.getValue() && fScale == BIG_BANG.getScale()) || (ts.getValue() == BIG_CRUNCH.getValue() && ts.getScale() == BIG_CRUNCH.getScale())) { 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(); if ((delta == 0) || (withinPrecision && (Math.abs(delta) <= (fPrecision + nts.getPrecision())))) { return 0; } return (delta > 0) ? 1 : -1; } catch (final ArithmeticException e) { // Scaling error. We can figure it out nonetheless. // First, look at the sign of the mantissa final long value = ts.getValue(); if (fValue == 0 && value == 0) { return 0; } if (fValue < 0 && value >= 0) { return -1; } if (fValue >= 0 && value < 0) { return 1; } // Otherwise, just compare the scales final int scale = ts.getScale(); return (fScale > scale) ? (fValue >= 0) ? 1 : -1 : (fValue >= 0) ? -1 : 1; } } @Override public ITmfTimestamp getDelta(final ITmfTimestamp ts) { final ITmfTimestamp nts = ts.normalize(0, fScale); final long value = fValue - nts.getValue(); return new TmfTimestampDelta(value, fScale, fPrecision + nts.getPrecision()); } @Override public boolean intersects(TmfTimeRange range) { if (this.compareTo(range.getStartTime()) >= 0 && this.compareTo(range.getEndTime()) <= 0) { return true; } return false; } // ------------------------------------------------------------------------ // Comparable // ------------------------------------------------------------------------ @Override public int compareTo(final ITmfTimestamp ts) { return compareTo(ts, false); } // ------------------------------------------------------------------------ // Object // ------------------------------------------------------------------------ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (fValue ^ (fValue >>> 32)); result = prime * result + fScale; result = prime * result + fPrecision; return result; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null) { return false; } if (!(other instanceof TmfTimestamp)) { return false; } final TmfTimestamp ts = (TmfTimestamp) other; return compareTo(ts, false) == 0; } @Override public String toString() { return toString(TmfTimestampFormat.getDefaulTimeFormat()); } /** * @since 2.0 */ @Override public String toString(final TmfTimestampFormat format) { try { ITmfTimestamp ts = normalize(0, ITmfTimestamp.NANOSECOND_SCALE); return format.format(ts.getValue()); } 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 * * @since 3.0 */ public void serialize(ByteBuffer bufferOut) { bufferOut.putLong(fValue); bufferOut.putInt(fScale); bufferOut.putInt(fPrecision); } }