Skip to content

Commit c2424e8

Browse files
authored
Variant V7 (#48)
1 parent 6f9a9e6 commit c2424e8

File tree

4 files changed

+198
-4
lines changed

4 files changed

+198
-4
lines changed

src/main/java/com/fasterxml/uuid/Generators.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.fasterxml.uuid.impl.NameBasedGenerator;
2424
import com.fasterxml.uuid.impl.RandomBasedGenerator;
25+
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;
2526
import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator;
2627
import com.fasterxml.uuid.impl.TimeBasedGenerator;
2728

@@ -119,6 +120,28 @@ public static NameBasedGenerator nameBasedGenerator(UUID namespace, MessageDiges
119120
return new NameBasedGenerator(namespace, digester, type);
120121
}
121122

123+
// // Epoch Time+random generation
124+
125+
/**
126+
* Factory method for constructing UUID generator that generates UUID using
127+
* variant 7 (Unix Epoch time+random based).
128+
*/
129+
public static TimeBasedEpochGenerator timeBasedEpochGenerator()
130+
{
131+
return timeBasedEpochGenerator(null);
132+
}
133+
134+
/**
135+
* Factory method for constructing UUID generator that generates UUID using
136+
* variant 7 (time+random based), using specified Ethernet address
137+
* as the location part of UUID.
138+
* No additional external synchronization is used.
139+
*/
140+
public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random)
141+
{
142+
return new TimeBasedEpochGenerator(random);
143+
}
144+
122145
// // Time+location-based generation
123146

124147
/**

src/main/java/com/fasterxml/uuid/impl/RandomBasedGenerator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public UUID generate()
9999
/**********************************************************************
100100
*/
101101

102-
private final static long _toLong(byte[] buffer, int offset)
102+
protected final static long _toLong(byte[] buffer, int offset)
103103
{
104104
long l1 = _toInt(buffer, offset);
105105
long l2 = _toInt(buffer, offset+4);
@@ -126,7 +126,7 @@ private final static long _toInt(byte[] buffer, int offset)
126126
* mechanism for lazy instantiation of the shared secure random
127127
* instance.
128128
*/
129-
private final static class LazyRandom
129+
protected final static class LazyRandom
130130
{
131131
private final static SecureRandom shared = new SecureRandom();
132132

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.fasterxml.uuid.impl;
2+
3+
4+
import java.nio.ByteBuffer;
5+
import java.security.SecureRandom;
6+
import java.util.Random;
7+
import java.util.UUID;
8+
9+
import com.fasterxml.uuid.NoArgGenerator;
10+
import com.fasterxml.uuid.UUIDType;
11+
import com.fasterxml.uuid.impl.RandomBasedGenerator.LazyRandom;
12+
13+
/**
14+
* Implementation of UUID generator that uses time/location based generation
15+
* method field from the Unix Epoch timestamp source - the number of
16+
* milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded
17+
* <p>
18+
* As all JUG provided implementations, this generator is fully thread-safe.
19+
* Additionally it can also be made externally synchronized with other instances
20+
* (even ones running on other JVMs); to do this, use
21+
* {@link com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer} (or
22+
* equivalent).
23+
*
24+
* @since 3.1
25+
*/
26+
public class TimeBasedEpochGenerator extends NoArgGenerator
27+
{
28+
29+
/*
30+
/**********************************************************************
31+
/* Configuration
32+
/**********************************************************************
33+
*/
34+
35+
36+
/**
37+
* Random number generator that this generator uses.
38+
*/
39+
protected final Random _random;
40+
41+
/*
42+
/**********************************************************************
43+
/* Construction
44+
/**********************************************************************
45+
*/
46+
47+
/**
48+
* @param rnd Random number generator to use for generating UUIDs; if null,
49+
* shared default generator is used. Note that it is strongly recommend to
50+
* use a <b>good</b> (pseudo) random number generator; for example, JDK's
51+
* {@link SecureRandom}.
52+
*/
53+
54+
public TimeBasedEpochGenerator(Random rnd)
55+
{
56+
if (rnd == null) {
57+
rnd = LazyRandom.sharedSecureRandom();
58+
}
59+
_random = rnd;
60+
}
61+
62+
/*
63+
/**********************************************************************
64+
/* Access to config
65+
/**********************************************************************
66+
*/
67+
68+
@Override
69+
public UUIDType getType() { return UUIDType.TIME_BASED_EPOCH; }
70+
71+
/*
72+
/**********************************************************************
73+
/* UUID generation
74+
/**********************************************************************
75+
*/
76+
77+
@Override
78+
public UUID generate()
79+
{
80+
ByteBuffer buff = ByteBuffer.allocate(2 * 8);
81+
final long rawTimestamp = System.currentTimeMillis();
82+
final byte[] buffer = new byte[10];
83+
_random.nextBytes(buffer);
84+
buff.position(6);
85+
buff.put(buffer);
86+
buff.position(0);
87+
buff.putLong(rawTimestamp << 16);
88+
buff.flip();
89+
return UUIDUtil.constructUUID(UUIDType.TIME_BASED_EPOCH, buff.array());
90+
}
91+
}

src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.fasterxml.uuid;
1919

20+
import java.nio.ByteBuffer;
2021
import java.security.MessageDigest;
2122
import java.util.*;
2223

@@ -29,6 +30,7 @@
2930
import com.fasterxml.uuid.impl.UUIDUtil;
3031
import com.fasterxml.uuid.impl.NameBasedGenerator;
3132
import com.fasterxml.uuid.impl.RandomBasedGenerator;
33+
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;
3234
import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator;
3335
import com.fasterxml.uuid.impl.TimeBasedGenerator;
3436

@@ -236,6 +238,60 @@ public void testGenerateTimeBasedUUIDWithEthernetAddress()
236238
// check that all UUIDs have the correct ethernet address in the UUID
237239
checkUUIDArrayForCorrectEthernetAddress(uuid_array, ethernet_address);
238240
}
241+
242+
public void testV7value()
243+
{
244+
// Test vector from spec
245+
UUID testValue = UUID.fromString("017F22E2-79B0-7CC3-98C4-DC0C0C07398F");
246+
checkUUIDArrayForCorrectCreationTimeEpoch(new UUID[] { testValue }, 1645557742000L, 1645557742010L);
247+
}
248+
249+
/**
250+
* Test of generateTimeBasedEpochUUID() method,
251+
* of class com.fasterxml.uuid.UUIDGenerator.
252+
*/
253+
public void testGenerateTimeBasedEpochUUID()
254+
{
255+
// this test will attempt to check for reasonable behavior of the
256+
// generateTimeBasedUUID method
257+
258+
// we need a instance to use
259+
TimeBasedEpochGenerator uuid_gen = Generators.timeBasedEpochGenerator();
260+
261+
// first check that given a number of calls to generateTimeBasedEpochUUID,
262+
// all returned UUIDs order after the last returned UUID
263+
// we'll check this by generating the UUIDs into one array and sorting
264+
// then in another and checking the order of the two match
265+
// change the number in the array statement if you want more or less
266+
// UUIDs to be generated and tested
267+
UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY];
268+
269+
// before we generate all the uuids, lets get the start time
270+
long start_time = System.currentTimeMillis();
271+
272+
// now create the array of uuids
273+
for (int i = 0; i < uuid_array.length; i++) {
274+
uuid_array[i] = uuid_gen.generate();
275+
}
276+
277+
// now capture the end time
278+
long end_time = System.currentTimeMillis();
279+
280+
// check that none of the UUIDs are null
281+
checkUUIDArrayForNonNullUUIDs(uuid_array);
282+
283+
// check that all the uuids were correct variant and version (type-1)
284+
checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.TIME_BASED_EPOCH);
285+
286+
// check that all the uuids were generated with correct order
287+
// checkUUIDArrayForCorrectOrdering(uuid_array);
288+
289+
// check that all uuids were unique
290+
checkUUIDArrayForUniqueness(uuid_array);
291+
292+
// check that all uuids have timestamps between the start and end time
293+
checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time);
294+
}
239295

