htslib 0.2.3 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -22
- data/lib/hts/bam/header.rb +53 -2
- data/lib/hts/bam/header_record.rb +11 -0
- data/lib/hts/bam/record.rb +64 -23
- data/lib/hts/bam.rb +73 -66
- data/lib/hts/bcf/format.rb +4 -4
- data/lib/hts/bcf/header.rb +79 -2
- data/lib/hts/bcf/header_record.rb +35 -1
- data/lib/hts/bcf/info.rb +4 -4
- data/lib/hts/bcf.rb +85 -76
- data/lib/hts/hts.rb +2 -2
- data/lib/hts/libhts/constants.rb +32 -0
- data/lib/hts/libhts/tbx_funcs.rb +2 -0
- data/lib/hts/libhts/vcf_funcs.rb +16 -8
- data/lib/hts/tbx.rb +1 -5
- data/lib/hts/version.rb +1 -1
- data/lib/htslib.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a49c4758453c5c39915d9d17ccc33ce104699fca39c4083d4b2801767b655f1
|
4
|
+
data.tar.gz: 46ee49f6b452471e26afedfce70489cd24eebf86b1a18240bab125d465921fb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 918c0919750eb3d436cf19b1011ffed0a8d0760b29e0a3c2e675a9efa563a3f5c3b9a9ae38232e67192df4d066c446bbc0443f00d07cab9ac0c3401b38bd3fb7
|
7
|
+
data.tar.gz: 343fbaec89a2e60a54feadd29856b26b772baa65ef52d181cd5845ed53a8e549504ddcdcc739dff716f9069a5b0b3e6f22f5136c2a612cc6014eb79ee225ab2b
|
data/README.md
CHANGED
@@ -8,9 +8,7 @@
|
|
8
8
|
|
9
9
|
Ruby-htslib is the [Ruby](https://www.ruby-lang.org) bindings to [HTSlib](https://github.com/samtools/htslib), a C library for high-throughput sequencing data formats. It allows you to read and write file formats commonly used in genomics, such as [SAM, BAM, VCF, and BCF](http://samtools.github.io/hts-specs/), in the Ruby language.
|
10
10
|
|
11
|
-
:apple: Feel free to fork it
|
12
|
-
|
13
|
-
:bowtie: Alpha stage.
|
11
|
+
:apple: Feel free to fork it!
|
14
12
|
|
15
13
|
## Requirements
|
16
14
|
|
@@ -18,6 +16,7 @@ Ruby-htslib is the [Ruby](https://www.ruby-lang.org) bindings to [HTSlib](https:
|
|
18
16
|
* [HTSlib](https://github.com/samtools/htslib)
|
19
17
|
* Ubuntu : `apt install libhts-dev`
|
20
18
|
* macOS : `brew install htslib`
|
19
|
+
* Windows : [mingw-w64-htslib](https://packages.msys2.org/base/mingw-w64-htslib) is automatically fetched when installing the gem ([RubyInstaller](https://rubyinstaller.org) only).
|
21
20
|
* Build from source code (see Development section)
|
22
21
|
|
23
22
|
## Installation
|
@@ -34,11 +33,13 @@ Alternatively, you can specify the directory of the shared library by setting th
|
|
34
33
|
export HTSLIBDIR="/your/path/to/htslib" # libhts.so
|
35
34
|
```
|
36
35
|
|
36
|
+
ruby-htslib also works on Windows; if you use RubyInstaller, htslib will be prepared automatically.
|
37
|
+
|
37
38
|
## Overview
|
38
39
|
|
39
|
-
### High
|
40
|
+
### High-level API
|
40
41
|
|
41
|
-
|
42
|
+
HTS::Bam - SAM / BAM / CRAM - Sequence Alignment Map file
|
42
43
|
|
43
44
|
```ruby
|
44
45
|
require 'htslib'
|
@@ -56,14 +57,14 @@ bam.each do |r|
|
|
56
57
|
mpos: r.mpos + 1,
|
57
58
|
isiz: r.isize,
|
58
59
|
seqs: r.seq,
|
59
|
-
qual: r.
|
60
|
+
qual: r.qual_string,
|
60
61
|
MC: r.aux("MC")
|
61
62
|
end
|
62
63
|
|
63
64
|
bam.close
|
64
65
|
```
|
65
66
|
|
66
|
-
|
67
|
+
HTS::Bcf - VCF / BCF - Variant Call Format file
|
67
68
|
|
68
69
|
```ruby
|
69
70
|
bcf = HTS::Bcf.open("b.bcf")
|
@@ -96,9 +97,18 @@ fa.close
|
|
96
97
|
|
97
98
|
</details>
|
98
99
|
|
99
|
-
|
100
|
+
<details>
|
101
|
+
<summary><b>Tbx</b></summary>
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
|
105
|
+
```
|
100
106
|
|
101
|
-
|
107
|
+
</details>
|
108
|
+
|
109
|
+
### Low-level API
|
110
|
+
|
111
|
+
`HTS::LibHTS` provides native C functions.
|
102
112
|
|
103
113
|
```ruby
|
104
114
|
require 'htslib'
|
@@ -109,11 +119,17 @@ p b[:category]
|
|
109
119
|
p b[:format]
|
110
120
|
```
|
111
121
|
|
112
|
-
|
122
|
+
Macro functions
|
123
|
+
|
124
|
+
htslib has a lot of macro functions for speed. Ruby-FFI cannot call C macro functions. However, essential functions are reimplemented in Ruby, and you can call them.
|
125
|
+
|
126
|
+
Structs
|
127
|
+
|
128
|
+
Only a small number of C structs are implemented with FFI's ManagedStruct, which frees memory when Ruby's garbage collection fires. Other structs will need to be freed manually.
|
113
129
|
|
114
130
|
### Need more speed?
|
115
131
|
|
116
|
-
Try Crystal. [
|
132
|
+
Try Crystal. [HTS.cr](https://github.com/bio-cr/hts.cr) is implemented in Crystal language and provides an API compatible with ruby-htslib. Crystal language is not as flexible as Ruby language. You can not use `eval` methods and must always be careful with the types. Writing one-time scripts in Crystal may be less fun. However, if you have a clear idea of what you want to do in your mind, have already written code in Ruby, and need to run them over and over, try creating a command line tool in Crystal. The Crystal language is as fast as the Rust and C languages. It will give you incredible power to make tools.
|
117
133
|
|
118
134
|
## Documentation
|
119
135
|
|
@@ -134,26 +150,29 @@ bundle exec rake test
|
|
134
150
|
|
135
151
|
[GNU Autotools](https://en.wikipedia.org/wiki/GNU_Autotools) is required to compile htslib.
|
136
152
|
|
137
|
-
|
153
|
+
HTSlib has many macro functions. These macro functions cannot be called from FFI and must be reimplemented in Ruby.
|
138
154
|
|
139
155
|
* Use the new version of Ruby to take full advantage of Ruby's potential.
|
140
|
-
* This is possible because we have a small number of users.
|
141
|
-
* Remain compatible with [
|
142
|
-
* The most challenging part is the return value. In the Crystal language, methods are expected to return only one type. On the other hand, in the Ruby language, methods that return multiple classes are very common. For example, in the Crystal language, the compiler gets confused if the return value is one of six types: Int32, Int64, Float32, Float64, Nil, or String. In fact Crystal
|
156
|
+
* This is possible because we have a small number of users.
|
157
|
+
* Remain compatible with [HTS.cr](https://github.com/bio-cr/hts.cr).
|
158
|
+
* The most challenging part is the return value. In the Crystal language, methods are expected to return only one type. On the other hand, in the Ruby language, methods that return multiple classes are very common. For example, in the Crystal language, the compiler gets confused if the return value is one of six types: Int32, Int64, Float32, Float64, Nil, or String. In fact Crystal allows you to do that. But the code gets a little messy. In Ruby, this is very common and doesn't cause any problems.
|
159
|
+
* Ruby and Crystal are languages that use garbage collection. However, the memory release policy for allocated C structures is slightly different: in Ruby-FFI, you can define a `self.release` method in `FFI::Struct`. This method is called when GC. So you don't have to worry about memory in high-level APIs like Bam::Record or Bcf::Record, etc. Crystal requires you to define a finalize method on each class. So you need to define it in Bam::Record or Bcf::Record.
|
143
160
|
|
144
|
-
|
161
|
+
Method naming generally follows the Rust-htslib API.
|
145
162
|
|
146
163
|
#### FFI Extensions
|
147
164
|
|
148
165
|
* [ffi-bitfield](https://github.com/kojix2/ffi-bitfield) : Extension of Ruby-FFI to support bitfields.
|
149
166
|
|
150
|
-
#### Automatic
|
167
|
+
#### Automatic validation
|
168
|
+
|
169
|
+
In the `script` directory, there are several tools to help implement ruby-htslib. Scripts using c2ffi can check the coverage of htslib functions in Ruby-htslib. They are useful when new versions of htslib are released.
|
151
170
|
|
152
|
-
|
171
|
+
* [c2ffi](https://github.com/rpav/c2ffi) is a tool to create JSON format metadata from C header files.
|
153
172
|
|
154
173
|
## Contributing
|
155
174
|
|
156
|
-
Ruby-htslib is a library under development, so even
|
175
|
+
Ruby-htslib is a library under development, so even minor improvements like typo fixes are welcome! Please feel free to send us your pull requests.
|
157
176
|
|
158
177
|
* [Report bugs](https://github.com/kojix2/ruby-htslib/issues)
|
159
178
|
* Fix bugs and [submit pull requests](https://github.com/kojix2/ruby-htslib/pulls)
|
@@ -165,14 +184,14 @@ Ruby-htslib is a library under development, so even small improvements like typo
|
|
165
184
|
```
|
166
185
|
# Ownership and Commitment Rights
|
167
186
|
|
168
|
-
Do you need commit rights to ruby-htslib repository?
|
187
|
+
Do you need commit rights to the ruby-htslib repository?
|
169
188
|
Do you want to get admin rights and take over the project?
|
170
189
|
If so, please feel free to contact us @kojix2.
|
171
190
|
```
|
172
191
|
|
173
|
-
#### Why do you implement htslib in a language like Ruby, which is not widely used in
|
192
|
+
#### Why do you implement htslib in a language like Ruby, which is not widely used in bioinformatics?
|
174
193
|
|
175
|
-
One of the greatest joys of using a minor language like Ruby in bioinformatics is that
|
194
|
+
One of the greatest joys of using a minor language like Ruby in bioinformatics is that nothing stops you from reinventing the wheel. Reinventing the wheel can be fun. But with languages like Python and R, where many bioinformatics masters work, there is no chance for beginners to create htslib bindings. Bioinformatics file formats, libraries, and tools are very complex, and I need to learn how to understand them. So I started to implement the HTSLib binding myself to better understand how the pioneers of bioinformatics felt when establishing the file format and how they created their tools. I hope one day we can work on bioinformatics using Ruby and Crystal languages, not to replace other languages such as Python and R, but to add new power and value to this advancing field.
|
176
195
|
|
177
196
|
## Links
|
178
197
|
|
@@ -182,6 +201,7 @@ One of the greatest joys of using a minor language like Ruby in bioinformatics i
|
|
182
201
|
## Funding support
|
183
202
|
|
184
203
|
This work was supported partially by [Ruby Association Grant 2020](https://www.ruby.or.jp/en/news/20201022).
|
204
|
+
|
185
205
|
## License
|
186
206
|
|
187
207
|
[MIT License](https://opensource.org/licenses/MIT).
|
data/lib/hts/bam/header.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "header_record"
|
4
|
+
|
3
5
|
module HTS
|
4
6
|
class Bam < Hts
|
5
7
|
# A class for working with alignment header.
|
6
8
|
class Header
|
7
|
-
def
|
8
|
-
|
9
|
+
def self.parse(str)
|
10
|
+
new(LibHTS.sam_hdr_parse(str.size, str))
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(arg = nil)
|
14
|
+
case arg
|
15
|
+
when LibHTS::HtsFile
|
16
|
+
@sam_hdr = LibHTS.sam_hdr_read(arg)
|
17
|
+
when LibHTS::SamHdr
|
18
|
+
@sam_hdr = arg
|
19
|
+
when nil
|
20
|
+
@sam_hdr = LibHTS.sam_hdr_init
|
21
|
+
else
|
22
|
+
raise TypeError, "Invalid argument"
|
23
|
+
end
|
9
24
|
end
|
10
25
|
|
11
26
|
def struct
|
@@ -17,6 +32,7 @@ module HTS
|
|
17
32
|
end
|
18
33
|
|
19
34
|
def target_count
|
35
|
+
# FIXME: sam_hdr_nref
|
20
36
|
@sam_hdr[:n_targets]
|
21
37
|
end
|
22
38
|
|
@@ -32,6 +48,41 @@ module HTS
|
|
32
48
|
end
|
33
49
|
end
|
34
50
|
|
51
|
+
# experimental
|
52
|
+
def add_lines(str)
|
53
|
+
LibHTS.sam_hdr_add_lines(@sam_hdr, str, 0)
|
54
|
+
end
|
55
|
+
|
56
|
+
# experimental
|
57
|
+
def add_line(type, *args)
|
58
|
+
args = args.flat_map { |arg| [:string, arg] }
|
59
|
+
LibHTS.sam_hdr_add_line(@sam_hdr, type, *args, :pointer, FFI::Pointer::NULL)
|
60
|
+
end
|
61
|
+
|
62
|
+
# experimental
|
63
|
+
def find_line(type, key, value)
|
64
|
+
ks = LibHTS::KString.new
|
65
|
+
r = LibHTS.sam_hdr_find_line_id(@sam_hdr, type, key, value, ks)
|
66
|
+
r == 0 ? ks[:s] : nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# experimental
|
70
|
+
def find_line_at(type, pos)
|
71
|
+
ks = LibHTS::KString.new
|
72
|
+
r = LibHTS.sam_hdr_find_line_pos(@sam_hdr, type, pos, ks)
|
73
|
+
r == 0 ? ks[:s] : nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# experimental
|
77
|
+
def remove_line(type, key, value)
|
78
|
+
LibHTS.sam_hdr_remove_line_id(@sam_hdr, type, key, value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# experimental
|
82
|
+
def remove_line_at(type, pos)
|
83
|
+
LibHTS.sam_hdr_remove_line_pos(@sam_hdr, type, pos)
|
84
|
+
end
|
85
|
+
|
35
86
|
def to_s
|
36
87
|
LibHTS.sam_hdr_str(@sam_hdr)
|
37
88
|
end
|
data/lib/hts/bam/record.rb
CHANGED
@@ -17,6 +17,7 @@ module HTS
|
|
17
17
|
@header = header
|
18
18
|
end
|
19
19
|
|
20
|
+
# Return the FFI::Struct object.
|
20
21
|
def struct
|
21
22
|
@bam1
|
22
23
|
end
|
@@ -25,17 +26,14 @@ module HTS
|
|
25
26
|
@bam1.to_ptr
|
26
27
|
end
|
27
28
|
|
28
|
-
#
|
29
|
+
# Get the read name. (a.k.a QNAME)
|
30
|
+
# @return [String] query template name
|
29
31
|
def qname
|
30
32
|
LibHTS.bam_get_qname(@bam1).read_string
|
31
33
|
end
|
32
34
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# raise 'Not Implemented'
|
36
|
-
# end
|
37
|
-
|
38
|
-
# returns the tid of the record or -1 if not mapped.
|
35
|
+
# Get the chromosome ID of the alignment. -1 if not mapped.
|
36
|
+
# @return [Integer] chromosome ID
|
39
37
|
def tid
|
40
38
|
@bam1[:core][:tid]
|
41
39
|
end
|
@@ -44,7 +42,8 @@ module HTS
|
|
44
42
|
@bam1[:core][:tid] = tid
|
45
43
|
end
|
46
44
|
|
47
|
-
#
|
45
|
+
# Get the chromosome ID of the mate. -1 if not mapped.
|
46
|
+
# @return [Integer] chromosome ID
|
48
47
|
def mtid
|
49
48
|
@bam1[:core][:mtid]
|
50
49
|
end
|
@@ -53,7 +52,8 @@ module HTS
|
|
53
52
|
@bam1[:core][:mtid] = mtid
|
54
53
|
end
|
55
54
|
|
56
|
-
#
|
55
|
+
# Get the 0-based leftmost coordinate of the alignment.
|
56
|
+
# @return [Integer] 0-based leftmost coordinate
|
57
57
|
def pos
|
58
58
|
@bam1[:core][:pos]
|
59
59
|
end
|
@@ -62,7 +62,8 @@ module HTS
|
|
62
62
|
@bam1[:core][:pos] = pos
|
63
63
|
end
|
64
64
|
|
65
|
-
#
|
65
|
+
# Get the 0-based leftmost coordinate of the mate.
|
66
|
+
# @return [Integer] 0-based leftmost coordinate
|
66
67
|
def mate_pos
|
67
68
|
@bam1[:core][:mpos]
|
68
69
|
end
|
@@ -74,6 +75,8 @@ module HTS
|
|
74
75
|
alias mpos mate_pos
|
75
76
|
alias mpos= mate_pos=
|
76
77
|
|
78
|
+
# Get the bin calculated by bam_reg2bin().
|
79
|
+
# @return [Integer] bin
|
77
80
|
def bin
|
78
81
|
@bam1[:core][:bin]
|
79
82
|
end
|
@@ -82,12 +85,15 @@ module HTS
|
|
82
85
|
@bam1[:core][:bin] = bin
|
83
86
|
end
|
84
87
|
|
85
|
-
#
|
88
|
+
# Get the rightmost base position of the alignment on the reference genome.
|
89
|
+
# @return [Integer] 0-based rightmost coordinate
|
86
90
|
def endpos
|
87
91
|
LibHTS.bam_endpos @bam1
|
88
92
|
end
|
89
93
|
|
90
|
-
#
|
94
|
+
# Get the reference sequence name of the alignment. (a.k.a RNAME)
|
95
|
+
# '' if not mapped.
|
96
|
+
# @return [String] reference sequence name
|
91
97
|
def chrom
|
92
98
|
return "" if tid == -1
|
93
99
|
|
@@ -96,7 +102,9 @@ module HTS
|
|
96
102
|
|
97
103
|
alias contig chrom
|
98
104
|
|
99
|
-
#
|
105
|
+
# Get the reference sequence name of the mate.
|
106
|
+
# '' if not mapped.
|
107
|
+
# @return [String] reference sequence name
|
100
108
|
def mate_chrom
|
101
109
|
return "" if mtid == -1
|
102
110
|
|
@@ -105,12 +113,20 @@ module HTS
|
|
105
113
|
|
106
114
|
alias mate_contig mate_chrom
|
107
115
|
|
108
|
-
# Get strand
|
116
|
+
# Get whether the query is on the reverse strand.
|
117
|
+
# @return [String] strand "+" or "-"
|
109
118
|
def strand
|
110
119
|
LibHTS.bam_is_rev(@bam1) ? "-" : "+"
|
111
120
|
end
|
112
121
|
|
113
|
-
#
|
122
|
+
# Get whether the query's mate is on the reverse strand.
|
123
|
+
# @return [String] strand "+" or "-"
|
124
|
+
def mate_strand
|
125
|
+
LibHTS.bam_is_mrev(@bam1) ? "-" : "+"
|
126
|
+
end
|
127
|
+
|
128
|
+
# Get the observed template length. (a.k.a TLEN)
|
129
|
+
# @return [Integer] isize
|
114
130
|
def insert_size
|
115
131
|
@bam1[:core][:isize]
|
116
132
|
end
|
@@ -122,7 +138,8 @@ module HTS
|
|
122
138
|
alias isize insert_size
|
123
139
|
alias isize= insert_size=
|
124
140
|
|
125
|
-
# mapping quality
|
141
|
+
# Get the mapping quality of the alignment. (a.k.a MAPQ)
|
142
|
+
# @return [Integer] mapping quality
|
126
143
|
def mapq
|
127
144
|
@bam1[:core][:qual]
|
128
145
|
end
|
@@ -131,11 +148,14 @@ module HTS
|
|
131
148
|
@bam1[:core][:qual] = mapq
|
132
149
|
end
|
133
150
|
|
134
|
-
#
|
151
|
+
# Get the Bam::Cigar object.
|
152
|
+
# @return [Bam::Cigar] cigar
|
135
153
|
def cigar
|
136
154
|
Cigar.new(LibHTS.bam_get_cigar(@bam1), @bam1[:core][:n_cigar])
|
137
155
|
end
|
138
156
|
|
157
|
+
# Calculate query length from CIGAR.
|
158
|
+
# @return [Integer] query length
|
139
159
|
def qlen
|
140
160
|
LibHTS.bam_cigar2qlen(
|
141
161
|
@bam1[:core][:n_cigar],
|
@@ -143,6 +163,8 @@ module HTS
|
|
143
163
|
)
|
144
164
|
end
|
145
165
|
|
166
|
+
# Calculate reference length from CIGAR.
|
167
|
+
# @return [Integer] reference length
|
146
168
|
def rlen
|
147
169
|
LibHTS.bam_cigar2rlen(
|
148
170
|
@bam1[:core][:n_cigar],
|
@@ -150,7 +172,8 @@ module HTS
|
|
150
172
|
)
|
151
173
|
end
|
152
174
|
|
153
|
-
#
|
175
|
+
# Get the sequence. (a.k.a SEQ)
|
176
|
+
# @return [String] sequence
|
154
177
|
def seq
|
155
178
|
r = LibHTS.bam_get_seq(@bam1)
|
156
179
|
seq = String.new
|
@@ -161,11 +184,15 @@ module HTS
|
|
161
184
|
end
|
162
185
|
alias sequence seq
|
163
186
|
|
187
|
+
# Get the length of the query sequence.
|
188
|
+
# @return [Integer] query length
|
164
189
|
def len
|
165
190
|
@bam1[:core][:l_qseq]
|
166
191
|
end
|
167
192
|
|
168
|
-
#
|
193
|
+
# Get the base of the requested index "i" of the query sequence.
|
194
|
+
# @param [Integer] i index
|
195
|
+
# @return [String] base
|
169
196
|
def base(n)
|
170
197
|
n += @bam1[:core][:l_qseq] if n < 0
|
171
198
|
return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base(-1000)
|
@@ -174,13 +201,23 @@ module HTS
|
|
174
201
|
SEQ_NT16_STR[LibHTS.bam_seqi(r, n)]
|
175
202
|
end
|
176
203
|
|
177
|
-
#
|
204
|
+
# Get the base qualities.
|
205
|
+
# @return [Array] base qualities
|
178
206
|
def qual
|
179
207
|
q_ptr = LibHTS.bam_get_qual(@bam1)
|
180
208
|
q_ptr.read_array_of_uint8(@bam1[:core][:l_qseq])
|
181
209
|
end
|
182
210
|
|
183
|
-
#
|
211
|
+
# Get the base qualities as a string. (a.k.a QUAL)
|
212
|
+
# ASCII of base quality + 33.
|
213
|
+
# @return [String] base qualities
|
214
|
+
def qual_string
|
215
|
+
qual.map { |q| (q + 33).chr }.join
|
216
|
+
end
|
217
|
+
|
218
|
+
# Get the base quality of the requested index "i" of the query sequence.
|
219
|
+
# @param [Integer] i index
|
220
|
+
# @return [Integer] base quality
|
184
221
|
def base_qual(n)
|
185
222
|
n += @bam1[:core][:l_qseq] if n < 0
|
186
223
|
return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base_qual(-1000)
|
@@ -189,7 +226,8 @@ module HTS
|
|
189
226
|
q_ptr.get_uint8(n)
|
190
227
|
end
|
191
228
|
|
192
|
-
#
|
229
|
+
# Get Bam::Flag object of the alignment.
|
230
|
+
# @return [Bam::Flag] flag
|
193
231
|
def flag
|
194
232
|
Flag.new(@bam1[:core][:flag])
|
195
233
|
end
|
@@ -205,7 +243,9 @@ module HTS
|
|
205
243
|
end
|
206
244
|
end
|
207
245
|
|
208
|
-
#
|
246
|
+
# Get the auxiliary data.
|
247
|
+
# @param [String] key tag name
|
248
|
+
# @return [String] value
|
209
249
|
def aux(key = nil)
|
210
250
|
aux = Aux.new(self)
|
211
251
|
if key
|
@@ -230,6 +270,7 @@ module HTS
|
|
230
270
|
end
|
231
271
|
end
|
232
272
|
|
273
|
+
# @return [String] a string representation of the alignment.
|
233
274
|
def to_s
|
234
275
|
kstr = LibHTS::KString.new
|
235
276
|
raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
|
data/lib/hts/bam.rb
CHANGED
@@ -57,7 +57,6 @@ module HTS
|
|
57
57
|
build_index(index) if build_index
|
58
58
|
@idx = load_index(index)
|
59
59
|
@start_position = tell
|
60
|
-
super # do nothing
|
61
60
|
end
|
62
61
|
|
63
62
|
def build_index(index_name = nil, min_shift: 0)
|
@@ -90,18 +89,21 @@ module HTS
|
|
90
89
|
!@idx.null?
|
91
90
|
end
|
92
91
|
|
93
|
-
# Close the current file.
|
94
92
|
def close
|
95
93
|
LibHTS.hts_idx_destroy(@idx) if @idx&.null?
|
96
94
|
@idx = nil
|
97
95
|
super
|
98
96
|
end
|
99
97
|
|
98
|
+
def fai=(fai)
|
99
|
+
check_closed
|
100
|
+
LibHTS.hts_set_fai_filename(@hts_file, fai) > 0 || raise
|
101
|
+
end
|
102
|
+
|
100
103
|
def write_header(header)
|
101
104
|
check_closed
|
102
105
|
|
103
106
|
@header = header.dup
|
104
|
-
LibHTS.hts_set_fai_filename(@hts_file, @file_name)
|
105
107
|
LibHTS.sam_hdr_write(@hts_file, header)
|
106
108
|
end
|
107
109
|
|
@@ -109,7 +111,8 @@ module HTS
|
|
109
111
|
check_closed
|
110
112
|
|
111
113
|
aln_dup = aln.dup
|
112
|
-
LibHTS.sam_write1(@hts_file, header, aln_dup)
|
114
|
+
r = LibHTS.sam_write1(@hts_file, header, aln_dup)
|
115
|
+
raise "Failed to write record" if r < 0
|
113
116
|
end
|
114
117
|
|
115
118
|
def each(copy: false, &block)
|
@@ -120,29 +123,6 @@ module HTS
|
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
123
|
-
private def each_record_copy
|
124
|
-
check_closed
|
125
|
-
return to_enum(__method__) unless block_given?
|
126
|
-
|
127
|
-
while LibHTS.sam_read1(@hts_file, header, bam1 = LibHTS.bam_init1) != -1
|
128
|
-
record = Record.new(bam1, header)
|
129
|
-
yield record
|
130
|
-
end
|
131
|
-
self
|
132
|
-
end
|
133
|
-
|
134
|
-
private def each_record_reuse
|
135
|
-
check_closed
|
136
|
-
# Each does not always start at the beginning of the file.
|
137
|
-
# This is the common behavior of IO objects in Ruby.
|
138
|
-
return to_enum(__method__) unless block_given?
|
139
|
-
|
140
|
-
bam1 = LibHTS.bam_init1
|
141
|
-
record = Record.new(bam1, header)
|
142
|
-
yield record while LibHTS.sam_read1(@hts_file, header, bam1) != -1
|
143
|
-
self
|
144
|
-
end
|
145
|
-
|
146
126
|
def query(region, copy: false, &block)
|
147
127
|
if copy
|
148
128
|
query_copy(region, &block)
|
@@ -151,45 +131,6 @@ module HTS
|
|
151
131
|
end
|
152
132
|
end
|
153
133
|
|
154
|
-
private def query_copy(region)
|
155
|
-
check_closed
|
156
|
-
raise "Index file is required to call the query method." unless index_loaded?
|
157
|
-
return to_enum(__method__, region) unless block_given?
|
158
|
-
|
159
|
-
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
160
|
-
|
161
|
-
begin
|
162
|
-
loop do
|
163
|
-
bam1 = LibHTS.bam_init1
|
164
|
-
slen = LibHTS.sam_itr_next(@hts_file, qiter, bam1)
|
165
|
-
break if slen == -1
|
166
|
-
raise if slen < -1
|
167
|
-
|
168
|
-
yield Record.new(bam1, header)
|
169
|
-
end
|
170
|
-
ensure
|
171
|
-
LibHTS.hts_itr_destroy(qiter)
|
172
|
-
end
|
173
|
-
self
|
174
|
-
end
|
175
|
-
|
176
|
-
private def query_reuse(region)
|
177
|
-
check_closed
|
178
|
-
raise "Index file is required to call the query method." unless index_loaded?
|
179
|
-
return to_enum(__method__, region) unless block_given?
|
180
|
-
|
181
|
-
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
182
|
-
|
183
|
-
bam1 = LibHTS.bam_init1
|
184
|
-
record = Record.new(bam1, header)
|
185
|
-
begin
|
186
|
-
yield record while LibHTS.sam_itr_next(@hts_file, qiter, bam1) > 0
|
187
|
-
ensure
|
188
|
-
LibHTS.hts_itr_destroy(qiter)
|
189
|
-
end
|
190
|
-
self
|
191
|
-
end
|
192
|
-
|
193
134
|
# @!macro [attach] define_getter
|
194
135
|
# @method $1
|
195
136
|
# Get $1 array
|
@@ -247,5 +188,71 @@ module HTS
|
|
247
188
|
|
248
189
|
self
|
249
190
|
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def query_reuse(region)
|
195
|
+
check_closed
|
196
|
+
raise "Index file is required to call the query method." unless index_loaded?
|
197
|
+
return to_enum(__method__, region) unless block_given?
|
198
|
+
|
199
|
+
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
200
|
+
raise "Failed to query region: #{region}" if qiter.null?
|
201
|
+
|
202
|
+
bam1 = LibHTS.bam_init1
|
203
|
+
record = Record.new(bam1, header)
|
204
|
+
begin
|
205
|
+
yield record while LibHTS.sam_itr_next(@hts_file, qiter, bam1) > 0
|
206
|
+
ensure
|
207
|
+
LibHTS.hts_itr_destroy(qiter)
|
208
|
+
end
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
def query_copy(region)
|
213
|
+
check_closed
|
214
|
+
raise "Index file is required to call the query method." unless index_loaded?
|
215
|
+
return to_enum(__method__, region) unless block_given?
|
216
|
+
|
217
|
+
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
218
|
+
raise "Failed to query region: #{region}" if qiter.null?
|
219
|
+
|
220
|
+
begin
|
221
|
+
loop do
|
222
|
+
bam1 = LibHTS.bam_init1
|
223
|
+
slen = LibHTS.sam_itr_next(@hts_file, qiter, bam1)
|
224
|
+
break if slen == -1
|
225
|
+
raise if slen < -1
|
226
|
+
|
227
|
+
yield Record.new(bam1, header)
|
228
|
+
end
|
229
|
+
ensure
|
230
|
+
LibHTS.hts_itr_destroy(qiter)
|
231
|
+
end
|
232
|
+
self
|
233
|
+
end
|
234
|
+
|
235
|
+
def each_record_reuse
|
236
|
+
check_closed
|
237
|
+
# Each does not always start at the beginning of the file.
|
238
|
+
# This is the common behavior of IO objects in Ruby.
|
239
|
+
return to_enum(__method__) unless block_given?
|
240
|
+
|
241
|
+
bam1 = LibHTS.bam_init1
|
242
|
+
record = Record.new(bam1, header)
|
243
|
+
yield record while LibHTS.sam_read1(@hts_file, header, bam1) != -1
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
def each_record_copy
|
248
|
+
check_closed
|
249
|
+
return to_enum(__method__) unless block_given?
|
250
|
+
|
251
|
+
while LibHTS.sam_read1(@hts_file, header, bam1 = LibHTS.bam_init1) != -1
|
252
|
+
record = Record.new(bam1, header)
|
253
|
+
yield record
|
254
|
+
end
|
255
|
+
self
|
256
|
+
end
|
250
257
|
end
|
251
258
|
end
|
data/lib/hts/bcf/format.rb
CHANGED
@@ -8,22 +8,22 @@ module HTS
|
|
8
8
|
@p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
|
9
9
|
end
|
10
10
|
|
11
|
-
# For compatibility with
|
11
|
+
# For compatibility with HTS.cr.
|
12
12
|
def get_int(key)
|
13
13
|
get(key, :int)
|
14
14
|
end
|
15
15
|
|
16
|
-
# For compatibility with
|
16
|
+
# For compatibility with HTS.cr.
|
17
17
|
def get_float(key)
|
18
18
|
get(key, :float)
|
19
19
|
end
|
20
20
|
|
21
|
-
# For compatibility with
|
21
|
+
# For compatibility with HTS.cr.
|
22
22
|
def get_flag(key)
|
23
23
|
get(key, :flag)
|
24
24
|
end
|
25
25
|
|
26
|
-
# For compatibility with
|
26
|
+
# For compatibility with HTS.cr.
|
27
27
|
def get_string(key)
|
28
28
|
get(key, :string)
|
29
29
|
end
|
data/lib/hts/bcf/header.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "header_record"
|
4
|
+
|
3
5
|
module HTS
|
4
6
|
class Bcf < Hts
|
5
7
|
# A class for working with VCF records.
|
8
|
+
# NOTE: This class has a lot of methods that are not stable.
|
9
|
+
# The method names and the number of arguments may change in the future.
|
6
10
|
class Header
|
7
|
-
def initialize(
|
8
|
-
|
11
|
+
def initialize(arg = nil)
|
12
|
+
case arg
|
13
|
+
when LibHTS::HtsFile
|
14
|
+
@bcf_hdr = LibHTS.bcf_hdr_read(arg)
|
15
|
+
when LibHTS::BcfHdr
|
16
|
+
@bcf_hdr = arg
|
17
|
+
when nil
|
18
|
+
@bcf_hdr = LibHTS.bcf_hdr_init("w")
|
19
|
+
else
|
20
|
+
raise TypeError, "Invalid argument"
|
21
|
+
end
|
9
22
|
end
|
10
23
|
|
11
24
|
def struct
|
@@ -20,6 +33,10 @@ module HTS
|
|
20
33
|
LibHTS.bcf_hdr_get_version(@bcf_hdr)
|
21
34
|
end
|
22
35
|
|
36
|
+
def set_version(version)
|
37
|
+
LibHTS.bcf_hdr_set_version(@bcf_hdr, version)
|
38
|
+
end
|
39
|
+
|
23
40
|
def nsamples
|
24
41
|
LibHTS.bcf_hdr_nsamples(@bcf_hdr)
|
25
42
|
end
|
@@ -31,6 +48,45 @@ module HTS
|
|
31
48
|
.map(&:read_string)
|
32
49
|
end
|
33
50
|
|
51
|
+
def add_sample(sample, sync: true)
|
52
|
+
LibHTS.bcf_hdr_add_sample(@bcf_hdr, sample)
|
53
|
+
self.sync if sync
|
54
|
+
end
|
55
|
+
|
56
|
+
def merge(hdr)
|
57
|
+
LibHTS.bcf_hdr_merge(@bcf_hdr, hdr.struct)
|
58
|
+
end
|
59
|
+
|
60
|
+
def sync
|
61
|
+
LibHTS.bcf_hdr_sync(@bcf_hdr)
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_bcf(fname)
|
65
|
+
LibHTS.bcf_hdr_set(@bcf_hdr, fname)
|
66
|
+
end
|
67
|
+
|
68
|
+
def append(line)
|
69
|
+
LibHTS.bcf_hdr_append(@bcf_hdr, line)
|
70
|
+
end
|
71
|
+
|
72
|
+
def delete(bcf_hl_type, key) # FIXME
|
73
|
+
type = bcf_hl_type_to_int(bcf_hl_type)
|
74
|
+
LibHTS.bcf_hdr_remove(@bcf_hdr, type, key)
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_hrec(bcf_hl_type, key, value, str_class = nil)
|
78
|
+
type = bcf_hl_type_to_int(bcf_hl_type)
|
79
|
+
hrec = LibHTS.bcf_hdr_get_hrec(@bcf_hdr, type, key, value, str_class)
|
80
|
+
HeaderRecord.new(hrec)
|
81
|
+
end
|
82
|
+
|
83
|
+
def seqnames
|
84
|
+
n = FFI::MemoryPointer.new(:int)
|
85
|
+
names = LibHTS.bcf_hdr_seqnames(@bcf_hdr, n)
|
86
|
+
names.read_array_of_pointer(n.read_int)
|
87
|
+
.map(&:read_string)
|
88
|
+
end
|
89
|
+
|
34
90
|
def to_s
|
35
91
|
kstr = LibHTS::KString.new
|
36
92
|
raise "Failed to get header string" unless LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr)
|
@@ -40,6 +96,27 @@ module HTS
|
|
40
96
|
|
41
97
|
private
|
42
98
|
|
99
|
+
def bcf_hl_type_to_int(bcf_hl_type)
|
100
|
+
return bcf_hl_type if bcf_hl_type.is_a?(Integer)
|
101
|
+
|
102
|
+
case bcf_hl_type.to_s.upcase
|
103
|
+
when "FILTER", "FIL"
|
104
|
+
LibHTS::BCF_HL_FLT
|
105
|
+
when "INFO"
|
106
|
+
LibHTS::BCF_HL_INFO
|
107
|
+
when "FORMAT", "FMT"
|
108
|
+
LibHTS::BCF_HL_FMT
|
109
|
+
when "CONTIG", "CTG"
|
110
|
+
LibHTS::BCF_HL_CTG
|
111
|
+
when "STRUCTURED", "STR"
|
112
|
+
LibHTS::BCF_HL_STR
|
113
|
+
when "GENOTYPE", "GEN"
|
114
|
+
LibHTS::BCF_HL_GEN
|
115
|
+
else
|
116
|
+
raise TypeError, "Invalid argument"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
43
120
|
def initialize_copy(orig)
|
44
121
|
@bcf_hdr = LibHTS.bcf_hdr_dup(orig.struct)
|
45
122
|
end
|
@@ -3,9 +3,43 @@
|
|
3
3
|
module HTS
|
4
4
|
class Bcf < Hts
|
5
5
|
class HeaderRecord
|
6
|
-
def initialize
|
6
|
+
def initialize(arg = nil)
|
7
|
+
case arg
|
8
|
+
when LibHTS::BcfHrec
|
9
|
+
@bcf_hrec = arg
|
10
|
+
else
|
11
|
+
raise TypeError, "Invalid argument"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def struct
|
7
16
|
@bcf_hrec
|
8
17
|
end
|
18
|
+
|
19
|
+
def add_key(key)
|
20
|
+
LibHTS.bcf_hrec_add_key(@bcf_hrec, key, key.length)
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_value(i, val, quote: true)
|
24
|
+
is_quoted = quote ? 1 : 0
|
25
|
+
LibHTS.bcf_hrec_set_val(@bcf_hrec, i, val, val.length, is_quoted)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_key(key)
|
29
|
+
LibHTS.bcf_hrec_find_key(@bcf_hrec, key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
kstr = LibHTS::KString.new
|
34
|
+
LibHTS.bcf_hrec_format(@bcf_hrec, kstr)
|
35
|
+
kstr[:s]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def initialize_copy(orig)
|
41
|
+
@bcf_hrec = LibHTS.bcf_hrec_dup(orig.struct)
|
42
|
+
end
|
9
43
|
end
|
10
44
|
end
|
11
45
|
end
|
data/lib/hts/bcf/info.rb
CHANGED
@@ -9,22 +9,22 @@ module HTS
|
|
9
9
|
@p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
|
10
10
|
end
|
11
11
|
|
12
|
-
# For compatibility with
|
12
|
+
# For compatibility with HTS.cr.
|
13
13
|
def get_int(key)
|
14
14
|
get(key, :int)
|
15
15
|
end
|
16
16
|
|
17
|
-
# For compatibility with
|
17
|
+
# For compatibility with HTS.cr.
|
18
18
|
def get_float(key)
|
19
19
|
get(key, :float)
|
20
20
|
end
|
21
21
|
|
22
|
-
# For compatibility with
|
22
|
+
# For compatibility with HTS.cr.
|
23
23
|
def get_string(key)
|
24
24
|
get(key, :string)
|
25
25
|
end
|
26
26
|
|
27
|
-
# For compatibility with
|
27
|
+
# For compatibility with HTS.cr.
|
28
28
|
def get_flag(key)
|
29
29
|
get(key, :flag)
|
30
30
|
end
|
data/lib/hts/bcf.rb
CHANGED
@@ -52,7 +52,6 @@ module HTS
|
|
52
52
|
build_index(index) if build_index
|
53
53
|
@idx = load_index(index)
|
54
54
|
@start_position = tell
|
55
|
-
super # do nothing
|
56
55
|
end
|
57
56
|
|
58
57
|
def build_index(index_name = nil, min_shift: 14)
|
@@ -85,11 +84,16 @@ module HTS
|
|
85
84
|
!@idx.null?
|
86
85
|
end
|
87
86
|
|
88
|
-
def
|
87
|
+
def close
|
88
|
+
LibHTS.hts_idx_destroy(@idx) unless @idx&.null?
|
89
|
+
@idx = nil
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_header(header)
|
89
94
|
check_closed
|
90
95
|
|
91
96
|
@header = header.dup
|
92
|
-
LibHTS.hts_set_fai_filename(header, @file_name)
|
93
97
|
LibHTS.bcf_hdr_write(@hts_file, header)
|
94
98
|
end
|
95
99
|
|
@@ -97,7 +101,8 @@ module HTS
|
|
97
101
|
check_closed
|
98
102
|
|
99
103
|
var_dup = var.dup
|
100
|
-
LibHTS.bcf_write(@hts_file, header, var_dup)
|
104
|
+
r = LibHTS.bcf_write(@hts_file, header, var_dup)
|
105
|
+
raise "Failed to write record" if r < 0
|
101
106
|
end
|
102
107
|
|
103
108
|
# Close the current file.
|
@@ -122,29 +127,6 @@ module HTS
|
|
122
127
|
end
|
123
128
|
end
|
124
129
|
|
125
|
-
private def each_record_copy
|
126
|
-
check_closed
|
127
|
-
|
128
|
-
return to_enum(__method__) unless block_given?
|
129
|
-
|
130
|
-
while LibHTS.bcf_read(@hts_file, header, bcf1 = LibHTS.bcf_init) != -1
|
131
|
-
record = Record.new(bcf1, header)
|
132
|
-
yield record
|
133
|
-
end
|
134
|
-
self
|
135
|
-
end
|
136
|
-
|
137
|
-
private def each_record_reuse
|
138
|
-
check_closed
|
139
|
-
|
140
|
-
return to_enum(__method__) unless block_given?
|
141
|
-
|
142
|
-
bcf1 = LibHTS.bcf_init
|
143
|
-
record = Record.new(bcf1, header)
|
144
|
-
yield record while LibHTS.bcf_read(@hts_file, header, bcf1) != -1
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
130
|
def query(...)
|
149
131
|
querys(...) # Fixme
|
150
132
|
end
|
@@ -166,55 +148,6 @@ module HTS
|
|
166
148
|
# private def queryi_reuse
|
167
149
|
# end
|
168
150
|
|
169
|
-
private def querys_copy(region)
|
170
|
-
check_closed
|
171
|
-
|
172
|
-
raise "query is only available for BCF files" unless file_format == "bcf"
|
173
|
-
raise "Index file is required to call the query method." unless index_loaded?
|
174
|
-
return to_enum(__method__, region) unless block_given?
|
175
|
-
|
176
|
-
qitr = LibHTS.bcf_itr_querys(@idx, header, region)
|
177
|
-
|
178
|
-
begin
|
179
|
-
loop do
|
180
|
-
bcf1 = LibHTS.bcf_init
|
181
|
-
slen = LibHTS.hts_itr_next(@hts_file[:fp][:bgzf], qitr, bcf1, ::FFI::Pointer::NULL)
|
182
|
-
break if slen == -1
|
183
|
-
raise if slen < -1
|
184
|
-
|
185
|
-
yield Record.new(bcf1, header)
|
186
|
-
end
|
187
|
-
ensure
|
188
|
-
LibHTS.bcf_itr_destroy(qitr)
|
189
|
-
end
|
190
|
-
self
|
191
|
-
end
|
192
|
-
|
193
|
-
private def querys_reuse(region)
|
194
|
-
check_closed
|
195
|
-
|
196
|
-
raise "query is only available for BCF files" unless file_format == "bcf"
|
197
|
-
raise "Index file is required to call the query method." unless index_loaded?
|
198
|
-
return to_enum(__method__, region) unless block_given?
|
199
|
-
|
200
|
-
qitr = LibHTS.bcf_itr_querys(@idx, header, region)
|
201
|
-
|
202
|
-
bcf1 = LibHTS.bcf_init
|
203
|
-
record = Record.new(bcf1, header)
|
204
|
-
begin
|
205
|
-
loop do
|
206
|
-
slen = LibHTS.hts_itr_next(@hts_file[:fp][:bgzf], qitr, bcf1, ::FFI::Pointer::NULL)
|
207
|
-
break if slen == -1
|
208
|
-
raise if slen < -1
|
209
|
-
|
210
|
-
yield record
|
211
|
-
end
|
212
|
-
ensure
|
213
|
-
LibHTS.bcf_itr_destroy(qitr)
|
214
|
-
end
|
215
|
-
self
|
216
|
-
end
|
217
|
-
|
218
151
|
# @!macro [attach] define_getter
|
219
152
|
# @method $1
|
220
153
|
# Get $1 array
|
@@ -285,5 +218,81 @@ module HTS
|
|
285
218
|
yield r.format(key)
|
286
219
|
end
|
287
220
|
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def querys_reuse(region)
|
225
|
+
check_closed
|
226
|
+
|
227
|
+
raise "query is only available for BCF files" unless file_format == "bcf"
|
228
|
+
raise "Index file is required to call the query method." unless index_loaded?
|
229
|
+
return to_enum(__method__, region) unless block_given?
|
230
|
+
|
231
|
+
qiter = LibHTS.bcf_itr_querys(@idx, header, region)
|
232
|
+
raise "Failed to query region #{region}" if qiter.null?
|
233
|
+
|
234
|
+
bcf1 = LibHTS.bcf_init
|
235
|
+
record = Record.new(bcf1, header)
|
236
|
+
begin
|
237
|
+
loop do
|
238
|
+
slen = LibHTS.hts_itr_next(@hts_file[:fp][:bgzf], qiter, bcf1, ::FFI::Pointer::NULL)
|
239
|
+
break if slen == -1
|
240
|
+
raise if slen < -1
|
241
|
+
|
242
|
+
yield record
|
243
|
+
end
|
244
|
+
ensure
|
245
|
+
LibHTS.bcf_itr_destroy(qiter)
|
246
|
+
end
|
247
|
+
self
|
248
|
+
end
|
249
|
+
|
250
|
+
def querys_copy(region)
|
251
|
+
check_closed
|
252
|
+
|
253
|
+
raise "query is only available for BCF files" unless file_format == "bcf"
|
254
|
+
raise "Index file is required to call the query method." unless index_loaded?
|
255
|
+
return to_enum(__method__, region) unless block_given?
|
256
|
+
|
257
|
+
qiter = LibHTS.bcf_itr_querys(@idx, header, region)
|
258
|
+
raise "Failed to query region #{region}" if qiter.null?
|
259
|
+
|
260
|
+
begin
|
261
|
+
loop do
|
262
|
+
bcf1 = LibHTS.bcf_init
|
263
|
+
slen = LibHTS.hts_itr_next(@hts_file[:fp][:bgzf], qiter, bcf1, ::FFI::Pointer::NULL)
|
264
|
+
break if slen == -1
|
265
|
+
raise if slen < -1
|
266
|
+
|
267
|
+
yield Record.new(bcf1, header)
|
268
|
+
end
|
269
|
+
ensure
|
270
|
+
LibHTS.bcf_itr_destroy(qiter)
|
271
|
+
end
|
272
|
+
self
|
273
|
+
end
|
274
|
+
|
275
|
+
def each_record_reuse
|
276
|
+
check_closed
|
277
|
+
|
278
|
+
return to_enum(__method__) unless block_given?
|
279
|
+
|
280
|
+
bcf1 = LibHTS.bcf_init
|
281
|
+
record = Record.new(bcf1, header)
|
282
|
+
yield record while LibHTS.bcf_read(@hts_file, header, bcf1) != -1
|
283
|
+
self
|
284
|
+
end
|
285
|
+
|
286
|
+
def each_record_copy
|
287
|
+
check_closed
|
288
|
+
|
289
|
+
return to_enum(__method__) unless block_given?
|
290
|
+
|
291
|
+
while LibHTS.bcf_read(@hts_file, header, bcf1 = LibHTS.bcf_init) != -1
|
292
|
+
record = Record.new(bcf1, header)
|
293
|
+
yield record
|
294
|
+
end
|
295
|
+
self
|
296
|
+
end
|
288
297
|
end
|
289
298
|
end
|
data/lib/hts/hts.rb
CHANGED
data/lib/hts/libhts/constants.rb
CHANGED
@@ -188,6 +188,10 @@ module HTS
|
|
188
188
|
:n_mapped, :uint64,
|
189
189
|
:n_unmapped, :uint64
|
190
190
|
)
|
191
|
+
|
192
|
+
def self.release(ptr)
|
193
|
+
LibHTS.hts_idx_destroy(ptr) unless ptr.null?
|
194
|
+
end
|
191
195
|
end
|
192
196
|
|
193
197
|
class HtsReglist < FFI::Struct
|
@@ -213,6 +217,10 @@ module HTS
|
|
213
217
|
:sdict, :pointer,
|
214
218
|
:hrecs, :pointer,
|
215
219
|
:ref_count, :uint32
|
220
|
+
|
221
|
+
def self.release(ptr)
|
222
|
+
LibHTS.sam_hdr_destroy(ptr) unless ptr.null?
|
223
|
+
end
|
216
224
|
end
|
217
225
|
|
218
226
|
BamHdr = SamHdr
|
@@ -251,6 +259,10 @@ module HTS
|
|
251
259
|
layout \
|
252
260
|
:pool, :pointer,
|
253
261
|
:qsize, :int
|
262
|
+
|
263
|
+
def self.release(ptr)
|
264
|
+
LibHTS.hts_tpool_destroy(ptr) unless ptr.null?
|
265
|
+
end
|
254
266
|
end
|
255
267
|
|
256
268
|
class HtsOpt < FFI::Struct
|
@@ -300,6 +312,10 @@ module HTS
|
|
300
312
|
:nocoor, 1,
|
301
313
|
:multi, 1,
|
302
314
|
:dummy, 27
|
315
|
+
|
316
|
+
def self.release(ptr)
|
317
|
+
LibHTS.hts_itr_destroy(ptr) unless ptr.null?
|
318
|
+
end
|
303
319
|
end
|
304
320
|
|
305
321
|
class Bam1Core < FFI::Struct
|
@@ -380,6 +396,10 @@ module HTS
|
|
380
396
|
:conf, TbxConf.ptr,
|
381
397
|
:idx, HtsIdx.ptr,
|
382
398
|
:dict, :pointer
|
399
|
+
|
400
|
+
def self.release(ptr)
|
401
|
+
LibHTS.tbx_destroy(ptr) unless ptr.null?
|
402
|
+
end
|
383
403
|
end
|
384
404
|
|
385
405
|
# faidx
|
@@ -393,6 +413,10 @@ module HTS
|
|
393
413
|
:name, :pointer,
|
394
414
|
:hash, :pointer,
|
395
415
|
:format, FaiFormatOptions
|
416
|
+
|
417
|
+
def self.release(ptr)
|
418
|
+
LibHTS.fai_destroy(ptr) unless ptr.null?
|
419
|
+
end
|
396
420
|
end
|
397
421
|
|
398
422
|
# bcf
|
@@ -412,6 +436,10 @@ module HTS
|
|
412
436
|
:nkeys, :int,
|
413
437
|
:keys, :pointer,
|
414
438
|
:vals, :pointer
|
439
|
+
|
440
|
+
def self.release(ptr)
|
441
|
+
LibHTS.bcf_hrec_destroy(ptr) unless ptr.null?
|
442
|
+
end
|
415
443
|
end
|
416
444
|
|
417
445
|
class BcfInfo < FFI::BitStruct
|
@@ -461,6 +489,10 @@ module HTS
|
|
461
489
|
:keep_samples, :pointer,
|
462
490
|
:mem, KString,
|
463
491
|
:m, [:int, 3]
|
492
|
+
|
493
|
+
def self.release(ptr)
|
494
|
+
LibHTS.bcf_hdr_destroy(ptr) unless ptr.null?
|
495
|
+
end
|
464
496
|
end
|
465
497
|
|
466
498
|
class BcfFmt < FFI::BitStruct
|
data/lib/hts/libhts/tbx_funcs.rb
CHANGED
data/lib/hts/libhts/vcf_funcs.rb
CHANGED
@@ -294,15 +294,23 @@ module HTS
|
|
294
294
|
end
|
295
295
|
|
296
296
|
# Typed value I/O
|
297
|
-
|
298
|
-
def
|
299
|
-
|
300
|
-
def
|
297
|
+
# INT8_MIN + 1
|
298
|
+
def bcf_int8_vector_end = -127
|
299
|
+
# INT16_MIN + 1
|
300
|
+
def bcf_int16_vector_end = -32_767
|
301
|
+
# INT32_MIN + 1
|
302
|
+
def bcf_int32_vector_end = -2_147_483_647
|
303
|
+
# INT64_MIN + 1
|
304
|
+
def bcf_int64_vector_end = -9_223_372_036_854_775_807
|
301
305
|
def bcf_str_vector_end = 0
|
302
|
-
|
303
|
-
def
|
304
|
-
|
305
|
-
def
|
306
|
+
# INT8_MIN
|
307
|
+
def bcf_int8_missing = -128
|
308
|
+
# INT16_MIN
|
309
|
+
def bcf_int16_missing = (-32_767 - 1)
|
310
|
+
# INT32_MIN
|
311
|
+
def bcf_int32_missing = (-2_147_483_647 - 1)
|
312
|
+
# INT64_MIN
|
313
|
+
def bcf_int64_missing = (-9_223_372_036_854_775_807 - 1)
|
306
314
|
def bcf_str_missing = 0x07
|
307
315
|
|
308
316
|
BCF_MAX_BT_INT8 = 0x7f # INT8_MAX */
|
data/lib/hts/tbx.rb
CHANGED
@@ -45,8 +45,6 @@ module HTS
|
|
45
45
|
|
46
46
|
# build_index(index) if build_index
|
47
47
|
@idx = load_index(index)
|
48
|
-
|
49
|
-
super # do nothing
|
50
48
|
end
|
51
49
|
|
52
50
|
def build_index
|
@@ -68,9 +66,7 @@ module HTS
|
|
68
66
|
def seqnames
|
69
67
|
nseq = FFI::MemoryPointer.new(:int)
|
70
68
|
LibHTS.tbx_seqnames(@idx, nseq).then do |pts|
|
71
|
-
pts.read_array_of_pointer(nseq.read_int).map
|
72
|
-
pt.read_string
|
73
|
-
end
|
69
|
+
pts.read_array_of_pointer(nseq.read_int).map(&:read_string)
|
74
70
|
end
|
75
71
|
end
|
76
72
|
end
|
data/lib/hts/version.rb
CHANGED
data/lib/htslib.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: htslib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kojix2
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- lib/hts/bam/cigar.rb
|
137
137
|
- lib/hts/bam/flag.rb
|
138
138
|
- lib/hts/bam/header.rb
|
139
|
+
- lib/hts/bam/header_record.rb
|
139
140
|
- lib/hts/bam/record.rb
|
140
141
|
- lib/hts/bcf.rb
|
141
142
|
- lib/hts/bcf/format.rb
|