package org.doomdark.uuid; import java.util.Random; /* JUG Java Uuid Generator * * Copyright (c) 2002 Tatu Saloranta, tatu.saloranta@iki.fi * * You can redistribute this work and/or modify it under the terms of * LGPL (Lesser General Public License), as published by * Free Software Foundation (http://www.fsf.org). No warranty is * implied. See LICENSE for details about licensing. */ /** * UUIDTimer produces the time stamps required for time-based UUIDs. * It works according to UUID-specification, with following * modifications: * *<ul> *<li>Java libs can only product time stamps with maximum resolution * of one millisecond. To compensate, an additional counter is used, * so that more than one UUID can be generated between java clock * updates. Counter may be used to generate up to 10000 UUIDs for * each distrinct java clock value (which in some cases may mean * getting 10k UUIDs for each 55 msecs, a la Windows, ie only * about 200000 UUIDs per second). *<li> An additional precaution, counter is initialized not to 0 * but to a random 8-bit number, and each time clock changes, lowest * 8-bits of counter are preserved. The purpose it to make likelyhood * of multi-JVM multi-instance generators to collide, without significantly * reducing max. UUID generation speed. Note though that using more than * one generator (from separate JVMs) is strongly discouraged, so * hopefully this enhancement isn't needed. * This 8-bit offset has to be reduced from total max. UUID count to * preserve ordering property of UUIDs (ie. one can see which UUID * was generated first for given UUID generator); the resulting * 9500 UUIDs isn't much different from the optimal choice. *</ul> *<p> *Some additional assumptions about calculating the timestamp: *<ul> *<li>System.currentTimeMillis() is assumed to give time offset in UTC, * or at least close enough thing to get correct timestamps. The * alternate route would have to go through calendar object, use * TimeZone offset to get to UTC, and then modify. Using currentTimeMillis * should be much faster to allow rapid UUID creation. *<li>Similarly, the constant used for time offset between 1.1.1970 and * start of Gregorian calendar is assumed to be correct (which seems * to be the case when testing with Java calendars). *</ul> */ public class UUIDTimer { /** * Since System.longTimeMillis() returns time from january 1st 1970, * and UUIDs need time from the beginning of gregorian calendar * (15-oct-1582), need to apply the offset: */ private final static long kClockOffset = 0x01b21dd213814000L; /** * Also, instead of getting time in units of 100nsecs, we get something * with max resolution of 1 msec... and need the multiplier as well */ private final static long kClockMultiplier = 10000; private final static long kClockMultiplierL = 10000L; private final Random mRnd; private final byte[] mClockSequence = new byte[2]; private long mLastTimeStamp = 0L; private int mClockCounter = 0; public UUIDTimer(Random rnd) { mRnd = rnd; initTimeStamps(); } private void initTimeStamps() { /* Let's also generate the clock sequence now, plus initialize * low 8-bits of the internal clock counter (which is used to * compensate for lacking accurace; instead of 100nsecs resolution * is at best 1 msec, ie. 10000 coarser... so we have to use * a counter) */ mRnd.nextBytes(mClockSequence); /* Counter is used to further make it slightly less likely that * two instances of UUIDGenerator (from separate JVMs as no more * than one can be created in one JVM) would produce colliding * time-based UUIDs. The practice of using multiple generators, * is strongly discouraged, of course, but just in case... */ byte[] tmp = new byte[1]; mRnd.nextBytes(tmp); mClockCounter = tmp[0] & 0xFF; mLastTimeStamp = 0L; } public void getTimestamp(byte[] uuidData) { // First the clock sequence: uuidData[UUID.INDEX_CLOCK_SEQUENCE] = mClockSequence[0]; uuidData[UUID.INDEX_CLOCK_SEQUENCE+1] = mClockSequence[1]; long now = System.currentTimeMillis(); /* Unless time goes backwards (ie. bug in currentTimeMillis()), * this should never happen. Nonetheless, UUID draft states that * if it does happen, clock sequence has to be re-randomized. * Fair enough. */ if (now < mLastTimeStamp) { initTimeStamps(); } else if (now == mLastTimeStamp) { // Ran 'out of timestamps' for this clock cycle? Have to wait! if (mClockCounter == kClockMultiplier) { // Should this be randomised now? mClockCounter &= 0xFF; do { try { Thread.sleep(1L); } catch (InterruptedException ie) { } now = System.currentTimeMillis(); } while (now == mLastTimeStamp); mLastTimeStamp = now; } } else { mClockCounter &= 0xFF; mLastTimeStamp = now; } /* Now, let's translate the timestamp to one UUID needs, 100ns * unit offset from the beginning of Gregorian calendar... */ now *= kClockMultiplierL; now += kClockOffset; // Plus add the clock counter: now += mClockCounter; /* Time fields are nicely split across the UUID, so can't just * linearly dump the stamp: */ int clockHi = (int) (now >>> 32); int clockLo = (int) now; uuidData[UUID.INDEX_CLOCK_HI] = (byte) (clockHi >>> 24); uuidData[UUID.INDEX_CLOCK_HI+1] = (byte) (clockHi >>> 16); uuidData[UUID.INDEX_CLOCK_MID] = (byte) (clockHi >>> 8); uuidData[UUID.INDEX_CLOCK_MID+1] = (byte) clockHi; uuidData[UUID.INDEX_CLOCK_LO] = (byte) (clockLo >>> 24); uuidData[UUID.INDEX_CLOCK_LO+1] = (byte) (clockLo >>> 16); uuidData[UUID.INDEX_CLOCK_LO+2] = (byte) (clockLo >>> 8); uuidData[UUID.INDEX_CLOCK_LO+3] = (byte) clockLo; ++mClockCounter; } }