Termite currently has a dependency on libuuid to generate universally unique identifiers. Since there is no need for the result to conform to the
uuid_t data type, just that the result is unique across space and time, I wrote a uuid generator in Scheme which will make running Termite easier on more platforms. I loosely followed the pattern for generating primary keys in EJB Design Patterns which does not require a singleton or a database, etc. i.e. multiple instances of this uuid maker can exist in the same address space or in different address spaces, and on different hosts. I had fun replacing the absolutely horrible
C code from libuuid with short, readable, Scheme.
> (load "my-uuid.scm")
> (define make-uuid (make-uuid-maker))
Each call to
make-uuid-maker returns a new closure around an "address part" and an "object part". Every uuid this closure makes will be unique to its IP address and unique to the internal id of the object in the specific Scheme runtime. (Note: the original uuid generators use the network card MAC address but the IP address is easier to get and just as good for this purpose.)
Each call to one of these closures combines its address part and object part to a "time part" and a "random part". The time part is roughly the number of milliseconds since January 1, 1970 mod (2^32)-1. The random part is a random 32-bit unsigned integer. So within the closure's IP address and randomly allocated object, each resulting uuid is also made unique to the millisecond and a random number *within* the millisecond.
(let ((address-part (or (hex-encode-ip-address)
(let ((time-part (hex-encode-current-milliseconds))
(string-append time-part "-" address-part "-" object-id-part "-" random-part)))))
The IP address is the host's internet address if it has one, or an intranet address, otherwise the address part becomes just another 32-bit random integer.
(let* ((address (or (get-internet-address)
(let ((result ""))
(set! result (string-append result (number->string n))))
The object id closed within a uuid maker is also random. If a closure is always created at a specific point, e.g. in initialization, then this object runs the risk of having the same id each time. Instead a random number of objects (not more than 100) are created before the one captured in the closure.
(let loop ((n (random-integer 100)))
(let ((id (object->serial-number (cons 1 2))))
(if (= n 0)
(lengthen-with-leading-zeros (number->string id 16))
(loop (- n 1))))))