Skip to content

Instantly share code, notes, and snippets.

@cgrand
Forked from anonymous/gist:126661
Created June 9, 2009 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cgrand/126676 to your computer and use it in GitHub Desktop.
Save cgrand/126676 to your computer and use it in GitHub Desktop.
(set! *warn-on-reflection* true)
(ns slow
(:import [java.nio ByteBuffer Buffer]))
(defn #^ints bytes-to-ints [#^bytes bs]
(let [#^ints is (make-array Integer/TYPE (alength bs))]
(loop [i (int 0)]
(if (< i (alength bs))
(do
(aset is i (bit-and 0xFF (int (aget bs i))))
(recur (inc i)))
is))))
(defmacro let-> [obj bindings & body]
(let [obj-sym (gensym "obj")]
`(let [~obj-sym ~obj
~@(mapcat (fn [[k v]] [k (list `-> obj-sym v)])
(partition 2 bindings))]
~@body)))
(defn skip [#^Buffer buf n]
(.position buf (+ (.position buf) n)))
(defn #^"[S" get-shorts [#^ByteBuffer buf n]
(let [#^shorts ss (make-array Short/TYPE n)]
(-> buf .asShortBuffer (.get ss))
(skip buf (* 2 n))
ss))
(defn #^"[B" get-bytes [#^ByteBuffer buf n]
(let [#^bytes bs (make-array Byte/TYPE n)]
(.get buf bs)
bs))
(defn #^ints get-unsigned-bytes [#^ByteBuffer buf n]
(bytes-to-ints (get-bytes buf n)))
(defn get-unsigned-byte [#^ByteBuffer buf]
(bit-and 0xFF (int (.get buf))))
(defn #^String get-string
([buf bytes-count]
(let [s (-> buf (get-bytes bytes-count) (String. "US-ASCII"))
i (.indexOf s 0)]
(if (neg? i) s (.substring s 0 i))))
([buf] (get-string buf 1)))
(def *readers* {'Byte `get-bytes
:unsigned-byte `get-unsigned-bytes
'String `get-string
'Short `get-shorts})
(def *read1* {'Byte `.get
:unsigned-byte `get-unsigned-byte
'String `get-string
'Short `.getShort
'Integer `.getInt})
(defn compile-spec [[len type f]]
(cond
f `(let-> [data# ~(compile-spec [len type])]
(~f data#))
(vector? len)
`(let-> [n# ~(compile-spec len)
data# (~(*readers* type) n#)]
data#)
(= 1 len)
(*read1* type)
:else
`(~(*readers* type) ~len)))
(defmacro parse-buffer [buf & field-specs]
(let [syms+field-specs (for [f field-specs] [(gensym) f])
result-map (into {}
(for [[sym [key]] syms+field-specs :when key] [key sym]))]
`(let-> ~buf [~@(mapcat (fn [[sym [field & spec]]] [sym (compile-spec spec)])
syms+field-specs)]
~result-map)))
(defn run
[#^ByteBuffer buf]
(parse-buffer buf
[:byte 1 :unsigned-byte]
[:short [1 :unsigned-byte] Short #(reduce + %)]
[:integer 1 Integer]
[:string 10 String]
[nil 10 Byte]))
(defn bench []
(let [arr (into-array Byte/TYPE (map byte [7 ; 1 Byte
3 0 97 0 98 0 99 ; 3 Shorts
0 0 100 100 ; 1 Integer
65 66 67 68 69 70 71 72 73 74 ; 10 String
0 0 0 0 0 0 0 0 0 0 ; 10 ignored
]))]
(time
(dotimes [_ 10000]
(let [buf (ByteBuffer/wrap arr)]
(run buf))))))
(comment
; this code
slow=> (dotimes [_ 10] (bench))
"Elapsed time: 53.092781 msecs"
"Elapsed time: 42.550939 msecs"
"Elapsed time: 53.071829 msecs"
"Elapsed time: 20.975012 msecs"
"Elapsed time: 20.679165 msecs"
"Elapsed time: 22.510123 msecs"
"Elapsed time: 18.797361 msecs"
"Elapsed time: 22.059787 msecs"
"Elapsed time: 44.361783 msecs"
"Elapsed time: 19.971812 msecs"
; code at http://gist.github.com/126661
slow=> (dotimes [_ 10] (bench))
"Elapsed time: 449.096743 msecs"
"Elapsed time: 451.16153 msecs"
"Elapsed time: 269.256009 msecs"
"Elapsed time: 302.183099 msecs"
"Elapsed time: 275.283032 msecs"
"Elapsed time: 289.821472 msecs"
"Elapsed time: 302.892127 msecs"
"Elapsed time: 271.186981 msecs"
"Elapsed time: 289.20184 msecs"
"Elapsed time: 272.354447 msecs"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment