(ns cljam.io.cram.bit-stream
  (:import [java.nio ByteBuffer]))
(defprotocol IBitStreamDecoder
  (read-bits [_ m]))
(definline ^:private right-nbits-of [x nbits]
  `(bit-and (long ~x) (dec (bit-shift-left 1 (long ~nbits)))))
(deftype BitStreamDecoder
         [^ByteBuffer bb
          ^:unsynchronized-mutable ^long buffer
          ^:unsynchronized-mutable ^long nbits]
  IBitStreamDecoder
  (read-bits [_ m]
    (let [m (long m)]
      (if (zero? m)
        m
        (loop [m m, buf buffer, n nbits, acc 0]
          (if (<= m n)
            (do (set! buffer buf)
                (set! nbits (- n m))
                (bit-or acc (right-nbits-of (unsigned-bit-shift-right buf nbits) m)))
            (let [m' (- m n)
                  acc' (bit-or acc (bit-shift-left (right-nbits-of buf n) m'))]
              (recur m' (long (.get bb)) 8 acc'))))))))

Creates a new bit stream decoder based on the given byte buffer.

(defn make-bit-stream-decoder
  [bb]
  (->BitStreamDecoder bb 0 0))