implement yeast
This commit is contained in:
58
src/main/java/io/socket/yeast/Yeast.java
Normal file
58
src/main/java/io/socket/yeast/Yeast.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package io.socket.yeast;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Java implementation of yeast. https://github.com/unshiftio/yeast
|
||||||
|
*/
|
||||||
|
public class Yeast {
|
||||||
|
private static char[] alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".toCharArray();
|
||||||
|
|
||||||
|
private static int length = alphabet.length;
|
||||||
|
|
||||||
|
private static Map<Character, Integer> map = new HashMap<Character, Integer>(length);
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
map.put(alphabet[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int seed = 0;
|
||||||
|
|
||||||
|
private static String prev;
|
||||||
|
|
||||||
|
public static String encode(long num) {
|
||||||
|
final StringBuilder encoded = new StringBuilder();
|
||||||
|
|
||||||
|
do {
|
||||||
|
encoded.insert(0, alphabet[(int)(num % length)]);
|
||||||
|
num = (long)Math.floor(num / length);
|
||||||
|
} while (num > 0);
|
||||||
|
|
||||||
|
return encoded.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long decode(String str) {
|
||||||
|
long decoded = 0;
|
||||||
|
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
decoded = decoded * length + map.get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String yeast() {
|
||||||
|
String now = encode(new Date().getTime());
|
||||||
|
|
||||||
|
if (!now.equals(prev)) {
|
||||||
|
seed = 0;
|
||||||
|
prev = now;
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
return now + "." + encode(seed++);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/test/java/io/socket/yeast/YeastTest.java
Normal file
76
src/test/java/io/socket/yeast/YeastTest.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package io.socket.yeast;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class YeastTest {
|
||||||
|
|
||||||
|
private void waitUntilNextMillisecond() {
|
||||||
|
long now = new Date().getTime();
|
||||||
|
while (new Date().getTime() == now) { /* do nothing */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prependsIteratedSeedWhenSamePreviousId() {
|
||||||
|
waitUntilNextMillisecond();
|
||||||
|
|
||||||
|
String[] ids = new String[] { Yeast.yeast(), Yeast.yeast(), Yeast.yeast() };
|
||||||
|
assertThat(ids[0], not(containsString(".")));
|
||||||
|
assertThat(ids[1], containsString(".0"));
|
||||||
|
assertThat(ids[2], containsString(".1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetsTheSeed() {
|
||||||
|
waitUntilNextMillisecond();
|
||||||
|
|
||||||
|
String[] ids = new String[] { Yeast.yeast(), Yeast.yeast(), Yeast.yeast() };
|
||||||
|
assertThat(ids[0], not(containsString(".")));
|
||||||
|
assertThat(ids[1], containsString(".0"));
|
||||||
|
assertThat(ids[2], containsString(".1"));
|
||||||
|
|
||||||
|
waitUntilNextMillisecond();
|
||||||
|
|
||||||
|
ids = new String[] { Yeast.yeast(), Yeast.yeast(), Yeast.yeast() };
|
||||||
|
assertThat(ids[0], not(containsString(".")));
|
||||||
|
assertThat(ids[1], containsString(".0"));
|
||||||
|
assertThat(ids[2], containsString(".1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doesNotCollide() {
|
||||||
|
int length = 30000;
|
||||||
|
String[] ids = new String[length];
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) ids[i] = Yeast.yeast();
|
||||||
|
|
||||||
|
Arrays.sort(ids);
|
||||||
|
|
||||||
|
for (int i = 0; i < length - 1; i++) {
|
||||||
|
assertThat(ids[i], not(equalTo(ids[i + 1])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canConvertIdToTimestamp() {
|
||||||
|
waitUntilNextMillisecond();
|
||||||
|
|
||||||
|
long now = new Date().getTime();
|
||||||
|
String id = Yeast.yeast();
|
||||||
|
|
||||||
|
assertThat(Yeast.encode(now), equalTo(id));
|
||||||
|
assertThat(Yeast.decode(id), equalTo(now));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user