├── .gitignore
├── project.clj
├── README.md
├── test
└── bytebuffer
│ └── buff_test.clj
└── src
└── bytebuffer
└── buff.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | lib
4 | classes
5 | autodoc/**
6 | .lein-failures
7 | .lein-deps-sum
8 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject bytebuffer "0.2.0"
2 | :description "Library for packing and unpacking binary data. Simplifies using java.util.ByteBuffer objects."
3 | :dependencies [[org.clojure/clojure "1.3.0"]]
4 | ;; :dev-dependencies [[autodoc "0.7.1"]]
5 | :autodoc {:name "Bytebuffer" :page-title "Bytebuffer API Documentation"
6 | :web-src-dir "http://github.com/geoffsalmon/bytebuffer/blob/"})
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bytebuffer
2 |
3 | Library for packing and unpacking binary data. Simplifies working with
4 | [java.nio.ByteBuffer](http://java.sun.com/j2se/1.5.0/docs/api/java/nio/ByteBuffer.html)
5 | objects.
6 |
7 | Handles signed and unsigned values pleasantly. Usually reading or
8 | writing unsigned fields with ByteBuffers is a pain because Java
9 | doesn't have unsigned primitives. The unsigned take-* functions here
10 | actually return a type that is one step larger than the requested
11 | type, ex. take-ushort returns an int, and take-uint returns a
12 | bigint.
13 |
14 | See the [API Docs](http://geoffsalmon.github.com/bytebuffer/index.html)
15 |
16 | If you're serializing or deserializing data, you should also checkout
17 | [gloss](https://github.com/ztellman/gloss).
18 |
19 | ## Usage
20 |
21 | Use put-* and take-* functions for each data type add to and remove
22 | from a bytebuffer.
23 |
24 | (def buff (byte-buffer 100))
25 | (put-int buff 84)
26 | (put-byte buff -44)
27 | (put-byte buff -44)
28 | (.flip buff)
29 | (take-int buff) => 84
30 | (take-byte buff) => -44
31 | (take-ubyte buff) => 212 ; -44 is interpreted as 212 when read as unsigned byte
32 |
33 | Use with-buffer to bind a default buffer and avoid passing it to every
34 | take-* and put-* function.
35 |
36 | (let [buff (byte-buffer 100)]
37 | (with-buffer buff
38 | (put-int 10)
39 | (put-int 6)
40 | (.flip buff)
41 | [(take-int) (take-int)]
42 | )
43 | ) => [10 6]
44 |
45 | A more compact way to add or remove data is the pack and unpack
46 | functions, which are inspired by Python's struct module. They use
47 | simple format strings, similar to printf's, to define how fields are
48 | arranged in the buffer.
49 |
50 | (pack buff "isbb" 123 43 23 3) ; puts an int, a short and two bytes into buff
51 | (.flip buff)
52 | (unpack buff "isbb") => (123 43 24 3)
53 |
54 | Use the functions pack-bits and unpack-bits to work with bit fields
55 | within numbers.
56 |
57 | (pack-bits 2 3 2 true 3 false 1 1) => 2r11010001
58 |
59 | (unpack-bits 2r101011001 4 \b 1 1 2) => (10 true 1 0 1)
60 |
61 | When parsing binary file or packet structures it's often useful to
62 | pull off a larger piece of data as a separate buffer using slice-off.
63 |
64 | (defn parse-header [buff] ...)
65 | (defn parse-body [buff] ...)
66 |
67 | (let [hdr (parse-header (slice-off buff 18)) ; slice off 18 byte header
68 | body (parse-body (slice-off buff (:len hdr))] ; parse body
69 | ...
70 | )
71 |
72 | See the
73 | [tests](http://github.com/geoffsalmon/bytebuffer/blob/master/test/bytebuffer/buff_test.clj)
74 | for more examples.
75 |
76 | ## Installation
77 |
78 | Either grab the source or use the jar from clojars. Version 0.2.0
79 | works with Clojure 1.3.
80 |
81 | ### Leiningen
82 |
83 | Add `[bytebuffer "0.2.0"]` to :dependencies in project.clj.
84 |
85 | ### Maven
86 |
87 | Add this dependency
88 |
89 |
90 | bytebuffer
91 | bytebuffer
92 | 0.2.0
93 |
94 |
95 | and ensure you have the following maven repository added.
96 |
97 |
98 | clojars.org
99 | http://clojars.org/repo
100 |
101 |
102 | ## License
103 |
104 | Copyright (c) Geoff Salmon
105 |
106 | Licensed under [EPL 1.0](http://www.eclipse.org/legal/epl-v10.html)
107 |
--------------------------------------------------------------------------------
/test/bytebuffer/buff_test.clj:
--------------------------------------------------------------------------------
1 | (ns bytebuffer.buff-test
2 | (:use [bytebuffer.buff] :reload-all)
3 | (:use [clojure.test])
4 | (:import (java.nio ByteBuffer ByteOrder)))
5 |
6 | ; choose values that have a different value for each byte
7 | (def b (byte 0x1))
8 | (def s 0x302)
9 | (def i 0x7060504)
10 | (def l 0x0f0e0d0c0b0a0908)
11 |
12 | ;; compute powers of 2
13 | (def pows
14 | (conj
15 | (vec (reduce (fn [list _] (conj list (* 2 (peek list)))) [1] (range 62)))
16 | ;; 2^63
17 | 0x8000000000000000N))
18 |
19 | ;; computer powers of 2 minus 1
20 | (def pows-1
21 | {8 0xFF
22 | 16 0xFFFF
23 | 32 0xFFFFFFFF
24 | 64 0xFFFFFFFFFFFFFFFF})
25 |
26 | (defn pow2 [e] (nth pows e))
27 |
28 | (deftest test-packing
29 | "Check that buffers filled with pack are identical to those
30 | filled callin the Java put* methods"
31 | (is (=
32 | (doto (ByteBuffer/allocate 100)
33 | (.put b)
34 | (.putShort s)
35 | (.putInt i)
36 | (.putLong l)
37 | (.flip)
38 | )
39 | (.flip (pack (byte-buffer 100) "bsil" b s i l))))
40 | )
41 |
42 | (deftest test-packing-error
43 | (is (thrown? Exception
44 | (pack (byte-buffer 100) "x" 1)))
45 |
46 | (is (thrown? Exception
47 | (pack (byte-buffer 100) "bb" 1)))
48 |
49 | (is (thrown? Exception
50 | (pack (byte-buffer 100) "b" 1 1)))
51 | )
52 |
53 | (deftest test-unpacking-error
54 | (let [buff (.flip (pack (byte-buffer 100) "i" 1))]
55 | (is (thrown? Exception
56 | (unpack buff "x")))
57 |
58 | )
59 | )
60 |
61 | (deftest test-unpacking
62 | "Check that unpack is the inverse of pack"
63 | (let [vals [b s i l]
64 | fmt "bsil"
65 | buff (.flip (apply pack (byte-buffer 100) fmt vals))]
66 | (is (=
67 | vals
68 | (unpack buff fmt)
69 | )))
70 | )
71 |
72 | (deftest test-signed-unsigned
73 | (let [max-unsigned (sort (vals pows-1))
74 | mid-unsigned (map #(pow2 %) [7 15 31 63])
75 | min-signed (map - mid-unsigned)
76 |
77 | buff
78 | (.flip
79 | (apply pack (byte-buffer 100)
80 | "bsilbsilbsilbsil"
81 |
82 | (concat
83 | (repeat 4 -1)
84 | max-unsigned
85 | mid-unsigned
86 | min-signed
87 | )
88 | ))]
89 |
90 | ; unpack as signed variables
91 | (is (= (repeat 8 -1)
92 | (unpack buff "bsilbsil")))
93 |
94 | (is (= (apply concat (repeat 2 min-signed))
95 | (unpack buff "bsilbsil")))
96 |
97 | (.position buff 0) ; reread buffer from start
98 |
99 | ; unpack as unsigned variables
100 | (is (= (apply concat (repeat 2 max-unsigned))
101 | (unpack buff "BSILBSIL")))
102 |
103 | (is (= (apply concat (repeat 2 mid-unsigned))
104 | (unpack buff "BSILBSIL")))
105 | )
106 | )
107 |
108 | (def b15 (pow2 15))
109 | (def b16 (pow2 16))
110 | (def b31 (pow2 31))
111 | (def b32 (pow2 32))
112 | (def b63 (pow2 63))
113 |
114 | (defn- pack-flip [fmt & vars]
115 | (.flip (apply pack (byte-buffer 100) fmt vars))
116 | )
117 |
118 | (deftest test-take-bytes
119 | (let [buff (pack-flip "bbbbbb" 0 0 -128 -128 -1 -1)]
120 | (is (= 0 (take-byte buff)))
121 | (is (= 0 (take-ubyte buff)))
122 |
123 | (is (= -128 (take-byte buff)))
124 | (is (= 128 (take-ubyte buff)))
125 |
126 | (is (= -1 (take-byte buff)))
127 | (is (= 255 (take-ubyte buff)))))
128 |
129 | (deftest test-take-short
130 | (let [buff (pack-flip "ssssss" 0 0 (- b15) (- b15) -1 -1)]
131 |
132 | (is (= 0 (take-short buff)))
133 | (is (= 0 (take-ushort buff)))
134 |
135 | (is (= (- b15) (take-short buff)))
136 | (is (= b15 (take-ushort buff)))
137 |
138 | (is (= -1 (take-short buff)))
139 | (is (= (- b16 1) (take-ushort buff)))))
140 |
141 | (deftest test-take-int
142 | (let [buff (pack-flip "iiiiii" 0 0 (- b31) (- b31) -1 -1)]
143 |
144 | (is (= 0 (take-int buff)))
145 | (is (= 0 (take-uint buff)))
146 |
147 | (is (= (- b31) (take-int buff)))
148 | (is (= b31 (take-uint buff)))
149 |
150 | (is (= -1 (take-int buff)))
151 | (is (= (- b32 1) (take-uint buff)))))
152 |
153 | (deftest test-take-long
154 | (let [buff (pack-flip "llllll" 0 0 (- b63) (- b63) -1 -1)]
155 |
156 | (is (= 0 (take-long buff)))
157 | (is (= 0 (take-ulong buff)))
158 |
159 | (is (= (- b63) (take-long buff)))
160 | (is (= b63 (take-ulong buff)))
161 |
162 | (is (= -1 (take-long buff)))
163 | (is (= (pows-1 64) (take-ulong buff)))))
164 |
165 |
166 | (deftest test-bit-pack
167 | (is (= 0x120428 (pack-bits 4 1 4 2 4 0 4 4 8 0x28)))
168 |
169 |
170 | (is (= 2r11010001 (pack-bits 2 3 2 true 3 false 1 true)) "use booleans for values 0 and 1")
171 |
172 | (is (thrown? Exception
173 | (pack-bits 4 1 4 5 4)) "field without value")
174 |
175 | (is (thrown? Exception
176 | (pack-bits 4 1 4 -5 4 2)) "negative values")
177 |
178 | (is (thrown? Exception
179 | (pack-bits 4 1 0 0 4 2)) "zero bit lengths")
180 |
181 | (is (thrown? Exception
182 | (pack-bits 4 1 -10 5 4 2)) "negative bit lengths")
183 |
184 | (is (thrown? Exception
185 | (pack-bits 4 1 4 16 4 2)) "insufficient bit lengths")
186 | )
187 |
188 | (deftest test-bit-unpack
189 | (is (= [0 0 0x12 0x34 0x5] (unpack-bits 0x12345 3 9 8 8 4)))
190 |
191 | (is (= [0x12 0x5] (unpack-bits 0x12345 8 -8 4)) "Skip bits")
192 |
193 | (is (= [2r1010 true false 2r01] (unpack-bits 2r10101001 4 \b \b 2)) "bits as booleans")
194 |
195 | (is (= [0x12 0x34 0x5] (unpack-bits 0x12345 0 0 8 8 0 4)) "Weird 0 bit length. Should this be an error instead?")
196 | )
197 |
198 | (deftest slice
199 | (let [buff (pack-flip "bbbbbb" 10 11 12 13 14 15)]
200 | (is (thrown? IndexOutOfBoundsException (slice-off buff 20)))
201 | (let [b1 (slice-off buff 4)]
202 | (is (= [10 11 12 13] (unpack b1 "bbbb")))
203 | (is (zero? (.remaining b1)))
204 |
205 | (is (thrown? IndexOutOfBoundsException (slice-off buff 4)))
206 | (is (= [14 15] (unpack buff "bb")))
207 | )
208 | ))
209 |
210 | (deftest test-fmt-size
211 | (is (= 0 (fmt-size "")))
212 | (let [fmts "bBsSiIlL"
213 | rand-fmt (fn [n] (apply str (for [i (range n)] (rand-nth fmts))))
214 | buf (byte-buffer 100)
215 | ;; compare fmt-size result against resulting position after
216 | ;; packing zeroes into a buffer
217 | test (fn [fmt]
218 | (.clear buf)
219 | (apply pack buf fmt (repeat (.length fmt) 0))
220 | (is (= (.position buf) (fmt-size fmt))))]
221 | ;; test all single letter fmt strings
222 | (doseq [fmt fmts]
223 | (test (str fmt)))
224 | ;; test random fmt strings of length >= 2
225 | (doseq [i (range 20)]
226 | (test (rand-fmt (+ 2 (rand-int 10)))))))
227 |
--------------------------------------------------------------------------------
/src/bytebuffer/buff.clj:
--------------------------------------------------------------------------------
1 | (ns
2 | #^{
3 | :doc "Library for packing and unpacking binary data. Simplifies working
4 | with java.nio.ByteBuffer objects.
5 |
6 | Notable features:
7 |
8 | 1. Handles signed and unsigned values pleasantly. Usually reading or
9 | writing unsigned fields with ByteBuffers is a pain because Java
10 | doesn't have unsigned primitives. The take-* functions return a long
11 | which is big enough to handle the entire unsigned range of most
12 | types. To handle large unsigned longs, the take-ulong function can
13 | return either a long or a clojure.lang.BigInt. Similarly, although
14 | there are not separate signed and unsigned version, the put-*
15 | functions will accept any number type and truncate it so both positive
16 | and negative numbers can be stored. TODO: Should add an overflow check
17 | to avoid overflows in put-* functions?
18 |
19 | 2. Provides pack and unpack functions inspired by Python's struct
20 | module. Use simple format strings, similar to printf's, to define how
21 | fields are layed out in the buffer.
22 |
23 | Usage:
24 | (pack buff \"isbb\" 123 43 23 3) ; puts an int, a short and two bytes into buff
25 | (.flip buff) ; assuming nothing else was written to the buffer
26 | (unpack buff \"isbb\") => (123 43 24 3)
27 |
28 | 3. Provides pack-bits and unpack-bits functions for working with bit
29 | fields within numbers. These are useful for pulling apart flag fields.
30 | "
31 | :see-also [["http://java.sun.com/j2se/1.5.0/docs/api/java/nio/ByteBuffer.html" "java.nio.ByteBuffer Documentation"]]
32 | }
33 |
34 | bytebuffer.buff
35 | (:import (java.nio ByteBuffer ByteOrder))
36 | )
37 |
38 | (defn byte-buffer
39 | "Creates a ByteBuffer of capacity bytes"
40 | [capacity]
41 | (ByteBuffer/allocate capacity))
42 |
43 | #_(def ^{:tag :dynamic
44 | :private true
45 | :doc "The current buffer. Use with-buffer to bind this."}
46 | *byte-buffer* nil)
47 |
48 | (def ^:dynamic *byte-buffer* nil)
49 |
50 | (defmacro with-buffer
51 | "Sets the buffer currently being used by the put-* and take-*
52 | functions which do not take buffers."
53 | [buffer & body]
54 | `(binding [*byte-buffer* ~buffer]
55 | ~@body))
56 |
57 | (defn put-byte
58 | "Puts a byte into the buffer"
59 | ([val]
60 | (put-byte *byte-buffer* val))
61 | ([^ByteBuffer buff val]
62 | (.put buff (.byteValue val)))
63 | )
64 |
65 | (defn put-short
66 | "Puts a short (2 bytes) into the buffer"
67 | ([val]
68 | (put-short *byte-buffer* val))
69 | ([^ByteBuffer buff val]
70 | (.putShort buff (.shortValue val)))
71 | )
72 |
73 | (defn put-int
74 | "Puts an int (4 bytes) into the buffer"
75 | ([val]
76 | (put-int *byte-buffer* val))
77 | ([^ByteBuffer buff val]
78 | (.putInt buff (.intValue val)))
79 | )
80 |
81 | (defn put-long
82 | "Puts a long (8 bytes) into the buffer"
83 | ([val]
84 | (put-long *byte-buffer* val))
85 | ([^ByteBuffer buff val]
86 | (.putLong buff (.longValue val)))
87 | )
88 |
89 | (defn take-byte
90 | "Takes a signed byte from the buffer"
91 | (^long []
92 | (take-byte *byte-buffer*))
93 | (^long [^ByteBuffer buff]
94 | (long (.get buff)))
95 | )
96 |
97 | (defn take-ubyte
98 | "Takes an unsigned signed byte from the buffer"
99 | (^long []
100 | (take-ubyte *byte-buffer*))
101 | (^long [^ByteBuffer buff]
102 | (bit-and 0xFF (long (.get buff))))
103 | )
104 |
105 | (defn take-short
106 | "Takes a signed short (2 bytes) from the buffer"
107 | (^long []
108 | (take-short *byte-buffer*))
109 | (^long [^ByteBuffer buff]
110 | (long (.getShort buff)))
111 | )
112 |
113 | (defn take-ushort
114 | "Takes a unsigned short (2 bytes) from the buffer"
115 | (^long []
116 | (take-ushort *byte-buffer*))
117 | (^long [^ByteBuffer buff]
118 | (bit-and 0xFFFF (long (.getShort buff))))
119 | )
120 |
121 | (defn take-int
122 | "Takes a signed int (4 bytes) from the buffer"
123 | (^long []
124 | (take-int *byte-buffer*))
125 | (^long [^ByteBuffer buff]
126 | (.getInt buff))
127 | )
128 |
129 | (defn take-uint
130 | "Takes a unsigned int (4 bytes) from the buffer"
131 | (^long []
132 | (take-uint *byte-buffer*))
133 | (^long [^ByteBuffer buff]
134 | (bit-and 0xFFFFFFFF (long (.getInt buff))))
135 | )
136 |
137 | (defn take-long
138 | "Takes a signed long (8 bytes) from the buffer"
139 | (^long []
140 | (take-long *byte-buffer*))
141 | (^long [^ByteBuffer buff]
142 | (.getLong buff))
143 | )
144 |
145 | (defn take-ulong
146 | "Takes a unsigned long (8 bytes) from the buffer"
147 | ([]
148 | (take-ulong *byte-buffer*))
149 | ([^ByteBuffer buff]
150 | (let [l (.getLong buff)]
151 | (if (>= l 0)
152 | l
153 | ;; add 2^64 to treat the negative 64bit 2's complement
154 | ;; num as unsigned.
155 | (+ 18446744073709551616N (bigint l)))))
156 | )
157 |
158 |
159 | (defn ^ByteBuffer slice-off [^ByteBuffer buff len]
160 | "Create a new bytebuffer by slicing off the first len bytes. Also
161 | consumes the bytes in the given buffer."
162 | (if (> len (.remaining buff))
163 | (throw (IndexOutOfBoundsException. (str "Cannot slice-off " len " bytes of remaining " (.remaining buff) " bytes.") ))
164 | (let [rdbuf (-> buff (.slice) (.limit len))]
165 | (.position buff (+ (.position buff) len)) ; advance the actual buffer
166 | rdbuf
167 | ))
168 | )
169 |
170 | (defn fmt-size [fmt]
171 | (reduce (fn [acc f]
172 | (+ acc
173 | (condp = f
174 | \b 1
175 | \B 1
176 | \s 2
177 | \S 2
178 | \i 4
179 | \I 4
180 | \l 8
181 | \L 8
182 | (throw (IllegalArgumentException. (str "Unknown format symbol \"" fmt \")))
183 | ))) 0 fmt))
184 |
185 | (defn- pack-one [buff fmt val]
186 | (condp = fmt
187 | \b (put-byte buff val)
188 | \B (put-byte buff val)
189 | \s (put-short buff val)
190 | \S (put-short buff val)
191 | \i (put-int buff val)
192 | \I (put-int buff val)
193 | \l (put-long buff val)
194 | \L (put-long buff val)
195 | (throw (IllegalArgumentException. (str "Unknown format symbol \"" fmt \")))
196 | ))
197 |
198 | (defn pack
199 | "Puts one or more numbers for vals into buff using field sizes
200 | determined by the characters in the fmt sequence. Valid characters are
201 | b - byte
202 | s - short
203 | i - int
204 | l - long
205 |
206 | The number of characters in fmt must match the number of numbers in vals.
207 |
208 | Usage: (pack buff \"isbb\" 123 43 23 3) ; puts an int, a short and two bytes into buff
209 |
210 | Returns buff."
211 | [buff fmt & vals]
212 | (when-not (= (count fmt) (count vals))
213 | (throw (IllegalArgumentException. "pack error. Number of format symbols must match number of values.")))
214 |
215 | (doseq [[f val] (partition 2 (interleave fmt vals))]
216 | (pack-one buff f val))
217 | buff
218 | )
219 |
220 | (defn- unpack-one [buff fmt]
221 | (condp = fmt
222 | \b (take-byte buff)
223 | \B (take-ubyte buff)
224 | \s (take-short buff)
225 | \S (take-ushort buff)
226 | \i (take-int buff)
227 | \I (take-uint buff)
228 | \l (take-long buff)
229 | \L (take-ulong buff)
230 | (throw (IllegalArgumentException. (str "Unknown format symbol \"" fmt \")))
231 | ))
232 |
233 | (defn unpack
234 | "Returns a sequence of one or more numbers taken from buff. The
235 | number and type of numbers taken is determined by fmt which is a
236 | sequence of characters. Valid characters in fmt:
237 |
238 | b - byte
239 | B - unsigned byte
240 | s - short
241 | S - unsigned short
242 | i - int
243 | I - unsigned int
244 | l - long
245 | L - unsigned long"
246 | [buff fmt]
247 | (doall (map (partial unpack-one buff) fmt))
248 | )
249 |
250 | (defn- bit-val [x]
251 | (if (instance? Boolean x)
252 | (if x 1 0)
253 | x
254 | )
255 | )
256 |
257 | (defn pack-bits
258 | "Packs multiple numbers into a single number using explicit bit lengths.
259 |
260 | fields => bit-length value
261 |
262 | The value can also be a boolean. true is stored as 1, false as 0"
263 | ([] 0)
264 | ([& fields]
265 | (when-not (zero? (mod (count fields) 2))
266 | (throw (IllegalArgumentException. (str "pack-bits Last field does not have a value.")))
267 | )
268 |
269 | (reduce (fn [acc [num-bits val-in]]
270 | (let [val (bit-val val-in)]
271 | (when-not (pos? num-bits)
272 | (throw (IllegalArgumentException.
273 | (str "pack-bits: Invalid bit length " num-bits ". Must be positive."))))
274 |
275 | (when (neg? val)
276 | (throw (IllegalArgumentException.
277 | (str "pack-bits: Invalid value " val ". Must be non-negative."))))
278 |
279 | ; ensure specified bit length was big enough
280 | (when-not (= val
281 | (bit-and val (dec (bit-shift-left 1 num-bits)))
282 |
283 | )
284 | (throw (IllegalArgumentException.
285 | (str "pack-bits: Invalid value " val ". Must fit in " num-bits " bits as specified.")))
286 | )
287 |
288 | (-> acc
289 | (bit-shift-left num-bits)
290 | (bit-or val)
291 | ))
292 | )
293 | 0 (partition 2 fields))
294 | )
295 | )
296 |
297 | (defn unpack-bits
298 | "Pulls apart a number into a list of fields of various bit lengths.
299 | Pass a non-positive bit length to skip that many bits without adding a
300 | corresponding value to the result list.
301 |
302 | Passing a field length of 1 will add either a 0 or a 1 to the
303 | resulting sequence. To get a boolean value instead, pass \\b."
304 | [x & bit-lengths]
305 | (loop [val x
306 | results '()
307 | ; iterate bit lengths in reverse to continually pull
308 | ; values from the low order bits
309 | rbit-lens (reverse bit-lengths)]
310 |
311 | (if (= '() rbit-lens)
312 | results
313 |
314 | (let [bits (first rbit-lens)]
315 | (cond
316 | (= \b bits) ; grab single bit as boolean
317 | (recur (bit-shift-right val 1)
318 | (cons (= 1 (bit-and val 1)) results)
319 | (rest rbit-lens)
320 | )
321 |
322 | (pos? bits)
323 | (recur (bit-shift-right val bits)
324 | (cons
325 | (bit-and val (dec (bit-shift-left 1 bits)))
326 | results)
327 | (rest rbit-lens)
328 | )
329 | :else ; skip bits
330 | (recur (bit-shift-right val (- bits))
331 | results
332 | (rest rbit-lens)
333 | )
334 | )
335 | )
336 | )
337 | )
338 | )
339 |
340 | (defn bin [x]
341 | "Returns x as a binary number in a string"
342 | (.toString (bigint x) 2)
343 | )
344 |
--------------------------------------------------------------------------------