UUID Version 7 Generation

Benchmark created on


Setup

/**
 * A Univerally Unique Identifier (UUID).
 */
class UUID
{
    static get VARIANT_APOLLO_NCS(){ return 0b0; }
    static get VARIANT_RFC4122(){ return 0b10; }
    static get VARIANT_COM(){ return 0b110; }

    /**
     * Construct a UUID from the bytes array.
     *
     * @param {Uint8Array} bytes The 16-bytes of the UUID.
     */
    constructor(bytes)
    {
        this.value = bytes;
    }

    /**
     * Convert the
     * @param {boolean} [use_separator=true]
     * @returns String
     */
    toString(use_separator = true)
    {
        const str = this.value.toHex();
        if (use_separator)
        {
            return `${str.slice(0, 8)}-${str.slice(8, 12)}-${str.slice(12, 16)}-${str.slice(16, 20)}-${str.slice(20, 32)}`;
        }
        else
        {
            return str;
        }
    }

    /**
     * The four-bit UUID version number.
     *
     * This is bits 48-51 (zero-indexed) of the UUID.
     *
     * @readonly
     * @memberof UUID
     * @returns Number
     */
    get version()
    {
        return this.value[6] >> 4;
    }

    /**
     * The UUID variant bits.
     *
     * Note:
     *     (Legacy) Apollo NCS UUIDs have a single `0` variant bit (bit 64).
     *     RFC 4122/DCE 1.1 UUIDs have two `10` variant bits (bits 64-65).
     *     Microsoft COM/DCOM UUIDs have 3 `110` variant bits (bits 64-66).
     *
     * @readonly
     * @memberof UUID
     * @returns Number
     */
    get variant()
    {
        const bits = this.value[8] >> 5;
        if ((bits & 0b100) == 0) return UUID.VARIANT_APOLLO_NCS;
        if ((bits & 0b010) == 0) return UUID.VARIANT_RFC4122;
        return UUID.VARIANT_COM;
    }
}

/**
 * A version 4 UUID.
 */
class UUID4 extends UUID
{
    /**
     * Generate a version 4 UUID.
     *
     * @param {Number} [variant=1] The UUID version 4 variant which determines the values of
     *     bits 64-66. The default is variant `1`.
     *     * Variant `0` is a legacy Apollo NCS UUID (bit 64 is `0` and bits 65-66 are
     *       random).
     *     * Variant `1` is a RFC 4122/DCE 1.1 UUID (bits 64-65 are `10` and bit 66 is
     *       random).
     *     * Variant `2` is a Microsoft COM/DCOM (where bits 64-66 are `110`).
     * @returns UUID4
     */
    static generate(variant = 1)
    {
        const rand = crypto.getRandomValues(new Uint8Array(16));
        // Set version
        rand[6] = 0b01000000 | (rand[6] & 0b00001111);
        // Set variant
        switch (variant)
        {
            case 2:
                rand[8] = (UUID.VARIANT_COM << 5) | (rand[8] & 0b00011111);
                break;
            case 0:
                rand[8] = (UUID.VARIANT_APOLLO_NCS << 7) | (rand[8] & 0b01111111);
                break;
            default:
                rand[8] = (UUID.VARIANT_RFC4122 << 6) | (rand[8] & 0b00111111);
        }

        return new UUID4(rand);
    }
}

/**
 * A version 7 UUID.
 */
class UUID7 extends UUID
{
    /**
     * Generate a version 7 UUID.
     *
     * @returns UUID7
     */
    static generate()
    {
        const time = Date.now();
        const rand = crypto.getRandomValues(new Uint8Array(10));
        // Set version
        rand[0] = 0b01110000 | (rand[0] & 0b00001111);
        // Set variant
        rand[2] = (UUID.VARIANT_RFC4122 << 6) | (rand[2] & 0b00111111);

        return new UUID7(
            Uint8Array.of(
                Math.floor(time / 0x010000000000) % 256,
                Math.floor(time / 0x000100000000) % 256,
                Math.floor(time / 0x000001000000) % 256,
                Math.floor(time / 0x000000010000) % 256,
                Math.floor(time / 0x000000000100) % 256,
                time % 256,
                ...rand,
            ),
        );
    }

    static generate2()
    {
        const time = Date.now();
        const rand = crypto.getRandomValues(new Uint8Array(16));
        // Set time
        rand[0] = Math.floor(time / 0x010000000000) % 256
        rand[1] = Math.floor(time / 0x000100000000) % 256
        rand[2] = Math.floor(time / 0x000001000000) % 256
        rand[3] = Math.floor(time / 0x000000010000) % 256
        rand[4] = Math.floor(time / 0x000000000100) % 256
        rand[5] = time % 256
        // Set version
        rand[6] = 0b01110000 | (rand[6] & 0b00001111);
        // Set variant
        rand[8] = (UUID.VARIANT_RFC4122 << 6) | (rand[8] & 0b00111111);

        return new UUID7(rand);
    }

    /**
     * The time component of the UUID.
     *
     * Note: This is stored in bits 0-47 (zero-indexed) of the UUID.
     *
     * @readonly
     * @memberof UUID7
     * @returns Date
     */
    get time()
    {
        return new Date(
              this.value[0] * 0x010000000000
            + this.value[1] * 0x000100000000
            + this.value[2] * 0x000001000000
            + this.value[3] * 0x000000010000
            + this.value[4] * 0x000000000100
            + this.value[5],
        );
    }
}

Test runner

Ready to run.

Testing in
TestOps/sec
Generate 10 random bytes and concatenate time.
UUID7.generate()
ready
Generate 16 random bytes and overwrite time.
UUID7.generate2()
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.