Emacs Lisp: Insert Random UUID

By Xah Lee. Date: . Last updated: .

This week's exercise is to write a function insert-random-uuid. When called, it should insert a UUID. Here's some examples of UUID:

0a1cd3bc-96fa-71d1-4338-27092ca4cfa5
1e27a053-60a4-af61-f38d-9f1f123740d6
115024d2-7c74-326e-c9ec-064f42d08b31
070f1f0b-2454-3ffa-4aa2-d6e0652d03fe

Basically, it's a random string of symbols 0 to 9 and a to f, arranged in 8-4-4-4-12 blocks. There are many ways to implement this.

Solution

Here's a sloppy toy solution that gets the idea across:

(random t)

(defun insert-random-uuid ()
  "Insert a random UUID.
Example of a UUID: 1df63142-a513-c850-31a3-535fc3520c3d

WARNING: this is a simple implementation. The chance of generating the same UUID is much higher than a robust algorithm.."
  (interactive)
  (insert
   (format "%04x%04x-%04x-%04x-%04x-%06x%06x"
           (random (expt 16 4))
           (random (expt 16 4))
           (random (expt 16 4))
           (random (expt 16 4))
           (random (expt 16 4))
           (random (expt 16 6))
           (random (expt 16 6)) ) ) )

Elisp's random function can be called in 3 ways:

Elisp's documentation does not say exactly how large a pool the random number is generated from, only that “On most systems, this is 29 bits' worth.”. (See: (info "(elisp) Random Numbers"))

UUID is basically a random number between 0 and 2^128-1, inclusive. Since emacs's random is only from a space of 2^29 possibilities, so we need to call it several times.

Note: if you don't know already, the random number generating function in almost all computer languages are called Pseudorandom number generator. They are numbers generated in a predictive way. For example, if you start emacs, then if you call (random 100) few times, you will always get this sequence: {0 38 17 70 …}. (For this to work, make sure none of your init files calls (random t). (start emacs by emacs -Q to prevent loading any init files).)

It's a good idea to set a seed when you call random in elisp, and the setting of seed should be outside of your function.

The meaning of randomness is a deep math topic. In general, there is no one absolute universal definition. You might check relevant articles on Wikipedia, starting with Random sequence.

Using MD5 to Generate UUID

Here's a better solution, by Christopher Wellons.

(defun xah-insert-random-uuid ()
  "Insert a UUID. This uses a simple hashing of variable data.
Example of a UUID: 1df63142-a513-c850-31a3-535fc3520c3d

Note: this code uses https://en.wikipedia.org/wiki/Md5 , which is not cryptographically safe. I'm not sure what's the implication of its use here.

Version 2015-01-30
URL `http://ergoemacs.org/emacs/elisp_generate_uuid.html'
"
;; by Christopher Wellons, 2011-11-18. Editted by Xah Lee.
;; Edited by Hideki Saito further to generate all valid variants for "N" in xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx format.
  (interactive)
  (let ((myStr (md5 (format "%s%s%s%s%s%s%s%s%s%s"
                            (user-uid)
                            (emacs-pid)
                            (system-name)
                            (user-full-name)
                            (current-time)
                            (emacs-uptime)
                            (garbage-collect)
                            (buffer-string)
                            (random)
                            (recent-keys)))))

    (insert (format "%s-%s-4%s-%s%s-%s"
                    (substring myStr 0 8)
                    (substring myStr 8 12)
                    (substring myStr 13 16)
                    (format "%x" (+ 8 (random 4)))
                    (substring myStr 17 20)
                    (substring myStr 20 32)))))

It uses several random elements from emacs, then call md5 to generate the UUID.

Robust Implementation of UUID

Note, according to Wikipedia's description of UUID, in the “Variants and Versions” section, some digits should be fixed when the UUID is generated by a random or pseudo-random number.

That is, in the format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

The “M” should be “4” and the N should be one of {8, 9, A, B}.

Call Linux Shell 「uuidgen」

Linux has a shell command “uuidgen”. You can write a elisp wrapper to call it, like this:

(defun insert-random-uuid ()
  (interactive)
  (shell-command "uuidgen" t))

Thanks to Yuri Khan and Jon Snader for discussion about UUID. See their comments at bottom of: http://xahlee.blogspot.com/2011/11/emacs-lisp-exercise-insert-random-uuid.html.

Thanks to Christopher Wellons and Hideki Saito.

Like it? Buy Xah Emacs Tutorial. Thanks.