Skip to content

Instantly share code, notes, and snippets.

Created June 9, 2009 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anonymous/126661 to your computer and use it in GitHub Desktop.
Save anonymous/126661 to your computer and use it in GitHub Desktop.
(set! *warn-on-reflection* true)
(ns slow
(:import [java.nio ByteBuffer]))
(defn mask8 [x] (bit-and x 0xff))
(defn mask16 [x] (bit-and x 0xffff))
(defn read-null-string [#^ByteBuffer buf len]
(let [#^bytes arr (make-array Byte/TYPE len)]
(.get buf arr)
(apply str (map char (take-while (complement zero?) arr)))))
(defn make-reader
[type mask-fn]
(let [getter ({Byte (fn [#^ByteBuffer buf] (.get buf))
Short (fn [#^ByteBuffer buf] (.getShort buf))
Integer (fn [#^ByteBuffer buf] (.getInt buf))} type)]
(fn [#^ByteBuffer buf len]
(if (= len 1)
(mask-fn (getter buf))
(let [#^ints arr (int-array len)]
(dotimes [i len]
(aset-int arr i (mask-fn (getter buf))))
arr)))))
(def *readers* {Byte (make-reader Byte mask8)
Short (make-reader Short mask16)
Integer (make-reader Integer identity)
String read-null-string})
;; compile-field :: Integer|[Integer Class] -> Class -> (ByteBuffer -> a)
(defn compile-field
"Return a function that will take a ByteBuffer and read
`size` units of type `type`."
[size type]
(let [reader (*readers* type)]
(if (vector? size)
(let [size-reader-fn (apply compile-field size)]
(fn [#^ByteBuffer buf]
(reader buf (size-reader-fn buf))))
(fn [#^ByteBuffer buf]
(reader buf size)))))
;; type FieldSpec = [Keyword, Integer|[Integer Class], Class, Maybe Fn]
;; compile-record :: [FieldSpec] -> (ByteBuffer -> Map Keyword a)
(defn compile-record
"Return a function that will read some fields from a ByteBuffer according
to a field-spec and return a map of those fields.
A field-spec is a vector of 3 or 4 elements:
[:field-name length type]
[:field-name length type func]
If the field name is nil, the data will be read, but not stored in the map.
If a function is supplied in the field-spec, it will be executed against
the byte(s) that the byte reader returned."
[& field-specs]
(let [read-fields (map (fn [[name size type modifier-fn]]
[name
(compile-field size type)
modifier-fn])
field-specs)]
(fn [#^ByteBuffer buf]
(reduce (fn [m v]
(let [name (v 0)
read-fn (v 1)
modifier-fn (v 2)
result (read-fn buf)]
(if (nil? name)
m
(assoc m name (if (nil? modifier-fn)
result
(modifier-fn result))))))
{}
read-fields))))
(defn run
[#^ByteBuffer buf]
(let [getter (compile-record
[:byte 1 Byte]
[:short [1 Byte] Short #(reduce + %)]
[:integer 1 Integer]
[:string 10 String]
[nil 10 Byte])]
(getter buf)))
(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