GetTerminus / ngx-tools

:hammer_and_wrench: A collection of tools and utilities for Terminus applications.
MIT License
9 stars 2 forks source link

Utility: UUID #91

Closed benjamincharity closed 5 years ago

benjamincharity commented 6 years ago

Core is currently using:

export function uuid(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    // tslint:disable-next-line
    const r = Math.random() * 16 | 0;
    // tslint:disable-next-line
    const v = c == 'x' ? r : (r&0x3 | 0x8);
    return v.toString(16);
  });
}

NOTE: Math.random is not truly collision safe so we should look to improve this method.

Also found at the link is a setup to test for collisions.

Possible resources

benjamincharity commented 5 years ago

The functions to test

1: Math.random

Math.random method added as a baseline for timing.

function uuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {  
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

2: window.crypto

// http://derpturkey.com/generating-a-uuid-in-javascript/
function uuid2() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {  
      var nums = new Uint32Array(1)
        , r
        , v;

      nums = window.crypto.getRandomValues(nums);

      r = nums[0] % 16,
      v = (c === 'x') ? r : (r&0x3|0x8);
      return v.toString(16);
  });
}

3: window.crypto with clamping

// http://derpturkey.com/generating-a-uuid-in-javascript/ (in comments)
function uuid() {
    var uuidFormat = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
    var nums = window.crypto.getRandomValues(new Uint8ClampedArray(uuidFormat.split(/[xy]/).length - 1));
    var pointer =0;

    return uuidFormat.replace(/[xy]/g, function(c) {  
        var r = nums[pointer++] % 16,
            v = (c === 'x') ? r : (r&0x3|0x8);

        return v.toString(16);
    });
}

4: Golf version

// https://gist.github.com/jed/982883
function uuid4(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

5: StackOverflow 1

// https://stackoverflow.com/a/8472700/722367
function uuid5() {
        // If we have a cryptographically secure PRNG, use that
        // https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript
        var buf = new Uint16Array(8);
        window.crypto.getRandomValues(buf);
        var S4 = function(num) {
            var ret = num.toString(16);
            while(ret.length < 4){
                ret = "0"+ret;
            }
            return ret;
        };
        return (S4(buf[0])+S4(buf[1])+"-"+S4(buf[2])+"-"+S4(buf[3])+"-"+S4(buf[4])+"-"+S4(buf[5])+S4(buf[6])+S4(buf[7]));
    }

6: StackOverflow 2

// https://stackoverflow.com/a/873856/722367
function uuid6() {
    // http://www.ietf.org/rfc/rfc4122.txt
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
}

NOTE: I was unable to consistently prove that predefining an Array length improves speed: https://jsperf.com/pre-determined-array-length

Results

https://jsperf.com/test-uuid-generation

In Chrome, all options are almost identical with the ability to execute ~830MM operations/second. The consistent better performers are 4 & 6;

In Firefox, all options outperform their match in Chrome with the ability to execute ~1.3B operations/second. The consistent (slightly) better performers are 4 & 6.