Skip to content

Instantly share code, notes, and snippets.

Created March 20, 2009 00:02
Show Gist options
  • Save anonymous/82136 to your computer and use it in GitHub Desktop.
Save anonymous/82136 to your computer and use it in GitHub Desktop.
(set! *warn-on-reflection* true)
(ns slow
(:import [java.nio ByteBuffer]))
(defn null-string
"Read a nul-terminated string. Stop at \\0 or at length n
whichever comes first."
[#^ByteBuffer buf n]
;; use doall to read the buffer non-lazily.
(let [bytes (doall (for [_ (range n)] (char (.get buf))))]
(apply str (take-while #(not= % \u0000) bytes))))
(defn get-byte
[#^ByteBuffer buf]
(let [x (int (.get buf))]
(bit-and x 0xff)))
(defn get-short
[#^ByteBuffer buf]
(let [x (int (.getShort buf))]
(bit-and x 0xffff)))
(defn get-integer
[#^ByteBuffer buf]
(.getInt buf))
(defn- read-field-aux
[#^ByteBuffer buf n type]
(let [f ({Byte get-byte
Short get-short
Integer get-integer} type)]
(if (= n 1)
(f buf)
(vec (for [_ (range n)] (f buf))))))
(derive Byte ::integer)
(derive Short ::integer)
(derive Integer ::integer)
(defmulti read-field
"Read `size` units of type `type` from buf.
`buf` : a java.nio.ByteBuffer
`size`: an integer or a vector of the form [integer type]
`type`: String, Byte, Short or Integer."
(fn [buf size type] [(vector? size) type]))
(defmethod read-field :default [& args]
(throw (Exception. "invalid type")))
(defmethod read-field [false String]
[buf size type]
(null-string buf size))
(defmethod read-field [false ::integer]
[buf size type]
(read-field-aux buf size type))
(defmethod read-field [true String]
[buf [n type-aux] type]
(let [size (read-field-aux buf n type-aux)]
(null-string buf size)))
(defmethod read-field [true ::integer]
[buf [n type-aux] type]
(let [size (read-field-aux buf n type-aux)]
(read-field-aux buf size type)))
(defn parse-buffer
"A v-form is a vector of the form: [:field-name length Type func?]
Each v-form is read from buf and the whole data is return as a map.
If a field-name is nil, the data is not returned (but the field is
read nonetheless to move forward into the buffer)."
[buf & v-forms]
(reduce (fn [m [field-name size type func]]
(let [data (read-field buf size type)]
(if (nil? field-name)
m
(assoc m field-name
(if (ifn? func)
(func data)
data)))))
{}
v-forms))
(defn run
[#^ByteBuffer buf]
(parse-buffer buf
[:byte 1 Byte]
[:short [1 Byte] Short #(reduce + %)]
[:integer 1 Integer]
[:string 10 String]
[nil 10 Byte]))
(time
(dotimes [_ 10000]
(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
]))
buf (ByteBuffer/wrap arr)]
(run buf))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment