All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kaphein.ulid.MonotonicUlidGenerator Maven / Gradle / Ivy

The newest version!
package kaphein.ulid;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * 

* A monotonic ULID generator. *

*

* Instances of this class are thread-safe if supplied * {@link EpochMilliSupplier} and {@link Random} are thread-safe. *

* * @author Hydrawisk793 */ public class MonotonicUlidGenerator extends AbstractUlidGenerator { /** * Constructs an instance of {@link MonotonicUlidGenerator}. */ public MonotonicUlidGenerator() { this(null, null); } /** * Constructs an instance of {@link MonotonicUlidGenerator}. * * @param epochMilliSupplier A epoch milli supplier. If {@code null}, the * default one that uses {@link System#currentTimeMillis()} is selected. * @param rng A random generator. If {@code null}, the default random * generator is selected. */ public MonotonicUlidGenerator( EpochMilliSupplier epochMilliSupplier, Random rng ) { super(epochMilliSupplier, rng); thisLock = new Object(); lastTimestamp = 0L; lastRandomnessMsv = 0L; lastRandomnessLsv = 0L; } /** * {@inheritDoc} */ @Override public List generate(int count) { final int c = count; return ThreadUtils .callAndReinterruptIfNeeded( new InterruptableCallable>() { @Override public List call() throws InterruptedException { return generateImpl(c, null, false); } }, Collections.emptyList()); } /** * {@inheritDoc} */ @Override public List generate(int count, long timestamp) { final int c = count; final long t = timestamp; return ThreadUtils .callAndReinterruptIfNeeded( new InterruptableCallable>() { @Override public List call() throws InterruptedException { return generateImpl(c, t, false); } }, Collections.emptyList()); } /** * {@inheritDoc} */ @Override public List generateExact(int count) throws InterruptedException { return generateImpl(count, null, true); } /** * {@inheritDoc} */ @Override public List generateExact(int count, long timestamp) throws InterruptedException { return generateImpl(count, timestamp, true); } private static List createUlidList(int count) { return new LinkedList<>(); } private static List toImmutableArrayList(List ulids) { return Collections.unmodifiableList(new ArrayList<>(ulids)); } private List generateImpl( int count, Long timestamp, boolean waitForNextTimestampWhenOverflow ) throws InterruptedException { if(count < 0) { throw new IllegalArgumentException("'count' cannot be negative"); } if(null != timestamp && timestamp < Ulid.TIMESTAMP_MIN_VALUE) { throw new IllegalArgumentException( "'timestamp' cannot be lower than " + Ulid.TIMESTAMP_MIN_VALUE); } final List ulids = createUlidList(count); if(count > 0) { generateAndAdd(ulids, count, timestamp, waitForNextTimestampWhenOverflow); } return toImmutableArrayList(ulids); } private void generateAndAdd( List ulids, int count, Long timestamp, boolean waitForNextTimestampWhenOverflow ) throws InterruptedException { final EpochMilliSupplier epochMilliSupplier = getEpochMilliSupplier(); long now = (null == timestamp ? epochMilliSupplier.get() : timestamp); long timestampToUse; final long[] randomness = new long[2]; final long[] maxRandomness = new long[2]; boolean canGenerate = true; while(canGenerate && count > 0) { boolean shouldGenerate = true; synchronized(thisLock) { boolean initializingRandomness = true; do { if(!lastRandomnessInitialized) { final long[] initialRandomness = generateRandomness(); lastRandomnessMsv = initialRandomness[0]; lastRandomnessLsv = initialRandomness[1]; lastRandomnessInitialized = true; } timestampToUse = lastTimestamp; randomness[0] = lastRandomnessMsv; randomness[1] = lastRandomnessLsv; maxRandomness[0] = lastRandomnessMsv; maxRandomness[1] = lastRandomnessLsv; if(now > timestampToUse) { timestampToUse = now; final long[] newRandomness = generateRandomness(); randomness[0] = newRandomness[0]; randomness[1] = newRandomness[1]; maxRandomness[0] = newRandomness[0]; maxRandomness[1] = newRandomness[1]; initializingRandomness = false; } else if( Ulid.RANDOMNESS_MAX_VALUE_MS_BITS == maxRandomness[0] && Ulid.RANDOMNESS_MAX_VALUE_LS_BITS == maxRandomness[1] ) { if(waitForNextTimestampWhenOverflow) { Thread.sleep(1); now = epochMilliSupplier.get(); } else { // Cannot generate ULIDs anymore with the current timestamp. canGenerate = false; shouldGenerate = false; initializingRandomness = false; } } else { initializingRandomness = false; } } while(initializingRandomness); if(addRandomness(maxRandomness, count)) { maxRandomness[0] = Ulid.RANDOMNESS_MAX_VALUE_MS_BITS; maxRandomness[1] = Ulid.RANDOMNESS_MAX_VALUE_LS_BITS; } lastTimestamp = timestampToUse; lastRandomnessMsv = maxRandomness[0]; lastRandomnessLsv = maxRandomness[1]; } while(shouldGenerate && count > 0) { ulids.add(Ulid.from(timestampToUse, randomness)); if(--count > 0) { if(incrementRandomness(randomness)) { shouldGenerate = false; } } } } } private final Object thisLock; private volatile long lastTimestamp; private volatile long lastRandomnessMsv; private volatile long lastRandomnessLsv; private volatile boolean lastRandomnessInitialized; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy