uuid | | | Search

This code provides a set of functions for generating, converting, and manipulating UUIDs, including support for SHA-1 hashing.

Run example

npm run import -- "Generate a universally unique identifier"

Generate a universally unique identifier

var crypto = require('crypto');
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
    byteToHex[i] = (i + 0x100).toString(16).substr(1);
}

function bytesToUuid(buf, offset) {
    var i = offset || 0;
    var bth = byteToHex;
    return bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] + '-' +
        bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]] +
        bth[buf[i++]] + bth[buf[i++]];
}

function sha1(bytes) {
    if (typeof Buffer.from === 'function') {
        // Support modern Buffer API
        if (Array.isArray(bytes)) bytes = Buffer.from(bytes);
        else if (typeof bytes === 'string') bytes = Buffer.from(bytes, 'utf8');
    } else {
        // Support pre-v4 Buffer API
        if (Array.isArray(bytes)) bytes = new Buffer(bytes);
        else if (typeof bytes === 'string') bytes = new Buffer(bytes, 'utf8');
    }

    return crypto.createHash('sha1').update(bytes).digest();
}

function uuidToBytes(uuid) {
    // Note: We assume we're being passed a valid uuid string
    var bytes = [];
    uuid.replace(/[a-fA-F0-9]{2}/g, function (hex) {
        bytes.push(parseInt(hex, 16));
    });

    return bytes;
}

function stringToBytes(str) {
    str = unescape(encodeURIComponent(str)); // UTF8 escape
    var bytes = new Array(str.length);
    for (var i = 0; i < str.length; i++) {
        bytes[i] = str.charCodeAt(i);
    }
    return bytes;
}

function v35(name, version, hashfunc) {
    var generateUUID = function (value, namespace, buf, offset) {
        var off = buf && offset || 0;

        if (typeof(value) == 'string') value = stringToBytes(value);
        if (typeof(namespace) == 'string') namespace = uuidToBytes(namespace);

        if (!Array.isArray(value)) throw TypeError('value must be an array of bytes');
        if (!Array.isArray(namespace) || namespace.length !== 16) throw TypeError(
            'namespace must be uuid string or an Array of 16 byte values');

        // Per 4.3
        var bytes = hashfunc(namespace.concat(value));
        bytes[6] = (bytes[6] & 0x0f) | version;
        bytes[8] = (bytes[8] & 0x3f) | 0x80;

        if (buf) {
            for (var idx = 0; idx < 16; ++idx) {
                buf[off + idx] = bytes[idx];
            }
        }

        return buf || bytesToUuid(bytes);
    };

    generateUUID.name = name;

    // Pre-defined namespaces, per Appendix C
    generateUUID.DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
    generateUUID.URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';

    return generateUUID;
};

module.exports = v35('v5', 0x50, sha1)

What the code could have been:

const crypto = require('crypto');

// Create a byte to hex mapping for efficient lookup
const byteToHex = Array(256).fill(null).map((_, i) => (i + 0x100).toString(16).substr(1));

/**
 * Convert a buffer to a UUID string.
 * @param {Buffer} buf The buffer to convert.
 * @param {number} [offset] The offset into the buffer to start from.
 * @returns {string} The UUID string.
 */
function bytesToUuid(buf, offset = 0) {
    return Array.from(byteToHex)
       .slice(offset, offset + 16)
       .join('');
}

/**
 * Generate an SHA1 hash of a buffer.
 * @param {Buffer|string|Array} bytes The buffer to hash.
 * @returns {Buffer} The SHA1 hash.
 */
function sha1(bytes) {
    if (typeof Buffer.from === 'function') {
        if (Array.isArray(bytes)) bytes = Buffer.from(bytes);
        else if (typeof bytes ==='string') bytes = Buffer.from(bytes, 'utf8');
    } else {
        if (Array.isArray(bytes)) bytes = new Buffer(bytes);
        else if (typeof bytes ==='string') bytes = new Buffer(bytes, 'utf8');
    }

    return crypto.createHash('sha1').update(bytes).digest();
}

/**
 * Convert a UUID string to a byte array.
 * @param {string} uuid The UUID string to convert.
 * @returns {Array} The byte array.
 */
function uuidToBytes(uuid) {
    return Array.from(uuid.match(/.{2}/g), (match) => parseInt(match, 16));
}

/**
 * Convert a string to a byte array.
 * @param {string} str The string to convert.
 * @returns {Array} The byte array.
 */
function stringToBytes(str) {
    return Array.from(str, (char) => str.charCodeAt(char));
}

/**
 * Generate a UUID5 hash based on a namespace and value.
 * @param {number} version The version of the UUID.
 * @param {Array | string} hashfunc The hash function to use.
 * @returns {function} A function that generates a UUID5 hash.
 */
function v35(version, hashfunc = sha1) {
    const generateUUID = (value, namespace, buf, offset) => {
        if (typeof value ==='string') value = stringToBytes(value);
        if (typeof namespace ==='string') namespace = uuidToBytes(namespace);

        if (!Array.isArray(value)) throw new TypeError('value must be an array of bytes');
        if (!Array.isArray(namespace) || namespace.length!== 16) throw new TypeError('namespace must be uuid string or an Array of 16 byte values');

        const bytes = hashfunc(namespace.concat(value));
        bytes[6] = (bytes[6] & 0x0f) | version;
        bytes[8] = (bytes[8] & 0x3f) | 0x80;

        if (buf) {
            for (let idx = 0; idx < 16; ++idx) {
                buf[offset + idx] = bytes[idx];
            }
        }

        return buf || bytesToUuid(bytes);
    };

    generateUUID.name = `v5-${version}`;

    generateUUID.DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
    generateUUID.URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';

    return generateUUID;
};

module.exports = v35(0x50, sha1);

This code defines functions for generating and manipulating UUIDs (Universally Unique Identifiers) using the SHA-1 hashing algorithm.

Here's a breakdown:

  1. byteToHex:

  2. bytesToUuid:

  3. sha1:

  4. uuidToBytes:

  5. stringToBytes:

  6. v35:

In essence, this code provides a set of utilities for working with UUIDs, including generating them from strings, bytes, and SHA-1 hashes.