package org.apache.cassandra.utils; /* * * 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. * */ import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.UUID; /** * The goods are here: www.ietf.org/rfc/rfc4122.txt. */ public class UUIDGen { // A grand day! millis at 00:00:00.000 15 Oct 1582. private static final long START_EPOCH = -12219292800000L; private static final long clock = new Random(System.currentTimeMillis()).nextLong(); // placement of this singleton is important. It needs to be instantiated *AFTER* the other statics. private static final UUIDGen instance = new UUIDGen(); private long lastNanos; private final Map<InetAddress, Long> nodeCache = new HashMap<InetAddress, Long>(); private UUIDGen() { // make sure someone didn't whack the clock by changing the order of instantiation. if (clock == 0) throw new RuntimeException("singleton instantiation is misplaced."); } /** * Creates a type 1 UUID (time-based UUID) that substitutes a hash of * an IP address in place of the MAC (unavailable to Java). * * @param addr the host address to use * @return a UUID instance */ public static UUID makeType1UUIDFromHost(InetAddress addr) { return new UUID(instance.createTimeSafe(), instance.getClockSeqAndNode(addr)); } /** creates a type 1 uuid from raw bytes. */ public static UUID getUUID(ByteBuffer raw) { return new UUID(raw.getLong(raw.position()), raw.getLong(raw.position() + 8)); } /** decomposes a uuid into raw bytes. */ public static byte[] decompose(UUID uuid) { long most = uuid.getMostSignificantBits(); long least = uuid.getLeastSignificantBits(); byte[] b = new byte[16]; for (int i = 0; i < 8; i++) { b[i] = (byte)(most >>> ((7-i) * 8)); b[8+i] = (byte)(least >>> ((7-i) * 8)); } return b; } /** * Returns a 16 byte representation of a type 1 UUID (a time-based UUID), * based on the current system time. * * @return a type 1 UUID represented as a byte[] */ public static byte[] getTimeUUIDBytes() { return createTimeUUIDBytes(instance.createTimeSafe()); } /** * Converts a milliseconds-since-epoch timestamp into the 16 byte representation * of a type 1 UUID (a time-based UUID). * * <p><i><b>Warning:</b> This method is not guaranteed to return unique UUIDs; Multiple * invocations using identical timestamps will result in identical UUIDs.</i></p> * * @param timeMillis * @return a type 1 UUID represented as a byte[] */ public static byte[] getTimeUUIDBytes(long timeMillis) { return createTimeUUIDBytes(instance.createTimeUnsafe(timeMillis)); } private static byte[] createTimeUUIDBytes(long msb) { long lsb = instance.getClockSeqAndNode(); byte[] uuidBytes = new byte[16]; for (int i = 0; i < 8; i++) uuidBytes[i] = (byte) (msb >>> 8 * (7 - i)); for (int i = 8; i < 16; i++) uuidBytes[i] = (byte) (lsb >>> 8 * (7 - i)); return uuidBytes; } /** * 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 InvalidArgumentException if the UUID is not version 1 */ public static long getAdjustedTimestamp(UUID uuid) { if (uuid.version() != 1) throw new IllegalArgumentException("incompatible with uuid version: "+uuid.version()); return (uuid.timestamp() / 10000) - START_EPOCH; } private long getClockSeqAndNode() { try { return getClockSeqAndNode(InetAddress.getLocalHost()); } catch (UnknownHostException e) { throw new RuntimeException(e); } } // todo: could cache value if we assume node doesn't change. private long getClockSeqAndNode(InetAddress addr) { long lsb = 0; lsb |= (clock & 0x3f00000000000000L) >>> 56; // was 58? lsb |= 0x0000000000000080; lsb |= (clock & 0x00ff000000000000L) >>> 48; lsb |= makeNode(addr); return lsb; } // needs to return two different values for the same when. // we can generate at most 10k UUIDs per ms. private synchronized long createTimeSafe() { long nanosSince = (System.currentTimeMillis() - START_EPOCH) * 10000; if (nanosSince > lastNanos) lastNanos = nanosSince; else nanosSince = ++lastNanos; return createTime(nanosSince); } private long createTimeUnsafe(long when) { long nanosSince = (when - START_EPOCH) * 10000; return createTime(nanosSince); } private long createTime(long nanosSince) { long msb = 0L; msb |= (0x00000000ffffffffL & nanosSince) << 32; msb |= (0x0000ffff00000000L & nanosSince) >>> 16; msb |= (0xffff000000000000L & nanosSince) >>> 48; msb |= 0x0000000000001000L; // sets the version to 1. return msb; } // Lazily create node hashes, and cache them for later private long makeNode(InetAddress addr) { if (nodeCache.containsKey(addr)) return nodeCache.get(addr); // ideally, we'd use the MAC address, but java doesn't expose that. byte[] hash = FBUtilities.hash(ByteBuffer.wrap(addr.toString().getBytes())); long node = 0; for (int i = 0; i < Math.min(6, hash.length); i++) node |= (0x00000000000000ff & (long)hash[i]) << (5-i)*8; assert (0xff00000000000000L & node) == 0; nodeCache.put(addr, node); return node; } } // 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();