All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fizzed.crux.util.UUIDs Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 Fizzed, Inc.
 *
 * Licensed 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 com.fizzed.crux.util;

import java.util.Objects;
import java.util.UUID;

/**
 * Utilities for working with UUIDs, especially time-based UUIDs.
 * 
 * @author jjlauer
 */
public class UUIDs {
    
    static public UUID fromBytes(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        if (bytes.length != 16) {
            throw new IllegalArgumentException("must be 16 bytes in length");
        }
        long msb = 0;
        long lsb = 0;
        for (int i = 0; i < 8; i++) {
            msb = (msb << 8) | (bytes[i] & 0xff);
        }
        for (int i = 8; i < 16; i++) {
            lsb = (lsb << 8) | (bytes[i] & 0xff);
        }
        return new UUID(msb, lsb);
    }
    
    static public byte[] toBytes(String uuid) {
        return toBytes(UUID.fromString(uuid));
    }
    
    static public byte[] toBytes(UUID uuid) {
        if (uuid == null) {
            return null;
        }
        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        byte[] bytes = new byte[16];
        for (int i = 7; i >= 0; i--) {
            bytes[i] = (byte)(msb & 0xFF);
            msb >>= 8;
        }
        for (int i = 15; i >= 8; i--) {
            bytes[i] = (byte)(lsb & 0xFF);
            lsb >>= 8;
        }
        return bytes;
    }
 
    /**
     * Creates a byte array of a time-based UUID that is suitable for sorting
     * and lexigraphical ordering.  Matches the algorithm in MySQL 8.0's 
     * uuid_to_bin() method.  https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid-to-bin
     * For example, 6ccd780c-babb-1026-9564-5b8c656024db will return a byte
     * array as 1026babb-6ccd-780c-9564-5b8c656024db.  The embedded timestamp
     * is moved to the front.
     * @param uuid The time-based UUID
     * @return A byte array representation
     */
    static public byte[] toTimeBytes(UUID uuid) {
        if (uuid == null) {
            return null;
        }
        if (uuid.version() != 1) {
            throw new IllegalArgumentException("not a time-based uuid");
        }
        
        // move the 6 & 7 octet of msb to front
        // move the 4 & 5 octet of msg to next
        // then move 0-3 to the back
        //  in: 6ccd-780c-babb-1026-9564-5b8c-6560-24db
        // out: 1026-babb-6ccd-780c-9564-5b8c-6560-24db
        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        byte[] bytes = new byte[16];
        bytes[1] = (byte)(msb & 0xFF); msb >>= 8;
        bytes[0] = (byte)(msb & 0xFF); msb >>= 8;
        bytes[3] = (byte)(msb & 0xFF); msb >>= 8;
        bytes[2] = (byte)(msb & 0xFF); msb >>= 8;
        for (int i = 7; i >= 4; i--) {
            bytes[i] = (byte)(msb & 0xFF);
            msb >>= 8;
        }
        for (int i = 15; i >= 8; i--) {
            bytes[i] = (byte)(lsb & 0xFF);
            lsb >>= 8;
        }
        return bytes;
    }
    
    static public UUID fromTimeBytes(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        if (bytes.length != 16) {
            throw new IllegalArgumentException("must be 16 bytes in length");
        }

        // most significant nibble MUST be 1
        int version = (bytes[0] >> 4) & 0x0F;
        if (version != 1) {
            throw new IllegalArgumentException("not a time-based byte array");
        }
        
        //  in: 1026-babb-6ccd-780c 9564-5b8c-6560-24db
        // out: 6ccd-780c-babb-1026 9564-5b8c-6560-24db
        long msb = 0;
        long lsb = 0;
        for (int i = 4; i < 8; i++) {
            msb = (msb << 8) | (bytes[i] & 0xff);
        }
        msb = (msb << 8) | (bytes[2] & 0xff);
        msb = (msb << 8) | (bytes[3] & 0xff);
        msb = (msb << 8) | (bytes[0] & 0xff);
        msb = (msb << 8) | (bytes[1] & 0xff);
        for (int i = 8; i < 16; i++) {
            lsb = (lsb << 8) | (bytes[i] & 0xff);
        }
        return new UUID(msb, lsb);
    }
    
    // millis at 00:00:00.000 15 Oct 1582
    private static final long START_EPOCH = -12219292800000L;
    
    /**
     * Extracts the timestamp embedded within a Type 1 time-based UUID and
     * converts it to "unix time" as the number of milliseconds that have elapsed
     * since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970.
     * The same kind of value that System.currentTimeMillis() returns.
     * @param uuid The time-based uuid
     * @return The number of epoch milliseconds
     */
    static public long getEpochMillis(UUID uuid) {
        Objects.requireNonNull(uuid, "uuid was null");
        if (uuid.version() != 1) {
            throw new IllegalArgumentException("not a time-based uuid");
        }
        return (uuid.timestamp() / 10000L) + START_EPOCH;
    }
    
    /**
     * Creates a new time-based UUID based on the one provided and sets its
     * embedded timestamp to the supplied one.  This method does not guarantee
     * uniqueness since the caller is supplying a timestamp -- so you should only
     * use this method if you will externally verify uniqueness in some other way.
     * @param uuid The uuid to use as base
     * @param epochMillis The timestamp to use based on "unix time"
     * @return A new UUID
     */
    static public UUID setEpochMillis(UUID uuid, long epochMillis) {
        Objects.requireNonNull(uuid, "uuid was null");
        if (uuid.version() != 1) {
            throw new IllegalArgumentException("not a time-based uuid");
        }
        // convert unix time to type 1 timestamp
        // note: some granularity will be lost since unix time is less precise
        long type1Timestamp = (epochMillis - START_EPOCH) * 10000L;
        long msb = buildMSBWithType1Timestamp(type1Timestamp);
        return new UUID(msb, uuid.getLeastSignificantBits());
    }
    
    /**
     * Creates a new time-based UUID based on the one provided and sets its
     * embedded timestamp to the supplied one. This method does not guarantee
     * uniqueness since the caller is supplying a timestamp -- so you should only
     * use this method if you will externally verify uniqueness in some other way.
     * @param uuid The uuid to use as a base
     * @param type1Timestamp The type-1 timestamp to use. Type 1 timestamps
     *      are NOT epoch time!
     * @return A new UUID
     */
    static public UUID setTimestamp(UUID uuid, long type1Timestamp) {
        Objects.requireNonNull(uuid, "uuid was null");
        if (uuid.version() != 1) {
            throw new IllegalArgumentException("not a time-based uuid");
        }
        long msb = buildMSBWithType1Timestamp(type1Timestamp);
        return new UUID(msb, uuid.getLeastSignificantBits());
    }
    
    //
    // methods below copied from Apache-licensed code here:
    // https://github.com/tfredrich/cassandra-java-driver/blob/master/driver-core/src/main/java/com/datastax/driver/core/utils/UUIDs.java
    //
    
    static private long buildMSBWithType1Timestamp(long type1Timestamp) {
        long msb = 0L;
        msb |= (0x00000000ffffffffL & type1Timestamp) << 32;
        msb |= (0x0000ffff00000000L & type1Timestamp) >>> 16;
        msb |= (0x0fff000000000000L & type1Timestamp) >>> 48;
        msb |= 0x0000000000001000L; // sets the version to 1.
        return msb;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy