htslib 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c0c00377d4485fa702cc429c3005e98d1d095842d51fa6d27346fad9891c280
4
- data.tar.gz: 56e680d3f890ef1df37f2d99644c13bde250a130ca46e5d6b756ca16366fe81e
3
+ metadata.gz: 3eb6fbb228a9fa0642c55cdfa39d5189b311df191725954ae46b8dda135dc8c7
4
+ data.tar.gz: c90ad39aa4919cefa56e534a706bf9659bba9c6ad69eb374491092ce16ae93f6
5
5
  SHA512:
6
- metadata.gz: 1a38ecbb819edb2f81e5b76250c8ef070bf5d252925fecb859a4083462a7934feefd8ac50b2628f9f3912915982354bd61d424224302b45e9e2911321469bba6
7
- data.tar.gz: 01d5ac7826b5db6b6838986d05801b69c76320bbb4a89029cf21a6f01031c9bcc23c287526fbda02c8a2c0a32453e53ed6d689373754680c584f969214c58f6f
6
+ metadata.gz: 392ea7c6acb5b86e9e1c1a29f066049c54954edfd277be30541aee433a7349f9d74df09e663ec0269f30d6b87ff47d66a4a668470d3aa536e25a2058cf963546
7
+ data.tar.gz: 9cc235f8876b8fd4339593eb84a0f2cb8b605b9c82b624cd0f97691b4109a672f55ae90447bba17fb5efcb8257279ad8c2d87234bca390b1624c7aa8f9a7014e
data/README.md CHANGED
@@ -1,15 +1,26 @@
1
- # HTSlib
1
+ # ruby-htslib
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/htslib.svg)](https://badge.fury.io/rb/htslib)
4
4
  ![CI](https://github.com/kojix2/ruby-htslib/workflows/CI/badge.svg)
5
5
  [![The MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
6
6
  [![DOI](https://zenodo.org/badge/247078205.svg)](https://zenodo.org/badge/latestdoi/247078205)
7
+ [![Docs Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://rubydoc.info/gems/htslib)
7
8
 
8
- :dna: [HTSlib](https://github.com/samtools/htslib) - high-throughput sequencing data manipulation - for Ruby
9
+ :dna: [HTSlib](https://github.com/samtools/htslib) - for Ruby
10
+
11
+ Ruby-htslib is the Ruby bindings to HTSlib, a C library for processing high throughput sequencing (HTS) data.
12
+ It will provide APIs to read and write file formats such as SAM, BAM, VCF, and BCF.
9
13
 
10
14
  :apple: Feel free to fork it out if you can develop it!
11
15
 
12
- :bowtie: Just a prototype.
16
+ :bowtie: alpha stage.
17
+ ## Requirements
18
+
19
+ * [Ruby](https://github.com/ruby/ruby) 2.7 or above.
20
+ * [HTSlib](https://github.com/samtools/htslib)
21
+ * Ubuntu : `apt install libhts-dev`
22
+ * macOS : `brew install htslib`
23
+ * Build from source code (see Development section)
13
24
 
14
25
  ## Installation
15
26
 
@@ -17,30 +28,53 @@
17
28
  gem install htslib
18
29
  ```
19
30
 
20
- Set environment variable HTSLIBDIR.
31
+ If you have installed htslib with apt on Ubuntu or homebrew on Mac, [pkg-config](https://github.com/ruby-gnome/pkg-config)
32
+ will automatically detect the location of the shared library.
33
+ Alternatively, you can specify the directory of the shared library by setting the environment variable `HTSLIBDIR`.
21
34
 
22
35
  ```sh
23
- export HTSLIBDIR="/your/path/to/htslib"
36
+ export HTSLIBDIR="/your/path/to/htslib" # libhts.so
24
37
  ```
25
38
 
26
- ## Requirements
27
-
28
- * [htslib](https://github.com/samtools/htslib)
39
+ ## Overview
29
40
 
30
- ## Usage
41
+ ### Low level API
31
42
 
32
- HTS::FFI - Low-level API
43
+ `HTS::LibHTS` provides native functions.
33
44
 
34
45
  ```ruby
35
46
  require 'htslib'
36
47
 
37
- a = HTS::FFI.hts_open("a.bam", "r")
38
- b = HTS::FFI.hts_get_format(a)
48
+ a = HTS::LibHTS.hts_open("a.bam", "r")
49
+ b = HTS::LibHTS.hts_get_format(a)
39
50
  p b[:category]
40
51
  p b[:format]
41
52
  ```
42
53
 
43
- A high-level API based on [hts-python](https://github.com/quinlan-lab/hts-python) is under development.
54
+ Note: Managed struct is not used in ruby-htslib. You may need to free the memory by yourself.
55
+
56
+ ### High level API (Plan)
57
+
58
+ `Cram` `Bam` `Bcf` `Faidx` `Tabix`
59
+
60
+ A high-level API is under development. We will change and improve the API to make it better.
61
+
62
+ ```ruby
63
+ require 'htslib'
64
+
65
+ bam = HTS::Bam.new("a.bam")
66
+
67
+ bam.each do |r|
68
+ p name: r.qname,
69
+ flag: r.flag,
70
+ start: r.start + 1,
71
+ mpos: r.mate_pos + 1,
72
+ mqual: r.mapping_quality,
73
+ seq: r.sequence,
74
+ cigar: r.cigar.to_s,
75
+ qual: r.base_qualities.map { |i| (i + 33).chr }.join
76
+ end
77
+ ```
44
78
 
45
79
  ## Documentation
46
80
 
@@ -51,25 +85,42 @@ A high-level API based on [hts-python](https://github.com/quinlan-lab/hts-python
51
85
  To get started with development
52
86
 
53
87
  ```sh
54
- git clone --recurse-submodules https://github.com/kojix2/ruby-htslib
88
+ git clone --recursive https://github.com/kojix2/ruby-htslib
55
89
  cd ruby-htslib
56
90
  bundle install
57
- bundle exec rake htslib:compile
58
- bundle exec rake spec
91
+ bundle exec rake htslib:build
92
+ bundle exec rake test
59
93
  ```
60
94
 
95
+ We plan to actively use the new features of Ruby. Since the number of users is small, backward compatibility is not important.
96
+ On the other hand, we will consider compatibility with [Crystal](https://github.com/bio-crystal/htslib.cr) to some extent.
97
+
98
+ #### FFI Extensions
99
+
100
+ * [ffi-bitfield](https://github.com/kojix2/ffi-bitfield) : Extension of Ruby-FFI to support bitfields.
101
+
102
+ #### Automatic generation or automatic validation (Future plan)
103
+
104
+ + [c2ffi](https://github.com/rpav/c2ffi) is a tool to create JSON format metadata from C header files. It is planned to use c2ffi to automatically generate bindings or tests.
105
+
61
106
  ## Contributing
62
107
 
108
+ Ruby-htslib is a library under development, so even small improvements like typofix are welcome! Please feel free to send us your pull requests.
109
+
63
110
  * [Report bugs](https://github.com/kojix2/ruby-htslib/issues)
64
111
  * Fix bugs and [submit pull requests](https://github.com/kojix2/ruby-htslib/pulls)
65
112
  * Write, clarify, or fix documentation
66
113
  * Suggest or add new features
114
+ * [financial contributions](https://github.com/sponsors/kojix2)
67
115
 
68
116
  ## Links
69
117
 
70
118
  * [samtools/hts-spec](https://github.com/samtools/hts-specs)
71
- * [c2ffi](https://github.com/rpav/c2ffi)
119
+ * [bioruby](https://github.com/bioruby/bioruby)
120
+
121
+ ## Funding support
72
122
 
123
+ This work was supported partially by [Ruby Association Grant 2020](https://www.ruby.or.jp/en/news/20201022).
73
124
  ## License
74
125
 
75
126
  [MIT License](https://opensource.org/licenses/MIT).
data/lib/hts/bam/cigar.rb CHANGED
@@ -7,22 +7,25 @@ module HTS
7
7
  class Bam
8
8
  class Cigar
9
9
  include Enumerable
10
- OPS = 'MIDNSHP=XB'
11
10
 
12
- def initialize(cigar, n_cigar)
13
- @c = cigar
11
+ def initialize(pointer, n_cigar)
12
+ @pointer = pointer
14
13
  @n_cigar = n_cigar
15
14
  end
16
15
 
16
+ def to_ptr
17
+ @pointer
18
+ end
19
+
17
20
  def to_s
18
21
  to_a.flatten.join
19
22
  end
20
23
 
21
24
  def each
22
25
  @n_cigar.times do |i|
23
- c = @c[i].read_uint32
24
- yield [FFI.bam_cigar_oplen(c),
25
- FFI.bam_cigar_opchr(c)]
26
+ c = @pointer[i].read_uint32
27
+ yield [LibHTS.bam_cigar_oplen(c),
28
+ LibHTS.bam_cigar_opchr(c)]
26
29
  end
27
30
  end
28
31
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on hts-nim
4
+ # https://github.com/brentp/hts-nim/blob/master/src/hts/bam/flag.nim
5
+
6
+ module HTS
7
+ class Bam
8
+ class Flag
9
+ def initialize(flag_value)
10
+ raise TypeError unless flag_value.is_a? Integer
11
+
12
+ @value = flag_value
13
+ end
14
+
15
+ attr_accessor :value
16
+
17
+ # BAM_FPAIRED = 1
18
+ # BAM_FPROPER_PAIR = 2
19
+ # BAM_FUNMAP = 4
20
+ # BAM_FMUNMAP = 8
21
+ # BAM_FREVERSE = 16
22
+ # BAM_FMREVERSE = 32
23
+ # BAM_FREAD1 = 64
24
+ # BAM_FREAD2 = 128
25
+ # BAM_FSECONDARY = 256
26
+ # BAM_FQCFAIL = 512
27
+ # BAM_FDUP = 1024
28
+ # BAM_FSUPPLEMENTARY = 2048
29
+
30
+ # TODO: Enabling bitwise operations
31
+ # hts-nim
32
+ # proc `and`*(f: Flag, o: uint16): uint16 {. borrow, inline .}
33
+ # proc `and`*(f: Flag, o: Flag): uint16 {. borrow, inline .}
34
+ # proc `or`*(f: Flag, o: uint16): uint16 {. borrow .}
35
+ # proc `or`*(o: uint16, f: Flag): uint16 {. borrow .}
36
+ # proc `==`*(f: Flag, o: Flag): bool {. borrow, inline .}
37
+ # proc `==`*(f: Flag, o: uint16): bool {. borrow, inline .}
38
+ # proc `==`*(o: uint16, f: Flag): bool {. borrow, inline .}
39
+
40
+ def paired?
41
+ has_flag? LibHTS::BAM_FPAIRED
42
+ end
43
+
44
+ def proper_pair?
45
+ has_flag? LibHTS::BAM_FPROPER_PAIR
46
+ end
47
+
48
+ def unmapped?
49
+ has_flag? LibHTS::BAM_FUNMAP
50
+ end
51
+
52
+ def mate_unmapped?
53
+ has_flag? LibHTS::BAM_FMUNMAP
54
+ end
55
+
56
+ def reverse?
57
+ has_flag? LibHTS::BAM_FREVERSE
58
+ end
59
+
60
+ def mate_reverse?
61
+ has_flag? LibHTS::BAM_FMREVERSE
62
+ end
63
+
64
+ def read1?
65
+ has_flag? LibHTS::BAM_FREAD1
66
+ end
67
+
68
+ def read2?
69
+ has_flag? LibHTS::BAM_FREAD2
70
+ end
71
+
72
+ def secondary?
73
+ has_flag? LibHTS::BAM_FSECONDARY
74
+ end
75
+
76
+ def qcfail?
77
+ has_flag? LibHTS::BAM_FQCFAIL
78
+ end
79
+
80
+ def dup?
81
+ has_flag? LibHTS::BAM_FDUP
82
+ end
83
+
84
+ def supplementary?
85
+ has_flag? LibHTS::BAM_FSUPPLEMENTARY
86
+ end
87
+
88
+ def has_flag?(o)
89
+ @value[o] != 0
90
+ end
91
+ end
92
+ end
93
+ end
@@ -6,21 +6,27 @@
6
6
  module HTS
7
7
  class Bam
8
8
  class Header
9
- attr_reader :h
9
+ def initialize(pointer)
10
+ @sam_hdr = pointer
11
+ end
12
+
13
+ def struct
14
+ @sam_hdr
15
+ end
10
16
 
11
- def initialize(h)
12
- @h = h
17
+ def to_ptr
18
+ @sam_hdr.to_ptr
13
19
  end
14
20
 
15
21
  # FIXME: better name?
16
22
  def seqs
17
- Array.new(@h[:n_targets]) do |i|
18
- FFI.sam_hdr_tid2name(@h, i)
23
+ Array.new(@sam_hdr[:n_targets]) do |i|
24
+ LibHTS.sam_hdr_tid2name(@sam_hdr, i)
19
25
  end
20
26
  end
21
27
 
22
28
  def text
23
- FFI.sam_hdr_str(@h)
29
+ LibHTS.sam_hdr_str(@sam_hdr)
24
30
  end
25
31
  end
26
32
  end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on hts-python
4
+ # https://github.com/quinlan-lab/hts-python
5
+
6
+ module HTS
7
+ class Bam
8
+ class Record
9
+ SEQ_NT16_STR = "=ACMGRSVTWYHKDBN"
10
+
11
+ def initialize(bam1_t, header)
12
+ @bam1 = bam1_t
13
+ @header = header
14
+ end
15
+
16
+ def struct
17
+ @bam1
18
+ end
19
+
20
+ def to_ptr
21
+ @bam1.to_ptr
22
+ end
23
+
24
+ attr_reader :header
25
+
26
+ # def initialize_copy
27
+ # super
28
+ # end
29
+
30
+ def self.rom_sam_str; end
31
+
32
+ def tags; end
33
+
34
+ # returns the query name.
35
+ def qname
36
+ LibHTS.bam_get_qname(@bam1).read_string
37
+ end
38
+
39
+ # Set (query) name.
40
+ # def qname=(name)
41
+ # raise 'Not Implemented'
42
+ # end
43
+
44
+ # returns the tid of the record or -1 if not mapped.
45
+ def tid
46
+ @bam1[:core][:tid]
47
+ end
48
+
49
+ # returns the tid of the mate or -1 if not mapped.
50
+ def mate_tid
51
+ @bam1[:core][:mtid]
52
+ end
53
+
54
+ # returns 0-based start position.
55
+ def start
56
+ @bam1[:core][:pos]
57
+ end
58
+
59
+ # returns end position of the read.
60
+ def stop
61
+ LibHTS.bam_endpos @bam1
62
+ end
63
+
64
+ # returns 0-based mate position
65
+ def mate_start
66
+ @bam1[:core][:mpos]
67
+ end
68
+ alias mate_pos mate_start
69
+
70
+ # returns the chromosome or '' if not mapped.
71
+ def chrom
72
+ tid = @bam1[:core][:tid]
73
+ return "" if tid == -1
74
+
75
+ LibHTS.sam_hdr_tid2name(@header, tid)
76
+ end
77
+
78
+ # returns the chromosome of the mate or '' if not mapped.
79
+ def mate_chrom
80
+ tid = @bam1[:core][:mtid]
81
+ return "" if tid == -1
82
+
83
+ LibHTS.sam_hdr_tid2name(@header, tid)
84
+ end
85
+
86
+ def strand
87
+ LibHTS.bam_is_rev(@bam1) ? "-" : "+"
88
+ end
89
+
90
+ # def start=(v)
91
+ # raise 'Not Implemented'
92
+ # end
93
+
94
+ # insert size
95
+ def isize
96
+ @bam1[:core][:isize]
97
+ end
98
+
99
+ # mapping quality
100
+ def mapping_quality
101
+ @bam1[:core][:qual]
102
+ end
103
+
104
+ # returns a `Cigar` object.
105
+ def cigar
106
+ Cigar.new(LibHTS.bam_get_cigar(@bam1), @bam1[:core][:n_cigar])
107
+ end
108
+
109
+ def qlen
110
+ LibHTS.bam_cigar2qlen(
111
+ @bam1[:core][:n_cigar],
112
+ LibHTS.bam_get_cigar(@bam1)
113
+ )
114
+ end
115
+
116
+ def rlen
117
+ LibHTS.bam_cigar2rlen(
118
+ @bam1[:core][:n_cigar],
119
+ LibHTS.bam_get_cigar(@bam1)
120
+ )
121
+ end
122
+
123
+ # return the read sequence
124
+ def sequence
125
+ r = LibHTS.bam_get_seq(@bam1)
126
+ seq = String.new
127
+ (@bam1[:core][:l_qseq]).times do |i|
128
+ seq << SEQ_NT16_STR[LibHTS.bam_seqi(r, i)]
129
+ end
130
+ seq
131
+ end
132
+
133
+ # return only the base of the requested index "i" of the query sequence.
134
+ def base_at(n)
135
+ n += @bam1[:core][:l_qseq] if n < 0
136
+ return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base_at(-1000)
137
+
138
+ r = LibHTS.bam_get_seq(@bam1)
139
+ SEQ_NT16_STR[LibHTS.bam_seqi(r, n)]
140
+ end
141
+
142
+ # return the base qualities
143
+ def base_qualities
144
+ q_ptr = LibHTS.bam_get_qual(@bam1)
145
+ q_ptr.read_array_of_uint8(@bam1[:core][:l_qseq])
146
+ end
147
+
148
+ # return only the base quality of the requested index "i" of the query sequence.
149
+ def base_quality_at(n)
150
+ n += @bam1[:core][:l_qseq] if n < 0
151
+ return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base_quality_at(-1000)
152
+
153
+ q_ptr = LibHTS.bam_get_qual(@bam1)
154
+ q_ptr.get_uint8(n)
155
+ end
156
+
157
+ def flag_str
158
+ LibHTS.bam_flag2str(@bam1[:core][:flag])
159
+ end
160
+
161
+ # returns a `Flag` object.
162
+ def flag
163
+ Flag.new(@bam1[:core][:flag])
164
+ end
165
+
166
+ def tag(str)
167
+ aux = LibHTS.bam_aux_get(@bam1, str)
168
+ return nil if aux.null?
169
+
170
+ t = aux.read_string(1)
171
+ case t
172
+ when "i", "I", "c", "C", "s", "S"
173
+ LibHTS.bam_aux2i(aux)
174
+ when "f", "d"
175
+ LibHTS.bam_aux2f(aux)
176
+ when "Z", "H"
177
+ LibHTS.bam_aux2Z(aux)
178
+ when "A"
179
+ LibHTS.bam_aux2A(aux)
180
+ end
181
+ end
182
+
183
+ def to_s
184
+ kstr = LibHTS::KString.new
185
+ raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
186
+
187
+ kstr[:s]
188
+ end
189
+
190
+ # TODO:
191
+ # def eql?
192
+ # def hash
193
+ end
194
+ end
195
+ end
data/lib/hts/bam.rb CHANGED
@@ -3,76 +3,111 @@
3
3
  # Based on hts-python
4
4
  # https://github.com/quinlan-lab/hts-python
5
5
 
6
- require_relative 'bam/header'
7
- require_relative 'bam/cigar'
8
- require_relative 'bam/alignment'
6
+ require_relative "bam/header"
7
+ require_relative "bam/cigar"
8
+ require_relative "bam/flag"
9
+ require_relative "bam/record"
9
10
 
10
11
  module HTS
11
12
  class Bam
12
13
  include Enumerable
13
- attr_reader :fname, :mode, :header, :htf
14
14
 
15
- def initialize(fname, mode = 'r', create_index: nil, header: nil, fasta: nil)
16
- @fname = File.expand_path(fname)
17
- File.exist?(@fname) || raise("No such SAM/BAM file - #{@fname}")
15
+ attr_reader :file_path, :mode, :header
16
+ # HtfFile is FFI::BitStruct
17
+ attr_reader :htf_file
18
18
 
19
- @mode = mode
20
- @htf = FFI.hts_open(@fname, mode)
19
+ class << self
20
+ alias open new
21
+ end
21
22
 
22
- if mode[0] == 'r'
23
- @idx = FFI.sam_index_load(@htf, @fname)
24
- if (@idx.null? && create_index.nil?) || create_index
25
- FFI.sam_index_build(fname, -1)
26
- @idx = FFI.sam_index_load(@htf, @fname)
27
- warn 'NO querying'
28
- end
29
- @header = Bam::Header.new(FFI.sam_hdr_read(@htf))
30
- @b = FFI.bam_init1
23
+ def initialize(file_path, mode = "r", create_index: nil)
24
+ file_path = File.expand_path(file_path)
25
+
26
+ unless File.exist?(file_path)
27
+ message = "No such SAM/BAM file - #{file_path}"
28
+ raise message
29
+ end
31
30
 
31
+ @file_path = file_path
32
+ @mode = mode
33
+ @htf_file = LibHTS.hts_open(file_path, mode)
34
+ @header = Bam::Header.new(LibHTS.sam_hdr_read(htf_file))
35
+
36
+ # read
37
+ if mode[0] == "r"
38
+ # load index
39
+ @idx = LibHTS.sam_index_load(htf_file, file_path)
40
+ # create index
41
+ if create_index || (@idx.null? && create_index.nil?)
42
+ warn "Create index for #{file_path}"
43
+ LibHTS.sam_index_build(file_path, -1)
44
+ @idx = LibHTS.sam_index_load(htf_file, file_path)
45
+ end
32
46
  else
33
- # FIXME
34
- raise 'not implemented yet.'
47
+ # FIXME: implement
48
+ raise "not implemented yet."
49
+ end
35
50
 
51
+ # IO like API
52
+ if block_given?
53
+ begin
54
+ yield self
55
+ ensure
56
+ close
57
+ end
36
58
  end
37
59
  end
38
60
 
39
- def self.header_from_fasta; end
61
+ def struct
62
+ htf_file
63
+ end
64
+
65
+ def to_ptr
66
+ htf_file.to_ptr
67
+ end
40
68
 
41
69
  def write(alns)
42
70
  alns.each do
43
- FFI.sam_write1(@htf, @header, alns.b) > 0 || raise
71
+ LibHTS.sam_write1(htf_file, header, alns.b) > 0 || raise
44
72
  end
45
73
  end
46
74
 
47
75
  # Close the current file.
48
76
  def close
49
- FFI.hts_close(@htf)
77
+ LibHTS.hts_close(htf_file)
50
78
  end
51
79
 
52
80
  # Flush the current file.
53
81
  def flush
54
- raise
55
- # FFI.bgzf_flush(@htf.fp.bgzf)
82
+ # LibHTS.bgzf_flush(@htf_file.fp.bgzf)
56
83
  end
57
84
 
58
- def each(&block)
85
+ def each
59
86
  # Each does not always start at the beginning of the file.
60
87
  # This is the common behavior of IO objects in Ruby.
61
88
  # This may change in the future.
62
- block.call(Alignment.new(@b, @header.h)) while FFI.sam_read1(@htf, @header.h, @b) > 0
89
+ return to_enum(__method__) unless block_given?
90
+
91
+ while LibHTS.sam_read1(htf_file, header, bam1 = LibHTS.bam_init1) > 0
92
+ record = Record.new(bam1, header)
93
+ yield record
94
+ end
95
+ self
63
96
  end
64
97
 
65
98
  # query [WIP]
66
99
  def query(region)
67
- qiter = FFI.sam_itr_querys(@idx, @header.h, region)
100
+ qiter = LibHTS.sam_itr_querys(@idx, header, region)
68
101
  begin
69
- slen = FFI.sam_itr_next(@htf, qiter, @b)
102
+ bam1 = LibHTS.bam_init1
103
+ slen = LibHTS.sam_itr_next(htf_file, qiter, bam1)
70
104
  while slen > 0
71
- yield Alignment.new(@b, @header.h)
72
- slen = FFI.sam_itr_next(@htf, qiter, @b)
105
+ yield Record.new(bam1, header)
106
+ bam1 = LibHTS.bam_init1
107
+ slen = LibHTS.sam_itr_next(htf_file, qiter, bam1)
73
108
  end
74
109
  ensure
75
- FFI.hts_itr_destroy(qiter)
110
+ LibHTS.hts_itr_destroy(qiter)
76
111
  end
77
112
  end
78
113
  end