Version 4.0.0 #15

Now you call `UlidFactory.newInstance()` and it's variants to get a new
`UlidFactory`.

List of changes:

- Added `UlidFactory.newInstance()`
- Added `UlidFactory.newInstance(Random)`
- Added `UlidFactory.newInstance(Supplier<byte[]>)`
- Added `UlidFactory.newMonotonicInstance()`
- Added `UlidFactory.newMonotonicInstance(Random)`
- Added `UlidFactory.newMonotonicInstance(Supplier<byte[]>)`
- Added benchmark code to compare ULID with UUID
- Removed `DefaultFactory`
- Removed `MonotonicFactory`
- Removed `Ulid.toUpperCase()`
- Removed `RandomGenerator`
This commit is contained in:
Fabio Lima 2021-08-08 01:26:04 -03:00
parent 8a68ad617e
commit 27a4f60cb5
17 changed files with 316 additions and 460 deletions

View File

@ -4,9 +4,28 @@ All notable changes to this project will be documented in this file.
## [Unreleased] ## [Unreleased]
## Added Nothing unreleased.
- Add benchmark code to compare ULID with UUID ## [4.0.0] - 2021-08-08
Now you call `UlidFactory.newInstance()` and it's variants to get a new `UlidFactory`.
### Added
- Added `UlidFactory.newInstance()`
- Added `UlidFactory.newInstance(Random)`
- Added `UlidFactory.newInstance(Supplier<byte[]>)`
- Added `UlidFactory.newMonotonicInstance()`
- Added `UlidFactory.newMonotonicInstance(Random)`
- Added `UlidFactory.newMonotonicInstance(Supplier<byte[]>)`
- Added benchmark code to compare ULID with UUID
### Removed
- Removed `DefaultFactory`
- Removed `MonotonicFactory`
- Removed `Ulid.toUpperCase()`
- Removed `RandomGenerator`
## [3.2.0] - 2021-07-17 ## [3.2.0] - 2021-07-17
@ -243,7 +262,8 @@ Project created as an alternative Java implementation of [ULID spec](https://git
- Added `LICENSE` - Added `LICENSE`
- Added test cases - Added test cases
[unreleased]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.2.0...HEAD [unreleased]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-4.0.0...HEAD
[4.0.0]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.2.0...ulid-creator-4.0.0
[3.2.0]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.1.1...ulid-creator-3.2.0 [3.2.0]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.1.1...ulid-creator-3.2.0
[3.1.1]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.1.0...ulid-creator-3.1.1 [3.1.1]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.1.0...ulid-creator-3.1.1
[3.1.0]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.0.1...ulid-creator-3.1.0 [3.1.0]: https://github.com/f4b6a3/ulid-creator/compare/ulid-creator-3.0.1...ulid-creator-3.1.0

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 Fabio Lima Copyright (c) 2020-2021 Fabio Lima
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -35,7 +35,7 @@ Add these lines to your `pom.xml`.
<dependency> <dependency>
<groupId>com.github.f4b6a3</groupId> <groupId>com.github.f4b6a3</groupId>
<artifactId>ulid-creator</artifactId> <artifactId>ulid-creator</artifactId>
<version>3.2.0</version> <version>4.0.0</version>
</dependency> </dependency>
``` ```
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator). See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator).
@ -44,7 +44,7 @@ Module name: `com.github.f4b6a3.ulid`.
### ULID ### ULID
The ULID is a 128 bit long identifier. The first 48 bits represent the count of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator. The ULID is a 128 bit long identifier. The first 48 bits represent the number of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator.
```java ```java
// Generate a ULID // Generate a ULID
@ -83,7 +83,7 @@ Sequence of ULIDs:
### Monotonic ULID ### Monotonic ULID
The Monotonic ULID is a 128 bit long identifier. The first 48 bits represent the count of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator. The Monotonic ULID is a 128 bit long identifier. The first 48 bits represent the number of milliseconds since Unix Epoch, 1970-01-01. The remaining 80 bits are generated by a secure random number generator.
The random component is incremented by 1 whenever the current millisecond is equal to the previous one. But when the current millisecond is different, the random component changes to another random value. The random component is incremented by 1 whenever the current millisecond is equal to the previous one. But when the current millisecond is different, the random component changes to another random value.
@ -197,22 +197,20 @@ byte[] random = Ulid.getRandom("0123456789ABCDEFGHJKMNPQRS"); // 10 bytes (80 bi
Use a `UlidFactory` with `java.util.Random`: Use a `UlidFactory` with `java.util.Random`:
```java ```java
// use a `Random` instance // use a `java.util.Random` instance for fast generation
UlidFactory factory = new DefaultFactory(new Random()); UlidFactory factory = UlidFactory.newInstance(new Random());
Ulid ulid = factory.create(); Ulid ulid = factory.create();
``` ```
Use a `UlidFactory` with a random generator of your choice: Use a `UlidFactory` with a random generator of your choice:
```java ```java
// use a method of any RNG with this signature: `void nextBytes(byte[])` // use a random supplier that returns an array of 10 bytes
import com.github.niceguy.random.AwesomeRandom; // a hypothetical RNG AwesomeRandom awesomeRandom = new AwesomeRandom(); // a hypothetical RNG
UlidFactory factory = new DefaultFactory(new AwesomeRandom()::nextBytes); UlidFactory factory = UlidFactory.newInstance(() -> awesomeRandom.nextBytes(Ulid.RANDOM_BYTES));
Ulid ulid = factory.create(); Ulid ulid = factory.create();
``` ```
(*) as long as it provides a void method like `nextBytes(byte[])`.
Benchmark Benchmark
------------------------------------------------------ ------------------------------------------------------
@ -222,12 +220,12 @@ This section shows benchmarks comparing `UlidCreator` to `java.util.UUID`.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
THROUGHPUT (operations/msec) Mode Cnt Score Error Units THROUGHPUT (operations/msec) Mode Cnt Score Error Units
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
UUID_randomUUID thrpt 5 2051,918 ± 24,797 ops/ms UUID_randomUUID thrpt 5 2060,570 ± 37,242 ops/ms
UUID_randomUUID_toString thrpt 5 1176,382 ± 32,709 ops/ms UUID_randomUUID_toString thrpt 5 1177,386 ± 39,803 ops/ms
UlidCreator_getUlid thrpt 5 2738,998 ± 51,092 ops/ms UlidCreator_getUlid thrpt 5 2740,609 ± 86,350 ops/ms
UlidCreator_getUlid_toString thrpt 5 2548,178 ± 25,484 ops/ms UlidCreator_getUlid_toString thrpt 5 2526,284 ± 56,726 ops/ms
UlidCreator_getMonotonicUlid thrpt 5 19807,920 ± 410,996 ops/ms UlidCreator_getMonotonicUlid thrpt 5 19373,150 ± 192,402 ops/ms
UlidCreator_getMonotonicUlid_toString thrpt 5 13178,389 ± 147,323 ops/ms UlidCreator_getMonotonicUlid_toString thrpt 5 13269,201 ± 254,953 ops/ms
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Total time: 00:08:01 Total time: 00:08:01
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
To execute the benchmark, run the script `./benchmark/run.sh`. To execute the benchmark, run the script `./benchmark/run.sh`.
Sorry, there is no `run.bat` file for Windows. Sorry, there is no `run.bat` file for Windows at this time.