240296
/**
241297
* Test of generateNameBasedUUID(UUID, String)
@@ -409,7 +465,7 @@ public void testGenerateTimeBasedReorderedUUID()
409465
// we need a instance to use
410466
TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator();
411467

412-
// first check that given a number of calls to generateTimeBasedUUID,
468+
// first check that given a number of calls to generateTimeBasedReorderedUUID,
413469
// all returned UUIDs order after the last returned UUID
414470
// we'll check this by generating the UUIDs into one array and sorting
415471
// then in another and checking the order of the two match
@@ -458,7 +514,7 @@ public void testGenerateTimeBasedReorderedUUIDWithEthernetAddress()
458514
// we need a instance to use
459515
TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator(ethernet_address);
460516

461-
// check that given a number of calls to generateTimeBasedUUID,
517+
// check that given a number of calls to generateTimeBasedReorderedUUID,
462518
// all returned UUIDs order after the last returned UUID
463519
// we'll check this by generating the UUIDs into one array and sorting
464520
// then in another and checking the order of the two match
@@ -701,6 +757,30 @@ private void checkUUIDArrayForCorrectCreationTimeReorder(UUID[] uuidArray,
701757
}
702758
}
703759

760+
// Modified version for Variant 7 (Unix Epoch timestamps)
761+
private void checkUUIDArrayForCorrectCreationTimeEpoch(UUID[] uuidArray,
762+
long startTime, long endTime)
763+
{
764+
765+
// 21-Feb-2020, tatu: Not sure why this would be checked, as timestamps come
766+
// from
767+
// System.currenTimeMillis()...
768+
assertTrue("Start time: " + startTime + " was after the end time: " + endTime, startTime <= endTime);
769+
770+
// let's check that all uuids in the array have a timestamp which lands
771+
// between the start and end time
772+
for (int i = 0; i < uuidArray.length; i++) {
773+
byte[] temp_uuid = UUIDUtil.asByteArray(uuidArray[i]);
774+
ByteBuffer buff = ByteBuffer.wrap(temp_uuid);
775+
long uuid_time = buff.getLong() >>> 16;
776+
// now check that the times are correct
777+
assertTrue("Start time: " + startTime + " was not before UUID timestamp: " + uuid_time,
778+
startTime <= uuid_time);
779+
assertTrue("UUID timestamp: " + uuid_time + " was not before the end time: " + endTime,
780+
uuid_time <= endTime);
781+
}
782+
}
783+
704784
private void checkUUIDArrayForCorrectEthernetAddress(UUID[] uuidArray,
705785
EthernetAddress ethernetAddress)
706786
{

0 commit comments

Comments
 (0)