htslib 0.0.6 → 0.0.8

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: f05bc93a621f3d5fb8d06e44c20ac4a3a95c5f6e243df2aebc97e10125fcb779
4
- data.tar.gz: 73e747aa0999e54b3c8f01b981b91c4e293167265276a24eac85ea0fd6b85c13
3
+ metadata.gz: dd81ebe87741d1019acd341189ee0f8fb6ef09d3a10690686b3d40bdfea73076
4
+ data.tar.gz: 39a7fa296cc1797c7cd6317923b3336c67f0cf16e8c9f7eb8ae8dbef9e6c7f56
5
5
  SHA512:
6
- metadata.gz: 8d0f3f8bb9f7a9c063222fd2dc6463f58bdfd784aff39757921dda1c8d417023b024ca9f6c92d7f0eecd30b8064adb1b94966cf962cf9e0e56d67303de0a903b
7
- data.tar.gz: 8ef35b7aef04bf2f51ae0ba110d9ffc81c26d7a0c97a821aa1274c04b31ec29515b33c278391494f6a0d8efc5e2d965060ccd190ee0f92a2ec3562fb59c49169
6
+ metadata.gz: c349f647e798efc920492877807db88d03dfe88edf1434e733d38221ab15f1bea179ac524b3e26feb83c7063c878138918be3dcb30bcbbc583e0cc5bbbe1b8df
7
+ data.tar.gz: 120052721db361d285968331d2091f4bfe17b45a4204443a591141bc12aef3b153866077dc85ddff63451873800f78db377a151ecbec7084b1f5f409fe5507b0
data/README.md CHANGED
@@ -39,26 +39,12 @@ export HTSLIBDIR="/your/path/to/htslib" # libhts.so
39
39
 
40
40
  ## Overview
41
41
 
42
- ### Low level API
43
-
44
- `HTS::LibHTS` provides native functions.
45
-
46
- ```ruby
47
- require 'htslib'
48
-
49
- a = HTS::LibHTS.hts_open("a.bam", "r")
50
- b = HTS::LibHTS.hts_get_format(a)
51
- p b[:category]
52
- p b[:format]
53
- ```
42
+ ### High level API
54
43
 
55
- Note: Managed struct is not used in ruby-htslib. You may need to free the memory by yourself.
44
+ A high-level API is under development.
45
+ Classes such as `Cram` `Bam` `Bcf` `Faidx` `Tabix` are partially implemented.
56
46
 
57
- ### High level API (Plan)
58
-
59
- `Cram` `Bam` `Bcf` `Faidx` `Tabix`
60
-
61
- A high-level API is under development. We will change and improve the API to make it better.
47
+ Read SAM / BAM - Sequence Alignment Map file
62
48
 
63
49
  ```ruby
64
50
  require 'htslib'
@@ -68,15 +54,56 @@ bam = HTS::Bam.new("a.bam")
68
54
  bam.each do |r|
69
55
  p name: r.qname,
70
56
  flag: r.flag,
71
- start: r.start + 1,
72
- mpos: r.mate_pos + 1,
57
+ pos: r.start + 1,
58
+ mpos: r.mate_start + 1,
73
59
  mqual: r.mapping_quality,
74
60
  seq: r.sequence,
75
61
  cigar: r.cigar.to_s,
76
62
  qual: r.base_qualities.map { |i| (i + 33).chr }.join
77
63
  end
64
+
65
+ bam.close
78
66
  ```
79
67
 
