Skip to content

Commit

Permalink
Merge branch 'master' into feature/epochRandom
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder authored Feb 19, 2024
2 parents ebf9312 + 191f37f commit 9ba8ce8
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
4 changes: 4 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Releases
5.0.0 (not yet released)

#53: Increase JDK baseline to JDK 8
#81: Add `UUIDUtil.extractTimestamp()` for extracting 64-bit timestamp for
all timestamp-based versions
(requested by @gabrielbalan)
(contributed by @magdel)
#85: Fix `LazyRandom` for native code generation tools
(contributed by @Maia-Everett)

Expand Down
62 changes: 62 additions & 0 deletions src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,66 @@ private final static void _checkUUIDByteArray(byte[] bytes, int offset)
throw new IllegalArgumentException("Invalid offset ("+offset+") passed: not enough room in byte array (need 16 bytes)");
}
}

/**
* Extract 64-bit timestamp from time-based UUIDs (if time-based type);
* returns 0 for other types.
*
* @param uuid uuid timestamp to extract from
*
* @return timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps
*
* @since 5.0.0
*/
public static long extractTimestamp(UUID uuid)
{
UUIDType type = typeOf(uuid);
if (type == null) {
// Likely null UUID:
return 0L;
}
switch (type) {
case NAME_BASED_SHA1:
case UNKNOWN:
case DCE:
case RANDOM_BASED:
case FREE_FORM:
case NAME_BASED_MD5:
return 0L;
case TIME_BASED:
return _getTimestampFromUuidV1(uuid);
case TIME_BASED_REORDERED:
return _getTimestampFromUuidV6(uuid);
case TIME_BASED_EPOCH:
return _getTimestampFromUuidV7(uuid);
default:
throw new IllegalArgumentException("Invalid `UUID`: unexpected type " + type);
}
}

private static long _getTimestampFromUuidV1(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110_1111_1111_1111L;
long low = mostSignificantBits >>> 32;
long lowOfHigher = mostSignificantBits & 0xFFFF0000L;
lowOfHigher = lowOfHigher >>> 16;
long highOfHigher = mostSignificantBits & 0xFFFFL;
return highOfHigher << 48 | lowOfHigher << 32 | low;
}

private static long _getTimestampFromUuidV6(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L;
long lowL = mostSignificantBits & 0xFFFL;
long lowH = mostSignificantBits & 0xFFFF0000L;
lowH = lowH >>> 16;
long high = mostSignificantBits & 0xFFFFFFFF00000000L;
return high >>> 4 | lowH << 12 | lowL;
}

private static long _getTimestampFromUuidV7(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L;
return mostSignificantBits >>> 16;
}
}
41 changes: 41 additions & 0 deletions src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.fasterxml.uuid.impl;

import java.util.Random;
import java.util.UUID;

import com.fasterxml.uuid.Generators;
import junit.framework.TestCase;

/**
Expand All @@ -13,6 +15,8 @@
*/
public class UUIDUtilTest extends TestCase
{
final static int TEST_REPS = 1_000_000;

public void testNilUUID() {
UUID nil = UUIDUtil.nilUUID();
// Should be all zeroes:
Expand All @@ -26,4 +30,41 @@ public void testMaxUUID() {
assertEquals(~0, max.getMostSignificantBits());
assertEquals(~0, max.getLeastSignificantBits());
}

public void testExtractTimestampUUIDTimeBased() {
TimeBasedGenerator generator = Generators.timeBasedGenerator();
final Random rnd = new Random(1);
for (int i = 0; i < TEST_REPS; i++) {
long rawTimestamp = rnd.nextLong() >>> 4;
UUID uuid = generator.construct(rawTimestamp);
assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid));
}
}

public void testExtractTimestampUUIDTimeBasedReordered() {
TimeBasedReorderedGenerator generator = Generators.timeBasedReorderedGenerator();
final Random rnd = new Random(2);
for (int i = 0; i < TEST_REPS; i++) {
long rawTimestamp = rnd.nextLong() >>> 4;
UUID uuid = generator.construct(rawTimestamp);
assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid));
}
}

public void testExtractTimestampUUIDEpochBased() {
TimeBasedEpochGenerator generator = Generators.timeBasedEpochGenerator();
final Random rnd = new Random(3);
for (int i = 0; i < TEST_REPS; i++) {
long rawTimestamp = rnd.nextLong() >>> 16;
UUID uuid = generator.construct(rawTimestamp);
assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid));
}
}

public void testExtractTimestampUUIDOnOtherValues() {
assertEquals(0L, UUIDUtil.extractTimestamp(null));
assertEquals(0L, UUIDUtil.extractTimestamp(UUID.fromString("00000000-0000-0000-0000-000000000000")));
assertEquals(0L, UUIDUtil.extractTimestamp(UUIDUtil.nilUUID()));
assertEquals(0L, UUIDUtil.extractTimestamp(UUIDUtil.maxUUID()));
}
}

0 comments on commit 9ba8ce8

Please sign in to comment.