hienlt0610
9/10/2019 - 7:40 AM

FirebasePushIdGenerator

public class FirebasePushIdGenerator {

	// Modeled after base64 web-safe chars, but ordered by ASCII.
	private final static String PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";

	// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
	private static long LAST_PUSH_TIME = 0L;
	
	// We generate 72-bits of randomness which get turned into 12 characters and
	// appended to the timestamp to prevent collisions with other clients. We store the last
	// characters we generated because in the event of a collision, we'll use those same
	// characters except "incremented" by one.
	private static int[] LAST_RAND_CHARS = new int[72];
	
	public static synchronized String generate() {

		long now = System.currentTimeMillis();
		boolean duplicateTime = now == LAST_PUSH_TIME;
		LAST_PUSH_TIME = now;

		char[] timeStampChars = new char[8];
		for (int i = 7; i >= 0; i--) {
			timeStampChars[i] = PUSH_CHARS.charAt((int)(now % 64));
			now = (long) Math.floor(now / 64);
		}
		
		if (now != 0) {
			throw new AssertionError("We should have converted the entire timestamp.");
		}
		
		StringBuilder id = new StringBuilder(20);
		for (char c : timeStampChars) {
			id.append(c);
		}
		
		if (!duplicateTime) {
			for (int i = 0; i < 12; i++) {
				LAST_RAND_CHARS[i] = (int) Math.floor(Double.valueOf(Math.random() * 64).intValue());
			}
		} else {
			// If the timestamp hasn't changed since last push, use the same random number,
 			//except incremented by 1.
			int i=0;
			for (i = 11; i >= 0 && LAST_RAND_CHARS[i] == 63; i--) {
				LAST_RAND_CHARS[i] = 0;
			}
			LAST_RAND_CHARS[i]++;
		}
		
		for (int i = 0; i < 12; i++) {
			id.append(PUSH_CHARS.charAt(LAST_RAND_CHARS[i]));
		}
		
		if (id.length() != 20) {
			throw new AssertionError("Length should be 20.");
		}

		return id.toString();
	}
	
}