View File

@ -1,7 +1,7 @@
/* /*
* MIT License * MIT License
* *
* Copyright (c) 2020 Fabio Lima * Copyright (c) 2020-2021 Fabio Lima
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -48,13 +48,13 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
private final long msb; // most significant bits private final long msb; // most significant bits
private final long lsb; // least significant bits private final long lsb; // least significant bits
public static final int ULID_LENGTH = 26; public static final int ULID_CHARS = 26;
public static final int TIME_LENGTH = 10; public static final int TIME_CHARS = 10;
public static final int RANDOM_LENGTH = 16; public static final int RANDOM_CHARS = 16;
public static final int ULID_BYTES_LENGTH = 16; public static final int ULID_BYTES = 16;
public static final int TIME_BYTES_LENGTH = 6; public static final int TIME_BYTES = 6;
public static final int RANDOM_BYTES_LENGTH = 10; public static final int RANDOM_BYTES = 10;
private static final char[] ALPHABET_UPPERCASE = // private static final char[] ALPHABET_UPPERCASE = //
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
@ -180,7 +180,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
throw new IllegalArgumentException("Invalid time value"); // time overflow! throw new IllegalArgumentException("Invalid time value"); // time overflow!
} }
// The random component has 80 bits (10 bytes). // The random component has 80 bits (10 bytes).
if (random == null || random.length != RANDOM_BYTES_LENGTH) { if (random == null || random.length != RANDOM_BYTES) {
throw new IllegalArgumentException("Invalid random bytes"); // null or wrong length! throw new IllegalArgumentException("Invalid random bytes"); // null or wrong length!
} }
@ -222,7 +222,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
*/ */
public static Ulid from(byte[] bytes) { public static Ulid from(byte[] bytes) {
if (bytes == null || bytes.length != ULID_BYTES_LENGTH) { if (bytes == null || bytes.length != ULID_BYTES) {
throw new IllegalArgumentException("Invalid ULID bytes"); // null or wrong length! throw new IllegalArgumentException("Invalid ULID bytes"); // null or wrong length!
} }
@ -322,7 +322,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
*/ */
public byte[] toBytes() { public byte[] toBytes() {
final byte[] bytes = new byte[ULID_BYTES_LENGTH]; final byte[] bytes = new byte[ULID_BYTES];
bytes[0x0] = (byte) (msb >>> 56); bytes[0x0] = (byte) (msb >>> 56);
bytes[0x1] = (byte) (msb >>> 48); bytes[0x1] = (byte) (msb >>> 48);
@ -351,11 +351,15 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
* The output string is 26 characters long and contains only characters from * The output string is 26 characters long and contains only characters from
* Crockford's base 32 alphabet. * Crockford's base 32 alphabet.
* *
* For lower case string, use the shorthand {@code Ulid#toLowerCase()}, instead
* of {@code Ulid#toString()#toLowerCase()}.
*
* See: https://www.crockford.com/base32.html * See: https://www.crockford.com/base32.html
* *
* @return a string * @return a ULID string
*/ */
public String toUpperCase() { @Override
public String toString() {
return toString(ALPHABET_UPPERCASE); return toString(ALPHABET_UPPERCASE);
} }
@ -486,7 +490,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
*/ */
public byte[] getRandom() { public byte[] getRandom() {
final byte[] bytes = new byte[RANDOM_BYTES_LENGTH]; final byte[] bytes = new byte[RANDOM_BYTES];
bytes[0x0] = (byte) (msb >>> 8); bytes[0x0] = (byte) (msb >>> 8);
bytes[0x1] = (byte) (msb); bytes[0x1] = (byte) (msb);
@ -536,7 +540,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
random1 |= ALPHABET_VALUES[chars[0x18]] << 5; random1 |= ALPHABET_VALUES[chars[0x18]] << 5;
random1 |= ALPHABET_VALUES[chars[0x19]]; random1 |= ALPHABET_VALUES[chars[0x19]];
final byte[] bytes = new byte[RANDOM_BYTES_LENGTH]; final byte[] bytes = new byte[RANDOM_BYTES];
bytes[0x0] = (byte) (random0 >>> 32); bytes[0x0] = (byte) (random0 >>> 32);
bytes[0x1] = (byte) (random0 >>> 24); bytes[0x1] = (byte) (random0 >>> 24);
@ -617,24 +621,6 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
return string != null && isValidCharArray(string.toCharArray()); return string != null && isValidCharArray(string.toCharArray());
} }
/**
* Converts the ULID into a canonical string in upper case.
*
* The output string is 26 characters long and contains only characters from
* Crockford's base 32 alphabet.
*
* For lower case string, use the shorthand {@code Ulid#toLowerCase()}, instead
* of {@code Ulid#toString()#toLowerCase()}.
*
* See: https://www.crockford.com/base32.html
*
* @return a ULID string
*/
@Override
public String toString() {
return this.toUpperCase();
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
@ -675,7 +661,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
protected String toString(char[] alphabet) { protected String toString(char[] alphabet) {
final char[] chars = new char[ULID_LENGTH]; final char[] chars = new char[ULID_CHARS];
long time = this.msb >>> 16; long time = this.msb >>> 16;
long random0 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40); long random0 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40);
@ -734,7 +720,7 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
*/ */
protected static boolean isValidCharArray(final char[] chars) { protected static boolean isValidCharArray(final char[] chars) {
if (chars == null || chars.length != ULID_LENGTH) { if (chars == null || chars.length != ULID_CHARS) {
return false; // null or wrong size! return false; // null or wrong size!
} }

View File

@ -1,7 +1,7 @@
/* /*
* MIT License * MIT License
* *
* Copyright (c) 2020 Fabio Lima * Copyright (c) 2020-2021 Fabio Lima
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -24,16 +24,12 @@
package com.github.f4b6a3.ulid; package com.github.f4b6a3.ulid;
import com.github.f4b6a3.ulid.factory.DefaultFactory;
import com.github.f4b6a3.ulid.factory.MonotonicFactory;
import com.github.f4b6a3.ulid.factory.UlidFactory;
/** /**
* Facade to the ULID factories. * A class for generating ULIDs.
* *
* The ULID has two components: * The ULID has two components:
* *
* - Time component: a part of 48 bits that represent the amount of milliseconds * - Time component: a part of 48 bits that represent the number of milliseconds
* since Unix Epoch, 1970-01-01. * since Unix Epoch, 1970-01-01.
* *
* - Random component: a byte array of 80 bits that has a random value generated * - Random component: a byte array of 80 bits that has a random value generated
@ -54,27 +50,28 @@ public final class UlidCreator {
* @return a ULID * @return a ULID
*/ */
public static Ulid getUlid() { public static Ulid getUlid() {
return DefaultFactoryHolder.INSTANCE.create(); return UlidFactoryHolder.INSTANCE.create();
} }
/** /**
* Returns a ULID with a specific time. * Returns a ULID with a given time.
* *
* @param time a specific time * The time must be the number of milliseconds since 1970-01-01 (Unix epoch).
*
* @param time a given time
* @return a ULID * @return a ULID
*/ */
public static Ulid getUlid(final long time) { public static Ulid getUlid(final long time) {
return DefaultFactoryHolder.INSTANCE.create(time); return UlidFactoryHolder.INSTANCE.create(time);
} }
/** /**
* Returns a Monotonic ULID. * Returns a Monotonic ULID.
* *
* The random component is reset to a new value every time the millisecond * The random component is reset to a new value whenever the time changes.
* changes.
* *
* If more than one ULID is generated within the same millisecond, the random * If more than one ULID is generated within the same time, the random component
* component is incremented by one. * is incremented by one.
* *
* @return a ULID * @return a ULID
*/ */
@ -83,20 +80,27 @@ public final class UlidCreator {
} }
/** /**
* Returns a Monotonic ULID with a specific time. * Returns a Monotonic ULID with a given time.
* *
* @param time a specific time * The time must be the number of milliseconds since 1970-01-01 (Unix epoch).
*
* The random component is reset to a new value whenever the time changes.
*
* If more than one ULID is generated within the same time, the random component
* is incremented by one.
*
* @param time a given time
* @return a ULID * @return a ULID
*/ */
public static Ulid getMonotonicUlid(final long time) { public static Ulid getMonotonicUlid(final long time) {
return MonotonicFactoryHolder.INSTANCE.create(time); return MonotonicFactoryHolder.INSTANCE.create(time);
} }
private static class DefaultFactoryHolder { private static class UlidFactoryHolder {
static final UlidFactory INSTANCE = new DefaultFactory(); static final UlidFactory INSTANCE = UlidFactory.newInstance();
} }
private static class MonotonicFactoryHolder { private static class MonotonicFactoryHolder {
static final UlidFactory INSTANCE = new MonotonicFactory(); static final UlidFactory INSTANCE = UlidFactory.newMonotonicInstance();
} }
} }

View File

@ -0,0 +1,217 @@
/*
* MIT License
*
* Copyright (c) 2020-2021 Fabio Lima
*
* 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 com.github.f4b6a3.ulid;
import java.security.SecureRandom;
import java.util.Random;
import java.util.function.LongFunction;
import java.util.function.Supplier;
/**
* Factory that generates ULIDs.
*
* If the factory is not monotonic, the random component always changes.
*
* If the factory is monotonic, the random component changes whenever the
* millisecond changes. If more than one ULID is generated within the same
* millisecond, the random component is incremented by one.
*
* The maximum ULIDs that can be generated per millisecond is 2^80.
*/
public final class UlidFactory {
private final LongFunction<Ulid> ulidFunction;
public UlidFactory() {
this.ulidFunction = new UlidFunction();
}
private UlidFactory(LongFunction<Ulid> ulidFunction) {
this.ulidFunction = ulidFunction;
}
/**
* Returns a new factory.
*
* It is equivalent to {@code new UlidFactory()}.
*
* @return {@link UlidFactory}
*/
public static UlidFactory newInstance() {
return new UlidFactory(new UlidFunction());
}
/**
* Returns a new factory.
*
* @param random a {@link Random} generator
* @return {@link UlidFactory}
*/
public static UlidFactory newInstance(Random random) {
return new UlidFactory(new UlidFunction(random));
}
/**
* Returns a new factory.
*
* The given random supplier must return an array of 10 bytes.
*
* @param randomSupplier a random supplier that returns 10 bytes
* @return {@link UlidFactory}
*/
public static UlidFactory newInstance(Supplier<byte[]> randomSupplier) {
return new UlidFactory(new UlidFunction(randomSupplier));
}
/**
* Returns a new monotonic factory.
*
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance() {
return new UlidFactory(new MonotonicFunction());
}
/**
* Returns a new monotonic factory.
*
* @param random a {@link Random} generator
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance(Random random) {
return new UlidFactory(new MonotonicFunction(random));
}
/**
* Returns a new monotonic factory.
*
* The given random supplier must return an array of 10 bytes.
*
* @param randomSupplier a random supplier that returns 10 bytes
* @return {@link UlidFactory}
*/
public static UlidFactory newMonotonicInstance(Supplier<byte[]> randomSupplier) {
return new UlidFactory(new MonotonicFunction(randomSupplier));
}
/**
* Returns a UUID.
*
* @return a ULID
*/
public Ulid create() {
return create(System.currentTimeMillis());
}
/**
* Returns a UUID with a specific time.
*
* The time must be the number of milliseconds since 1970-01-01 (Unix epoch).
*
* @param time a given time
* @return a ULID
*/
public Ulid create(final long time) {
return this.ulidFunction.apply(time);
}
/**
* Function that creates ULIDs.
*/
protected static final class UlidFunction implements LongFunction<Ulid> {
// it must return an array of 10 bytes
private Supplier<byte[]> randomSupplier;
public UlidFunction() {
this(new SecureRandom());
}
public UlidFunction(Random random) {
this(getRandomSupplier(random));
}
public UlidFunction(Supplier<byte[]> randomSupplier) {
this.randomSupplier = randomSupplier;
}
@Override
public Ulid apply(final long time) {
return new Ulid(time, this.randomSupplier.get());
}
}
/**
* Function that creates Monotonic ULIDs.
*/
protected static final class MonotonicFunction implements LongFunction<Ulid> {
private long lastTime = -1;
private Ulid lastUlid = null;
// it must return an array of 10 bytes
private Supplier<byte[]> randomSupplier;
public MonotonicFunction() {
this(new SecureRandom());
}
public MonotonicFunction(Random random) {
this(getRandomSupplier(random));
}
public MonotonicFunction(Supplier<byte[]> randomSupplier) {
this.randomSupplier = randomSupplier;
}
@Override
public synchronized Ulid apply(final long time) {
if (time == this.lastTime) {
this.lastUlid = lastUlid.increment();
} else {
this.lastUlid = new Ulid(time, this.randomSupplier.get());
}
this.lastTime = time;
return new Ulid(this.lastUlid);
}
}
/**
* It instantiates a supplier that returns an array of 10 bytes.
*
* @param random a {@link Random} generator
* @return a random supplier that returns 10 bytes
*/
protected static Supplier<byte[]> getRandomSupplier(Random random) {
return () -> {
byte[] payload = new byte[Ulid.RANDOM_BYTES];
random.nextBytes(payload);
return payload;
};
}
}

View File

@ -1,78 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Fabio Lima
*
* 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 com.github.f4b6a3.ulid.factory;
import java.util.Random;
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.random.RandomGenerator;
/**
* Factory that generates ULIDs.
*
* The random component is always reset to a new random value.
*
* The maximum ULIDs that can be generated per millisecond is 2^80.
*/
public class DefaultFactory extends UlidFactory {
/**
* Use the default {@link java.security.SecureRandom}.
*/
public DefaultFactory() {
super();
}
/**
* Use a random generator that inherits from {@link Random}.
*
* @param random a {@link Random} instance
*/
public DefaultFactory(Random random) {
this(random::nextBytes);
}
/**
* Use a random generator that inherits from {@link RandomGenerator}.
*
* @param randomGenerator a {@link RandomGenerator} instance
*/
public DefaultFactory(RandomGenerator randomGenerator) {
super(randomGenerator);
}
/**
* Returns a ULID.
*
* @param time a specific time
* @return a ULID
*/
@Override
public Ulid create(final long time) {
final byte[] random = new byte[Ulid.RANDOM_BYTES_LENGTH];
this.randomGenerator.nextBytes(random);
return new Ulid(time, random);
}
}

View File

@ -1,93 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Fabio Lima
*
* 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 com.github.f4b6a3.ulid.factory;
import java.util.Random;
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.random.RandomGenerator;
/**
* Factory that generates Monotonic ULIDs.
*
* The random component is reset to a new value every time the millisecond
* changes.
*
* If more than one ULID is generated within the same millisecond, the random
* component is incremented by one.
*
* The maximum ULIDs that can be generated per millisecond is 2^80.
*/
public final class MonotonicFactory extends UlidFactory {
private long lastTime = -1;
private Ulid lastUlid = null;
/**
* Use the default {@link java.security.SecureRandom}.
*/
public MonotonicFactory() {
super();
}
/**
* Use a random generator that inherits from {@link Random}.
*
* @param random a {@link Random} instance
*/
public MonotonicFactory(Random random) {
this(random::nextBytes);
}
/**
* Use a random generator that inherits from {@link RandomGenerator}.
*
* @param randomGenerator a {@link RandomGenerator} instance
*/
public MonotonicFactory(RandomGenerator randomGenerator) {
super(randomGenerator);
}
/**
* Returns a ULID.
*
* @param time a specific time
* @return a ULID
*/
@Override
public synchronized Ulid create(final long time) {
if (time == this.lastTime) {
this.lastUlid = lastUlid.increment();
} else {
final byte[] random = new byte[Ulid.RANDOM_BYTES_LENGTH];
this.randomGenerator.nextBytes(random);
this.lastUlid = new Ulid(time, random);
}
this.lastTime = time;
return new Ulid(this.lastUlid);
}
}

View File

@ -1,75 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Fabio Lima
*
* 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 com.github.f4b6a3.ulid.factory;
import java.security.SecureRandom;
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.random.RandomGenerator;
/**
* An abstract factory for generating ULIDs.
*
* The only method that must be implemented is {@link UlidFactory#create(long)}.
*/
public abstract class UlidFactory {
protected RandomGenerator randomGenerator;
/**
* Use the default {@link java.security.SecureRandom}.
*/
public UlidFactory() {
this(new SecureRandom()::nextBytes);
}
/**
* Use a random generator that inherits from {@link RandomGenerator}.
*
* @param randomGenerator a {@link RandomGenerator} instance
*/
public UlidFactory(RandomGenerator randomGenerator) {
this.randomGenerator = randomGenerator;
}
/**
* Returns a UUID.
*
* @return a ULID
*/
public Ulid create() {
return create(System.currentTimeMillis());
}
/**
* Returns a UUID with a specific time.
*
* This method must be implemented by all subclasses.
*
* @param time a specific time
* @return a ULID
*/
public abstract Ulid create(final long time);
}

View File

@ -1,30 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018-2020 Fabio Lima
*
* 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 com.github.f4b6a3.ulid.random;
@FunctionalInterface
public interface RandomGenerator {
void nextBytes(byte[] bytes);
}

View File

@ -50,7 +50,7 @@ public class UlidTest {
long time = msb >>> 16; long time = msb >>> 16;
// get the random bytes // get the random bytes
ByteBuffer buffer = ByteBuffer.allocate(Ulid.RANDOM_BYTES_LENGTH); ByteBuffer buffer = ByteBuffer.allocate(Ulid.RANDOM_BYTES);
buffer.put((byte) ((msb >>> 8) & 0xff)); buffer.put((byte) ((msb >>> 8) & 0xff));
buffer.put((byte) (msb & 0xff)); buffer.put((byte) (msb & 0xff));
buffer.putLong(lsb); buffer.putLong(lsb);
@ -63,7 +63,7 @@ public class UlidTest {
try { try {
long time = 0x0000ffffffffffffL + 1; // greater than 2^48-1 long time = 0x0000ffffffffffffL + 1; // greater than 2^48-1
byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH]; byte[] bytes = new byte[Ulid.RANDOM_BYTES];
new Ulid(time, bytes); new Ulid(time, bytes);
fail("Should throw an exception"); fail("Should throw an exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -72,7 +72,7 @@ public class UlidTest {
try { try {
long time = 0x8000000000000000L; // negative number long time = 0x8000000000000000L; // negative number
byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH]; byte[] bytes = new byte[Ulid.RANDOM_BYTES];
new Ulid(time, bytes); new Ulid(time, bytes);
fail("Should throw an exception"); fail("Should throw an exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -90,7 +90,7 @@ public class UlidTest {
try { try {
long time = 0x0000000000000000L; long time = 0x0000000000000000L;
byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH + 1]; // random component with invalid size byte[] bytes = new byte[Ulid.RANDOM_BYTES + 1]; // random component with invalid size
new Ulid(time, bytes); new Ulid(time, bytes);
fail("Should throw an exception"); fail("Should throw an exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -131,12 +131,12 @@ public class UlidTest {
Ulid ulid0 = new Ulid(msb, lsb); Ulid ulid0 = new Ulid(msb, lsb);
String string1 = toString(ulid0); String string1 = toString(ulid0);
String string2 = ulid0.toUpperCase(); // <- test Ulid.toUpperCase() String string2 = ulid0.toString(); // <- test Ulid.toString()
assertEquals(string1, string2); assertEquals(string1, string2);
// RFC-4122 UUID v4 // RFC-4122 UUID v4
UUID uuid0 = new UUID(msb, lsb); UUID uuid0 = new UUID(msb, lsb);
String string3 = ulid0.toRfc4122().toUpperCase(); // <- test Ulid.toRfc4122().toUpperCase() String string3 = ulid0.toRfc4122().toString(); // <- test Ulid.toRfc4122().toString()
Ulid ulid3 = fromString(string3); Ulid ulid3 = fromString(string3);
UUID uuid3 = new UUID(ulid3.getMostSignificantBits(), ulid3.getLeastSignificantBits()); UUID uuid3 = new UUID(ulid3.getMostSignificantBits(), ulid3.getLeastSignificantBits());
assertEquals(4, uuid3.version()); // check version assertEquals(4, uuid3.version()); // check version
@ -218,11 +218,11 @@ public class UlidTest {
Random random = new Random(); Random random = new Random();
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) { for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
byte[] bytes0 = new byte[Ulid.ULID_BYTES_LENGTH]; byte[] bytes0 = new byte[Ulid.ULID_BYTES];
random.nextBytes(bytes0); random.nextBytes(bytes0);
Ulid ulid0 = Ulid.from(bytes0); // <- test Ulid.from(UUID) Ulid ulid0 = Ulid.from(bytes0); // <- test Ulid.from(UUID)
ByteBuffer buffer = ByteBuffer.allocate(Ulid.ULID_BYTES_LENGTH); ByteBuffer buffer = ByteBuffer.allocate(Ulid.ULID_BYTES);
buffer.putLong(ulid0.getMostSignificantBits()); buffer.putLong(ulid0.getMostSignificantBits());
buffer.putLong(ulid0.getLeastSignificantBits()); buffer.putLong(ulid0.getLeastSignificantBits());
byte[] bytes1 = buffer.array(); byte[] bytes1 = buffer.array();
@ -241,7 +241,7 @@ public class UlidTest {
} }
try { try {
byte[] bytes = new byte[Ulid.ULID_BYTES_LENGTH + 1]; byte[] bytes = new byte[Ulid.ULID_BYTES + 1];
Ulid.from(bytes); Ulid.from(bytes);
fail("Should throw an exception"); fail("Should throw an exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -281,7 +281,7 @@ public class UlidTest {
public void testGetTimeAndGetRandom() { public void testGetTimeAndGetRandom() {
long time = 0; long time = 0;
byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH]; byte[] bytes = new byte[Ulid.RANDOM_BYTES];
Random random = new Random(); Random random = new Random();
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {

View File

@ -3,9 +3,6 @@ package com.github.f4b6a3.ulid;
import java.util.HashSet; import java.util.HashSet;
import java.util.Random; import java.util.Random;
import com.github.f4b6a3.ulid.factory.MonotonicFactory;
import com.github.f4b6a3.ulid.factory.UlidFactory;
/** /**
* *
* This test starts many threads that keep requesting thousands of ULIDs to a * This test starts many threads that keep requesting thousands of ULIDs to a
@ -119,7 +116,7 @@ public class UniquenessTest {
} }
public static void execute(boolean verbose, int threadCount, int requestCount) { public static void execute(boolean verbose, int threadCount, int requestCount) {
UlidFactory factory = new MonotonicFactory(new Random()); UlidFactory factory = UlidFactory.newMonotonicInstance(new Random());
UniquenessTest test = new UniquenessTest(threadCount, requestCount, factory, verbose); UniquenessTest test = new UniquenessTest(threadCount, requestCount, factory, verbose);
test.start(); test.start();
} }

View File

@ -1,90 +0,0 @@
package com.github.f4b6a3.ulid.bench;
// Add theese dependencies to pom.xml:
//
// <dependency>
// <groupId>org.openjdk.jmh</groupId>
// <artifactId>jmh-core</artifactId>
// <version>1.23</version>
// </dependency>
// <dependency>
// <groupId>org.openjdk.jmh</groupId>
// <artifactId>jmh-generator-annprocess</artifactId>
// <version>1.23</version>
// </dependency>
/*
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.UlidCreator;
@Threads(1)
@State(Scope.Thread)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
public class Benchmarks {
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public UUID getUuid() {
return UUID.randomUUID();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public String getUuidString() {
return UUID.randomUUID().toString();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public Ulid getUlid() {
return UlidCreator.getUlid();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public String getUlidString() {
return UlidCreator.getUlid().toString();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public Ulid getMonotonicUlid() {
return UlidCreator.getMonotonicUlid();
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public String getMonotonicUlidString() {
return UlidCreator.getMonotonicUlid().toString();
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(Benchmarks.class.getSimpleName()).forks(1).build();
new Runner(opt).run();
}
}
*/

View File

@ -4,7 +4,7 @@ import org.junit.Test;
import com.github.f4b6a3.ulid.Ulid; import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.UlidCreator; import com.github.f4b6a3.ulid.UlidCreator;
import com.github.f4b6a3.ulid.factory.UlidFactory; import com.github.f4b6a3.ulid.UlidFactory;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -67,7 +67,7 @@ public class DefaultFactoryTest extends UlidFactoryTest {
// Instantiate and start many threads // Instantiate and start many threads
for (int i = 0; i < THREAD_TOTAL; i++) { for (int i = 0; i < THREAD_TOTAL; i++) {
UlidFactory factory = new DefaultFactory(new Random()); UlidFactory factory = UlidFactory.newInstance(new Random());
threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX); threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
threads[i].start(); threads[i].start();
} }

View File

@ -4,7 +4,7 @@ import org.junit.Test;
import com.github.f4b6a3.ulid.Ulid; import com.github.f4b6a3.ulid.Ulid;
import com.github.f4b6a3.ulid.UlidCreator; import com.github.f4b6a3.ulid.UlidCreator;
import com.github.f4b6a3.ulid.factory.UlidFactory; import com.github.f4b6a3.ulid.UlidFactory;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -78,7 +78,7 @@ public class MonotonicFactoryTest extends UlidFactoryTest {
// Instantiate and start many threads // Instantiate and start many threads
for (int i = 0; i < THREAD_TOTAL; i++) { for (int i = 0; i < THREAD_TOTAL; i++) {
UlidFactory factory = new MonotonicFactory(new Random()); UlidFactory factory = UlidFactory.newMonotonicInstance(new Random());
threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX); threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
threads[i].start(); threads[i].start();
} }

View File

@ -5,7 +5,7 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import com.github.f4b6a3.ulid.factory.UlidFactory; import com.github.f4b6a3.ulid.UlidFactory;
public abstract class UlidFactoryTest { public abstract class UlidFactoryTest {