From 27a4f60cb51e5843cf614e29975dd71d0bbb1d11 Mon Sep 17 00:00:00 2001 From: Fabio Lima Date: Sun, 8 Aug 2021 01:26:04 -0300 Subject: [PATCH] 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)` - Added `UlidFactory.newMonotonicInstance()` - Added `UlidFactory.newMonotonicInstance(Random)` - Added `UlidFactory.newMonotonicInstance(Supplier)` - Added benchmark code to compare ULID with UUID - Removed `DefaultFactory` - Removed `MonotonicFactory` - Removed `Ulid.toUpperCase()` - Removed `RandomGenerator` --- CHANGELOG.md | 26 ++- LICENSE | 2 +- README.md | 30 ++- benchmark/README.md | 2 +- .../java/com/github/f4b6a3/ulid/Ulid.java | 54 ++--- .../com/github/f4b6a3/ulid/UlidCreator.java | 44 ++-- .../com/github/f4b6a3/ulid/UlidFactory.java | 217 ++++++++++++++++++ .../f4b6a3/ulid/factory/DefaultFactory.java | 78 ------- .../f4b6a3/ulid/factory/MonotonicFactory.java | 93 -------- .../f4b6a3/ulid/factory/UlidFactory.java | 75 ------ .../f4b6a3/ulid/random/RandomGenerator.java | 30 --- .../java/com/github/f4b6a3/ulid/UlidTest.java | 20 +- .../github/f4b6a3/ulid/UniquenessTest.java | 5 +- .../github/f4b6a3/ulid/bench/Benchmarks.java | 90 -------- .../ulid/factory/DefaultFactoryTest.java | 4 +- .../ulid/factory/MonotonicFactoryTest.java | 4 +- .../f4b6a3/ulid/factory/UlidFactoryTest.java | 2 +- 17 files changed, 316 insertions(+), 460 deletions(-) create mode 100644 src/main/java/com/github/f4b6a3/ulid/UlidFactory.java delete mode 100644 src/main/java/com/github/f4b6a3/ulid/factory/DefaultFactory.java delete mode 100644 src/main/java/com/github/f4b6a3/ulid/factory/MonotonicFactory.java delete mode 100644 src/main/java/com/github/f4b6a3/ulid/factory/UlidFactory.java delete mode 100644 src/main/java/com/github/f4b6a3/ulid/random/RandomGenerator.java delete mode 100644 src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 03f7d4e..236d43f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,28 @@ All notable changes to this project will be documented in this file. ## [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)` +- Added `UlidFactory.newMonotonicInstance()` +- Added `UlidFactory.newMonotonicInstance(Random)` +- Added `UlidFactory.newMonotonicInstance(Supplier)` +- Added benchmark code to compare ULID with UUID + +### Removed + +- Removed `DefaultFactory` +- Removed `MonotonicFactory` +- Removed `Ulid.toUpperCase()` +- Removed `RandomGenerator` ## [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 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.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 diff --git a/LICENSE b/LICENSE index 02d2a8f..67098b5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ 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 of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ded0587..59729f6 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Add these lines to your `pom.xml`. com.github.f4b6a3 ulid-creator - 3.2.0 + 4.0.0 ``` 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 -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 // Generate a ULID @@ -83,7 +83,7 @@ Sequence of ULIDs: ### 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. @@ -197,22 +197,20 @@ byte[] random = Ulid.getRandom("0123456789ABCDEFGHJKMNPQRS"); // 10 bytes (80 bi Use a `UlidFactory` with `java.util.Random`: ```java -// use a `Random` instance -UlidFactory factory = new DefaultFactory(new Random()); +// use a `java.util.Random` instance for fast generation +UlidFactory factory = UlidFactory.newInstance(new Random()); Ulid ulid = factory.create(); ``` Use a `UlidFactory` with a random generator of your choice: ```java -// use a method of any RNG with this signature: `void nextBytes(byte[])` -import com.github.niceguy.random.AwesomeRandom; // a hypothetical RNG -UlidFactory factory = new DefaultFactory(new AwesomeRandom()::nextBytes); +// use a random supplier that returns an array of 10 bytes +AwesomeRandom awesomeRandom = new AwesomeRandom(); // a hypothetical RNG +UlidFactory factory = UlidFactory.newInstance(() -> awesomeRandom.nextBytes(Ulid.RANDOM_BYTES)); Ulid ulid = factory.create(); ``` -(*) as long as it provides a void method like `nextBytes(byte[])`. - Benchmark ------------------------------------------------------ @@ -222,12 +220,12 @@ This section shows benchmarks comparing `UlidCreator` to `java.util.UUID`. -------------------------------------------------------------------------------- THROUGHPUT (operations/msec) Mode Cnt Score Error Units -------------------------------------------------------------------------------- -UUID_randomUUID thrpt 5 2051,918 ± 24,797 ops/ms -UUID_randomUUID_toString thrpt 5 1176,382 ± 32,709 ops/ms -UlidCreator_getUlid thrpt 5 2738,998 ± 51,092 ops/ms -UlidCreator_getUlid_toString thrpt 5 2548,178 ± 25,484 ops/ms -UlidCreator_getMonotonicUlid thrpt 5 19807,920 ± 410,996 ops/ms -UlidCreator_getMonotonicUlid_toString thrpt 5 13178,389 ± 147,323 ops/ms +UUID_randomUUID thrpt 5 2060,570 ± 37,242 ops/ms +UUID_randomUUID_toString thrpt 5 1177,386 ± 39,803 ops/ms +UlidCreator_getUlid thrpt 5 2740,609 ± 86,350 ops/ms +UlidCreator_getUlid_toString thrpt 5 2526,284 ± 56,726 ops/ms +UlidCreator_getMonotonicUlid thrpt 5 19373,150 ± 192,402 ops/ms +UlidCreator_getMonotonicUlid_toString thrpt 5 13269,201 ± 254,953 ops/ms -------------------------------------------------------------------------------- Total time: 00:08:01 -------------------------------------------------------------------------------- diff --git a/benchmark/README.md b/benchmark/README.md index db2664c..509c1a0 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,4 +1,4 @@ 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. diff --git a/src/main/java/com/github/f4b6a3/ulid/Ulid.java b/src/main/java/com/github/f4b6a3/ulid/Ulid.java index 6673b46..8f702a2 100644 --- a/src/main/java/com/github/f4b6a3/ulid/Ulid.java +++ b/src/main/java/com/github/f4b6a3/ulid/Ulid.java @@ -1,7 +1,7 @@ /* * 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 * of this software and associated documentation files (the "Software"), to deal @@ -48,13 +48,13 @@ public final class Ulid implements Serializable, Comparable { private final long msb; // most significant bits private final long lsb; // least significant bits - public static final int ULID_LENGTH = 26; - public static final int TIME_LENGTH = 10; - public static final int RANDOM_LENGTH = 16; + public static final int ULID_CHARS = 26; + public static final int TIME_CHARS = 10; + public static final int RANDOM_CHARS = 16; - public static final int ULID_BYTES_LENGTH = 16; - public static final int TIME_BYTES_LENGTH = 6; - public static final int RANDOM_BYTES_LENGTH = 10; + public static final int ULID_BYTES = 16; + public static final int TIME_BYTES = 6; + public static final int RANDOM_BYTES = 10; private static final char[] ALPHABET_UPPERCASE = // { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // @@ -180,7 +180,7 @@ public final class Ulid implements Serializable, Comparable { throw new IllegalArgumentException("Invalid time value"); // time overflow! } // 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! } @@ -222,7 +222,7 @@ public final class Ulid implements Serializable, Comparable { */ 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! } @@ -322,7 +322,7 @@ public final class Ulid implements Serializable, Comparable { */ public byte[] toBytes() { - final byte[] bytes = new byte[ULID_BYTES_LENGTH]; + final byte[] bytes = new byte[ULID_BYTES]; bytes[0x0] = (byte) (msb >>> 56); bytes[0x1] = (byte) (msb >>> 48); @@ -351,11 +351,15 @@ public final class Ulid implements Serializable, Comparable { * 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 string + * @return a ULID string */ - public String toUpperCase() { + @Override + public String toString() { return toString(ALPHABET_UPPERCASE); } @@ -486,7 +490,7 @@ public final class Ulid implements Serializable, Comparable { */ public byte[] getRandom() { - final byte[] bytes = new byte[RANDOM_BYTES_LENGTH]; + final byte[] bytes = new byte[RANDOM_BYTES]; bytes[0x0] = (byte) (msb >>> 8); bytes[0x1] = (byte) (msb); @@ -536,7 +540,7 @@ public final class Ulid implements Serializable, Comparable { random1 |= ALPHABET_VALUES[chars[0x18]] << 5; 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[0x1] = (byte) (random0 >>> 24); @@ -617,24 +621,6 @@ public final class Ulid implements Serializable, Comparable { 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 public int hashCode() { final int prime = 31; @@ -675,7 +661,7 @@ public final class Ulid implements Serializable, Comparable { 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 random0 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40); @@ -734,7 +720,7 @@ public final class Ulid implements Serializable, Comparable { */ 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! } diff --git a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java index fbfdd71..8446421 100644 --- a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java +++ b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java @@ -1,7 +1,7 @@ /* * 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 * of this software and associated documentation files (the "Software"), to deal @@ -24,16 +24,12 @@ 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: * - * - 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. * * - 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 */ 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 */ public static Ulid getUlid(final long time) { - return DefaultFactoryHolder.INSTANCE.create(time); + return UlidFactoryHolder.INSTANCE.create(time); } /** * Returns a Monotonic ULID. * - * The random component is reset to a new value every time the millisecond - * changes. + * The random component is reset to a new value whenever the time changes. * - * If more than one ULID is generated within the same millisecond, the random - * component is incremented by one. + * If more than one ULID is generated within the same time, the random component + * is incremented by one. * * @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 */ public static Ulid getMonotonicUlid(final long time) { return MonotonicFactoryHolder.INSTANCE.create(time); } - private static class DefaultFactoryHolder { - static final UlidFactory INSTANCE = new DefaultFactory(); + private static class UlidFactoryHolder { + static final UlidFactory INSTANCE = UlidFactory.newInstance(); } private static class MonotonicFactoryHolder { - static final UlidFactory INSTANCE = new MonotonicFactory(); + static final UlidFactory INSTANCE = UlidFactory.newMonotonicInstance(); } } diff --git a/src/main/java/com/github/f4b6a3/ulid/UlidFactory.java b/src/main/java/com/github/f4b6a3/ulid/UlidFactory.java new file mode 100644 index 0000000..d5437c9 --- /dev/null +++ b/src/main/java/com/github/f4b6a3/ulid/UlidFactory.java @@ -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 ulidFunction; + + public UlidFactory() { + this.ulidFunction = new UlidFunction(); + } + + private UlidFactory(LongFunction 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 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 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 { + + // it must return an array of 10 bytes + private Supplier randomSupplier; + + public UlidFunction() { + this(new SecureRandom()); + } + + public UlidFunction(Random random) { + this(getRandomSupplier(random)); + } + + public UlidFunction(Supplier 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 { + + private long lastTime = -1; + private Ulid lastUlid = null; + + // it must return an array of 10 bytes + private Supplier randomSupplier; + + public MonotonicFunction() { + this(new SecureRandom()); + } + + public MonotonicFunction(Random random) { + this(getRandomSupplier(random)); + } + + public MonotonicFunction(Supplier 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 getRandomSupplier(Random random) { + return () -> { + byte[] payload = new byte[Ulid.RANDOM_BYTES]; + random.nextBytes(payload); + return payload; + }; + } + +} diff --git a/src/main/java/com/github/f4b6a3/ulid/factory/DefaultFactory.java b/src/main/java/com/github/f4b6a3/ulid/factory/DefaultFactory.java deleted file mode 100644 index 12bf885..0000000 --- a/src/main/java/com/github/f4b6a3/ulid/factory/DefaultFactory.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/com/github/f4b6a3/ulid/factory/MonotonicFactory.java b/src/main/java/com/github/f4b6a3/ulid/factory/MonotonicFactory.java deleted file mode 100644 index a708f1b..0000000 --- a/src/main/java/com/github/f4b6a3/ulid/factory/MonotonicFactory.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/com/github/f4b6a3/ulid/factory/UlidFactory.java b/src/main/java/com/github/f4b6a3/ulid/factory/UlidFactory.java deleted file mode 100644 index b3f239a..0000000 --- a/src/main/java/com/github/f4b6a3/ulid/factory/UlidFactory.java +++ /dev/null @@ -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); -} diff --git a/src/main/java/com/github/f4b6a3/ulid/random/RandomGenerator.java b/src/main/java/com/github/f4b6a3/ulid/random/RandomGenerator.java deleted file mode 100644 index a58bce5..0000000 --- a/src/main/java/com/github/f4b6a3/ulid/random/RandomGenerator.java +++ /dev/null @@ -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); -} diff --git a/src/test/java/com/github/f4b6a3/ulid/UlidTest.java b/src/test/java/com/github/f4b6a3/ulid/UlidTest.java index e305855..c5ef8b9 100644 --- a/src/test/java/com/github/f4b6a3/ulid/UlidTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/UlidTest.java @@ -50,7 +50,7 @@ public class UlidTest { long time = msb >>> 16; // 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 & 0xff)); buffer.putLong(lsb); @@ -63,7 +63,7 @@ public class UlidTest { try { 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); fail("Should throw an exception"); } catch (IllegalArgumentException e) { @@ -72,7 +72,7 @@ public class UlidTest { try { long time = 0x8000000000000000L; // negative number - byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH]; + byte[] bytes = new byte[Ulid.RANDOM_BYTES]; new Ulid(time, bytes); fail("Should throw an exception"); } catch (IllegalArgumentException e) { @@ -90,7 +90,7 @@ public class UlidTest { try { 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); fail("Should throw an exception"); } catch (IllegalArgumentException e) { @@ -131,12 +131,12 @@ public class UlidTest { Ulid ulid0 = new Ulid(msb, lsb); String string1 = toString(ulid0); - String string2 = ulid0.toUpperCase(); // <- test Ulid.toUpperCase() + String string2 = ulid0.toString(); // <- test Ulid.toString() assertEquals(string1, string2); // RFC-4122 UUID v4 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); UUID uuid3 = new UUID(ulid3.getMostSignificantBits(), ulid3.getLeastSignificantBits()); assertEquals(4, uuid3.version()); // check version @@ -218,11 +218,11 @@ public class UlidTest { Random random = new Random(); 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); 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.getLeastSignificantBits()); byte[] bytes1 = buffer.array(); @@ -241,7 +241,7 @@ public class UlidTest { } try { - byte[] bytes = new byte[Ulid.ULID_BYTES_LENGTH + 1]; + byte[] bytes = new byte[Ulid.ULID_BYTES + 1]; Ulid.from(bytes); fail("Should throw an exception"); } catch (IllegalArgumentException e) { @@ -281,7 +281,7 @@ public class UlidTest { public void testGetTimeAndGetRandom() { long time = 0; - byte[] bytes = new byte[Ulid.RANDOM_BYTES_LENGTH]; + byte[] bytes = new byte[Ulid.RANDOM_BYTES]; Random random = new Random(); for (int i = 0; i < 100; i++) { diff --git a/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java b/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java index 8616799..8aa809b 100644 --- a/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java @@ -3,9 +3,6 @@ package com.github.f4b6a3.ulid; import java.util.HashSet; 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 @@ -119,7 +116,7 @@ public class UniquenessTest { } 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); test.start(); } diff --git a/src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java b/src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java deleted file mode 100644 index 60f0816..0000000 --- a/src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.github.f4b6a3.ulid.bench; - -// Add theese dependencies to pom.xml: -// -// -// org.openjdk.jmh -// jmh-core -// 1.23 -// -// -// org.openjdk.jmh -// jmh-generator-annprocess -// 1.23 -// - -/* - -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(); - } -} -*/ diff --git a/src/test/java/com/github/f4b6a3/ulid/factory/DefaultFactoryTest.java b/src/test/java/com/github/f4b6a3/ulid/factory/DefaultFactoryTest.java index 5a0ce24..05b42af 100644 --- a/src/test/java/com/github/f4b6a3/ulid/factory/DefaultFactoryTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/factory/DefaultFactoryTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import com.github.f4b6a3.ulid.Ulid; import com.github.f4b6a3.ulid.UlidCreator; -import com.github.f4b6a3.ulid.factory.UlidFactory; +import com.github.f4b6a3.ulid.UlidFactory; import static org.junit.Assert.*; @@ -67,7 +67,7 @@ public class DefaultFactoryTest extends UlidFactoryTest { // Instantiate and start many threads 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].start(); } diff --git a/src/test/java/com/github/f4b6a3/ulid/factory/MonotonicFactoryTest.java b/src/test/java/com/github/f4b6a3/ulid/factory/MonotonicFactoryTest.java index 47fd2b5..6634282 100644 --- a/src/test/java/com/github/f4b6a3/ulid/factory/MonotonicFactoryTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/factory/MonotonicFactoryTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import com.github.f4b6a3.ulid.Ulid; import com.github.f4b6a3.ulid.UlidCreator; -import com.github.f4b6a3.ulid.factory.UlidFactory; +import com.github.f4b6a3.ulid.UlidFactory; import static org.junit.Assert.*; @@ -78,7 +78,7 @@ public class MonotonicFactoryTest extends UlidFactoryTest { // Instantiate and start many threads 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].start(); } diff --git a/src/test/java/com/github/f4b6a3/ulid/factory/UlidFactoryTest.java b/src/test/java/com/github/f4b6a3/ulid/factory/UlidFactoryTest.java index 55e049b..7af1fc7 100644 --- a/src/test/java/com/github/f4b6a3/ulid/factory/UlidFactoryTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/factory/UlidFactoryTest.java @@ -5,7 +5,7 @@ import java.util.Random; import java.util.Set; import java.util.UUID; -import com.github.f4b6a3.ulid.factory.UlidFactory; +import com.github.f4b6a3.ulid.UlidFactory; public abstract class UlidFactoryTest {