Skip to content

Instantly share code, notes, and snippets.

@franks42
Created November 28, 2012 06:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save franks42/4159427 to your computer and use it in GitHub Desktop.
Save franks42/4159427 to your computer and use it in GitHub Desktop.
UUID generation algorithm for a v4/random UUID
(ns cljs-uuidv4
"Generator for a v4/random UUID that works with cljs.core/UUID"
(:require [goog.string.StringBuffer]))
(defn UUIDv4
"Returns a new randomly generated (version 4) cljs.core/UUID,
like: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
as per http://www.ietf.org/rfc/rfc4122.txt.
Usage:
(UUIDv4) => #uuid \"305e764d-b451-47ae-a90d-5db782ac1f2e\"
(type (UUIDv4)) => cljs.core/UUID"
[]
(letfn [(f [] (.toString (rand-int 16) 16))
(g [] (.toString (bit-or 0x8 (bit-and 0x3 (rand-int 15))) 16))]
(UUID. (.append (goog.string.StringBuffer.)
(f) (f) (f) (f) (f) (f) (f) (f) "-" (f) (f) (f) (f)
"-4" (f) (f) (f) "-" (g) (f) (f) (f) "-"
(f) (f) (f) (f) (f) (f) (f) (f) (f) (f) (f) (f)))))
(def UUID-random "Alias to function UUIDv4" UUIDv4)
;; Below are different versions of UUIDv4 generators (comment'ed out)
;; the above is the fastest by factor of about 2.5-3.
;; implementation is half cljs, half js... if speed matters, maybe we should go js all the way
;; (confusing that closure doesn't include an implementation (?)).
;; lessons learnt: if speed matters, use goog.string.StringBuffer instead of cljs.core/str
;; for concats. Also random number generation is neglectable compared to all the string
;; concats and list manipulations.
;; informal timing result in browser's js-vm:
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (cljs-uuidv4/UUIDv4) (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 2.483
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (UUIDv41) (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 6.68
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (UUIDv42) (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 7.043
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (UUIDv43) (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 157.351
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (.toString (rand-int 16) 16) (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 0.058
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (str "1" "2" "3" "4" "5" "6" "7" "8" "9" "10") (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 1.784
;; ClojureScript:cljs.user> (let [t0 (js/Date.now)] (loop [n 500000] (.append (goog.string.StringBuffer.) "1" "2" "3" "4" "5" "6" "7" "8" "9" "10") (when (> n 0) (recur (dec n)))) (/ (- (js/Date.now) t0) 1000))
;; 0.244
(comment
;; Adapted from https://github.com/davesann/cljs-uuid and made to work with cljs.core/UUID
(defn ^:private num->string [number radix]
(let [num (js/Number. number)]
(.toString num radix)))
;; constants
(def ^:private pow2-6 64)
(def ^:private pow2-8 256)
(def ^:private pow2-12 4096)
(def ^:private pow2-14 16384)
(def ^:private pow2-16 65536)
(def ^:private pow2-32 4294967296)
(def ^:private pow2-48 281474976710656)
(defn ^:private padded-hex
"append a padded hex number to stringbuffer"
[sb number len]
(let [num-s (num->string number 16)]
(doseq [i (range (- len (.-length num-s)))]
(.append sb "0"))
(.append sb num-s)))
;; UUIDv4 record
(defrecord UUIDv4Rec [time-low
time-mid
time-hi-and-version
clock-seq-and-reserved
clock-seq-low
node]
Object
(toString
[uuid]
(let [sb (goog.string.StringBuffer.)]
(padded-hex sb (:time-low uuid) 8)
(.append sb "-")
(padded-hex sb (:time-mid uuid) 4)
(.append sb "-")
(padded-hex sb (:time-hi-and-version uuid) 4)
(.append sb "-")
(padded-hex sb (:clock-seq-and-reserved uuid) 2)
(padded-hex sb (:clock-seq-low uuid) 2)
(.append sb "-")
(padded-hex sb (:node uuid) 12)
(.toString sb))))
(defn UUIDv41
"Returns a new randomly generated (version 4) cljs.core/UUID
as per http://www.ietf.org/rfc/rfc4122.txt
(UUIDv4) => #uuid \"305e764d-b451-47ae-a90d-5db782ac1f2e\"
(type (UUIDv4)) => cljs.core/UUID"
[]
(UUID.
(str (UUIDv4Rec.
(rand-int pow2-32) ; time-low 4 bytes
(rand-int pow2-16) ; time-mid 2 bytes
(bit-or 0x4000 (rand-int pow2-12)) ; time-hi-and-version 4 bytes
(bit-or 0x80 (rand-int pow2-6)) ; clock-seq-and-reserved 2 bytes
(rand-int pow2-8) ; clock-seq-low 2 bytes
; node 6 bytes
; - gets special attention due to js int behaviour beyond 32 bits
(+ (* (rand-int pow2-32) pow2-16)
(rand-int pow2-16))))))
) ;; comment
(comment
;; variant of https://github.com/davesann/cljs-uuid
;; compressed into single function
(defn UUIDv42
"Returns a new randomly generated (version 4) cljs.core/UUID
as per http://www.ietf.org/rfc/rfc4122.txt
(UUIDv4) => #uuid \"305e764d-b451-47ae-a90d-5db782ac1f2e\"
(type (UUIDv4)) => cljs.core/UUID"
[]
(letfn [(padded-hex [sb number len]
;; "append a padded hex number to stringbuffer"
(let [num-s (.toString (js/Number. number) 16)]
(doseq [i (range (- len (.-length num-s)))]
(.append sb "0"))
(.append sb num-s)))
(padded-hex-str [number len]
;; "append a padded hex number to stringbuffer"
(let [sb (goog.string.StringBuffer.)
num-s (.toString (js/Number. number) 16)]
(doseq [i (range (- len (.-length num-s)))]
(.append sb "0"))
(.append sb num-s)))
]
(UUID.
(let [time-low (rand-int 4294967296) ; 4 bytes
time-mid (rand-int 65536) ; 2 bytes
time-hi-and-version (bit-or 0x4000 (rand-int 4096)) ; 4 bytes
clock-seq-and-reserved (bit-or 0x80 (rand-int 64)) ; 2 bytes
clock-seq-low (rand-int 256) ; 2 bytes
; node 6 bytes
; - gets special attention due to js int behaviour beyond 32 bits
node (+ (* (rand-int 4294967296) 65536)(rand-int 65536))
;;
]
(str
(padded-hex-str time-low 8)
"-"
(padded-hex-str time-mid 4)
"-"
(padded-hex-str time-hi-and-version 4)
"-"
(padded-hex-str clock-seq-and-reserved 2)
(padded-hex-str clock-seq-low 2)
"-"
(padded-hex-str node 12))))))
) ;; comment
(comment
;; http://catamorphic.wordpress.com/2012/03/02/generating-a-random-uuid-in-clojurescript/
;; interesting approach, but way to sloooow
(defn UUIDv43
"returns a type 4 random UUID: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
[]
(let [r (repeatedly 30 (fn [] (.toString (rand-int 16) 16)))]
(apply str (concat (take 8 r) ["-"]
(take 4 (drop 8 r)) ["-4"]
(take 3 (drop 12 r)) ["-"]
[(.toString (bit-or 0x8 (bit-and 0x3 (rand-int 15))) 16)]
(take 3 (drop 15 r)) ["-"]
(take 12 (drop 18 r))))))
) ;; comment
(comment
;; javascript version
'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);
});
) ;; comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment