Created
March 26, 2018 22:45
-
-
Save briandilley/3caaf8dd4bcf1ab6293ccd6d2b6f6ac4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.flipagram.lib.model; | |
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.io.Serializable; | |
import javax.validation.constraints.NotNull; | |
import com.flipagram.lib.util.Base62; | |
/** | |
* An immutable id with the following properties: | |
* timestamp: left most 41 bits | |
* shard id: next 9 bits | |
* object type: next 4 bits | |
* sequence: next 10 bits | |
* . | |
*/ | |
@SuppressWarnings("serial") | |
public class Fid | |
implements Comparable<Fid>, | |
Serializable { | |
public static final long EPOCH = 1380585600000L; | |
public static final int TS_BITS = 63-40; | |
public static final int SHARD_BITS = TS_BITS-9; | |
public static final int TYPE_BITS = SHARD_BITS-4; | |
public static final int SEQ_BITS = TYPE_BITS-10; | |
public static final long TS_MASK = 9223372036846387200L; | |
public static final long SHARD_MASK = 8372224L; | |
public static final long TYPE_MASK = 15360L; | |
public static final long SEQ_MASK = 1023L; | |
public static final long TS_SHIFTED_MASK = 2199023255551L; | |
public static final long SHARD_SHIFTED_MASK = 511L; | |
public static final long TYPE_SHIFTED_MASK = 15L; | |
public static final long SEQ_SHIFTED_MASK = 1023L; | |
private long id; | |
/** | |
* Creates with the given id. | |
* @param id the id | |
*/ | |
public Fid(long id) { | |
this.id = id; | |
} | |
/** | |
* Creates an empty id. | |
*/ | |
public Fid() { | |
this(0); | |
} | |
/** | |
* Create an id from the string representation. | |
* @param value the string id | |
*/ | |
public Fid(String value) { | |
this(Base62.decode(value).longValue()); | |
} | |
/** | |
* Creates with the given components. | |
* @param timestamp the timestamp component | |
* @param shardId the shard id component | |
* @param type the type component | |
* @param sequence the sequence component | |
*/ | |
public Fid(long timestamp, Number shardId, Number type, Number sequence) { | |
if (timestamp<EPOCH) { | |
throw new IllegalArgumentException("timestamp must be > "+EPOCH); | |
} | |
this.id = 0; | |
this.id = (this.id & ~TS_MASK) | ((timestamp-EPOCH) << TS_BITS); | |
this.id = ((this.id & ~SHARD_MASK) | (shardId.longValue() << SHARD_BITS)); | |
this.id = ((this.id & ~TYPE_MASK) | (type.longValue() << TYPE_BITS)); | |
this.id = ((this.id & ~SEQ_MASK) | (sequence.longValue() << SEQ_BITS)); | |
} | |
/** | |
* Returns the long value. | |
* @return the long value | |
*/ | |
@NotNull | |
public long asLong() { | |
return id; | |
} | |
/** | |
* @return the timestamp | |
*/ | |
public long getTimestamp() { | |
return (((this.id & TS_MASK) >>> TS_BITS) & TS_SHIFTED_MASK) + EPOCH; | |
} | |
/** | |
* @return the shard id | |
*/ | |
public long getShardId() { | |
return (((this.id & SHARD_MASK) >>> SHARD_BITS) & SHARD_SHIFTED_MASK); | |
} | |
/** | |
* @return the object type | |
*/ | |
public long getObjectType() { | |
return (((this.id & TYPE_MASK) >>> TYPE_BITS) & TYPE_SHIFTED_MASK); | |
} | |
/** | |
* @return the sequence | |
*/ | |
public long getSequence() { | |
return (((this.id & SEQ_MASK) >>> SEQ_BITS) & SEQ_SHIFTED_MASK); | |
} | |
/** | |
* Creates a new {@link Fid} with the given timestamp. | |
* @param timestamp the timestamp | |
* @return the new Fid | |
*/ | |
public Fid withTimestamp(long timestamp) { | |
if (timestamp<EPOCH) { | |
throw new IllegalArgumentException("timestamp must be > "+EPOCH); | |
} | |
return new Fid((this.id & ~TS_MASK) | ((timestamp-EPOCH) << TS_BITS)); | |
} | |
/** | |
* Creates a new {@link Fid} with the given shard id. | |
* @param shardId the shard id | |
* @return the new Fid | |
*/ | |
public Fid withShardId(long shardId) { | |
return new Fid((this.id & ~SHARD_MASK) | (shardId << SHARD_BITS)); | |
} | |
/** | |
* Creates a new {@link Fid} with the given type. | |
* @param objectType the object type | |
* @return the new Fid | |
*/ | |
public Fid withObjectType(long objectType) { | |
return new Fid((this.id & ~TYPE_MASK) | (objectType << TYPE_BITS)); | |
} | |
/** | |
* Creates a new {@link Fid} with the given type. | |
* @param objectType the object type | |
* @return the new Fid | |
*/ | |
public Fid withObjectType(FidObjectType objectType) { | |
if (objectType == null) { | |
throw new IllegalArgumentException("objectType cannot be null"); | |
} | |
return withObjectType(objectType.toLong()); | |
} | |
/** | |
* Creates a new {@link Fid} with the given sequence. | |
* @param sequence the sequence | |
* @return the new Fid | |
*/ | |
public Fid withSequence(long sequence) { | |
return new Fid(id | (sequence % 1024)); | |
} | |
/** | |
* Returns the id as a bit string. | |
* @return the string | |
*/ | |
public String asBitString() { | |
StringBuilder ret = new StringBuilder(Long.toBinaryString(id)); | |
int count = 64 - ret.length(); | |
if (count>0) { | |
ret.reverse(); | |
for (int i=0; i<count; i++) { | |
ret.append("0"); | |
} | |
ret.reverse(); | |
} | |
return ret.toString(); | |
} | |
/** | |
* Returns the id as a Long string. | |
* @return the string | |
*/ | |
public String asLongString() { | |
return String.valueOf(id); | |
} | |
private void writeObject(ObjectOutputStream stream) | |
throws IOException { | |
stream.writeLong(id); | |
} | |
private void readObject(ObjectInputStream stream) | |
throws IOException, | |
ClassNotFoundException { | |
id = stream.readLong(); | |
} | |
@Override | |
public int compareTo(Fid o) { | |
return Long.valueOf(this.id).compareTo(o.id); | |
} | |
@Override | |
public String toString() { | |
return id>=0 ? Base62.encode(id) : "-1"; | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + (int) (id ^ (id >>> 32)); | |
return result; | |
} | |
@Override | |
public boolean equals(Object obj) { | |
if (this == obj) { | |
return true; | |
} else if (obj == null) { | |
return false; | |
} else if (getClass() != obj.getClass()) { | |
return false; | |
} | |
Fid other = (Fid) obj; | |
return (id == other.id); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment