get_time_UUID_bytes()
{
return create_time_UUID_bytes(instance->create_time_safe());
}
/**
* Returns the smaller possible type 1 UUID having the provided timestamp.
*
* Warning: this method should only be used for querying as this
* doesn't at all guarantee the uniqueness of the resulting UUID.
*/
static UUID min_time_UUID(int64_t timestamp)
{
return UUID(create_time(from_unix_timestamp(timestamp)), MIN_CLOCK_SEQ_AND_NODE);
}
/**
* Returns the biggest possible type 1 UUID having the provided timestamp.
*
* Warning: this method should only be used for querying as this
* doesn't at all guarantee the uniqueness of the resulting UUID.
*/
static UUID max_time_UUID(int64_t timestamp)
{
// unix timestamp are milliseconds precision, uuid timestamp are 100's
// nanoseconds precision. If we ask for the biggest uuid have unix
// timestamp 1ms, then we should not extend 100's nanoseconds
// precision by taking 10000, but rather 19999.
int64_t uuid_tstamp = from_unix_timestamp(timestamp + 1) - 1;
return UUID(create_time(uuid_tstamp), MAX_CLOCK_SEQ_AND_NODE);
}
/**
* @param uuid
* @return milliseconds since Unix epoch
*/
static int64_t unix_timestamp(UUID uuid)
{
return (uuid.timestamp() / 10000) + START_EPOCH;
}
/**
* @param uuid
* @return microseconds since Unix epoch
*/
static int64_t micros_timestamp(UUID uuid)
{
return (uuid.timestamp() / 10) + START_EPOCH * 1000;
}
private:
/**
* @param timestamp milliseconds since Unix epoch
* @return
*/
static int64_t from_unix_timestamp(int64_t timestamp) {
return (timestamp - START_EPOCH) * 10000;
}
public:
/**
* Converts a 100-nanoseconds precision timestamp into the 16 byte representation
* of a type 1 UUID (a time-based UUID).
*
* To specify a 100-nanoseconds precision timestamp, one should provide a milliseconds timestamp and
* a number 0 <= n < 10000 such that n*100 is the number of nanoseconds within that millisecond.
*
* Warning: This method is not guaranteed to return unique UUIDs; Multiple
* invocations using identical timestamps will result in identical UUIDs.
*
* @return a type 1 UUID represented as a byte[]
*/
static std::array get_time_UUID_bytes(int64_t time_millis, int nanos)
{
#if 0
if (nanos >= 10000)
throw new IllegalArgumentException();
#endif
return create_time_UUID_bytes(instance->create_time_unsafe(time_millis, nanos));
}
private:
static std::array create_time_UUID_bytes(uint64_t msb)
{
uint64_t lsb = clock_seq_and_node;
std::array uuid_bytes;
for (int i = 0; i < 8; i++)
uuid_bytes[i] = (int8_t) (msb >> 8 * (7 - i));
for (int i = 8; i < 16; i++)
uuid_bytes[i] = (int8_t) (lsb >> 8 * (7 - (i - 8)));
return uuid_bytes;
}
public:
/**
* Returns a milliseconds-since-epoch value for a type-1 UUID.
*
* @param uuid a type-1 (time-based) UUID
* @return the number of milliseconds since the unix epoch
* @throws IllegalArgumentException if the UUID is not version 1
*/
static int64_t get_adjusted_timestamp(UUID uuid)
{
#if 0
if (uuid.version() != 1)
throw new IllegalArgumentException("incompatible with uuid version: "+uuid.version());
#endif
return (uuid.timestamp() / 10000) + START_EPOCH;
}
private:
// needs to return two different values for the same when.
// we can generate at most 10k UUIDs per ms.
// NOTE: In the original Java code this function was "synchronized". This isn't
// needed if we assume our code will run on just one CPU.
int64_t create_time_safe()
{
using namespace std::chrono;
int64_t millis = duration_cast(
system_clock::now().time_since_epoch()).count();
int64_t nanos_since = (millis - START_EPOCH) * 10000;
if (nanos_since > last_nanos)
last_nanos = nanos_since;
else
nanos_since = ++last_nanos;
return create_time(nanos_since);
}
int64_t create_time_unsafe(int64_t when, int nanos)
{
uint64_t nanos_since = ((when - START_EPOCH) * 10000) + nanos;
return create_time(nanos_since);
}
static int64_t create_time(uint64_t nanos_since)
{
uint64_t msb = 0L;
msb |= (0x00000000ffffffffL & nanos_since) << 32;
msb |= (0x0000ffff00000000UL & nanos_since) >> 16;
msb |= (0xffff000000000000UL & nanos_since) >> 48;
msb |= 0x0000000000001000L; // sets the version to 1.
return msb;
}
};
// for the curious, here is how I generated START_EPOCH
// Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
// c.set(Calendar.YEAR, 1582);
// c.set(Calendar.MONTH, Calendar.OCTOBER);
// c.set(Calendar.DAY_OF_MONTH, 15);
// c.set(Calendar.HOUR_OF_DAY, 0);
// c.set(Calendar.MINUTE, 0);
// c.set(Calendar.SECOND, 0);
// c.set(Calendar.MILLISECOND, 0);
// long START_EPOCH = c.getTimeInMillis();
} // namespace utils