/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Lachlan Dowding
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package permafrost.tundra.id;
/**
* A Java implementation of ULID, as per https://github.com/alizain/ulid.
*/
public class ULID {
/**
* Table of characters used to base32-encode the ULID time and random components.
*/
private static final char[] ENCODE_TABLE = {
'0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f','g','h','j','k',
'm','n','p','q','r','s','t','v','w','x',
'y','z'
};
/**
* The number of characters in the ULID devoted to the time component.
*/
private static final int TIME_LENGTH = 10;
/**
* The number of characters in the ULID devoted to the random component.
*/
private static final int RANDOM_LENGTH = 16;
/**
* The multiplication factor used for 40-bits of randomness.
*/
private static final double RANDOM_MULTIPLIER = Math.pow(2, 40);
/**
* Returns a newly generated ULID.
*
* @return A newly generated ULID.
*/
public static String generate() {
StringBuilder buffer = new StringBuilder();
encodeRandom(buffer);
encodeTime(buffer);
return buffer.reverse().toString();
}
/**
* Base-32 encodes the given value for the given length into the given buffer.
*
* @param buffer The buffer to encode the value into.
* @param value The value to be encoded.
* @param length The number of base-32 characters the value is to be represented by.
*/
private static void encode(StringBuilder buffer, long value, int length) {
for (int i = 0; i < length; i++) {
int remainder = (int)(value % ENCODE_TABLE.length);
value = (value - remainder) / ENCODE_TABLE.length;
buffer.append(ENCODE_TABLE[remainder]);
}
}
/**
* Encode the current time into this ULID's string buffer.
*
* @param buffer The buffer into which the time is encoded.
*/
private static void encodeTime(StringBuilder buffer) {
encode(buffer, System.currentTimeMillis(), TIME_LENGTH);
}
/**
* Encodes a randomness component into this ULID's string buffer.
*
* @param buffer The buffer into which the random number is encoded.
*/
private static void encodeRandom(StringBuilder buffer) {
encode(buffer, (long)(Math.random() * RANDOM_MULTIPLIER), RANDOM_LENGTH/2);
encode(buffer, (long)(Math.random() * RANDOM_MULTIPLIER), RANDOM_LENGTH/2);
}
}