Skip to content

Variant V7 #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/main/java/com/fasterxml/uuid/Generators.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.fasterxml.uuid.impl.NameBasedGenerator;
import com.fasterxml.uuid.impl.RandomBasedGenerator;
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;
import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator;
import com.fasterxml.uuid.impl.TimeBasedGenerator;

Expand Down Expand Up @@ -114,6 +115,28 @@ public static NameBasedGenerator nameBasedGenerator(UUID namespace, MessageDiges
return new NameBasedGenerator(namespace, digester, type);
}

// // Epoch Time+random generation

/**
* Factory method for constructing UUID generator that generates UUID using
* variant 7 (Unix Epoch time+random based).
*/
public static TimeBasedEpochGenerator timeBasedEpochGenerator()
{
return timeBasedEpochGenerator(null);
}

/**
* Factory method for constructing UUID generator that generates UUID using
* variant 7 (time+random based), using specified Ethernet address
* as the location part of UUID.
* No additional external synchronization is used.
*/
public static TimeBasedEpochGenerator timeBasedEpochGenerator(Random random)
{
return new TimeBasedEpochGenerator(random);
}

// // Time+location-based generation

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public UUID generate()
/**********************************************************************
*/

private final static long _toLong(byte[] buffer, int offset)
protected final static long _toLong(byte[] buffer, int offset)
{
long l1 = _toInt(buffer, offset);
long l2 = _toInt(buffer, offset+4);
Expand All @@ -126,7 +126,7 @@ private final static long _toInt(byte[] buffer, int offset)
* mechanism for lazy instantation of the shared secure random
* instance.
*/
private final static class LazyRandom
protected final static class LazyRandom
{
private final static SecureRandom shared = new SecureRandom();

Expand Down
91 changes: 91 additions & 0 deletions src/main/java/com/fasterxml/uuid/impl/TimeBasedEpochGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.fasterxml.uuid.impl;


import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;

import com.fasterxml.uuid.NoArgGenerator;
import com.fasterxml.uuid.UUIDType;
import com.fasterxml.uuid.impl.RandomBasedGenerator.LazyRandom;

/**
* Implementation of UUID generator that uses time/location based generation
* method field from the Unix Epoch timestamp source - the number of
* milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded
* <p>
* As all JUG provided implementations, this generator is fully thread-safe.
* Additionally it can also be made externally synchronized with other instances
* (even ones running on other JVMs); to do this, use
* {@link com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer} (or
* equivalent).
*
* @since 3.1
*/
public class TimeBasedEpochGenerator extends NoArgGenerator
{

/*
/**********************************************************************
/* Configuration
/**********************************************************************
*/


/**
* Random number generator that this generator uses.
*/
protected final Random _random;

/*
/**********************************************************************
/* Construction
/**********************************************************************
*/

/**
* @param rnd Random number generator to use for generating UUIDs; if null,
* shared default generator is used. Note that it is strongly recommend to
* use a <b>good</b> (pseudo) random number generator; for example, JDK's
* {@link SecureRandom}.
*/

public TimeBasedEpochGenerator(Random rnd)
{
if (rnd == null) {
rnd = LazyRandom.sharedSecureRandom();
}
_random = rnd;
}

/*
/**********************************************************************
/* Access to config
/**********************************************************************
*/

@Override
public UUIDType getType() { return UUIDType.TIME_BASED_EPOCH; }

/*
/**********************************************************************
/* UUID generation
/**********************************************************************
*/

@Override
public UUID generate()
{
ByteBuffer buff = ByteBuffer.allocate(2 * 8);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taking the cowards way out with a byte buffer

final long rawTimestamp = System.currentTimeMillis();
final byte[] buffer = new byte[10];
_random.nextBytes(buffer);
buff.position(6);
buff.put(buffer);
buff.position(0);
buff.putLong(rawTimestamp << 16);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I think this would overwrite bytes #6 and #7, so need to reverse the order.

buff.flip();
return UUIDUtil.constructUUID(UUIDType.TIME_BASED_EPOCH, buff.array());
}
}
84 changes: 82 additions & 2 deletions src/test/java/com/fasterxml/uuid/UUIDGeneratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package com.fasterxml.uuid;

import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.*;

Expand All @@ -29,6 +30,7 @@
import com.fasterxml.uuid.impl.UUIDUtil;
import com.fasterxml.uuid.impl.NameBasedGenerator;
import com.fasterxml.uuid.impl.RandomBasedGenerator;
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;
import com.fasterxml.uuid.impl.TimeBasedReorderedGenerator;
import com.fasterxml.uuid.impl.TimeBasedGenerator;

Expand Down Expand Up @@ -233,6 +235,60 @@ public void testGenerateTimeBasedUUIDWithEthernetAddress()
// check that all UUIDs have the correct ethernet address in the UUID
checkUUIDArrayForCorrectEthernetAddress(uuid_array, ethernet_address);
}

public void testV7value()
{
// Test vector from spec
UUID testValue = UUID.fromString("017F22E2-79B0-7CC3-98C4-DC0C0C07398F");
checkUUIDArrayForCorrectCreationTimeEpoch(new UUID[] { testValue }, 1645557742000L, 1645557742010L);
}

/**
* Test of generateTimeBasedEpochUUID() method,
* of class com.fasterxml.uuid.UUIDGenerator.
*/
public void testGenerateTimeBasedEpochUUID()
{
// this test will attempt to check for reasonable behavior of the
// generateTimeBasedUUID method

// we need a instance to use
TimeBasedEpochGenerator uuid_gen = Generators.timeBasedEpochGenerator();

// first check that given a number of calls to generateTimeBasedEpochUUID,
// all returned UUIDs order after the last returned UUID
// we'll check this by generating the UUIDs into one array and sorting
// then in another and checking the order of the two match
// change the number in the array statement if you want more or less
// UUIDs to be generated and tested
UUID uuid_array[] = new UUID[SIZE_OF_TEST_ARRAY];

// before we generate all the uuids, lets get the start time
long start_time = System.currentTimeMillis();

// now create the array of uuids
for (int i = 0; i < uuid_array.length; i++) {
uuid_array[i] = uuid_gen.generate();
}

// now capture the end time
long end_time = System.currentTimeMillis();

// check that none of the UUIDs are null
checkUUIDArrayForNonNullUUIDs(uuid_array);

// check that all the uuids were correct variant and version (type-1)
checkUUIDArrayForCorrectVariantAndVersion(uuid_array, UUIDType.TIME_BASED_EPOCH);

// check that all the uuids were generated with correct order
// checkUUIDArrayForCorrectOrdering(uuid_array);

// check that all uuids were unique
checkUUIDArrayForUniqueness(uuid_array);

// check that all uuids have timestamps between the start and end time
checkUUIDArrayForCorrectCreationTimeEpoch(uuid_array, start_time, end_time);
}

/**
* Test of generateNameBasedUUID(UUID, String)
Expand Down Expand Up @@ -406,7 +462,7 @@ public void testGenerateTimeBasedReorderedUUID()
// we need a instance to use
TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator();

// first check that given a number of calls to generateTimeBasedUUID,
// first check that given a number of calls to generateTimeBasedReorderedUUID,
// all returned UUIDs order after the last returned UUID
// we'll check this by generating the UUIDs into one array and sorting
// then in another and checking the order of the two match
Expand Down Expand Up @@ -455,7 +511,7 @@ public void testGenerateTimeBasedReorderedUUIDWithEthernetAddress()
// we need a instance to use
TimeBasedReorderedGenerator uuid_gen = Generators.timeBasedReorderedGenerator(ethernet_address);

// check that given a number of calls to generateTimeBasedUUID,
// check that given a number of calls to generateTimeBasedReorderedUUID,
// all returned UUIDs order after the last returned UUID
// we'll check this by generating the UUIDs into one array and sorting
// then in another and checking the order of the two match
Expand Down Expand Up @@ -698,6 +754,30 @@ private void checkUUIDArrayForCorrectCreationTimeReorder(UUID[] uuidArray,
}
}

// Modified version for Variant 7 (Unix Epoch timestamps)
private void checkUUIDArrayForCorrectCreationTimeEpoch(UUID[] uuidArray,
long startTime, long endTime)
{

// 21-Feb-2020, tatu: Not sure why this would be checked, as timestamps come
// from
// System.currenTimeMillis()...
assertTrue("Start time: " + startTime + " was after the end time: " + endTime, startTime <= endTime);

// let's check that all uuids in the array have a timestamp which lands
// between the start and end time
for (int i = 0; i < uuidArray.length; i++) {
byte[] temp_uuid = UUIDUtil.asByteArray(uuidArray[i]);
ByteBuffer buff = ByteBuffer.wrap(temp_uuid);
long uuid_time = buff.getLong() >>> 16;
// now check that the times are correct
assertTrue("Start time: " + startTime + " was not before UUID timestamp: " + uuid_time,
startTime <= uuid_time);
assertTrue("UUID timestamp: " + uuid_time + " was not before the end time: " + endTime,
uuid_time <= endTime);
}
}

private void checkUUIDArrayForCorrectEthernetAddress(UUID[] uuidArray,
EthernetAddress ethernetAddress)
{
Expand Down