htslib 0.4.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +83 -51
- data/TUTORIAL.md +11 -22
- data/ext/htslib_native/extconf.rb +41 -0
- data/ext/htslib_native/htslib_native.h +11 -0
- data/ext/htslib_native/htslib_native_ext.c +48 -0
- data/ext/htslib_native/native_bam.c +1167 -0
- data/ext/htslib_native/native_bcf.c +447 -0
- data/ext/htslib_native/native_faidx.c +164 -0
- data/ext/htslib_native/native_tabix.c +255 -0
- data/lib/hts/bam/auxi.rb +87 -342
- data/lib/hts/bam/base_mod.rb +38 -73
- data/lib/hts/bam/cigar.rb +9 -55
- data/lib/hts/bam/flag.rb +24 -30
- data/lib/hts/bam/header.rb +29 -56
- data/lib/hts/bam/mpileup.rb +158 -132
- data/lib/hts/bam/pileup.rb +120 -145
- data/lib/hts/bam/record.rb +233 -270
- data/lib/hts/bam.rb +154 -45
- data/lib/hts/bcf/errors.rb +4 -0
- data/lib/hts/bcf/format.rb +283 -377
- data/lib/hts/bcf/header.rb +40 -122
- data/lib/hts/bcf/header_record.rb +9 -35
- data/lib/hts/bcf/info.rb +73 -320
- data/lib/hts/bcf/record.rb +73 -101
- data/lib/hts/bcf.rb +178 -321
- data/lib/hts/faidx.rb +30 -87
- data/lib/hts/hts.rb +6 -94
- data/lib/hts/native.rb +13 -0
- data/lib/hts/tabix.rb +109 -51
- data/lib/hts/version.rb +1 -1
- data/lib/htslib.rb +6 -52
- metadata +13 -64
- data/lib/hts/ffi_ext/README.md +0 -8
- data/lib/hts/ffi_ext/pointer.rb +0 -18
- data/lib/hts/ffi_ext/struct.rb +0 -45
- data/lib/hts/libhts/bgzf.rb +0 -199
- data/lib/hts/libhts/constants.rb +0 -658
- data/lib/hts/libhts/cram.rb +0 -471
- data/lib/hts/libhts/fai.rb +0 -146
- data/lib/hts/libhts/hfile.rb +0 -121
- data/lib/hts/libhts/hts.rb +0 -477
- data/lib/hts/libhts/kfunc.rb +0 -38
- data/lib/hts/libhts/sam.rb +0 -760
- data/lib/hts/libhts/sam_funcs.rb +0 -155
- data/lib/hts/libhts/tbx.rb +0 -94
- data/lib/hts/libhts/tbx_funcs.rb +0 -36
- data/lib/hts/libhts/thread_pool.rb +0 -139
- data/lib/hts/libhts/vcf.rb +0 -567
- data/lib/hts/libhts/vcf_funcs.rb +0 -366
- data/lib/hts/libhts.rb +0 -47
data/lib/hts/bcf.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../htslib"
|
|
4
|
-
|
|
5
4
|
require_relative "hts"
|
|
5
|
+
require_relative "native"
|
|
6
6
|
require_relative "bcf/errors"
|
|
7
7
|
require_relative "bcf/header"
|
|
8
8
|
require_relative "bcf/info"
|
|
@@ -10,35 +10,36 @@ require_relative "bcf/format"
|
|
|
10
10
|
require_relative "bcf/record"
|
|
11
11
|
|
|
12
12
|
module HTS
|
|
13
|
-
# A class for working with VCF, BCF files.
|
|
14
13
|
class Bcf < Hts
|
|
15
14
|
include Enumerable
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
def self.filter_records(records, rid: nil, beg: nil, end_: nil, min_qual: nil, filter_id: nil)
|
|
17
|
+
Array(records).select do |record|
|
|
18
|
+
(rid.nil? || record.rid == Integer(rid)) &&
|
|
19
|
+
(beg.nil? || record.endpos > Integer(beg)) && (end_.nil? || record.pos < Integer(end_)) &&
|
|
20
|
+
(min_qual.nil? || (!record.qual.nan? && record.qual >= Float(min_qual))) &&
|
|
21
|
+
(filter_id.nil? || record.filter_id?(filter_id))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
attr_reader :file_name, :index_name, :mode, :header, :nthreads, :unpack
|
|
26
|
+
|
|
27
|
+
def self.open(*args, **keywords)
|
|
28
|
+
file = new(*args, **keywords)
|
|
21
29
|
return file unless block_given?
|
|
22
30
|
|
|
23
31
|
begin
|
|
24
|
-
yield file
|
|
32
|
+
result = yield file
|
|
25
33
|
ensure
|
|
26
34
|
file.close
|
|
27
35
|
end
|
|
28
|
-
|
|
36
|
+
result
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
def self.build_index(file_name, index_name = nil, min_shift = 14, threads = 0, verbose = true)
|
|
32
|
-
if verbose
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else
|
|
36
|
-
warn "Create index for #{file_name}"
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
case LibHTS.bcf_index_build3(file_name, index_name, min_shift, threads)
|
|
41
|
-
when 0 # successful
|
|
40
|
+
warn(index_name ? "Create index for #{file_name} to #{index_name}" : "Create index for #{file_name}") if verbose
|
|
41
|
+
case Native::BcfFileHandle.build_index(file_name, index_name, min_shift, threads)
|
|
42
|
+
when 0 then nil
|
|
42
43
|
when -1 then raise IndexError, "Indexing failed for #{file_name}"
|
|
43
44
|
when -2 then raise IndexError, "Opening #{file_name} failed while building the index"
|
|
44
45
|
when -3 then raise IndexError, "#{file_name} is not in an indexable format"
|
|
@@ -47,86 +48,117 @@ module HTS
|
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
def initialize(file_name, mode = "r", index: nil, threads: nil,
|
|
51
|
-
|
|
52
|
-
if block_given?
|
|
53
|
-
|
|
54
|
-
raise message
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# NOTE: Do not check for the existence of local files, since file_names may be remote URIs.
|
|
51
|
+
def initialize(file_name, mode = "r", index: nil, threads: nil, build_index: false,
|
|
52
|
+
subset: nil, samples: nil, unpack: :all)
|
|
53
|
+
raise "HTS::Bcf.new() does not take block; Please use HTS::Bcf.open() instead" if block_given?
|
|
54
|
+
raise ArgumentError, "specify either samples: or subset:, not both" if samples && subset
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
subset = samples unless samples.nil?
|
|
57
|
+
@unpack = unpack.to_sym
|
|
58
|
+
@max_unpack = resolve_max_unpack(@unpack)
|
|
59
|
+
@file_name = file_name
|
|
60
60
|
@index_name = index
|
|
61
|
-
@mode
|
|
62
|
-
@nthreads
|
|
63
|
-
@
|
|
64
|
-
|
|
65
|
-
raise OpenError, "Failed to open #{@file_name}" if @hts_file.null?
|
|
66
|
-
|
|
61
|
+
@mode = mode
|
|
62
|
+
@nthreads = threads
|
|
63
|
+
@index_load_attempted = false
|
|
64
|
+
@native = Native::BcfFileHandle.open(@file_name, mode)
|
|
67
65
|
set_threads(threads) if threads
|
|
66
|
+
if subset && mode.start_with?("w")
|
|
67
|
+
raise SubsetError,
|
|
68
|
+
"Sample subsetting is only available when reading BCF/VCF files"
|
|
69
|
+
end
|
|
70
|
+
if writing?
|
|
71
|
+
@auto_index_on_close = build_index
|
|
72
|
+
@index_name_on_close = index
|
|
73
|
+
return
|
|
74
|
+
end
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return if @mode[0] == "w"
|
|
72
|
-
|
|
73
|
-
@read_header = Bcf::Header.new(@hts_file)
|
|
76
|
+
@read_header = Header.new(@native.read_header)
|
|
74
77
|
@header = subset ? @read_header.subset(subset) : @read_header
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
if build_index
|
|
79
|
+
build_index(index)
|
|
80
|
+
load_index(index)
|
|
81
|
+
elsif index
|
|
82
|
+
load_index(index)
|
|
83
|
+
end
|
|
77
84
|
@start_position = tell
|
|
85
|
+
rescue Errno::ENOENT
|
|
86
|
+
raise OpenError, "Failed to open #{@file_name}"
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
def build_index(index_name = nil, min_shift: 14, verbose: true)
|
|
81
90
|
check_closed
|
|
82
|
-
|
|
83
91
|
self.class.build_index(@file_name, index_name, min_shift, @nthreads || 0, verbose)
|
|
84
|
-
|
|
92
|
+
@index_name = index_name
|
|
93
|
+
@index_load_attempted = false
|
|
94
|
+
self
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
def load_index(index_name = nil)
|
|
88
|
-
|
|
98
|
+
return self if try_load_index(index_name)
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
@index_format = :tabix
|
|
92
|
-
if index_name
|
|
93
|
-
LibHTS.tbx_index_load2(@file_name, index_name)
|
|
94
|
-
else
|
|
95
|
-
LibHTS.tbx_index_load3(@file_name, nil, 2)
|
|
96
|
-
end
|
|
97
|
-
elsif index_name
|
|
98
|
-
@index_format = :bcf
|
|
99
|
-
LibHTS.bcf_index_load2(@file_name, index_name)
|
|
100
|
-
else
|
|
101
|
-
@index_format = :bcf
|
|
102
|
-
LibHTS.bcf_index_load3(@file_name, nil, 2)
|
|
103
|
-
end
|
|
100
|
+
raise MissingIndexError, "Failed to load index #{index_name || "for #{@file_name}"}"
|
|
104
101
|
end
|
|
105
102
|
|
|
106
|
-
def
|
|
103
|
+
def try_load_index(index_name = nil)
|
|
107
104
|
check_closed
|
|
105
|
+
@index_name = index_name
|
|
106
|
+
@index_load_attempted = true
|
|
107
|
+
@native.load_index(index_name)
|
|
108
|
+
end
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
def index_loaded?
|
|
111
|
+
check_closed
|
|
112
|
+
@native.index_loaded?
|
|
110
113
|
end
|
|
111
114
|
|
|
112
115
|
def close
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
was_closed = closed?
|
|
117
|
+
result = @native&.close
|
|
118
|
+
raise WriteError, "Failed to close #{@file_name}: buffered output may be incomplete" if writing? && result&.negative?
|
|
119
|
+
|
|
120
|
+
if writing? && @auto_index_on_close && !was_closed
|
|
121
|
+
@auto_index_on_close = false
|
|
122
|
+
self.class.build_index(@file_name, @index_name_on_close, 14, @nthreads || 0, false)
|
|
120
123
|
end
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
|
|
125
|
+
nil
|
|
126
|
+
end
|
|
127
|
+
def closed? = @native.nil? || @native.closed?
|
|
128
|
+
def file_format = @native.file_format
|
|
129
|
+
def file_format_version = @native.file_format_version
|
|
130
|
+
def seek(offset) = @native.seek(offset)
|
|
131
|
+
def tell = @native.tell
|
|
132
|
+
|
|
133
|
+
def rewind
|
|
134
|
+
raise "Cannot rewind: no start position" unless @start_position
|
|
135
|
+
|
|
136
|
+
result = seek(@start_position)
|
|
137
|
+
raise "Failed to rewind: #{result}" if result.negative?
|
|
138
|
+
|
|
139
|
+
tell
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def set_threads(count = nil)
|
|
143
|
+
if count.nil?
|
|
144
|
+
require "etc"
|
|
145
|
+
count = [Etc.nprocessors - 1, 1].max
|
|
146
|
+
end
|
|
147
|
+
raise TypeError unless count.is_a?(Integer)
|
|
148
|
+
raise ArgumentError, "Number of threads must be positive" if count < 1
|
|
149
|
+
raise "Failed to set number of threads: #{count}" if @native.set_threads(count).negative?
|
|
150
|
+
|
|
151
|
+
@nthreads = count
|
|
152
|
+
self
|
|
123
153
|
end
|
|
124
154
|
|
|
125
155
|
def write_header(header)
|
|
126
156
|
check_closed
|
|
127
|
-
|
|
128
157
|
@header = header.dup
|
|
129
|
-
|
|
158
|
+
result = @native.write_header(header.__send__(:native_handle))
|
|
159
|
+
raise HeaderError, "Failed to write BCF header" if result.negative?
|
|
160
|
+
|
|
161
|
+
nil
|
|
130
162
|
end
|
|
131
163
|
|
|
132
164
|
def header=(header)
|
|
@@ -135,32 +167,27 @@ module HTS
|
|
|
135
167
|
|
|
136
168
|
def write(record)
|
|
137
169
|
check_closed
|
|
170
|
+
result = @native.write(header.__send__(:native_handle), record.__send__(:native_handle))
|
|
171
|
+
raise "Failed to write record" if result.negative?
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
r = LibHTS.bcf_write(@hts_file, header, record)
|
|
141
|
-
raise "Failed to write record" if r < 0
|
|
173
|
+
nil
|
|
142
174
|
end
|
|
143
175
|
|
|
144
|
-
def <<(
|
|
145
|
-
write(
|
|
176
|
+
def <<(record)
|
|
177
|
+
write(record)
|
|
178
|
+
self
|
|
146
179
|
end
|
|
147
180
|
|
|
148
181
|
def nsamples
|
|
149
182
|
check_closed
|
|
150
|
-
|
|
151
183
|
header.nsamples
|
|
152
184
|
end
|
|
153
185
|
|
|
154
186
|
def samples
|
|
155
187
|
check_closed
|
|
156
|
-
|
|
157
188
|
header.samples
|
|
158
189
|
end
|
|
159
190
|
|
|
160
|
-
# @!macro [attach] define_getter
|
|
161
|
-
# @method $1
|
|
162
|
-
# Get $1 array
|
|
163
|
-
# @return [Array] the $1 array
|
|
164
191
|
define_getter :chrom
|
|
165
192
|
define_getter :pos
|
|
166
193
|
define_getter :endpos
|
|
@@ -172,35 +199,28 @@ module HTS
|
|
|
172
199
|
|
|
173
200
|
def info(key = nil)
|
|
174
201
|
check_closed
|
|
175
|
-
position = tell
|
|
176
202
|
raise NotImplementedError unless key
|
|
177
203
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# ary = each_copy.map { |r| r.info }
|
|
181
|
-
# ary = map { |r| r.info.clone }
|
|
182
|
-
|
|
183
|
-
seek(position)
|
|
184
|
-
ary
|
|
204
|
+
position = tell
|
|
205
|
+
map { |record| record.info(key) }.tap { seek(position) if position }
|
|
185
206
|
end
|
|
207
|
+
alias info_array info
|
|
186
208
|
|
|
187
209
|
def format(key = nil)
|
|
188
210
|
check_closed
|
|
189
|
-
position = tell
|
|
190
211
|
raise NotImplementedError unless key
|
|
191
212
|
|
|
192
|
-
|
|
213
|
+
position = tell
|
|
214
|
+
map { |record| record.format(key) }.tap { seek(position) if position }
|
|
215
|
+
end
|
|
216
|
+
alias format_array format
|
|
193
217
|
|
|
194
|
-
|
|
195
|
-
# ary = map { |r| r.format.clone }
|
|
218
|
+
def collect_records = each(copy: true).to_a
|
|
196
219
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
220
|
+
# Materialize independent, owning records rather than retaining the
|
|
221
|
+
# reused Record yielded by the allocation-conscious default iterator.
|
|
222
|
+
def to_a = collect_records
|
|
200
223
|
|
|
201
|
-
# @!macro [attach] define_iterator
|
|
202
|
-
# @method each_$1
|
|
203
|
-
# Get $1 iterator
|
|
204
224
|
define_iterator :chrom
|
|
205
225
|
define_iterator :pos
|
|
206
226
|
define_iterator :endpos
|
|
@@ -211,47 +231,40 @@ module HTS
|
|
|
211
231
|
define_iterator :filter
|
|
212
232
|
|
|
213
233
|
def each_info(key)
|
|
214
|
-
check_closed
|
|
215
234
|
return to_enum(__method__, key) unless block_given?
|
|
216
235
|
|
|
217
|
-
each
|
|
218
|
-
|
|
219
|
-
end
|
|
236
|
+
each { |record| yield record.info(key) }
|
|
237
|
+
self
|
|
220
238
|
end
|
|
221
239
|
|
|
222
240
|
def each_format(key)
|
|
223
|
-
check_closed
|
|
224
241
|
return to_enum(__method__, key) unless block_given?
|
|
225
242
|
|
|
226
|
-
each
|
|
227
|
-
|
|
228
|
-
end
|
|
243
|
+
each { |record| yield record.format(key) }
|
|
244
|
+
self
|
|
229
245
|
end
|
|
230
246
|
|
|
231
|
-
def each(copy: false, &block)
|
|
232
|
-
if copy
|
|
233
|
-
each_record_copy(&block)
|
|
234
|
-
else
|
|
235
|
-
each_record_reuse(&block)
|
|
236
|
-
end
|
|
237
|
-
end
|
|
247
|
+
def each(copy: false, &block) = copy ? each_record_copy(&block) : each_record_reuse(&block)
|
|
238
248
|
|
|
239
249
|
def query(region, beg = nil, end_ = nil, copy: false, &block)
|
|
240
250
|
check_closed
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
unless ensure_index_loaded
|
|
252
|
+
raise MissingIndexError, "Index file is required to call the query method for #{@file_name}"
|
|
253
|
+
end
|
|
243
254
|
|
|
244
255
|
case region
|
|
245
256
|
when Array
|
|
246
257
|
raise ArgumentError, "beg and end must not be specified when region is an Array" unless beg.nil? && end_.nil?
|
|
258
|
+
return to_enum(__method__, region, copy:) unless block
|
|
247
259
|
|
|
248
|
-
|
|
260
|
+
region.each { |item| query(item, copy:, &block) }
|
|
261
|
+
self
|
|
249
262
|
else
|
|
250
263
|
if beg && end_
|
|
251
|
-
|
|
252
|
-
|
|
264
|
+
iterate_query(@native.query_interval(read_header_native, header.name2id(region), beg, end_), copy, region,
|
|
265
|
+
&block)
|
|
253
266
|
elsif beg.nil? && end_.nil?
|
|
254
|
-
|
|
267
|
+
iterate_query(@native.query_region(read_header_native, region), copy, region, &block)
|
|
255
268
|
else
|
|
256
269
|
raise ArgumentError, "beg and end must be specified together"
|
|
257
270
|
end
|
|
@@ -260,241 +273,85 @@ module HTS
|
|
|
260
273
|
|
|
261
274
|
private
|
|
262
275
|
|
|
263
|
-
def
|
|
264
|
-
if copy
|
|
265
|
-
queryi_copy(tid, beg, end_, &block)
|
|
266
|
-
else
|
|
267
|
-
queryi_reuse(tid, beg, end_, &block)
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def querys(region, copy: false, &block)
|
|
272
|
-
if copy
|
|
273
|
-
querys_copy(region, &block)
|
|
274
|
-
else
|
|
275
|
-
querys_reuse(region, &block)
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def query_regions(regions, copy: false, &block)
|
|
280
|
-
if copy
|
|
281
|
-
query_regions_copy(regions, &block)
|
|
282
|
-
else
|
|
283
|
-
query_regions_reuse(regions, &block)
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
def queryi_reuse(tid, beg, end_, &block)
|
|
288
|
-
return to_enum(__method__, tid, beg, end_) unless block_given?
|
|
289
|
-
|
|
290
|
-
return queryi_reuse_vcf(tid, beg, end_, &block) if tabix_index?
|
|
291
|
-
|
|
292
|
-
qiter = LibHTS.bcf_itr_queryi(@idx, tid, beg, end_)
|
|
293
|
-
raise QueryError, "Failed to query region #{tid}:#{beg}-#{end_} in #{@file_name}" if qiter.null?
|
|
294
|
-
|
|
295
|
-
query_reuse_yield(qiter, &block)
|
|
296
|
-
self
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def querys_reuse(region, &block)
|
|
300
|
-
return to_enum(__method__, region) unless block_given?
|
|
301
|
-
|
|
302
|
-
return querys_reuse_vcf(region, &block) if tabix_index?
|
|
303
|
-
|
|
304
|
-
qiter = LibHTS.bcf_itr_querys(@idx, read_header, region)
|
|
305
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" if qiter.null?
|
|
306
|
-
|
|
307
|
-
query_reuse_yield(qiter, &block)
|
|
308
|
-
self
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def query_regions_reuse(regions, &block)
|
|
312
|
-
return to_enum(__method__, regions) unless block_given?
|
|
313
|
-
|
|
314
|
-
regions.each do |region|
|
|
315
|
-
querys_reuse(region, &block)
|
|
316
|
-
end
|
|
317
|
-
self
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def query_reuse_yield(qiter)
|
|
321
|
-
bcf1 = LibHTS.bcf_init
|
|
322
|
-
record = Record.new(header, bcf1)
|
|
323
|
-
begin
|
|
324
|
-
loop do
|
|
325
|
-
slen = LibHTS.bcf_itr_next(@hts_file, qiter, bcf1)
|
|
326
|
-
break if slen == -1
|
|
327
|
-
raise if slen < -1
|
|
328
|
-
|
|
329
|
-
apply_subset!(record)
|
|
330
|
-
yield record
|
|
331
|
-
end
|
|
332
|
-
ensure
|
|
333
|
-
LibHTS.bcf_itr_destroy(qiter)
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
def queryi_copy(tid, beg, end_, &block)
|
|
338
|
-
return to_enum(__method__, tid, beg, end_) unless block_given?
|
|
339
|
-
|
|
340
|
-
return queryi_copy_vcf(tid, beg, end_, &block) if tabix_index?
|
|
341
|
-
|
|
342
|
-
qiter = LibHTS.bcf_itr_queryi(@idx, tid, beg, end_)
|
|
343
|
-
raise QueryError, "Failed to query region #{tid}:#{beg}-#{end_} in #{@file_name}" if qiter.null?
|
|
344
|
-
|
|
345
|
-
query_copy_yield(qiter, &block)
|
|
346
|
-
self
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
def querys_copy(region, &block)
|
|
350
|
-
return to_enum(__method__, region) unless block_given?
|
|
276
|
+
def writing? = @mode&.start_with?("w", "a")
|
|
351
277
|
|
|
352
|
-
|
|
278
|
+
def ensure_index_loaded
|
|
279
|
+
return true if index_loaded?
|
|
280
|
+
return false if @index_load_attempted
|
|
353
281
|
|
|
354
|
-
|
|
355
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" if qiter.null?
|
|
356
|
-
|
|
357
|
-
query_copy_yield(qiter, &block)
|
|
358
|
-
self
|
|
282
|
+
load_index(@index_name)
|
|
359
283
|
end
|
|
360
284
|
|
|
361
|
-
def
|
|
362
|
-
|
|
285
|
+
def native_handle = @native
|
|
286
|
+
def read_header_native = (@read_header || @header).__send__(:native_handle)
|
|
363
287
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
end
|
|
367
|
-
self
|
|
368
|
-
end
|
|
288
|
+
def each_record_reuse
|
|
289
|
+
return to_enum(__method__) unless block_given?
|
|
369
290
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
record = Record.new(header, bcf1)
|
|
291
|
+
record = Record.new(header)
|
|
292
|
+
prepare_record(record)
|
|
373
293
|
loop do
|
|
374
|
-
|
|
375
|
-
break if
|
|
376
|
-
raise if
|
|
294
|
+
result = @native.read(read_header_native, record.__send__(:native_handle))
|
|
295
|
+
break if result == -1
|
|
296
|
+
raise QueryError, "Failed to read variant record from #{@file_name}" if result < -1
|
|
377
297
|
|
|
378
298
|
apply_subset!(record)
|
|
379
|
-
yield record
|
|
299
|
+
yield record
|
|
380
300
|
end
|
|
381
|
-
ensure
|
|
382
|
-
LibHTS.bcf_itr_destroy(qiter)
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
def tabix_index?
|
|
386
|
-
@index_format == :tabix
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
def queryi_reuse_vcf(tid, beg, end_, &block)
|
|
390
|
-
qiter = LibHTS.tbx_itr_queryi(@idx, tid, beg, end_)
|
|
391
|
-
raise QueryError, "Failed to query region #{tid}:#{beg}-#{end_} in #{@file_name}" if qiter.null?
|
|
392
|
-
|
|
393
|
-
query_reuse_yield_vcf(qiter, &block)
|
|
394
|
-
self
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def querys_reuse_vcf(region, &block)
|
|
398
|
-
qiter = LibHTS.tbx_itr_querys(@idx, region)
|
|
399
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" if qiter.null?
|
|
400
|
-
|
|
401
|
-
query_reuse_yield_vcf(qiter, &block)
|
|
402
301
|
self
|
|
403
302
|
end
|
|
404
303
|
|
|
405
|
-
def
|
|
406
|
-
|
|
407
|
-
bcf1 = LibHTS.bcf_init
|
|
408
|
-
record = Record.new(header, bcf1)
|
|
409
|
-
begin
|
|
410
|
-
while (slen = LibHTS.tbx_itr_next(@hts_file, @idx, qiter, line)) >= 0
|
|
411
|
-
raise QueryError, "Failed to parse VCF record in #{@file_name}" if LibHTS.vcf_parse(line, read_header,
|
|
412
|
-
bcf1) < 0
|
|
413
|
-
|
|
414
|
-
apply_subset!(record)
|
|
415
|
-
yield record
|
|
416
|
-
end
|
|
417
|
-
raise if slen < -1
|
|
418
|
-
ensure
|
|
419
|
-
line.free_buffer
|
|
420
|
-
LibHTS.hts_itr_destroy(qiter)
|
|
421
|
-
end
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
def queryi_copy_vcf(tid, beg, end_, &block)
|
|
425
|
-
qiter = LibHTS.tbx_itr_queryi(@idx, tid, beg, end_)
|
|
426
|
-
raise QueryError, "Failed to query region #{tid}:#{beg}-#{end_} in #{@file_name}" if qiter.null?
|
|
304
|
+
def each_record_copy
|
|
305
|
+
return to_enum(__method__) unless block_given?
|
|
427
306
|
|
|
428
|
-
|
|
307
|
+
each_record_reuse { |record| yield record.dup }
|
|
429
308
|
self
|
|
430
309
|
end
|
|
431
310
|
|
|
432
|
-
def
|
|
433
|
-
|
|
434
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}"
|
|
311
|
+
def iterate_query(iterator, copy, region)
|
|
312
|
+
return to_enum(__method__, iterator, copy, region) unless block_given?
|
|
313
|
+
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" unless iterator
|
|
435
314
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def query_copy_yield_vcf(qiter)
|
|
441
|
-
line = LibHTS::KString.new
|
|
315
|
+
record = Record.new(header)
|
|
316
|
+
prepare_record(record)
|
|
442
317
|
begin
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
318
|
+
loop do
|
|
319
|
+
result = iterator.next(record.__send__(:native_handle))
|
|
320
|
+
break if result == -1
|
|
321
|
+
raise QueryError, "Failed to parse/query record in #{@file_name}" if result < -1
|
|
447
322
|
|
|
448
|
-
record = Record.new(header, bcf1)
|
|
449
323
|
apply_subset!(record)
|
|
450
|
-
yield record
|
|
324
|
+
yield(copy ? record.dup : record)
|
|
451
325
|
end
|
|
452
|
-
raise if slen < -1
|
|
453
326
|
ensure
|
|
454
|
-
|
|
455
|
-
LibHTS.hts_itr_destroy(qiter)
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
def each_record_reuse
|
|
460
|
-
check_closed
|
|
461
|
-
|
|
462
|
-
return to_enum(__method__) unless block_given?
|
|
463
|
-
|
|
464
|
-
bcf1 = LibHTS.bcf_init
|
|
465
|
-
record = Record.new(header, bcf1)
|
|
466
|
-
while LibHTS.bcf_read(@hts_file, read_header, bcf1) != -1
|
|
467
|
-
apply_subset!(record)
|
|
468
|
-
yield record
|
|
327
|
+
iterator.close
|
|
469
328
|
end
|
|
470
329
|
self
|
|
471
330
|
end
|
|
472
331
|
|
|
473
|
-
def
|
|
474
|
-
|
|
332
|
+
def apply_subset!(record)
|
|
333
|
+
return unless header&.subset?
|
|
475
334
|
|
|
476
|
-
|
|
335
|
+
map = header.__send__(:subset_imap)
|
|
336
|
+
result = record.__send__(:native_handle).subset(header.__send__(:native_handle), map)
|
|
337
|
+
return unless result.negative?
|
|
477
338
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
while LibHTS.bcf_read(@hts_file, read_header, bcf1) != -1
|
|
481
|
-
apply_subset!(record)
|
|
482
|
-
yield record.dup
|
|
483
|
-
end
|
|
484
|
-
self
|
|
339
|
+
raise SubsetError,
|
|
340
|
+
"Failed to subset samples #{header.subset_samples.inspect} while reading #{@file_name}"
|
|
485
341
|
end
|
|
486
342
|
|
|
487
|
-
def
|
|
488
|
-
|
|
343
|
+
def resolve_max_unpack(level)
|
|
344
|
+
case level
|
|
345
|
+
when :all, :format then 15
|
|
346
|
+
when :site_only, :info then 4
|
|
347
|
+
when :filter then 2
|
|
348
|
+
when :string, :alleles then 1
|
|
349
|
+
else raise ArgumentError, "unknown unpack level: #{level.inspect}"
|
|
350
|
+
end
|
|
489
351
|
end
|
|
490
352
|
|
|
491
|
-
def
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
rc = LibHTS.bcf_subset(header.struct, record.struct, header.subset_sample_count, header.subset_imap_pointer || ::FFI::Pointer::NULL)
|
|
495
|
-
return if rc >= 0
|
|
496
|
-
|
|
497
|
-
raise SubsetError, "Failed to subset samples #{header.subset_samples.inspect} while reading #{@file_name}"
|
|
353
|
+
def prepare_record(record)
|
|
354
|
+
record.__send__(:native_handle).max_unpack = @max_unpack
|
|
498
355
|
end
|
|
499
356
|
end
|
|
500
357
|
end
|