Repository: chrovis/cljam
Pull request #265 (2022-09-09 11:39:25)

Extend the supported data types in encoding alignment options
The topic of this PR is about encoding SAM format into BAM format. According to [BAM format specification](https://github.com/samtools/hts-specs/blob/59a0d0c8dc01b46192f6da1cec61db59c9b3d1d9/SAMv1.tex#L1117), there are several data types for options in the alignment, but [current cljam doesn't support all of them for encoding](https://github.com/chrovis/cljam/blob/c7cd8c43f13432d1cbcc95640108f7d32c6dcdef/src/cljam/io/bam/encoder.clj#L52). If you try to encode a data type that is not supported, an Execution error will be returned.

This PR extends the data types of options that can be encoded, so that all data types except the H-type can be supported. Since the H-type seems to be a specification that remains only for backward compatibility, this PR did not provide support for the H-type encoding. Tests have also been added for the functions I have modified.


codecov[bot]: # [Codecov](https://codecov.io/gh/chrovis/cljam/pull/265?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis) Report
> Merging [#265](https://codecov.io/gh/chrovis/cljam/pull/265?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis) (8b5ca1e) into [master](https://codecov.io/gh/chrovis/cljam/commit/c7cd8c43f13432d1cbcc95640108f7d32c6dcdef?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis) (c7cd8c4) will **increase** coverage by `0.09%`.
> The diff coverage is `36.36%`.

```diff
@@            Coverage Diff             @@
##           master     #265      +/-   ##
==========================================
+ Coverage   88.71%   88.80%   +0.09%     
==========================================
  Files          78       78              
  Lines        6389     6406      +17     
  Branches      442      453      +11     
==========================================
+ Hits         5668     5689      +21     
+ Misses        279      264      -15     
- Partials      442      453      +11     
```


| [Impacted Files](https://codecov.io/gh/chrovis/cljam/pull/265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis) | Coverage Δ | |
|---|---|---|
| [src/cljam/io/bam/encoder.clj](https://codecov.io/gh/chrovis/cljam/pull/265/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis#diff-c3JjL2NsamFtL2lvL2JhbS9lbmNvZGVyLmNsag==) | `67.03% <36.36%> (-3.24%)` | :arrow_down: |
| [src/cljam/io/bam/decoder.clj](https://codecov.io/gh/chrovis/cljam/pull/265/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis#diff-c3JjL2NsamFtL2lvL2JhbS9kZWNvZGVyLmNsag==) | `93.44% <0.00%> (+0.43%)` | :arrow_up: |
| [src/cljam/io/util/lsb.clj](https://codecov.io/gh/chrovis/cljam/pull/265/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis#diff-c3JjL2NsamFtL2lvL3V0aWwvbHNiLmNsag==) | `87.84% <0.00%> (+6.07%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=chrovis)


Patch:
diff --git a/src/cljam/io/bam/encoder.clj b/src/cljam/io/bam/encoder.clj
index c274fc6..dbe18ea 100644
--- a/src/cljam/io/bam/encoder.clj
+++ b/src/cljam/io/bam/encoder.clj
@@ -52,24 +52,35 @@
 (defn- encode-tag-value [writer val-type value]
   (case val-type
     \A (lsb/write-char writer (char value))
+    \c (lsb/write-ubyte writer (byte value))
+    \C (lsb/write-ubyte writer (int value))
+    \s (lsb/write-short writer (short value))
+    \S (lsb/write-ushort writer (int value))
     \i (lsb/write-int writer (int value))
+    \I (lsb/write-uint writer (long value))
     \f (lsb/write-float writer (float value))
     \Z (do (lsb/write-string writer value)
            (lsb/write-char writer (char 0)))
     ;; \H nil
     \B (let [[array-type & array] (cstr/split value #",")]
+         (lsb/write-bytes writer (byte-array 1 (byte (first array-type))))
+         (lsb/write-int writer (count array))
          (case (first array-type)
-           \c nil
-           \C nil
-           \s nil
-           \S (do
-                (lsb/write-bytes writer (byte-array 1 (byte \S)))
-                (lsb/write-int writer (count array))
-                (doseq [v array]
-                  (lsb/write-ushort writer (Integer/parseInt v))))
-           \i nil
-           \I nil
-           \f nil))))
+           \c (doseq [v array]
+                (lsb/write-ubyte writer (Byte/parseByte v)))
+           \C (doseq [v array]
+                (lsb/write-ubyte writer (Integer/parseInt v)))
+           \s (doseq [v array]
+                (lsb/write-short writer (Short/parseShort v)))
+           \S (doseq [v array]
+                (lsb/write-ushort writer (Integer/parseInt v)))
+           \i (doseq [v array]
+                (lsb/write-int writer (Integer/parseInt v)))
+           \I (doseq [v array]
+                (lsb/write-uint writer (Long/parseLong v)))
+           \f (doseq [v array]
+                (lsb/write-float writer (Float/parseFloat v))))
+         writer)))
 
 (defn get-block-size [aln]
   (let [read-length (.length ^String (:seq aln))


Test patch:
diff --git a/test/cljam/io/bam/encoder_test.clj b/test/cljam/io/bam/encoder_test.clj
new file mode 100644
index 0000000..c0856f2
--- /dev/null
+++ b/test/cljam/io/bam/encoder_test.clj
@@ -0,0 +1,114 @@
+(ns cljam.io.bam.encoder-test
+  "Tests for cljam.io.bam.encoder."
+  (:require [clojure.test :refer [deftest are testing]]
+            [clojure.string :as cstr]
+            [cljam.io.bam.encoder :as encoder])
+  (:import [java.nio ByteBuffer]))
+
+(defn- get-encoded-option-data [?type ?values]
+  (let [bb (ByteBuffer/allocate 200)]
+    (#'encoder/encode-tag-value bb ?type ?values)
+    (take (.position bb) (.array bb))))
+
+(deftest encode-tag-value-test
+  (testing "non-array types"
+    (are [?type ?values ?bytes]
+         (= (map unchecked-byte ?bytes)
+            (get-encoded-option-data ?type ?values))
+      \A \@ [0x40]
+      \A \A [0x41]
+      \A \z [0x7a]
+      \c 0 [0x00]
+      \c 127 [0x7f]
+      \c -128 [0x80]
+      \c -1 [0xff]
+      \C 0 [0x00]
+      \C 127 [0x7f]
+      \C 128 [0x80]
+      \C 255 [0xff]
+      \s 0 [0x00 0x00]
+      \s 32767 [0xff 0x7f]
+      \s -32768 [0x00 0x80]
+      \s -1 [0xff 0xff]
+      \S 0 [0x00 0x00]
+      \S 32767 [0xff 0x7f]
+      \S 32768 [0x00 0x80]
+      \S 65535 [0xff 0xff]
+      \i 0 [0x00 0x00 0x00 0x00]
+      \i 2147483647 [0xff 0xff 0xff 0x7f]
+      \i -2147483648 [0x00 0x00 0x00 0x80]
+      \i -1 [0xff 0xff 0xff 0xff]
+      \I 0 [0x00 0x00 0x00 0x00]
+      \I 2147483647 [0xff 0xff 0xff 0x7f]
+      \I 2147483648 [0x00 0x00 0x00 0x80]
+      \I 4294967295 [0xff 0xff 0xff 0xff]
+      \f -17.25 [0x00 0x00 0x8a 0xc1]
+      \f 0.0 [0x00 0x00 0x00 0x00]
+      \f 5.75 [0x00 0x00 0xb8 0x40]
+      \f Float/MAX_VALUE [0xff 0xff 0x7f 0x7f]
+      \f Float/MIN_NORMAL [0x00 0x00 0x80 0x00]
+      \f Float/MIN_VALUE [0x01 0x00 0x00 0x00]
+      \f Float/NEGATIVE_INFINITY [0x00 0x00 0x80 0xff]
+      \f Float/POSITIVE_INFINITY [0x00 0x00 0x80 0x7f]))
+
+  (testing "NULL-terminated text"
+    (are [?type ?values ?bytes]
+         (= (map unchecked-byte ?bytes)
+            (get-encoded-option-data ?type ?values))
+      \Z "aaaBBB0011223344@@@+++"
+      [0x61 0x61 0x61 0x42 0x42 0x42 0x30 0x30 0x31 0x31 0x32 0x32 0x33 0x33
+       0x34 0x34 0x40 0x40 0x40 0x2b 0x2b 0x2b 0x00]
+      \Z (str "!\"#$%&'()*+,-./0123456789:;<=>?@"
+              "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
+      [0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e
+       0x2f 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c
+       0x3d 0x3e 0x3f 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a
+       0x4b 0x4c 0x4d 0x4e 0x4f 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58
+       0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f 0x60 0x61 0x62 0x63 0x64 0x65 0x66
+       0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73 0x74
+       0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x00]))
+
+  (testing "array types"
+    (are [?sub-type ?values ?bytes]
+         (= (map unchecked-byte ?bytes)
+            (get-encoded-option-data
+             \B (cstr/join \, (cons ?sub-type ?values))))
+      \c [0 127 -128 -1]
+      [0x63                ;; c
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x7f 0x80 0xff]
+      \C [0 127 128 255]
+      [0x43                ;; C
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x7f 0x80 0xff]
+      \s [0 32767 -32768 -1]
+      [0x73                ;; s
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x00 0xff 0x7f
+       0x00 0x80 0xff 0xff]
+      \S [0 32767 32768 65535]
+      [0x53                ;; S
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x00 0xff 0x7f
+       0x00 0x80 0xff 0xff]
+      \i [0 2147483647 -2147483648 -1]
+      [0x69                ;; i
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x00 0x00 0x00
+       0xff 0xff 0xff 0x7f
+       0x00 0x00 0x00 0x80
+       0xff 0xff 0xff 0xff]
+      \I [0 2147483647 2147483648 4294967295]
+      [0x49                ;; I
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x00 0x00 0x00
+       0xff 0xff 0xff 0x7f
+       0x00 0x00 0x00 0x80
+       0xff 0xff 0xff 0xff]
+      \f [-17.25 0.0 5.75 Float/MAX_VALUE]
+      [0x66                ;; f
+       0x04 0x00 0x00 0x00 ;; 4
+       0x00 0x00 0x8a 0xc1
+       0x00 0x00 0x00 0x00
+       0x00 0x00 0xb8 0x40
+       0xff 0xff 0x7f 0x7f])))
diff --git a/test/cljam/io/bam_test.clj b/test/cljam/io/bam_test.clj
new file mode 100644
index 0000000..ccbe174
--- /dev/null
+++ b/test/cljam/io/bam_test.clj
@@ -0,0 +1,85 @@
+(ns cljam.io.bam-test
+  "Tests for Processing BAM files."
+  (:require [clojure.test :refer [deftest are testing]]
+            [cljam.io.bam.encoder :as encoder]
+            [cljam.io.bam.decoder :as decoder])
+  (:import [java.nio ByteBuffer]))
+
+(deftest aux-data-codec-test
+  (testing "non-array types"
+    (are [?type ?value]
+         (= ?value
+            (as-> (#'encoder/encode-tag-value
+                   (ByteBuffer/allocate 100) ?type ?value) ^ByteBuffer bb
+              (.position bb 0)
+              (decoder/parse-tag-single ?type bb) ?type ?value))
+      \A \@
+      \A \A
+      \A \z
+      \c -128
+      \c -1
+      \c 0
+      \c 127
+      \C 0
+      \C 127
+      \C 128
+      \C 255
+      \s -32768
+      \s -1
+      \s 0
+      \s 32767
+      \S 0
+      \S 32767
+      \S 32768
+      \S 65535
+      \i -2147483648
+      \i 0
+      \i -1
+      \i 2147483647
+      \I 0
+      \I 2147483647
+      \I 2147483648
+      \I 4294967295
+      \f -17.75
+      \f 0.0
+      \f 5.75
+      \f (Float/MAX_VALUE)
+      \f (Float/MIN_NORMAL)
+      \f (Float/MIN_VALUE)
+      \f (Float/NEGATIVE_INFINITY)
+      \f (Float/POSITIVE_INFINITY)))
+
+  (testing "NULL-terminated text"
+    (are [?type ?value]
+         (= ?value
+            (as-> (#'encoder/encode-tag-value
+                   (ByteBuffer/allocate 100) ?type ?value) ^ByteBuffer bb
+              (.position bb 0)
+              (decoder/parse-tag-single ?type bb) ?type ?value))
+      \Z "aaaBBB0011223344@@@+++"
+      \Z (str "!\"#$%&'()*+,-./0123456789:;<=>?@"
+              "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")))
+
+  (testing "array types"
+    (are [?value]
+         (= ?value
+            (as->
+             (#'encoder/encode-tag-value
+              (ByteBuffer/allocate 100) \B ?value) ^ByteBuffer bb
+              (.position bb 0)
+              (#'decoder/parse-tag-array bb)))
+      "c,0"
+      "C,0"
+      "s,0"
+      "S,0"
+      "i,0"
+      "I,0"
+      "f,0.0"
+      "c,-128,0,127"
+      "C,0,255"
+      "s,-32768,0,32767"
+      "S,0,65535"
+      "i,-2147483648,0,2147483647"
+      "I,0,4294967295"
+      "f,-17.25,0.0,5.75"
+      "f,3.4028235E38,1.17549435E-38,1.4E-45,-Infinity,Infinity")))