Skip to content

Instantly share code, notes, and snippets.

@briandilley
Created March 26, 2018 22:45
Show Gist options
  • Save briandilley/3caaf8dd4bcf1ab6293ccd6d2b6f6ac4 to your computer and use it in GitHub Desktop.
Save briandilley/3caaf8dd4bcf1ab6293ccd6d2b6f6ac4 to your computer and use it in GitHub Desktop.
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