org.apache.activemq.artemis.utils.UUID Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.activemq.artemis.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) {
assert data.length == 16;
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;
}
private UUID(final byte[] data) {
assert data.length == 16;
mId = data;
}
/** This is for conversions between two types of UUID */
public UUID(java.util.UUID uuid) {
this(ByteUtil.doubleLongToBytes(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()));
}
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 The 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;
}
}