Created
November 28, 2012 06:34
-
-
Save franks42/4159427 to your computer and use it in GitHub Desktop.
UUID generation algorithm for a v4/random UUID
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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