Generating a UUID in Javascript

Say you want to generate an RFC4122 version 4 compliant UUID in Javascript. Here are a few techniques. I created a test harness to test various generation methods.

Test Harness

This harness allows testing performance, validity, and collisions. It works by accepting a generator as an argument. Pretty straight forward.

var testUUIDGenerator = function(generator, iterations, testCollisions, testValidity) {  
  var start = new Date()
    , end
    , i
    , uuidstore = {}
    , uuid
    , pattern = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/

  for(i = 0; i < iterations; i++) {
    uuid = generator();

    if(testCollisions) {
      if(uuidstore[uuid])
        throw 'Collision on ' + uuid;
      uuidstore[uuid] = uuid;
    }

    if(testValidity) {
      if(!uuid.match(pattern))
        throw 'Invalid uuid ' + uuid;
    }
  }

  end = new Date();

  console.log((end.getTime() - start.getTime()));
}

First Attempt

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);
});

Source: Stackoverflow

Now this solution is nice but it relies on Math.random() which is not ideal for a "random" based UUID. In fact, there are reports of collisions using this technique.

Running 100,000 iterations on my Laptop took an average of 376ms.

Second Attempt

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);
});

This attempts to add additional security via the crypto.getRandomValues method available in modern browsers.

Unfortunately, despite being more secure, this technique is massively slower, clocking in at 9376ms.

Third Attempt

var srng = new SecureRandom()  
  , uuid = ""
  , bytes = new Array(16)
  , i
  , hex;

// get random byte array
srng.nextBytes(bytes);

// construct uuid
for(i = 0; i < bytes.length; i += 1) {

  // convert byte to hex
  hex = bytes[i].toString(16);
  if(hex.length === 1) {
    hex = "0" + hex;
  }

  // 12th position is a 4 signifying version 4 uuid
  if(i === 6) {
    uuid += "4" + hex[1];
  } 

  // 16th position must be 8, 9, A, or B
  else if (i === 8) {
    uuid += (parseInt(hex[0], 16) & 0x3 | 0x8).toString(16) + hex[1];
  } 

  // otherwise use hex character
  else {
    uuid += hex;
  }

  // insert dashes appropriately
  if(i === 3 || i === 5 || i === 7 || i === 9) {
    uuid += "-";
  }
}

return uuid;  

This technique takes advantage of SecureRandom created by Tom Wu, and is available on GitHub. This library provides secure random number generation.

Performance for this technique is on part with technique number 1.

comments powered by Disqus