blob: 083464f8ff550343cff7ab282404bf3aa518e9d0 [file] [log] [blame]
package net.onrc.onos.core.util.distributed.sharedlog;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.concurrent.Immutable;
import com.google.common.annotations.Beta;
import com.google.common.primitives.Longs;
import com.google.common.primitives.UnsignedLong;
import com.google.common.primitives.UnsignedLongs;
// TODO register to Kryo
/**
* Sequence number used as log entry key.
*/
@Beta
@Immutable
public final class SeqNum extends Number implements Comparable<SeqNum> {
/**
* Long value reserved for {@link #INITIAL}.
*/
public static final long ZERO = 0L;
/**
* Special sequence number which will never have LogValue.
*/
public static final SeqNum INITIAL = new SeqNum(ZERO);
private final long seq;
/**
* Constructor.
*
* @param number sequence number.
*/
protected SeqNum(final long number) {
this.seq = number;
}
/**
* Gets an instance for specified number.
*
* @param number must not be {@link #ZERO}
* @return SeqNum
*
* @throws IllegalArgumentException if given number was {@link #ZERO}
*/
public static SeqNum valueOf(final long number) {
checkArgument(number != ZERO);
return new SeqNum(number);
}
// TODO give better name?
/**
* Gets an instance for specified number.
*
* @param number sequence number. Can be {@link #ZERO}
* @return SeqNum
*/
public static SeqNum anyValueOf(final long number) {
return new SeqNum(number);
}
/**
* Gets an instance for specified string.
*
* @param str parsed result must not be {@link #ZERO}
* @return SeqNum
*/
public static SeqNum valueOf(final String str) {
return valueOf(UnsignedLongs.decode(str));
}
/**
* Gets an instance for specified string.
*
* @param str string representation of unsigned long
* @return SeqNum
*/
public static SeqNum anyValueOf(final String str) {
return anyValueOf(UnsignedLongs.decode(str));
}
/**
* Gets the next sequence number.
* <p>
* WARN: This is not a atomic sequencer,
* this method just returns the next number in sequence.
*
* @return next sequence number
*/
public SeqNum next() {
return step(1);
}
/**
* Gets the previous sequence number.
* <p>
* WARN: This is not a atomic sequencer,
* this method just returns the previous number in sequence.
*
* @return prev sequence number
*/
public SeqNum prev() {
return step(-1);
}
/**
* Gets the sequence number stepping forward/backward by {@code delta}.
*
* @param delta step
* @return sequence number
*/
public SeqNum step(long delta) {
long next = seq + delta;
if (next == SeqNum.ZERO) {
if (delta >= 0) {
return SeqNum.valueOf(next + 1);
} else {
// XXX Revisit this behavior
return SeqNum.valueOf(next - 1);
}
}
return SeqNum.valueOf(next);
}
// TODO comparator which treats long as ring?
@Override
public int intValue() {
return UnsignedLong.valueOf(seq).intValue();
}
@Override
public long longValue() {
return seq;
}
@Override
public float floatValue() {
return UnsignedLong.valueOf(seq).floatValue();
}
@Override
public double doubleValue() {
return UnsignedLong.valueOf(seq).doubleValue();
}
@Override
public int hashCode() {
return Longs.hashCode(seq);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SeqNum other = (SeqNum) obj;
return seq == other.seq;
}
@Override
public String toString() {
return UnsignedLongs.toString(seq);
}
@Override
public int compareTo(final SeqNum o) {
checkNotNull(o);
return Long.signum(-this.distance(o));
}
/**
* Returns the distance between other and this SeqNum.
*
* @param other other SeqNum
* @return {@code other - this}
* or -Long.MAX_VALUE, Log.MAX_VALUE if too far apart.
*/
public long distance(final SeqNum other) {
checkNotNull(other);
// distance from INITIAL is always measured clockwise on the ring
if (this.equals(INITIAL)) {
return (other.seq >= 0) ? other.seq : Long.MAX_VALUE;
} else if (other.equals(INITIAL)) {
return (this.seq >= 0) ? -this.seq : -Long.MAX_VALUE;
}
/// other cases measures using "shorter" half of the ring
final long diff = other.seq - this.seq;
if (diff == Long.MIN_VALUE) {
// both arc is same distance
// treat arc including INITIAL as shorter
if (this.seq < 0) {
// clock wise arc contain INITIAL
return Long.MAX_VALUE;
} else {
// counter clock wise arc contain INITIAL
return -Long.MAX_VALUE;
}
} else {
return diff;
}
}
}