It is:
- 128-bit compatible with UUID
- 1.21e+24 unique ULIDs per millisecond
- Lexicographically sortable!
- Canonically encoded as a 26 character string, as opposed to the 36 character UUID
- Uses Crockford’s base32 for better efficiency and readability (5 bits per character)
- Case insensitive
- No special characters (URL safe)
In general the structure of a ULID is as follows:
01AN4Z07BY | 79KA1307SR9X4MV3 |
---|---|
Timestamp | Randomness |
48bits | 80bits |
For more information have a look at the original specification.
cl-ulid is not in quicklisp. It will need to be installed in the local-projects quicklisp directory:
> cd /$USER_HOME/quicklisp/local-projects/
> git clone https://github.com/jolby/cl-ulid.git
CL-USER>(ql:quickload :cl-ulid)
> (:CL-ULID)
CL-USER> ;; (ulid:ulid) returns the values of a ULID encoded as a string as well as a byte array
CL-USER> (ulid::ulid)
>"01H7NR28J0FEDWV9G66ZRKJY4V"
#(1 137 235 129 34 64 123 155 205 166 6 55 241 57 120 155)
CL-USER> ;; (Retrieve the encoded values from the string or byte array
CL-USER> (let* ((ulid-generator (ulid:make-ulid-generator :random-number-fn (lambda (x) 1)))
(time (ulid:get-unix-time-ms)))
(multiple-value-bind (ulid-str ulid-bytes) (funcall ulid-generator time)
(multiple-value-bind (ts random) (ulid:decode-to-values ulid-str)
(assert (= ts time))
(assert (= random 1)))
(multiple-value-bind (ts random) (ulid:decode-to-values ulid-bytes)
(assert (= ts time))
(assert (= random 1))))
(format nil "DONE~%"))
The default cl-ulid system uses ironclad’s strong-random for the RNG. It is very easy to provide another RNG source if desired. Look at the ironclad-random.lisp file to see how it is done.
There is at least one other common lisp implementation that I know of: https://gitlab.com/colinstrickland/cms-ulid.git
Unfortunately, I was already in the middle of this implementation when I found that one. The code in that implementation seems cleaner than this project, but it probably never hurts to have more than one implementation to choose from.