68
+ Read VCF / BCF - Variant Call Format
69
+
70
+ ```ruby
71
+ bcf = HTS::Bcf.new("b.bcf")
72
+
73
+ bcf.each do |r|
74
+ p chrom: r.chrom,
75
+ pos: r.pos,
76
+ id: r.id,
77
+ qual: r.qual.round(2),
78
+ ref: r.ref,
79
+ alt: r.alt,
80
+ filter: r.filter
81
+ end
82
+
83
+ bcf.close
84
+ ```
85
+
86
+ The methods for reading are implemented first. Methods for writing will be implemented in the coming days.
87
+
88
+ ### Low level API
89
+
90
+ `HTS::LibHTS` provides native functions.
91
+
92
+ ```ruby
93
+ require 'htslib'
94
+
95
+ a = HTS::LibHTS.hts_open("a.bam", "r")
96
+ b = HTS::LibHTS.hts_get_format(a)
97
+ p b[:category]
98
+ p b[:format]
99
+ ```
100
+
101
+ Note: Only some 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.
102
+
103
+ ### Need more speed?
104
+
105
+ Try [htslib.cr](https://github.com/bio-crystal/htslib.cr). htslib.cr is implemented in Crystal language and provides an API compatible with ruby-htslib. crsytal language is not as flexible as Ruby language. If you have a clear idea of the manipulation you want to do and need to perform it many times, then by all means try to implement a command line tool using htslib.cr. The Crystal language is very fast and can perform almost as well as the Rust and C languages.
106
+
80
107
  ## Documentation
81
108
 
82
109
  * [RubyDoc.info - HTSlib](https://rdoc.info/gems/htslib)
@@ -93,6 +120,8 @@ bundle exec rake htslib:build
93
120
  bundle exec rake test
94
121
  ```
95
122
 
123
+ Many macro functions are used in HTSlib. Since these macro functions cannot be called using FFI, they must be reimplemented in Ruby.
124
+
96
125
  * Actively use the advanced features of Ruby.
97
126
  * Consider compatibility with [htslib.cr](https://github.com/bio-crystal/htslib.cr) to some extent.
98
127
 
@@ -102,6 +131,7 @@ bundle exec rake test
102
131
 
103
132
  #### Automatic generation or automatic validation (Future plan)
104
133
 
134
+
105
135
  + [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.
106
136
 
107
137
  ## Contributing
@@ -114,6 +144,16 @@ Ruby-htslib is a library under development, so even small improvements like typo
114
144
  * Suggest or add new features
115
145
  * [financial contributions](https://github.com/sponsors/kojix2)
116
146
 
147
+ ```
148
+ Do you need commit rights to my repository?
149
+ Do you want to get admin rights and take over the project?
150
+ If so, please feel free to contact us @kojix2.
151
+ ```
152
+
153
+ #### Why do you implement htslib in a language like Ruby, which is not widely used in the bioinformatics?
154
+
155
+ One of the greatest joys of using a minor language like Ruby in bioinformatics is that there is nothing stopping 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 left for beginners to create htslib bindings. Bioinformatics file formats, libraries and tools are very complex and I don't know how to understand them. So I wanted to implement the HTSLib binding to better understand how to use the file formats and tools. And that effort is still going on today...
156
+
117
157
  ## Links
118
158
 
119
159
  * [samtools/hts-spec](https://github.com/samtools/hts-specs)
@@ -6,8 +6,8 @@
6
6
  module HTS
7
7
  class Bam
8
8
  class Header
9
- def initialize(pointer)
10
- @sam_hdr = pointer
9
+ def initialize(hts_file)
10
+ @sam_hdr = LibHTS.sam_hdr_read(hts_file)
11
11
  end
12
12
 
13
13
  def struct
@@ -22,16 +22,27 @@ module HTS
22
22
  @sam_hdr[:n_targets]
23
23
  end
24
24
 
25
- # FIXME: better name?
26
- def seqs
27
- Array.new(@sam_hdr[:n_targets]) do |i|
25
+ def target_names
26
+ Array.new(target_count) do |i|
28
27
  LibHTS.sam_hdr_tid2name(@sam_hdr, i)
29
28
  end
30
29
  end
31
30
 
31
+ def target_lengths
32
+ Array.new(target_count) do |i|
33
+ LibHTS.sam_hdr_tid2len(@sam_hdr, i)
34
+ end
35
+ end
36
+
32
37
  def to_s
33
38
  LibHTS.sam_hdr_str(@sam_hdr)
34
39
  end
40
+
41
+ private
42
+
43
+ def initialize_copy(orig)
44
+ @sam_hdr = LibHTS.sam_hdr_dup(orig.struct)
45
+ end
35
46
  end
36
47
  end
37
48
  end
@@ -74,6 +74,11 @@ module HTS
74
74
  LibHTS.sam_hdr_tid2name(@header, tid)
75
75
  end
76
76
 
77
+ # returns the chromosome or '' if not mapped.
78
+ def contig
79
+ chrom
80
+ end
81
+
77
82
  # returns the chromosome of the mate or '' if not mapped.
78
83
  def mate_chrom
79
84
  mtid = mate_tid
@@ -91,7 +96,7 @@ module HTS
91
96
  # end
92
97
 
93
98
  # insert size
94
- def isize
99
+ def insert_size
95
100
  @bam1[:core][:isize]
96
101
  end
97
102
 
@@ -191,9 +196,12 @@ module HTS
191
196
  kstr[:s]
192
197
  end
193
198
 
194
- # TODO:
195
- # def eql?
196
- # def hash
199
+ private
200
+
201
+ def initialize_copy(orig)
202
+ @header = orig.header
203
+ @bam = LibHTS.bam_dup1(orig.struct)
204
+ end
197
205
  end
198
206
  end
199
207
  end
data/lib/hts/bam.rb CHANGED
@@ -13,98 +13,133 @@ module HTS
13
13
  include Enumerable
14
14
 
15
15
  attr_reader :file_path, :mode, :header
16
- # HtfFile is FFI::BitStruct
17
- attr_reader :htf_file
18
16
 
19
- class << self
20
- alias open new
17
+ def self.open(...)
18
+ file = new(...)
19
+ return file unless block_given?
20
+
21
+ begin
22
+ yield file
23
+ ensure
24
+ file.close
25
+ end
26
+ file
21
27
  end
22
28
 
23
- def initialize(file_path, mode = "r", create_index: nil)
24
- file_path = File.expand_path(file_path)
29
+ def initialize(filename, mode = "r", fai: nil, threads: nil, index: nil)
30
+ raise "HTS::Bam.new() dose not take block; Please use HTS::Bam.open() instead" if block_given?
25
31
 
26
- unless File.exist?(file_path)
32
+ @file_path = filename == "-" ? "-" : File.expand_path(filename)
33
+
34
+ if mode[0] == "r" && !File.exist?(file_path)
27
35
  message = "No such SAM/BAM file - #{file_path}"
28
36
  raise message
29
37
  end
30
38
 
31
- @file_path = file_path
32
39
  @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
46
- else
47
- # FIXME: implement
48
- raise "not implemented yet."
40
+ @hts_file = LibHTS.hts_open(file_path, mode)
41
+
42
+ if fai
43
+ fai_path = File.expand_path(fai)
44
+ r = LibHTS.hts_set_fai_filename(@hts_file, fai_path)
45
+ raise "Failed to load fasta index: #{fai}" if r < 0
49
46
  end
50
47
 
51
- # IO like API
52
- if block_given?
53
- begin
54
- yield self
55
- ensure
56
- close
57
- end
48
+ if threads&.> 0
49
+ r = LibHTS.hts_set_threads(@hts_file, threads)
50
+ raise "Failed to set number of threads: #{threads}" if r < 0
58
51
  end
52
+
53
+ return if mode[0] == "w"
54
+
55
+ @header = Bam::Header.new(@hts_file)
56
+
57
+ create_index if index
58
+
59
+ # load index
60
+ @idx = LibHTS.sam_index_load(@hts_file, file_path)
59
61
  end
60
62
 
61
- def struct
62
- htf_file
63
+ def create_index
64
+ warn "Create index for #{file_path}"
65
+ LibHTS.sam_index_build(file_path, -1)
66
+ idx = LibHTS.sam_index_load(@hts_file, file_path)
67
+ raise "Failed to load index: #{file_path}" if idx.null?
63
68
  end
64
69
 
65
- def to_ptr
66
- htf_file.to_ptr
70
+ def struct
71
+ @hts_file
67
72
  end
68
73
 
69
- def write(alns)
70
- alns.each do
71
- LibHTS.sam_write1(htf_file, header, alns.b) > 0 || raise
72
- end
74
+ def to_ptr
75
+ @hts_file.to_ptr
73
76
  end
74
77
 
75
78
  # Close the current file.
76
79
  def close
77
- LibHTS.hts_close(htf_file)
80
+ LibHTS.hts_idx_destroy(@idx) if @idx
81
+ @idx = nil
82
+ LibHTS.hts_close(@hts_file)
83
+ @hts_file = nil
84
+ end
85
+
86
+ def closed?
87
+ @hts_file.nil?
88
+ end
89
+
90
+ def write_header(header)
91
+ @header = header.dup
92
+ LibHTS.hts_set_fai_filename(@hts_file, @file_path)
93
+ LibHTS.sam_hdr_write(@hts_file, header)
94
+ end
95
+
96
+ def write(aln)
97
+ aln_dup = aln.dup
98
+ LibHTS.sam_write1(@hts_file, header, aln_dup) > 0 || raise
78
99
  end
79
100
 
80
101
  # Flush the current file.
81
102
  def flush
82
- # LibHTS.bgzf_flush(@htf_file.fp.bgzf)
103
+ # LibHTS.bgzf_flush(@@hts_file.fp.bgzf)
83
104
  end
84
105
 
106
+ # Iterate over each record.
107
+ # Record object is reused.
108
+ # Faster than each_copy.
85
109
  def each
86
110
  # Each does not always start at the beginning of the file.
87
111
  # This is the common behavior of IO objects in Ruby.
88
112
  # This may change in the future.
89
113
  return to_enum(__method__) unless block_given?
90
114
 
91
- while LibHTS.sam_read1(htf_file, header, bam1 = LibHTS.bam_init1) > 0
115
+ bam1 = LibHTS.bam_init1
116
+ record = Record.new(bam1, header)
117
+ yield record while LibHTS.sam_read1(@hts_file, header, bam1) > 0
118
+ end
119
+
120
+ # Iterate over each record.
121
+ # Generate a new Record object each time.
122
+ # Slower than each.
123
+ def each_copy
124
+ return to_enum(__method__) unless block_given?
125
+
126
+ while LibHTS.sam_read1(@hts_file, header, bam1 = LibHTS.bam_init1) > 0
92
127
  record = Record.new(bam1, header)
93
128
  yield record
94
129
  end
95
- self
96
130
  end
97
131
 
98
132
  # query [WIP]
99
133
  def query(region)
134
+ # FIXME: when @idx is nil
100
135
  qiter = LibHTS.sam_itr_querys(@idx, header, region)
101
136
  begin
102
137
  bam1 = LibHTS.bam_init1
103
- slen = LibHTS.sam_itr_next(htf_file, qiter, bam1)
138
+ slen = LibHTS.sam_itr_next(@hts_file, qiter, bam1)
104
139
  while slen > 0
105
140
  yield Record.new(bam1, header)
106
141
  bam1 = LibHTS.bam_init1
107
- slen = LibHTS.sam_itr_next(htf_file, qiter, bam1)
142
+ slen = LibHTS.sam_itr_next(@hts_file, qiter, bam1)
108
143
  end
109
144
  ensure
110
145
  LibHTS.hts_itr_destroy(qiter)
@@ -12,10 +12,30 @@ module HTS
12
12
  @p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
13
13
  end
14
14
 
15
+ # For compatibility with htslib.cr.
16
+ def get_int(key)
17
+ get(key, :int)
18
+ end
19
+
20
+ # For compatibility with htslib.cr.
21
+ def get_float(key)
22
+ get(key, :float)
23
+ end
24
+
25
+ # For compatibility with htslib.cr.
26
+ def get_flag(key)
27
+ get(key, :flag)
28
+ end
29
+
30
+ # For compatibility with htslib.cr.
31
+ def get_string(key)
32
+ get(key, :string)
33
+ end
34
+
15
35
  def get(key, type = nil)
16
36
  n = FFI::MemoryPointer.new(:int)
17
37
  p1 = @p1
18
- h = @record.bcf.header.struct
38
+ h = @record.header.struct
19
39
  r = @record.struct
20
40
 
21
41
  format_values = proc do |type|
@@ -33,12 +53,13 @@ module HTS
33
53
  format_values.call(LibHTS::BCF_HT_REAL)
34
54
  .read_array_of_float(n.read_int)
35
55
  when :flag
36
- format_values.call(LibHTS::BCF_HT_FLAG)
37
- .read_int == 1
56
+ raise NotImplementedError, "Flag type not implemented yet."
57
+ # format_values.call(LibHTS::BCF_HT_FLAG)
58
+ # .read_int == 1
38
59
  when :string, :str
39
60
  raise NotImplementedError, "String type not implemented yet."
40
- format_values.call(LibHTS::BCF_HT_STR)
41
- .read_string
61
+ # format_values.call(LibHTS::BCF_HT_STR)
62
+ # .read_string
42
63
  end
43
64
  end
44
65
 
@@ -3,8 +3,8 @@
3
3
  module HTS
4
4
  class Bcf
5
5
  class Header
6
- def initialize(bcf_hdr)
7
- @bcf_hdr = bcf_hdr
6
+ def initialize(hts_file)
7
+ @bcf_hdr = LibHTS.bcf_hdr_read(hts_file)
8
8
  end
9
9
 
10
10
  def struct
@@ -15,12 +15,33 @@ module HTS
15
15
  @bcf_hdr.to_ptr
16
16
  end
17
17
 
18
+ def get_version
19
+ LibHTS.bcf_hdr_get_version(@bcf_hdr)
20
+ end
21
+
22
+ def sample_count
23
+ LibHTS.bcf_hdr_nsamples(@bcf_hdr)
24
+ end
25
+
26
+ def sample_names
27
+ # bcf_hdr_id2name is macro function
28
+ @bcf_hdr[:samples]
29
+ .read_array_of_pointer(sample_count)
30
+ .map(&:read_string)
31
+ end
32
+
18
33
  def to_s
19
34
  kstr = LibHTS::KString.new
20
35
  raise "Failed to get header string" unless LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr)
21
36
 
22
37
  kstr[:s]
23
38
  end
39
+
40
+ private
41
+
42
+ def initialize_copy(orig)
43
+ @bcf_hdr = LibHTS.bcf_hdr_dup(orig.struct)
44
+ end
24
45
  end
25
46
  end
26
47
  end
data/lib/hts/bcf/info.rb CHANGED
@@ -5,13 +5,34 @@ module HTS
5
5
  class Info
6
6
  def initialize(record)
7
7
  @record = record
8
+ @p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
9
+ end
10
+
11
+ # For compatibility with htslib.cr.
12
+ def get_int(key)
13
+ get(key, :int)
14
+ end
15
+
16
+ # For compatibility with htslib.cr.
17
+ def get_float(key)
18
+ get(key, :float)
19
+ end
20
+
21
+ # For compatibility with htslib.cr.
22
+ def get_string(key)
23
+ get(key, :string)
24
+ end
25
+
26
+ # For compatibility with htslib.cr.
27
+ def get_flag(key)
28
+ get(key, :flag)
8
29
  end
9
30
 
10
31
  # @note Specify the type. If you don't specify a type, it will still work, but it will be slower.
11
32
  def get(key, type = nil)
12
33
  n = FFI::MemoryPointer.new(:int)
13
- p1 = @record.p1
14
- h = @record.bcf.header.struct
34
+ p1 = @p1
35
+ h = @record.header.struct
15
36
  r = @record.struct
16
37
 
17
38
  info_values = proc do |type|
@@ -54,10 +75,10 @@ module HTS
54
75
  )
55
76
  {
56
77
  name: LibHTS.bcf_hdr_int2id(
57
- @record.bcf.header.struct, LibHTS::BCF_DT_ID, fld[:key]
78
+ @record.header.struct, LibHTS::BCF_DT_ID, fld[:key]
58
79
  ),
59
80
  n: LibHTS.bcf_hdr_id2number(
60
- @record.bcf.header.struct, LibHTS::BCF_HL_INFO, fld[:key]
81
+ @record.header.struct, LibHTS::BCF_HL_INFO, fld[:key]
61
82
  ),
62
83
  vtype: fld[:type], i: fld[:key]
63
84
  }
@@ -73,7 +94,7 @@ module HTS
73
94
  i * LibHTS::BcfInfo.size
74
95
  )
75
96
  id = LibHTS.bcf_hdr_int2id(
76
- @record.bcf.header.struct, LibHTS::BCF_DT_ID, fld[:key]
97
+ @record.header.struct, LibHTS::BCF_DT_ID, fld[:key]
77
98
  )
78
99
  return fld[:type] if id == key
79
100
  end
@@ -3,13 +3,12 @@
3
3
  module HTS
4
4
  class Bcf
5
5
  class Record
6
- def initialize(bcf_t, bcf)
6
+ def initialize(bcf_t, header)
7
7
  @bcf1 = bcf_t
8
- @bcf = bcf
9
- @p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
8
+ @header = header
10
9
  end
11
10
 
12
- attr_reader :p1, :bcf
11
+ attr_reader :header
13
12
 
14
13
  def struct
15
14
  @bcf1
@@ -26,10 +25,9 @@ module HTS
26
25
  def genotypes; end
27
26
 
28
27
  def chrom
29
- hdr = @bcf.header.struct
30
28
  rid = @bcf1[:rid]
31
29
 
32
- LibHTS.bcf_hdr_id2name(hdr, rid)
30
+ LibHTS.bcf_hdr_id2name(@header.struct, rid)
33
31
  end
34
32
 
35
33
  def pos
@@ -59,10 +57,10 @@ module HTS
59
57
  "PASS"
60
58
  when 1
61
59
  i = d[:flt].read_int
62
- LibHTS.bcf_hdr_int2id(@bcf.header.struct, LibHTS::BCF_DT_ID, i)
60
+ LibHTS.bcf_hdr_int2id(@header.struct, LibHTS::BCF_DT_ID, i)
63
61
  when 2
64
62
  d[:flt].get_array_of_int(0, n_flt).map do |i|
65
- LibHTS.bcf_hdr_int2id(@bcf.header.struct, LibHTS::BCF_DT_ID, i)
63
+ LibHTS.bcf_hdr_int2id(@header.struct, LibHTS::BCF_DT_ID, i)
66
64
  end
67
65
  else
68
66
  raise "Unexpected number of filters. n_flt: #{n_flt}"
@@ -104,10 +102,16 @@ module HTS
104
102
 
105
103
  def to_s
106
104
  ksr = LibHTS::KString.new
107
- raise "Failed to format record" if LibHTS.vcf_format(@bcf.header.struct, @bcf1, ksr) == -1
105
+ raise "Failed to format record" if LibHTS.vcf_format(@header.struct, @bcf1, ksr) == -1
108
106
 
109
107
  ksr[:s]
110
108
  end
109
+
110
+ private
111
+
112
+ def initialize_copy
113
+ raise "Not implemented"
114
+ end
111
115
  end
112
116
  end
113
117
  end
data/lib/hts/bcf.rb CHANGED
@@ -13,61 +13,87 @@ module HTS
13
13
  include Enumerable
14
14
 
15
15
  attr_reader :file_path, :mode, :header
16
- # HtfFile is FFI::BitStruct
17
- attr_reader :htf_file
18
16
 
19
- class << self
20
- alias open new
17
+ def self.open(...)
18
+ file = new(...)
19
+ return file unless block_given?
20
+
21
+ begin
22
+ yield file
23
+ ensure
24
+ file.close
25
+ end
26
+ file
21
27
  end
22
28
 
23
- def initialize(file_path, mode = "r")
24
- file_path = File.expand_path(file_path)
29
+ def initialize(filename, mode = "r", threads: nil)
30
+ raise "HTS::Bcf.new() dose not take block; Please use HTS::Bcf.open() instead" if block_given?
31
+
32
+ @file_path = filename == "-" ? "-" : File.expand_path(filename)
25
33
 
26
- unless File.exist?(file_path)
34
+ if mode[0] == "r" && !File.exist?(file_path)
27
35
  message = "No such VCF/BCF file - #{file_path}"
28
36
  raise message
29
37
  end
30
38
 
31
- @file_path = file_path
32
39
  @mode = mode
33
- @htf_file = LibHTS.hts_open(file_path, mode)
34
- @header = Bcf::Header.new(LibHTS.bcf_hdr_read(htf_file))
35
-
36
- # IO like API
37
- if block_given?
38
- begin
39
- yield self
40
- ensure
41
- close
42
- end
40
+ @hts_file = LibHTS.hts_open(file_path, mode)
41
+
42
+ if threads&.> 0
43
+ r = LibHTS.hts_set_threads(@hts_file, threads)
44
+ raise "Failed to set number of threads: #{threads}" if r < 0
43
45
  end
46
+
47
+ return if mode[0] == "w"
48
+
49
+ @header = Bcf::Header.new(@hts_file)
44
50
  end
45
51
 
46
52
  def struct
47
- htf_file
53
+ @hts_file
48
54
  end
49
55
 
50
56
  def to_ptr
51
- htf_file.to_ptr
57
+ @hts_file.to_ptr
58
+ end
59
+
60
+ def write_header
61
+ @header = header.dup
62
+ LibHTS.hts_set_fai_filename(header, @file_path)
63
+ LibHTS.bcf_hdr_write(@hts_file, header.struct)
64
+ end
65
+
66
+ def write(var)
67
+ var_dup = var.dup = var.dup
68
+ LibHTS.bcf_write(@hts_file, header, var_dup) > 0 || raise
52
69
  end
53
70
 
54
71
  # Close the current file.
55
72
  def close
56
- LibHTS.hts_close(htf_file)
73
+ LibHTS.hts_close(@hts_file)
74
+ @hts_file = nil
75
+ end
76
+
77
+ def closed?
78
+ @hts_file.nil?
79
+ end
80
+
81
+ def sample_count
82
+ header.sample_count
83
+ end
84
+
85
+ def sample_names
86
+ header.sample_names
57
87
  end
58
88
 
59
89
  def each
60
90
  return to_enum(__method__) unless block_given?
61
91
 
62
- while LibHTS.bcf_read(htf_file, header, bcf1 = LibHTS.bcf_init) != -1
63
- record = Record.new(bcf1, self)
92
+ while LibHTS.bcf_read(@hts_file, header, bcf1 = LibHTS.bcf_init) != -1
93
+ record = Record.new(bcf1, header)
64
94
  yield record
65
95
  end
66
96
  self
67
97
  end
68
-
69
- def sample_count
70
- LibHTS.bcf_hdr_nsamples(header.struct)
71
- end
72
98
  end
73
99
  end
@@ -218,7 +218,7 @@ module HTS
218
218
 
219
219
  SamFile = HtsFile
220
220
 
221
- class HtsThreadPool < FFI::Struct
221
+ class HtsTpool < FFI::Struct
222
222
  layout \
223
223
  :pool, :pointer,
224
224
  :qsize, :int
@@ -145,7 +145,7 @@ module HTS
145
145
  # Create extra threads to aid compress/decompression for this file
146
146
  attach_function \
147
147
  :hts_set_thread_pool,
148
- [HtsFile, HtsThreadPool],
148
+ [HtsFile, HtsTpool],
149
149
  :int
150
150
 
151
151
  # Adds a cache of decompressed blocks, potentially speeding up seeks.
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTS
4
+ module LibHTS
5
+ attach_function \
6
+ :hts_tpool_init,
7
+ [:int],
8
+ HtsTpool.by_ref
9
+
10
+ attach_function \
11
+ :hts_tpool_size,
12
+ [HtsTpool],
13
+ :int
14
+
15
+ # FIXME: struct
16
+ HtsTpoolProcess = :pointer
17
+ HtsTpoolResult = :pointer
18
+
19
+ attach_function \
20
+ :hts_tpool_dispatch,
21
+ [HtsTpool, HtsTpoolProcess, :pointer, :pointer],
22
+ :int
23
+
24
+ attach_function \
25
+ :hts_tpool_dispatch2,
26
+ [HtsTpool, HtsTpoolProcess, :pointer, :pointer, :int],
27
+ :int
28
+
29
+ attach_function \
30
+ :hts_tpool_dispatch3,
31
+ [HtsTpool, HtsTpoolProcess, :pointer, :pointer, :pointer, :pointer, :int],
32
+ :int
33
+
34
+ attach_function \
35
+ :hts_tpool_wake_dispatch,
36
+ [HtsTpoolProcess],
37
+ :void
38
+
39
+ attach_function \
40
+ :hts_tpool_process_flush,
41
+ [HtsTpoolProcess],
42
+ :int
43
+
44
+ attach_function \
45
+ :hts_tpool_process_reset,
46
+ [HtsTpoolProcess, :int],
47
+ :int
48
+
49
+ attach_function \
50
+ :hts_tpool_process_qsize,
51
+ [HtsTpoolProcess],
52
+ :int
53
+
54
+ attach_function \
55
+ :hts_tpool_destroy,
56
+ [HtsTpool],
57
+ :void
58
+
59
+ attach_function \
60
+ :hts_tpool_kill,
61
+ [HtsTpool],
62
+ :void
63
+
64
+ attach_function \
65
+ :hts_tpool_next_result,
66
+ [HtsTpoolProcess],
67
+ HtsTpoolResult # .by_ref
68
+
69
+ attach_function \
70
+ :hts_tpool_next_result_wait,
71
+ [HtsTpoolProcess],
72
+ HtsTpoolResult # .by_ref
73
+
74
+ attach_function \
75
+ :hts_tpool_delete_result,
76
+ [HtsTpoolResult, :int],
77
+ :void
78
+
79
+ attach_function \
80
+ :hts_tpool_result_data,
81
+ [HtsTpoolResult],
82
+ :pointer
83
+
84
+ attach_function \
85
+ :hts_tpool_process_init,
86
+ [HtsTpool, :int, :int],
87
+ HtsTpoolProcess # .by_ref
88
+
89
+ attach_function \
90
+ :hts_tpool_process_destroy,
91
+ [HtsTpoolProcess],
92
+ :void
93
+
94
+ attach_function \
95
+ :hts_tpool_process_empty,
96
+ [HtsTpoolProcess],
97
+ :int
98
+
99
+ attach_function \
100
+ :hts_tpool_process_len,
101
+ [HtsTpoolProcess],
102
+ :int
103
+
104
+ attach_function \
105
+ :hts_tpool_process_sz,
106
+ [HtsTpoolProcess],
107
+ :int
108
+
109
+ attach_function \
110
+ :hts_tpool_process_shutdown,
111
+ [HtsTpoolProcess],
112
+ :void
113
+
114
+ attach_function \
115
+ :hts_tpool_process_is_shutdown,
116
+ [HtsTpoolProcess],
117
+ :int
118
+
119
+ attach_function \
120
+ :hts_tpool_process_attach,
121
+ [HtsTpool, HtsTpoolProcess],
122
+ :void
123
+
124
+ attach_function \
125
+ :hts_tpool_process_detach,
126
+ [HtsTpool, HtsTpoolProcess],
127
+ :void
128
+
129
+ attach_function \
130
+ :hts_tpool_process_ref_incr,
131
+ [HtsTpoolProcess],
132
+ :void
133
+
134
+ attach_function \
135
+ :hts_tpool_process_ref_decr,
136
+ [HtsTpoolProcess],
137
+ :void
138
+ end
139
+ end
data/lib/hts/libhts.rb CHANGED
@@ -31,3 +31,4 @@ require_relative "libhts/sam"
31
31
  require_relative "libhts/kfunc"
32
32
  require_relative "libhts/tbx"
33
33
  require_relative "libhts/vcf"
34
+ require_relative "libhts/thread_pool"
data/lib/hts/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTS
4
- VERSION = "0.0.6"
4
+ VERSION = "0.0.8"
5
5
  end
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.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - kojix2
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-03 00:00:00.000000000 Z
11
+ date: 2022-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- description:
125
+ description:
126
126
  email:
127
127
  - 2xijok@gmail.com
128
128
  executables: []
@@ -154,6 +154,7 @@ files:
154
154
  - lib/hts/libhts/sam.rb
155
155
  - lib/hts/libhts/sam_funcs.rb
156
156
  - lib/hts/libhts/tbx.rb
157
+ - lib/hts/libhts/thread_pool.rb
157
158
  - lib/hts/libhts/vcf.rb
158
159
  - lib/hts/libhts/vcf_funcs.rb
159
160
  - lib/hts/tabix.rb
@@ -163,7 +164,7 @@ homepage: https://github.com/kojix2/ruby-htslib
163
164
  licenses:
164
165
  - MIT
165
166
  metadata: {}
166
- post_install_message:
167
+ post_install_message:
167
168
  rdoc_options: []
168
169
  require_paths:
169
170
  - lib
@@ -178,8 +179,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
179
  - !ruby/object:Gem::Version
179
180
  version: '0'
180
181
  requirements: []
181
- rubygems_version: 3.3.3
182
- signing_key:
182
+ rubygems_version: 3.3.7
183
+ signing_key:
183
184
  specification_version: 4
184
185
  summary: HTSlib bindings for Ruby
185
186
  test_files: []