org.hornetq.utils.UUID Maven / Gradle / Ivy
/* JUG Java Uuid Generator
*
* Copyright (c) 2002- Tatu Saloranta, [email protected]
*
* Licensed under the License specified in the file licenses/LICENSE.txt which is
* included with the source code.
* You may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hornetq.utils;
/**
* UUID represents Universally Unique Identifiers (aka Global UID in Windows
* world). UUIDs are usually generated via UUIDGenerator (or in case of 'Null
* UUID', 16 zero bytes, via static method getNullUUID()), or received from
* external systems.
*
* By default class caches the string presentations of UUIDs so that description
* is only created the first time it's needed. For memory stingy applications
* this caching can be turned off (note though that if uuid.toString() is never
* called, desc is never calculated so only loss is the space allocated for the
* desc pointer... which can of course be commented out to save memory).
*
* Similarly, hash code is calculated when it's needed for the first time, and
* from thereon that value is just returned. This means that using UUIDs as keys
* should be reasonably efficient.
*
* UUIDs can be compared for equality, serialized, cloned and even sorted.
* Equality is a simple bit-wise comparison. Ordering (for sorting) is done by
* first ordering based on type (in the order of numeric values of types),
* secondarily by time stamp (only for time-based time stamps), and finally by
* straight numeric byte-by-byte comparison (from most to least significant
* bytes).
*/
public final class UUID
{
private static final String kHexChars = "0123456789abcdefABCDEF";
public static final byte INDEX_CLOCK_HI = 6;
public static final byte INDEX_CLOCK_MID = 4;
public static final byte INDEX_CLOCK_LO = 0;
public static final byte INDEX_TYPE = 6;
// Clock seq. & variant are multiplexed...
public static final byte INDEX_CLOCK_SEQUENCE = 8;
public static final byte INDEX_VARIATION = 8;
public static final byte TYPE_NULL = 0;
public static final byte TYPE_TIME_BASED = 1;
public static final byte TYPE_DCE = 2; // Not used
public static final byte TYPE_NAME_BASED = 3;
public static final byte TYPE_RANDOM_BASED = 4;
/*
* 'Standard' namespaces defined (suggested) by UUID specs:
*/
public static final String NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
public static final String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
public static final String NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
public static final String NAMESPACE_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8";
/*
* By default let's cache desc, can be turned off. For hash code there's no
* point in turning it off (since the int is already part of the instance
* memory allocation); if you want to save those 4 bytes (or possibly bit
* more if alignment is bad) just comment out hash caching.
*/
private static boolean sDescCaching = true;
private final byte[] mId;
// Both string presentation and hash value may be cached...
private transient String mDesc = null;
private transient int mHashCode = 0;
/**
* @param type UUID type
* @param data 16 byte UUID contents
*/
public UUID(final int type, final byte[] data)
{
mId = data;
// Type is multiplexed with time_hi:
mId[UUID.INDEX_TYPE] &= (byte) 0x0F;
mId[UUID.INDEX_TYPE] |= (byte) (type << 4);
// Variant masks first two bits of the clock_seq_hi:
mId[UUID.INDEX_VARIATION] &= (byte) 0x3F;
mId[UUID.INDEX_VARIATION] |= (byte) 0x80;
}
public byte[] asBytes()
{
return mId;
}
/**
* Could use just the default hash code, but we can probably create a better
* identity hash (ie. same contents generate same hash) manually, without
* sacrificing speed too much. Although multiplications with modulos would
* generate better hashing, let's use just shifts, and do 2 bytes at a time.
*
* Of course, assuming UUIDs are randomized enough, even simpler approach
* might be good enough?
*
* Is this a good hash? ... one of these days I better read more about basic
* hashing techniques I swear!
*/
private static final int[] kShifts = {3, 7, 17, 21, 29, 4, 9};
@Override
public int hashCode()
{
if (mHashCode == 0)
{
// Let's handle first and last byte separately:
int result = mId[0] & 0xFF;
result |= result << 16;
result |= result << 8;
for (int i = 1; i < 15; i += 2)
{
int curr = (mId[i] & 0xFF) << 8 | mId[i + 1] & 0xFF;
int shift = UUID.kShifts[i >> 1];
if (shift > 16)
{
result ^= curr << shift | curr >>> 32 - shift;
}
else
{
result ^= curr << shift;
}
}
// and then the last byte:
int last = mId[15] & 0xFF;
result ^= last << 3;
result ^= last << 13;
result ^= last << 27;
// Let's not accept hash 0 as it indicates 'not hashed yet':
if (result == 0)
{
mHashCode = -1;
}
else
{
mHashCode = result;
}
}
return mHashCode;
}
@Override
public String toString()
{
/*
* Could be synchronized, but there isn't much harm in just taking our
* chances (ie. in the worst case we'll form the string more than once...
* but result is the same)
*/
if (mDesc == null)
{
StringBuffer b = new StringBuffer(36);
for (int i = 0; i < 16; ++i)
{
// Need to bypass hyphens:
switch (i)
{
case 4:
case 6:
case 8:
case 10:
b.append('-');
break;
default:
// no-op
}
int hex = mId[i] & 0xFF;
b.append(UUID.kHexChars.charAt(hex >> 4));
b.append(UUID.kHexChars.charAt(hex & 0x0f));
}
if (!UUID.sDescCaching)
{
return b.toString();
}
mDesc = b.toString();
}
return mDesc;
}
/**
* Creates a 128bit number from the String representation of {@link UUID}.
*
* @param uuid
* @return byte array that can be used to recreate a UUID instance from the given String
* representation
*/
public static byte[] stringToBytes(String uuid)
{
byte[] data = new byte[16];
int dataIdx = 0;
try
{
for (int i = 0; i < uuid.length(); )
{
while (uuid.charAt(i) == '-')
{
i++;
}
char c1 = uuid.charAt(i);
char c2 = uuid.charAt(i + 1);
i += 2;
int c1Bytes = Character.digit(c1, 16);
int c2Bytes = Character.digit(c2, 16);
data[dataIdx++] = (byte) ((c1Bytes << 4) + c2Bytes);
}
}
catch (RuntimeException e)
{
throw new IllegalArgumentException(e);
}
return data;
}
/**
* Checking equality of UUIDs is easy; just compare the 128-bit number.
*/
@Override
public boolean equals(final Object o)
{
if (!(o instanceof UUID))
{
return false;
}
byte[] otherId = ((UUID) o).mId;
byte[] thisId = mId;
for (int i = 0; i < 16; ++i)
{
if (otherId[i] != thisId[i])
{
return false;
}
}
return true;
}
}