├── settings.gradle ├── .gitignore ├── src └── main │ └── java │ └── me │ └── nimavat │ └── shortid │ ├── RandomFromSeed.java │ ├── Encode.java │ ├── ShortId.java │ └── Alphabet.java ├── README.md └── .github └── workflows ├── gradle-build.yml └── gradle-publish.yml /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'shortid' 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | build/* 4 | .idea/* 5 | .gradle/* 6 | *.ipr 7 | *.iws -------------------------------------------------------------------------------- /src/main/java/me/nimavat/shortid/RandomFromSeed.java: -------------------------------------------------------------------------------- 1 | package me.nimavat.shortid; 2 | 3 | class RandomFromSeed { 4 | 5 | private static int seed = 1; 6 | 7 | /** 8 | * return a random number based on a seed 9 | * @returns {number} 10 | */ 11 | static double nextValue() { 12 | seed = (seed * 9301 + 49297) % 233280; 13 | return seed/(233280.0); 14 | } 15 | 16 | static void setSeed(int _seed_) { 17 | seed = _seed_; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Java port of the short id -- https://github.com/dylang/shortid 3 | 4 | See [shortid](https://github.com/dylang/shortid) for documentation. 5 | 6 | ### How to use 7 | 8 | Gradle 9 | ```groovy 10 | repositories { 11 | maven { 12 | url "http://dl.bintray.com/snimavat/maven" 13 | } 14 | } 15 | 16 | dependencies { 17 | compile "me.nimavat:shortid:1.0.1.RC1" 18 | } 19 | 20 | 21 | ``` 22 | 23 | ```java 24 | import me.nimavat.shortid.ShortId 25 | 26 | System.out.println(Shortid.generate()) 27 | 28 | ``` 29 | 30 | Compatibility Java 1.8 + 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/gradle-build.yml: -------------------------------------------------------------------------------- 1 | name: Build with gradle 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: [ 'master' ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK 8 18 | uses: actions/setup-java@v2 19 | with: 20 | java-version: '8' 21 | distribution: 'adopt' 22 | cache: gradle 23 | # - name: Grant execute permission for gradlew 24 | # run: chmod +x gradlew 25 | - name: Build with Gradle 26 | run: gradle assemble 27 | -------------------------------------------------------------------------------- /src/main/java/me/nimavat/shortid/Encode.java: -------------------------------------------------------------------------------- 1 | package me.nimavat.shortid; 2 | 3 | import java.util.function.Function; 4 | 5 | /** 6 | * Created by sudhir on 03/03/17. 7 | */ 8 | class Encode { 9 | 10 | static String encode(Function lookup, int number) { 11 | int loopCounter = 0; 12 | boolean done = false; 13 | 14 | String str = ""; 15 | 16 | while (!done) { 17 | str = str + lookup.apply( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() ); 18 | done = number < (Math.pow(16, loopCounter + 1 ) ); 19 | loopCounter++; 20 | } 21 | 22 | return str; 23 | } 24 | 25 | static int randomByte() { 26 | return ((int)Math.floor(Math.random() * 256)) & 0x30; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/gradle-publish.yml: -------------------------------------------------------------------------------- 1 | name: Gradle Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK 8 18 | uses: actions/setup-java@v2 19 | with: 20 | java-version: '8' 21 | distribution: 'adopt' 22 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 23 | settings-path: ${{ github.workspace }} # location for the settings.xml file 24 | 25 | - name: Build with Gradle 26 | run: gradle build 27 | 28 | # The USERNAME and TOKEN need to correspond to the credentials environment variables used in 29 | # the publishing section of your build.gradle 30 | - name: Publish to GitHub Packages 31 | run: gradle publish 32 | env: 33 | USERNAME: ${{ github.actor }} 34 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /src/main/java/me/nimavat/shortid/ShortId.java: -------------------------------------------------------------------------------- 1 | package me.nimavat.shortid; 2 | 3 | public class ShortId { 4 | 5 | private static final int version = 6; 6 | private static final Long REDUCE_TIME = 1459707606518L; 7 | private static int clusterWorkerId = 0; 8 | 9 | private static int counter; 10 | private static int previousSeconds; 11 | 12 | 13 | /** 14 | * Set the seed. 15 | * Highly recommended if you don't want people to try to figure out your id schema. 16 | * exposed as shortid.seed(int) 17 | * @param seed Integer value to seed the random alphabet. ALWAYS USE THE SAME SEED or you might get overlaps. 18 | */ 19 | public static void seed(Long seedValue) { 20 | Alphabet.setSeed(seedValue); 21 | } 22 | 23 | /** 24 | * Set the cluster worker or machine id 25 | * exposed as shortid.worker(int) 26 | * @param workerId worker must be positive integer. Number less than 16 is recommended. 27 | * returns shortid module so it can be chained. 28 | */ 29 | public static void worker(int workerId) { 30 | clusterWorkerId = workerId; 31 | } 32 | 33 | /** 34 | * 35 | * sets new characters to use in the alphabet 36 | * returns the shuffled alphabet 37 | */ 38 | public static String characters(String newCharacters) { 39 | if (newCharacters != null) { 40 | Alphabet.characters(newCharacters); 41 | } 42 | 43 | return Alphabet.getShuffled(); 44 | } 45 | 46 | /** 47 | * Generate unique id 48 | * Returns string id 49 | */ 50 | public static String generate() { 51 | 52 | String str = ""; 53 | 54 | int seconds = (int) Math.floor((System.currentTimeMillis() - REDUCE_TIME) * 0.001); 55 | 56 | if (seconds == previousSeconds) { 57 | counter++; 58 | } else { 59 | counter = 0; 60 | previousSeconds = seconds; 61 | } 62 | 63 | str = str + Encode.encode(Alphabet::lookup, version); 64 | str = str + Encode.encode(Alphabet::lookup, clusterWorkerId); 65 | if (counter > 0) { 66 | str = str + Encode.encode(Alphabet::lookup, counter); 67 | } 68 | str = str + Encode.encode(Alphabet::lookup, seconds); 69 | 70 | return str; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/me/nimavat/shortid/Alphabet.java: -------------------------------------------------------------------------------- 1 | package me.nimavat.shortid; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.stream.Collectors; 8 | 9 | class Alphabet { 10 | private static final String ORIGINAL = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"; 11 | 12 | static long seed = 1; 13 | private static final Random random = new Random(seed); 14 | 15 | static String alphabet; 16 | static String shuffled; 17 | 18 | private static void reset() { 19 | shuffled = null; 20 | } 21 | 22 | private static void setCharacters(String _alphabet_) { 23 | if (_alphabet_ == null) { 24 | if (alphabet != ORIGINAL) { 25 | alphabet = ORIGINAL; 26 | reset(); 27 | } 28 | } 29 | 30 | if (_alphabet_.equals(alphabet)) { 31 | return; 32 | } 33 | 34 | 35 | if (_alphabet_.length() != ORIGINAL.length()) { 36 | throw new RuntimeException("Custom alphabet for shortid must be " + ORIGINAL.length() + " unique characters. You submitted " + _alphabet_.length() + " characters: " + _alphabet_); 37 | } 38 | 39 | List characters = Arrays.asList(_alphabet_.split("")); 40 | List duplicates = Arrays.asList(_alphabet_.split("")).stream().filter((String c) -> Collections.frequency(characters, c) > 1).collect(Collectors.toList()); 41 | 42 | 43 | if (duplicates.size() > 0) { 44 | throw new RuntimeException("Custom alphabet for shortid must be " + ORIGINAL.length() + " unique characters. These characters were not unique: " + duplicates.stream().collect(Collectors.joining(", "))); 45 | } 46 | 47 | alphabet = _alphabet_; 48 | reset(); 49 | } 50 | 51 | static String characters(String _alphabet_) { 52 | setCharacters(_alphabet_); 53 | return alphabet; 54 | } 55 | 56 | static void setSeed(Long _seed_) { 57 | random.setSeed(_seed_); 58 | if (seed != _seed_) { 59 | reset(); 60 | seed = _seed_; 61 | } 62 | } 63 | 64 | private static String shuffle() { 65 | if (alphabet == null) { 66 | setCharacters(ORIGINAL); 67 | } 68 | 69 | List sourceArray = Arrays.asList(alphabet.split("")); 70 | Collections.shuffle(sourceArray, random); 71 | return sourceArray.stream().collect(Collectors.joining("")); 72 | } 73 | 74 | static String getShuffled() { 75 | if (shuffled != null) { 76 | return shuffled; 77 | } 78 | shuffled = shuffle(); 79 | return shuffled; 80 | } 81 | 82 | static Character lookup(int index) { 83 | String alphabetShuffled = getShuffled(); 84 | return alphabetShuffled.charAt(index); 85 | } 86 | 87 | } 88 | --------------------------------------------------------------------------------