Home   Archive   Permalink

A UUID Implementation

I've been toying around with writing a decentralized, P2P application in REBOL 2, and one of the things I need to do is generate a universally unique identifier for users. Questions about how to generate a UUID came up on the old mailing list a few times, but no one ever had a solution. So I thought I'd share my implementation with you guys, in case anyone needs something similar. Feel free to pick it apart too. I'm fairly new to REBOL, and there might be a better approach to what I've done. Cheers!
R E B O L []
; IMPORTANT - random/seed now/precise must be called before running this function
; Also, it must only be called ONCE per application process or duplicates will occur
makeUUID: func [
     'Generates a Version 4 UUID that is compliant with RFC 4122'
     data: copy []
     ; generate 16 random integers
     ; Note: REBOL doesn't support bytes directly
     ; and so instead we pick numbers within the
     ; unsigned, 8-bit, byte range (0 - 255)
     ; Also random normally begins the range at 1,
     ; not 0, and so -1 allows 0 to be picked
     loop 16 [append data -1 + random/secure 255]
     ; get the 'byte' in position 7
     pos7: do [pick data 7]
     ; get the 'byte' in position 9
     pos9: do [pick data 9]
     ; set the first character in the 7th 'byte' to always be 4
     poke data 7 do [64 or do [pos7 and 15]]
     ; set the first character in the 9th 'byte' to always be 8, 9, A or B
     poke data 9 do [128 or do [pos9 and 63]]
     ; convert the integers to hexadecimal
     repeat n 16 [
         poke data n do [
             ; remove the padded 0s that REBOL adds, i.e. 0000002A = 2A
             copy/part skip do [to-string to-hex pick data n] 6 2
     uuid: join '' [
         join '' do [copy/part skip data 0 4] '-'
         join '' do [copy/part skip data 4 2] '-'
         join '' do [copy/part skip data 6 2] '-'
         join '' do [copy/part skip data 8 2] '-'
         join '' do [copy/part skip data 10 6]
random/seed now/precise
for num 1 30 1 [print makeUUID]

posted by:   Brian       26-Sep-2016/4:40:08-7:00

It looks like the forum automatically converted all my double quotes to single quotes, after I submitted the post. So just make sure you convert them back before running :)

posted by:   Brian       26-Sep-2016/4:50:44-7:00

Here's your same method with a few shortcuts:
makeUUID: func [
     "Generates a Version 4 UUID that is compliant with RFC 4122"
     /local data ; so 'data doesn't leak
     ; COLLECT/KEEP is a handy accumulator
     data: collect [
         loop 16 [keep -1 + random/secure 256]
     ; don't need to wrap each expression with DO
     ; Rebol infix evaluation is left to right, so don't need to parenthesize
     data/7: data/7 and 15 or 64
     data/9: data/9 and 63 or 128
     ; TO BINARY! converts a block of integers to code points
     ; ENBASE converts the codepoints to hex
     data: enbase/base to binary! data 16
     ; We'll just modify this new string and return the head
     data: insert skip data 8 "-"
     data: insert skip data 4 "-"
     data: insert skip data 4 "-"
     data: insert skip data 4 "-"
     head data
Can I also suggest that you post to Code Review (http://codereview.stackexchange.com/) and tag with 'Rebol'.

posted by:   Chris       26-Sep-2016/12:27:43-7:00

This is very cool! Thanks, I'll definitely be putting this to use!

posted by:   Edoc       26-Sep-2016/13:33:21-7:00

Oh awesome, thanks for the shortcuts Chris! I will check out Stack Exchange too.

posted by:   Brian       26-Sep-2016/13:51:02-7:00

I have updated the code with your changes Chris and submitted it to Stack Exchange. I'll make any further improvements based on the feedback I get there. Thanks again, and thanks for finding that bug with the 255/256 range too :)

posted by:   Brian       26-Sep-2016/19:04:45-7:00