/* * 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 java.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import libcore.io.Memory; /** * UUID is an immutable representation of a 128-bit universally unique * identifier (UUID). *
* There are multiple, variant layouts of UUIDs, but this class is based upon
* variant 2 of RFC 4122, the
* Leach-Salz variant. This class can be used to model alternate variants, but
* most of the methods will be unsupported in those cases; see each method for
* details.
*
* @since 1.5
*/
public final class UUID implements Serializable, Comparable
* Constructs an instance with the specified bits.
*
* @param mostSigBits
* The 64 most significant bits of the UUID.
* @param leastSigBits
* The 64 least significant bits of the UUID.
*/
public UUID(long mostSigBits, long leastSigBits) {
this.mostSigBits = mostSigBits;
this.leastSigBits = leastSigBits;
init();
}
/**
*
* Sets up the transient fields of this instance based on the current values
* of the {@code mostSigBits} and {@code leastSigBits} fields.
*/
private void init() {
// setup hash field
int msbHash = (int) (mostSigBits ^ (mostSigBits >>> 32));
int lsbHash = (int) (leastSigBits ^ (leastSigBits >>> 32));
hash = msbHash ^ lsbHash;
// setup variant field
if ((leastSigBits & 0x8000000000000000L) == 0) {
// MSB0 not set, NCS backwards compatibility variant
variant = 0;
} else if ((leastSigBits & 0x4000000000000000L) != 0) {
// MSB1 set, either MS reserved or future reserved
variant = (int) ((leastSigBits & 0xE000000000000000L) >>> 61);
} else {
// MSB1 not set, RFC 4122 variant
variant = 2;
}
// setup version field
version = (int) ((mostSigBits & 0x000000000000F000) >>> 12);
if (variant != 2 && version != 1) {
return;
}
// setup timestamp field
long timeLow = (mostSigBits & 0xFFFFFFFF00000000L) >>> 32;
long timeMid = (mostSigBits & 0x00000000FFFF0000L) << 16;
long timeHigh = (mostSigBits & 0x0000000000000FFFL) << 48;
timestamp = timeLow | timeMid | timeHigh;
// setup clock sequence field
clockSequence = (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
// setup node field
node = (leastSigBits & 0x0000FFFFFFFFFFFFL);
}
/**
*
* Generates a variant 2, version 4 (randomly generated number) UUID as per
* RFC 4122.
*
* @return an UUID instance.
*/
public static UUID randomUUID() {
byte[] data = new byte[16];
// lock on the class to protect lazy init
synchronized (UUID.class) {
if (rng == null) {
rng = new SecureRandom();
}
}
rng.nextBytes(data);
return makeUuid(data, 4);
}
/**
*
* Generates a variant 2, version 3 (name-based, MD5-hashed) UUID as per RFC 4122.
*
* @param name
* the name used as byte array to create an UUID.
* @return an UUID instance.
*/
public static UUID nameUUIDFromBytes(byte[] name) {
if (name == null) {
throw new NullPointerException();
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
return makeUuid(md.digest(name), 3);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static UUID makeUuid(byte[] hash, int version) {
long msb = Memory.peekLong(hash, 0, ByteOrder.BIG_ENDIAN);
long lsb = Memory.peekLong(hash, 8, ByteOrder.BIG_ENDIAN);
// Set the version field.
msb &= ~(0xfL << 12);
msb |= ((long) version) << 12;
// Set the variant field to 2. Note that the variant field is variable-width,
// so supporting other variants is not just a matter of changing the constant 2 below!
lsb &= ~(0x3L << 62);
lsb |= 2L << 62;
return new UUID(msb, lsb);
}
/**
*
* Parses a UUID string with the format defined by {@link #toString()}.
*
* @param uuid
* the UUID string to parse.
* @return an UUID instance.
* @throws NullPointerException
* if {@code uuid} is {@code null}.
* @throws IllegalArgumentException
* if {@code uuid} is not formatted correctly.
*/
public static UUID fromString(String uuid) {
if (uuid == null) {
throw new NullPointerException();
}
int[] position = new int[5];
int lastPosition = 1;
int startPosition = 0;
int i = 0;
for (; i < position.length && lastPosition > 0; i++) {
position[i] = uuid.indexOf("-", startPosition);
lastPosition = position[i];
startPosition = position[i] + 1;
}
// should have and only can have four "-" in UUID
if (i != position.length || lastPosition != -1) {
throw new IllegalArgumentException("Invalid UUID: " + uuid);
}
long m1 = Long.parseLong(uuid.substring(0, position[0]), 16);
long m2 = Long.parseLong(uuid.substring(position[0] + 1, position[1]), 16);
long m3 = Long.parseLong(uuid.substring(position[1] + 1, position[2]), 16);
long lsb1 = Long.parseLong(uuid.substring(position[2] + 1, position[3]), 16);
long lsb2 = Long.parseLong(uuid.substring(position[3] + 1), 16);
long msb = (m1 << 32) | (m2 << 16) | m3;
long lsb = (lsb1 << 48) | lsb2;
return new UUID(msb, lsb);
}
/**
*
* The 64 least significant bits of the UUID.
*
* @return the 64 least significant bits.
*/
public long getLeastSignificantBits() {
return leastSigBits;
}
/**
*
* The 64 most significant bits of the UUID.
*
* @return the 64 most significant bits.
*/
public long getMostSignificantBits() {
return mostSigBits;
}
/**
*
* The version of the variant 2 UUID as per RFC 4122. If the variant
* is not 2, then the version will be 0.
*
* The variant of the UUID as per RFC 4122.
*
* The timestamp value of the version 1, variant 2 UUID as per RFC 4122.
*
* @return a {@code long} value.
* @throws UnsupportedOperationException
* if {@link #version()} is not 1.
*/
public long timestamp() {
if (version != 1) {
throw new UnsupportedOperationException();
}
return timestamp;
}
/**
*
* The clock sequence value of the version 1, variant 2 UUID as per RFC 4122.
*
* @return a {@code long} value.
* @throws UnsupportedOperationException
* if {@link #version()} is not 1.
*/
public int clockSequence() {
if (version != 1) {
throw new UnsupportedOperationException();
}
return clockSequence;
}
/**
*
* The node value of the version 1, variant 2 UUID as per RFC 4122.
*
* @return a {@code long} value.
* @throws UnsupportedOperationException
* if {@link #version()} is not 1.
*/
public long node() {
if (version != 1) {
throw new UnsupportedOperationException();
}
return node;
}
/**
*
* Compares this UUID to the specified UUID. The natural ordering of UUIDs
* is based upon the value of the bits from most significant to least
* significant.
*
* @param uuid
* the UUID to compare to.
* @return a value of -1, 0 or 1 if this UUID is less than, equal to or
* greater than {@code uuid}.
*/
public int compareTo(UUID uuid) {
if (uuid == this) {
return 0;
}
if (this.mostSigBits != uuid.mostSigBits) {
return this.mostSigBits < uuid.mostSigBits ? -1 : 1;
}
assert this.mostSigBits == uuid.mostSigBits;
if (this.leastSigBits != uuid.leastSigBits) {
return this.leastSigBits < uuid.leastSigBits ? -1 : 1;
}
assert this.leastSigBits == uuid.leastSigBits;
return 0;
}
/**
*
* Compares this UUID to another object for equality. If {@code object}
* is not {@code null}, is a UUID instance, and all bits are equal, then
* {@code true} is returned.
*
* @param object
* the {@code Object} to compare to.
* @return {@code true} if this UUID is equal to {@code object}
* or {@code false} if not.
*/
@Override
public boolean equals(Object object) {
if (object == null) {
return false;
}
if (this == object) {
return true;
}
if (!(object instanceof UUID)) {
return false;
}
UUID that = (UUID) object;
return (this.leastSigBits == that.leastSigBits)
&& (this.mostSigBits == that.mostSigBits);
}
/**
*
* Returns a hash value for this UUID that is consistent with the
* {@link #equals(Object)} method.
*
* @return an {@code int} value.
*/
@Override
public int hashCode() {
return hash;
}
/**
*
* Returns a string representation of this UUID in the following format, as
* per RFC 4122.
*
*
* Resets the transient fields to match the behavior of the constructor.
*
* @param in
* the {@code InputStream} to read from.
* @throws IOException
* if {@code in} throws it.
* @throws ClassNotFoundException
* if {@code in} throws it.
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
// read in non-transient fields
in.defaultReadObject();
// setup transient fields
init();
}
}
*
*
* @return an {@code int} value.
*/
public int version() {
return version;
}
/**
*
*
*
* @return an {@code int} value.
*/
public int variant() {
return variant;
}
/**
*
* UUID = time-low "-" time-mid "-"
* time-high-and-version "-"
* clock-seq-and-reserved
* clock-seq-low "-" node
* time-low = 4hexOctet
* time-mid = 2hexOctet
* time-high-and-version = 2hexOctet
* clock-seq-and-reserved = hexOctet
* clock-seq-low = hexOctet
* node = 6hexOctet
* hexOctet = hexDigit hexDigit
* hexDigit =
* "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" /
* "a" / "b" / "c" / "d" / "e" / "f" /
* "A" / "B" / "C" / "D" / "E" / "F"
*
*
* @return a String instance.
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder(36);
String msbStr = Long.toHexString(mostSigBits);
if (msbStr.length() < 16) {
int diff = 16 - msbStr.length();
for (int i = 0; i < diff; i++) {
builder.append('0');
}
}
builder.append(msbStr);
builder.insert(8, '-');
builder.insert(13, '-');
builder.append('-');
String lsbStr = Long.toHexString(leastSigBits);
if (lsbStr.length() < 16) {
int diff = 16 - lsbStr.length();
for (int i = 0; i < diff; i++) {
builder.append('0');
}
}
builder.append(lsbStr);
builder.insert(23, '-');
return builder.toString();
}
/**
*