         (recur (conj options (parse-option bb)))))))
 
 (defn- read-alignment [rdr refs]
-  (let [block-size (lsb/read-int rdr)]
-    (when (< block-size fixed-block-size)
-      (throw (Exception. (str "Invalid block size:" block-size))))
-    (let [ref-id      (lsb/read-int rdr)
-          rname       (if (= ref-id -1) "*" (:name (nth refs ref-id)))
-          pos         (inc (lsb/read-int rdr))
-          l-read-name (lsb/read-ubyte rdr)
-          mapq        (lsb/read-ubyte rdr)
-          bin         (lsb/read-ushort rdr)
-          n-cigar-op  (lsb/read-ushort rdr)
-          flag        (lsb/read-ushort rdr)
-          l-seq       (lsb/read-int rdr)
-          rnext       (decode-next-ref-id refs (lsb/read-int rdr) rname)
-          pnext       (inc (lsb/read-int rdr))
-          tlen        (lsb/read-int rdr)
-          qname       (lsb/read-string rdr (dec (int l-read-name)))
-          _           (lsb/read-bytes rdr 1)
-          cigar       (decode-cigar (lsb/read-bytes rdr (* n-cigar-op 4)))
-          seq         (decode-seq (lsb/read-bytes rdr (/ (inc l-seq) 2)) l-seq)
-          qual        (decode-qual (lsb/read-bytes rdr l-seq))
-          rest        (lsb/read-bytes rdr (options-size block-size
-                                                        l-read-name
-                                                        n-cigar-op
-                                                        l-seq))
-          options     (parse-options rest)]
-      {:qname qname, :flag flag, :rname rname, :pos pos, :mapq  mapq,
-       :cigar cigar, :rnext rnext, :pnext pnext, :tlen tlen, :seq seq,
-       :qual qual, :options options})))
-
-(defn read-alignments
-  [rdr]
-  (try
-    (cons (read-alignment (.reader rdr) (.refs rdr))
-          (lazy-seq (read-alignments rdr)))
-    (catch EOFException e nil)))
+  (locking rdr
+    (let [block-size (lsb/read-int rdr)]
+      (when (< block-size fixed-block-size)
+        (throw (Exception. (str "Invalid block size:" block-size))))
+      (let [ref-id      (lsb/read-int rdr)
+            rname       (if (= ref-id -1) "*" (:name (nth refs ref-id)))
+            pos         (inc (lsb/read-int rdr))
+            l-read-name (lsb/read-ubyte rdr)
+            mapq        (lsb/read-ubyte rdr)
+            bin         (lsb/read-ushort rdr)
+            n-cigar-op  (lsb/read-ushort rdr)
+            flag        (lsb/read-ushort rdr)
+            l-seq       (lsb/read-int rdr)
+            rnext       (decode-next-ref-id refs (lsb/read-int rdr) rname)
+            pnext       (inc (lsb/read-int rdr))
+            tlen        (lsb/read-int rdr)
+            qname       (lsb/read-string rdr (dec (int l-read-name)))
+            _           (lsb/read-bytes rdr 1)
+            cigar       (decode-cigar (lsb/read-bytes rdr (* n-cigar-op 4)))
+            seq         (decode-seq (lsb/read-bytes rdr (/ (inc l-seq) 2)) l-seq)
+            qual        (decode-qual (lsb/read-bytes rdr l-seq))
+            rest        (lsb/read-bytes rdr (options-size block-size
+                                                          l-read-name
+                                                          n-cigar-op
+                                                          l-seq))
+            options     (parse-options rest)]
+        {:qname qname, :flag flag, :rname rname, :pos pos, :mapq  mapq,
+         :cigar cigar, :rnext rnext, :pnext pnext, :tlen tlen, :seq seq,
+         :qual qual, :options options}))))
 
 (defn- write-tag-value [writer val-type value]
   (case val-type

  [rdr finish]
  (when (and (not (zero? (.available (.reader rdr))))
             (> finish (.getFilePointer (.reader rdr))))
    (cons (read-alignment (.reader rdr) (.refs rdr))
          (lazy-seq (read-to-finish rdr finish)))))

(defn read-alignments
  ([rdr]
      (when-not (zero? (.available (.reader rdr)))
        (cons (read-alignment (.reader rdr) (.refs rdr))
              (lazy-seq (read-alignments rdr)))))
  ([rdr bai chr start end]
     (let [spans (get-spans bai chr start end)
           window (fn [a]


;;; I/O

(deftype BamReader [header refs reader seq-reader]
  java.io.Closeable
  (close [this]
    (.. this reader close)
    (.. this seq-reader close)))

(def ^:private buffer-size (* 1024 128))

(defn reader [f]
  (let [rdr (BlockCompressedInputStream. (file f))
        data-rdr (DataInputStream.
                  (BGZFInputStream.
                   (BufferedInputStream. (FileInputStream. (file f)) buffer-size)))]
    (if-not (Arrays/equals (lsb/read-bytes data-rdr 4) (.getBytes bam-magic))
      (throw (IOException. "Invalid BAM file header")))
    (let [header (sam/parse-header (lsb/read-string data-rdr (lsb/read-int data-rdr)))
          n-ref  (lsb/read-int data-rdr)
          refs   (loop [i n-ref, ret []]
                   (if (zero? i)
                     ret
                     (let [l-name (lsb/read-int data-rdr)
                           name   (lsb/read-string data-rdr l-name)
                           l-ref  (lsb/read-int data-rdr)]
                       (recur (dec i)
                              (conj ret {:name (subs name 0 (dec l-name))
                                         :len  l-ref})))))]
      (->BamReader header refs rdr data-rdr))))

(defn read-header
  [rdr]
