{
21 |
22 | // Explicit serialVersionUID for interoperability.
23 | private static final long serialVersionUID = -2441709761088574861L;
24 |
25 | // Base to use
26 | private static final int NUMBER_BASE = 36;
27 |
28 | /**
29 | * CUID internal value holder.
30 | */
31 | private final String value;
32 |
33 | /**
34 | * Creates a new instance.
35 | *
36 | * @param value A valid CUID value
37 | * @since 1.0.0
38 | */
39 | private CUID(final String value) {
40 |
41 | this.value = value;
42 | }
43 |
44 | /**
45 | * Generates a new random CUID (Version 2).
46 | *
47 | * @return Newly generated CUID (Version 2)
48 | * @since 2.0.0
49 | */
50 | public static CUID randomCUID2() {
51 |
52 | return randomCUID2(CUIDv2.LENGTH_STANDARD);
53 | }
54 |
55 | /**
56 | * Generates a new random CUID (Version 2).
57 | *
58 | * @param length requested CUID length
59 | * @return Newly generated CUID (Version 2)
60 | * @since 2.0.1
61 | */
62 | public static CUID randomCUID2(final int length) {
63 |
64 | if (length <= 0) {
65 | throw new CUIDGenerationException("the length must be at least 1");
66 | }
67 |
68 | final String time = Long.toString(System.currentTimeMillis(), NUMBER_BASE);
69 | final char firstLetter = CUIDv2.ALPHABET_ARRAY[safeAbs(Common.nextIntValue()) % CUIDv2.ALPHABET_ARRAY.length];
70 | final String hash = CUIDv2.computeHash(
71 | time + CUIDv2.createEntropy(length) + CUIDv2.nextCounterValue() + Common.MACHINE_FINGERPRINT,
72 | length);
73 |
74 | return new CUID(firstLetter + hash.substring(1, length));
75 | }
76 |
77 | /**
78 | * Generates a new random CUID (Version 1).
79 | *
80 | * @return Newly generated CUID (Version 1)
81 | * @since 2.0.0
82 | */
83 | public static CUID randomCUID1() {
84 |
85 | final String timestamp = Long.toString(System.currentTimeMillis(), NUMBER_BASE);
86 | final String counter = Common.padWithZero(Integer.toString(CUIDv1.nextCounterValue(), NUMBER_BASE), CUIDv1.BLOCK_SIZE);
87 | final String random = CUIDv1.getRandomBlock() + CUIDv1.getRandomBlock();
88 |
89 | return new CUID(CUIDv1.START_CHARACTER + timestamp + counter + Common.MACHINE_FINGERPRINT + random);
90 | }
91 |
92 | /**
93 | * Creates a {@code CUID} from the string standard representation.
94 | *
95 | * @param cuidAsString A string that specifies a {@code CUID} (Version 1 or 2)
96 | * @return A {@code CUID} with the specified value
97 | * @throws IllegalArgumentException If the string is not conform
98 | * @since 1.0.0
99 | */
100 | public static CUID fromString(final String cuidAsString) {
101 |
102 | if (isValid(cuidAsString)) {
103 | return new CUID(cuidAsString);
104 | }
105 |
106 | throw new IllegalArgumentException("CUID string is invalid: '" + cuidAsString + "'");
107 | }
108 |
109 | /**
110 | * Checks the {@code CUID} from the string standard representation.
111 | *
112 | * @param cuidAsString A string that specifies a {@code CUID} (Version 1 or 2)
113 | * @return {@code true} If the string is conforms, otherwise, {@code false}
114 | * @since 1.0.0
115 | */
116 | public static boolean isValid(final String cuidAsString) {
117 |
118 | return cuidAsString != null
119 | && (cuidAsString.length() == CUIDv1.LENGTH_STANDARD && cuidAsString.startsWith(CUIDv1.START_CHARACTER) // Version 1
120 | || (!cuidAsString.isEmpty())) // Version 2
121 | && cuidAsString.chars()
122 | .filter(c -> !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
123 | .count() == 0;
124 | }
125 |
126 | /**
127 | * Always return non-negative value.
128 | *
129 | * @param i the integer value
130 | * @return a non-negative value
131 | * @since 2.0.3
132 | */
133 | private static int safeAbs(final int i) {
134 |
135 | return i == Integer.MIN_VALUE ? 0 : Math.abs(i);
136 | }
137 |
138 | /**
139 | * {@inheritDoc}
140 | *
141 | * @since 1.0.0
142 | */
143 | @Override
144 | public int compareTo(final CUID cuid) {
145 |
146 | if (cuid == null) {
147 | return -1;
148 | }
149 |
150 | return this.value.compareTo(cuid.value);
151 | }
152 |
153 | /**
154 | * Returns the string representation.
155 | *
156 | * @return String containing the {@code CUID}
157 | * @since 1.0.0
158 | */
159 | @Override
160 | public String toString() {
161 |
162 | return this.value;
163 | }
164 |
165 | /**
166 | * Returns {@code true} if the argument is equal to current object, {@code false} otherwise.
167 | *
168 | * @param o An object
169 | * @since 1.0.0
170 | */
171 | @Override
172 | public boolean equals(final Object o) {
173 |
174 | if (this == o) return true;
175 | if (o == null || getClass() != o.getClass()) return false;
176 | final CUID cuid = (CUID) o;
177 | return Objects.equals(value, cuid.value);
178 | }
179 |
180 | /**
181 | * Generates a hash code.
182 | *
183 | * @return Generated hashcode
184 | * @since 1.0.0
185 | */
186 | @Override
187 | public int hashCode() {
188 |
189 | return Objects.hash(value);
190 | }
191 |
192 | /**
193 | * CUID Version 1.
194 | *
195 | * @since 1.0.0
196 | */
197 | private static final class CUIDv1 {
198 |
199 | // CUID configuration
200 | private static final int BLOCK_SIZE = 4;
201 | private static final int LENGTH_STANDARD = 25;
202 | private static final String START_CHARACTER = "c";
203 | private static final int DISCRETE_VALUE = (int) Math.pow(NUMBER_BASE, BLOCK_SIZE);
204 |
205 | // Counter
206 | private static int counter = 0;
207 |
208 | /**
209 | * Retrieves the counter next value.
210 | *
211 | * @return The counter next value
212 | * @since 1.0.0
213 | */
214 | private static synchronized int nextCounterValue() {
215 |
216 | counter = counter < DISCRETE_VALUE ? counter : 0;
217 | return counter++;
218 | }
219 |
220 | /**
221 | * Generates a random block of data.
222 | *
223 | * @return Newly generated block of data
224 | * @since 1.0.0
225 | */
226 | private static String getRandomBlock() {
227 |
228 | return Common.padWithZero(Integer.toString(Common.nextIntValue() * DISCRETE_VALUE, NUMBER_BASE), BLOCK_SIZE);
229 | }
230 | }
231 |
232 | /**
233 | * CUID Version 2.
234 | *
235 | * @since 2.0.0
236 | */
237 | private static final class CUIDv2 {
238 |
239 | private static final char[] ALPHABET_ARRAY = new char[]{
240 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
241 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
242 | private static final int[] PRIME_NUMBER_ARRAY = new int[]{
243 | 109717,
244 | 109721,
245 | 109741,
246 | 109751,
247 | 109789,
248 | 109793,
249 | 109807,
250 | 109819,
251 | 109829,
252 | 109831};
253 |
254 | // CUID configuration
255 | private static final int LENGTH_STANDARD = 24;
256 |
257 | // Counter
258 | private static int counter = Integer.MAX_VALUE;
259 |
260 | /**
261 | * Retrieves the counter next value.
262 | *
263 | * @return The counter next value
264 | */
265 | private static synchronized int nextCounterValue() {
266 |
267 | counter = counter < Integer.MAX_VALUE ? counter : safeAbs(Common.nextIntValue());
268 | return counter++;
269 | }
270 |
271 | /**
272 | * Creates an entropy string.
273 | *
274 | * @param length Length of the entropy string
275 | * @return String containing entropy in base {@link CUID#NUMBER_BASE}
276 | */
277 | private static String createEntropy(final int length) {
278 |
279 | int primeNumber;
280 | final StringBuilder stringBuilder = new StringBuilder(length);
281 |
282 | while (stringBuilder.length() < length) {
283 | primeNumber = PRIME_NUMBER_ARRAY[safeAbs(Common.nextIntValue()) % PRIME_NUMBER_ARRAY.length];
284 | stringBuilder.append(Integer.toString(primeNumber * Common.nextIntValue(), NUMBER_BASE));
285 | }
286 |
287 | return stringBuilder.toString();
288 | }
289 |
290 | /**
291 | * Computes hash.
292 | *
293 | * @return String containing hash
294 | */
295 | private static String computeHash(final String content, final int saltLength) {
296 |
297 | final String salt = createEntropy(saltLength);
298 | try {
299 | return new BigInteger(MessageDigest.getInstance("SHA3-256").digest((content + salt).getBytes(StandardCharsets.UTF_8)))
300 | .toString(NUMBER_BASE);
301 | } catch (final NoSuchAlgorithmException exception) {
302 | throw new CUIDGenerationException(exception);
303 | }
304 | }
305 | }
306 |
307 | /*
308 | * Holder class to defer initialization until needed.
309 | *
310 | * @since 1.0.0
311 | */
312 | private static final class Common {
313 |
314 | private static final int RANDOM_BUFFER_SIZE = 4096;
315 | private static final byte[] RANDOM_BUFFER = new byte[RANDOM_BUFFER_SIZE];
316 | private static final SecureRandom NUMBER_GENERATOR = new SecureRandom();
317 | private static final String MACHINE_FINGERPRINT = getMachineFingerprint();
318 |
319 | private static int randomBufferIndex = RANDOM_BUFFER_SIZE;
320 |
321 | /**
322 | * Retrieves next random integer value.
323 | *
324 | * @return A random integer
325 | * @since 1.0.0
326 | */
327 | private static synchronized int nextIntValue() {
328 |
329 | if (randomBufferIndex == RANDOM_BUFFER_SIZE) {
330 | Common.NUMBER_GENERATOR.nextBytes(Common.RANDOM_BUFFER);
331 | randomBufferIndex = 0;
332 | }
333 |
334 | return Common.RANDOM_BUFFER[randomBufferIndex++] << 24
335 | | (Common.RANDOM_BUFFER[randomBufferIndex++] & 0xff) << 16
336 | | (Common.RANDOM_BUFFER[randomBufferIndex++] & 0xff) << 8
337 | | (Common.RANDOM_BUFFER[randomBufferIndex++] & 0xff);
338 | }
339 |
340 | /**
341 | * Pads string with leading zero.
342 | *
343 | * @param str The string to pad
344 | * @param size The size to keep
345 | * @return The padded string
346 | * @since 1.0.0
347 | */
348 | private static String padWithZero(final String str, final int size) {
349 |
350 | final String paddedString = "000000000" + str;
351 | return paddedString.substring(paddedString.length() - size);
352 | }
353 |
354 | /**
355 | * retrieves the machine fingerprint.
356 | *
357 | * @return The machine fingerprint
358 | * @since 1.0.0
359 | */
360 | private static String getMachineFingerprint() {
361 |
362 | final String machineName = ManagementFactory.getRuntimeMXBean().getName();
363 | final String[] machineNameTokenArray = machineName.split("@");
364 | final String pid = machineNameTokenArray[0];
365 | final String hostname = machineNameTokenArray[1];
366 |
367 | int acc = hostname.length() + NUMBER_BASE;
368 | for (int i = 0; i < hostname.length(); i += 1) {
369 | acc = acc + hostname.charAt(i);
370 | }
371 |
372 | final String idBlock = padWithZero(pid, 2);
373 | final String nameBlock = padWithZero(Integer.toString(acc), 2);
374 |
375 | return idBlock + nameBlock;
376 | }
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/src/main/java/io/github/thibaultmeyer/cuid/exception/CUIDGenerationException.java:
--------------------------------------------------------------------------------
1 | package io.github.thibaultmeyer.cuid.exception;
2 |
3 | /**
4 | * Exception indicates that the generation of a new CUID has failed.
5 | *
6 | * @since 2.0.0
7 | */
8 | public class CUIDGenerationException extends RuntimeException {
9 |
10 | /**
11 | * Creates a new instance.
12 | *
13 | * @param cause Cause of the exception
14 | * @since 2.0.0
15 | */
16 | public CUIDGenerationException(final Throwable cause) {
17 |
18 | super("CUID generation failure", cause);
19 | }
20 |
21 | /**
22 | * Creates a new instance.
23 | *
24 | * @param cause Cause of the exception
25 | * @since 2.0.1
26 | */
27 | public CUIDGenerationException(final String cause) {
28 |
29 | super("CUID generation failure: " + cause);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/github/thibaultmeyer/cuid/exception/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains the exceptions that can be thrown when generating or manipulating a CUID.
3 | *
4 | * @since 2.0.0
5 | */
6 | package io.github.thibaultmeyer.cuid.exception;
7 |
--------------------------------------------------------------------------------
/src/main/java/io/github/thibaultmeyer/cuid/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Collision-resistant ID optimized for horizontal scaling and performance.
3 | * {@code
4 | * // Generates a random CUID (Version 1)
5 | * final CUID cuid = CUID.randomCUID1();
6 | * System.out.println("CUID: " + cuid);
7 | *
8 | * // Generates a random CUID (Version 2)
9 | * final CUID cuid = CUID.randomCUID2();
10 | * System.out.println("CUID: " + cuid);
11 | *
12 | * // Generates a random CUID with a custom length (Version 2)
13 | * final int customLength = 8; // Length must be, at least, 1
14 | * final CUID cuid = CUID.randomCUID2(customLength);
15 | * System.out.println("CUID: " + cuid);
16 | *
17 | * // Creates a CUID from a string
18 | * final CUID cuid = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
19 | * System.out.println("CUID: " + cuid);
20 | *
21 | * // Verifies if string contains a valid CUID
22 | * final boolean isValid = CUID.isValid("cl9gts1kw00393647w1z4v2tc");
23 | * System.out.println("Is 'cl9gts1kw00393647w1z4v2tc' a valid CUID ? " + isValid);
24 | * }
25 | *
26 | * @since 1.0.0
27 | */
28 | package io.github.thibaultmeyer.cuid;
29 |
--------------------------------------------------------------------------------
/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java implementation of CUID.
3 | *
4 | * @see io.github.thibaultmeyer.cuid.CUID
5 | * @see CUID official website
6 | */
7 | module cuid {
8 |
9 | requires java.management;
10 | exports io.github.thibaultmeyer.cuid;
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/io/github/thibaultmeyer/cuid/CUIDv1Test.java:
--------------------------------------------------------------------------------
1 | package io.github.thibaultmeyer.cuid;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.MethodOrderer;
5 | import org.junit.jupiter.api.Test;
6 | import org.junit.jupiter.api.TestMethodOrder;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | @TestMethodOrder(MethodOrderer.MethodName.class)
12 | final class CUIDv1Test {
13 |
14 | @Test
15 | void fromString() {
16 |
17 | // Arrange
18 | final String cuidAsString = "cl9gts1kw00393647w1z4v2tc";
19 |
20 | // Act
21 | final CUID cuid = CUID.fromString(cuidAsString);
22 |
23 | // Assert
24 | Assertions.assertNotNull(cuid);
25 | Assertions.assertEquals(25, cuid.toString().length());
26 | Assertions.assertEquals("cl9gts1kw00393647w1z4v2tc", cuid.toString());
27 | }
28 |
29 | @Test
30 | void fromStringInvalid() {
31 |
32 | // Arrange
33 | final String cuidAsString = "invalid-cuid";
34 |
35 | // Act
36 | final IllegalArgumentException exception = Assertions.assertThrows(
37 | IllegalArgumentException.class,
38 | () -> CUID.fromString(cuidAsString));
39 |
40 | // Assert
41 | Assertions.assertNotNull(exception);
42 | Assertions.assertEquals("CUID string is invalid: 'invalid-cuid'", exception.getMessage());
43 | }
44 |
45 | @Test
46 | void isValid() {
47 |
48 | // Arrange
49 | final String cuidAsString = "cl9gts1kw00393647w1z4v2tc";
50 |
51 | // Act
52 | final boolean isValid = CUID.isValid(cuidAsString);
53 |
54 | // Assert
55 | Assertions.assertTrue(isValid);
56 | }
57 |
58 | @Test
59 | void isValidInvalid() {
60 |
61 | // Arrange
62 | final String cuidAsString = "not-a-cuid";
63 |
64 | // Act
65 | final boolean isValid = CUID.isValid(cuidAsString);
66 |
67 | // Assert
68 | Assertions.assertFalse(isValid);
69 | }
70 |
71 | @Test
72 | void randomCUID() {
73 |
74 | // Act
75 | final CUID cuid = CUID.randomCUID1();
76 |
77 | // Assert
78 | Assertions.assertNotNull(cuid);
79 | Assertions.assertEquals(25, cuid.toString().length());
80 | }
81 |
82 | @Test
83 | void compareToNotSame() {
84 |
85 | // Arrange
86 | final CUID cuidOne = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
87 | final CUID cuidTwo = CUID.fromString("cl9gts1kw00393647ee45bn56");
88 |
89 | // Act
90 | final int result = cuidOne.compareTo(cuidTwo);
91 |
92 | // Assert
93 | Assertions.assertEquals(18, result);
94 | }
95 |
96 | @Test
97 | void compareToNotSameNull() {
98 |
99 | // Arrange
100 | final CUID cuidOne = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
101 |
102 | // Act
103 | final int result = cuidOne.compareTo(null);
104 |
105 | // Assert
106 | Assertions.assertEquals(-1, result);
107 | }
108 |
109 | @Test
110 | void compareToSame() {
111 |
112 | // Arrange
113 | final CUID cuidOne = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
114 | final CUID cuidTwo = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
115 |
116 | // Act
117 | final int result = cuidOne.compareTo(cuidTwo);
118 |
119 | // Assert
120 | Assertions.assertEquals(0, result);
121 | }
122 |
123 | @Test
124 | void unicityOver500000() {
125 |
126 | // Act
127 | final Set cuidSet = new HashSet<>();
128 | for (int i = 0; i < 500000; i += 1) {
129 | cuidSet.add(CUID.randomCUID1());
130 | }
131 |
132 | // Assert
133 | Assertions.assertEquals(500000, cuidSet.size());
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/java/io/github/thibaultmeyer/cuid/CUIDv2Test.java:
--------------------------------------------------------------------------------
1 | package io.github.thibaultmeyer.cuid;
2 |
3 | import io.github.thibaultmeyer.cuid.exception.CUIDGenerationException;
4 | import org.junit.jupiter.api.Assertions;
5 | import org.junit.jupiter.api.MethodOrderer;
6 | import org.junit.jupiter.api.Test;
7 | import org.junit.jupiter.api.TestMethodOrder;
8 |
9 | import java.util.HashSet;
10 | import java.util.Set;
11 |
12 | @TestMethodOrder(MethodOrderer.MethodName.class)
13 | final class CUIDv2Test {
14 |
15 | @Test
16 | void fromString() {
17 |
18 | // Arrange
19 | final String cuidAsString = "n1ht3jch1r23dy9ramd6ts16";
20 |
21 | // Act
22 | final CUID cuid = CUID.fromString(cuidAsString);
23 |
24 | // Assert
25 | Assertions.assertNotNull(cuid);
26 | Assertions.assertEquals(24, cuid.toString().length());
27 | Assertions.assertEquals("n1ht3jch1r23dy9ramd6ts16", cuid.toString());
28 | }
29 |
30 | @Test
31 | void fromStringInvalid() {
32 |
33 | // Arrange
34 | final String cuidAsString = "invalid-cuid";
35 |
36 | // Act
37 | final IllegalArgumentException exception = Assertions.assertThrows(
38 | IllegalArgumentException.class,
39 | () -> CUID.fromString(cuidAsString));
40 |
41 | // Assert
42 | Assertions.assertNotNull(exception);
43 | Assertions.assertEquals("CUID string is invalid: 'invalid-cuid'", exception.getMessage());
44 | }
45 |
46 | @Test
47 | void isValid() {
48 |
49 | // Arrange
50 | final String cuidAsString = "n1ht3jch1r23dy9ramd6ts16";
51 |
52 | // Act
53 | final boolean isValid = CUID.isValid(cuidAsString);
54 |
55 | // Assert
56 | Assertions.assertTrue(isValid);
57 | }
58 |
59 | @Test
60 | void isValidInvalid() {
61 |
62 | // Arrange
63 | final String cuidAsString = "not-a-cuid";
64 |
65 | // Act
66 | final boolean isValid = CUID.isValid(cuidAsString);
67 |
68 | // Assert
69 | Assertions.assertFalse(isValid);
70 | }
71 |
72 | @Test
73 | void randomCUIDv2() {
74 |
75 | // Act
76 | final CUID cuid = CUID.randomCUID2();
77 |
78 | // Assert
79 | Assertions.assertNotNull(cuid);
80 | Assertions.assertEquals(24, cuid.toString().length());
81 | }
82 |
83 | @Test
84 | void randomCUIDv2BigLength() {
85 |
86 | // Act
87 | final CUID cuid = CUID.randomCUID2(32);
88 |
89 | // Assert
90 | Assertions.assertNotNull(cuid);
91 | Assertions.assertEquals(32, cuid.toString().length());
92 | }
93 |
94 | @Test
95 | void compareToNotSame() {
96 |
97 | // Arrange
98 | final CUID cuidOne = CUID.fromString("g346rykdwn4m117cupchv9m6");
99 | final CUID cuidTwo = CUID.fromString("o7ti2h84195cdvxmbx9vb2gg");
100 |
101 | // Act
102 | final int result = cuidOne.compareTo(cuidTwo);
103 |
104 | // Assert
105 | Assertions.assertEquals(-8, result);
106 | }
107 |
108 | @Test
109 | void compareToNotSameNull() {
110 |
111 | // Arrange
112 | final CUID cuidOne = CUID.fromString("f23nsqjlsmd1oo0kooedsg07");
113 |
114 | // Act
115 | final int result = cuidOne.compareTo(null);
116 |
117 | // Assert
118 | Assertions.assertEquals(-1, result);
119 | }
120 |
121 | @Test
122 | void compareToSame() {
123 |
124 | // Arrange
125 | final CUID cuidOne = CUID.fromString("z976prixkgxs0u13x7g67fo3");
126 | final CUID cuidTwo = CUID.fromString("z976prixkgxs0u13x7g67fo3");
127 |
128 | // Act
129 | final int result = cuidOne.compareTo(cuidTwo);
130 |
131 | // Assert
132 | Assertions.assertEquals(0, result);
133 | }
134 |
135 | @Test
136 | void parameterizedLength() {
137 |
138 | // Act
139 | final CUID cuid = CUID.randomCUID2(2);
140 |
141 | // Assert
142 | Assertions.assertNotNull(cuid);
143 | Assertions.assertEquals(2, cuid.toString().length());
144 | }
145 |
146 | @Test
147 | void parameterizedLengthInvalidSize() {
148 |
149 | // Act
150 | final CUIDGenerationException exception = Assertions.assertThrows(
151 | CUIDGenerationException.class,
152 | () -> CUID.randomCUID2(-1));
153 |
154 | // Assert
155 | Assertions.assertNotNull(exception);
156 | Assertions.assertEquals("CUID generation failure: the length must be at least 1", exception.getMessage());
157 | }
158 |
159 | @Test
160 | void unicityOver500000() {
161 |
162 | // Act
163 | final Set cuidSet = new HashSet<>();
164 | for (int i = 0; i < 500000; i += 1) {
165 | cuidSet.add(CUID.randomCUID2());
166 | }
167 |
168 | // Assert
169 | Assertions.assertEquals(500000, cuidSet.size());
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/test/java/io/github/thibaultmeyer/cuid/PerformanceTest.java:
--------------------------------------------------------------------------------
1 | package io.github.thibaultmeyer.cuid;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.BeforeAll;
5 | import org.junit.jupiter.api.MethodOrderer;
6 | import org.junit.jupiter.api.Test;
7 | import org.junit.jupiter.api.TestMethodOrder;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.UUID;
12 |
13 | @TestMethodOrder(MethodOrderer.MethodName.class)
14 | final class PerformanceTest {
15 |
16 | @BeforeAll
17 | public static void beforeAll() {
18 |
19 | System.gc();
20 | }
21 |
22 | @Test
23 | void speedUUID() {
24 |
25 | for (int i = 0; i < 10; i += 1) {
26 | UUID.randomUUID();
27 | }
28 |
29 | final List uuidList = new ArrayList<>();
30 | final long start = System.nanoTime();
31 | for (int i = 0; i < 1_000_000; i += 1) {
32 | uuidList.add(UUID.randomUUID());
33 | }
34 | final long end = System.nanoTime();
35 |
36 | System.err.println("1,000,000 UUID have been generated in " + (end - start) / 1_000_000 + " ms");
37 | Assertions.assertEquals(1_000_000, uuidList.size());
38 | }
39 |
40 | @Test
41 | void speedCUIDv1() {
42 | for (int i = 0; i < 10; i += 1) {
43 | CUID.randomCUID1();
44 | }
45 |
46 | final List cuidList = new ArrayList<>();
47 | final long start = System.nanoTime();
48 | for (int i = 0; i < 1_000_000; i += 1) {
49 | cuidList.add(CUID.randomCUID1());
50 | }
51 | final long end = System.nanoTime();
52 |
53 | System.err.println("1,000,000 CUIDv1 have been generated in " + (end - start) / 1_000_000 + " ms");
54 | Assertions.assertEquals(1_000_000, cuidList.size());
55 | }
56 |
57 | @Test
58 | void speedCUIDv2Standard() {
59 | for (int i = 0; i < 10; i += 1) {
60 | CUID.randomCUID2();
61 | }
62 |
63 | final List cuidList = new ArrayList<>();
64 | final long start = System.nanoTime();
65 | for (int i = 0; i < 1_000_000; i += 1) {
66 | cuidList.add(CUID.randomCUID2());
67 | }
68 | final long end = System.nanoTime();
69 |
70 | System.err.println("1,000,000 CUIDv2 have been generated in " + (end - start) / 1_000_000 + " ms");
71 | Assertions.assertEquals(1_000_000, cuidList.size());
72 | }
73 |
74 | @Test
75 | void speedCUIDv2Big() {
76 | for (int i = 0; i < 10; i += 1) {
77 | CUID.randomCUID2(32);
78 | }
79 |
80 | final List cuidList = new ArrayList<>();
81 | final long start = System.nanoTime();
82 | for (int i = 0; i < 1_000_000; i += 1) {
83 | cuidList.add(CUID.randomCUID2(32));
84 | }
85 | final long end = System.nanoTime();
86 |
87 | System.err.println("1,000,000 CUIDv2 have been generated in " + (end - start) / 1_000_000 + " ms");
88 | Assertions.assertEquals(1_000_000, cuidList.size());
89 | }
90 | }
91 |
--------------------------------------------------------------------------------