htslib 0.4.1 → 0.5.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 +79 -49
- data/TUTORIAL.md +9 -20
- 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 +1108 -0
- data/ext/htslib_native/native_bcf.c +369 -0
- data/ext/htslib_native/native_faidx.c +155 -0
- data/ext/htslib_native/native_tabix.c +246 -0
- data/lib/hts/bam/auxi.rb +87 -342
- data/lib/hts/bam/base_mod.rb +37 -73
- data/lib/hts/bam/cigar.rb +9 -55
- data/lib/hts/bam/flag.rb +19 -27
- 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 +102 -42
- data/lib/hts/bcf/errors.rb +1 -0
- data/lib/hts/bcf/format.rb +278 -379
- data/lib/hts/bcf/header.rb +38 -123
- data/lib/hts/bcf/header_record.rb +9 -35
- data/lib/hts/bcf/info.rb +72 -320
- data/lib/hts/bcf/record.rb +61 -102
- data/lib/hts/bcf.rb +149 -323
- data/lib/hts/faidx.rb +28 -87
- data/lib/hts/hts.rb +6 -94
- data/lib/hts/native.rb +13 -0
- data/lib/hts/tabix.rb +93 -48
- 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 -109
- 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 -79
- data/lib/hts/libhts/tbx_funcs.rb +0 -32
- data/lib/hts/libhts/thread_pool.rb +0 -139
- data/lib/hts/libhts/vcf.rb +0 -557
- data/lib/hts/libhts/vcf_funcs.rb +0 -353
- 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,14 +10,22 @@ 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
|
|
@@ -29,16 +37,9 @@ module HTS
|
|
|
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,96 @@ 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
|
+
return if mode.start_with?("w")
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return if @mode[0] == "w"
|
|
72
|
-
|
|
73
|
-
@read_header = Bcf::Header.new(@hts_file)
|
|
72
|
+
@read_header = Header.new(@native.read_header)
|
|
74
73
|
@header = subset ? @read_header.subset(subset) : @read_header
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if build_index
|
|
75
|
+
build_index(index)
|
|
76
|
+
load_index(index)
|
|
77
|
+
elsif index
|
|
78
|
+
load_index(index)
|
|
79
|
+
end
|
|
77
80
|
@start_position = tell
|
|
81
|
+
rescue Errno::ENOENT
|
|
82
|
+
raise OpenError, "Failed to open #{@file_name}"
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
def build_index(index_name = nil, min_shift: 14, verbose: true)
|
|
81
86
|
check_closed
|
|
82
|
-
|
|
83
87
|
self.class.build_index(@file_name, index_name, min_shift, @nthreads || 0, verbose)
|
|
84
|
-
|
|
88
|
+
@index_name = index_name
|
|
89
|
+
@index_load_attempted = false
|
|
90
|
+
self
|
|
85
91
|
end
|
|
86
92
|
|
|
87
93
|
def load_index(index_name = nil)
|
|
88
94
|
check_closed
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
95
|
+
@index_name = index_name
|
|
96
|
+
@index_load_attempted = true
|
|
97
|
+
@native.load_index(index_name)
|
|
104
98
|
end
|
|
105
99
|
|
|
106
100
|
def index_loaded?
|
|
107
101
|
check_closed
|
|
102
|
+
@native.index_loaded?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def close = @native&.close
|
|
106
|
+
def closed? = @native.nil? || @native.closed?
|
|
107
|
+
def file_format = @native.file_format
|
|
108
|
+
def file_format_version = @native.file_format_version
|
|
109
|
+
def seek(offset) = @native.seek(offset)
|
|
110
|
+
def tell = @native.tell
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
def rewind
|
|
113
|
+
raise "Cannot rewind: no start position" unless @start_position
|
|
114
|
+
|
|
115
|
+
result = seek(@start_position)
|
|
116
|
+
raise "Failed to rewind: #{result}" if result.negative?
|
|
117
|
+
|
|
118
|
+
tell
|
|
110
119
|
end
|
|
111
120
|
|
|
112
|
-
def
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
LibHTS.hts_idx_destroy(@idx)
|
|
117
|
-
when :tabix
|
|
118
|
-
@idx.close
|
|
119
|
-
end
|
|
121
|
+
def set_threads(count = nil)
|
|
122
|
+
if count.nil?
|
|
123
|
+
require "etc"
|
|
124
|
+
count = [Etc.nprocessors - 1, 1].max
|
|
120
125
|
end
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
raise TypeError unless count.is_a?(Integer)
|
|
127
|
+
raise ArgumentError, "Number of threads must be positive" if count < 1
|
|
128
|
+
raise "Failed to set number of threads: #{count}" if @native.set_threads(count).negative?
|
|
129
|
+
|
|
130
|
+
@nthreads = count
|
|
131
|
+
self
|
|
123
132
|
end
|
|
124
133
|
|
|
125
134
|
def write_header(header)
|
|
126
135
|
check_closed
|
|
127
|
-
|
|
128
136
|
@header = header.dup
|
|
129
|
-
|
|
137
|
+
result = @native.write_header(header.__send__(:native_handle))
|
|
138
|
+
raise HeaderError, "Failed to write BCF header" if result.negative?
|
|
139
|
+
|
|
140
|
+
result
|
|
130
141
|
end
|
|
131
142
|
|
|
132
143
|
def header=(header)
|
|
@@ -135,32 +146,23 @@ module HTS
|
|
|
135
146
|
|
|
136
147
|
def write(record)
|
|
137
148
|
check_closed
|
|
149
|
+
result = @native.write(header.__send__(:native_handle), record.__send__(:native_handle))
|
|
150
|
+
raise "Failed to write record" if result.negative?
|
|
138
151
|
|
|
139
|
-
|
|
140
|
-
r = LibHTS.bcf_write(@hts_file, header, record)
|
|
141
|
-
raise "Failed to write record" if r < 0
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def <<(var)
|
|
145
|
-
write(var)
|
|
152
|
+
result
|
|
146
153
|
end
|
|
154
|
+
alias << write
|
|
147
155
|
|
|
148
156
|
def nsamples
|
|
149
157
|
check_closed
|
|
150
|
-
|
|
151
158
|
header.nsamples
|
|
152
159
|
end
|
|
153
160
|
|
|
154
161
|
def samples
|
|
155
162
|
check_closed
|
|
156
|
-
|
|
157
163
|
header.samples
|
|
158
164
|
end
|
|
159
165
|
|
|
160
|
-
# @!macro [attach] define_getter
|
|
161
|
-
# @method $1
|
|
162
|
-
# Get $1 array
|
|
163
|
-
# @return [Array] the $1 array
|
|
164
166
|
define_getter :chrom
|
|
165
167
|
define_getter :pos
|
|
166
168
|
define_getter :endpos
|
|
@@ -172,35 +174,24 @@ module HTS
|
|
|
172
174
|
|
|
173
175
|
def info(key = nil)
|
|
174
176
|
check_closed
|
|
175
|
-
position = tell
|
|
176
177
|
raise NotImplementedError unless key
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# ary = each_copy.map { |r| r.info }
|
|
181
|
-
# ary = map { |r| r.info.clone }
|
|
182
|
-
|
|
183
|
-
seek(position)
|
|
184
|
-
ary
|
|
179
|
+
position = tell
|
|
180
|
+
map { |record| record.info(key) }.tap { seek(position) if position }
|
|
185
181
|
end
|
|
182
|
+
alias info_array info
|
|
186
183
|
|
|
187
184
|
def format(key = nil)
|
|
188
185
|
check_closed
|
|
189
|
-
position = tell
|
|
190
186
|
raise NotImplementedError unless key
|
|
191
187
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
# ary = each_copy.map { |r| r.format }
|
|
195
|
-
# ary = map { |r| r.format.clone }
|
|
196
|
-
|
|
197
|
-
seek(position)
|
|
198
|
-
ary
|
|
188
|
+
position = tell
|
|
189
|
+
map { |record| record.format(key) }.tap { seek(position) if position }
|
|
199
190
|
end
|
|
191
|
+
alias format_array format
|
|
192
|
+
|
|
193
|
+
def collect_records = each(copy: true).to_a
|
|
200
194
|
|
|
201
|
-
# @!macro [attach] define_iterator
|
|
202
|
-
# @method each_$1
|
|
203
|
-
# Get $1 iterator
|
|
204
195
|
define_iterator :chrom
|
|
205
196
|
define_iterator :pos
|
|
206
197
|
define_iterator :endpos
|
|
@@ -211,47 +202,40 @@ module HTS
|
|
|
211
202
|
define_iterator :filter
|
|
212
203
|
|
|
213
204
|
def each_info(key)
|
|
214
|
-
check_closed
|
|
215
205
|
return to_enum(__method__, key) unless block_given?
|
|
216
206
|
|
|
217
|
-
each
|
|
218
|
-
|
|
219
|
-
end
|
|
207
|
+
each { |record| yield record.info(key) }
|
|
208
|
+
self
|
|
220
209
|
end
|
|
221
210
|
|
|
222
211
|
def each_format(key)
|
|
223
|
-
check_closed
|
|
224
212
|
return to_enum(__method__, key) unless block_given?
|
|
225
213
|
|
|
226
|
-
each
|
|
227
|
-
|
|
228
|
-
end
|
|
214
|
+
each { |record| yield record.format(key) }
|
|
215
|
+
self
|
|
229
216
|
end
|
|
230
217
|
|
|
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
|
|
218
|
+
def each(copy: false, &block) = copy ? each_record_copy(&block) : each_record_reuse(&block)
|
|
238
219
|
|
|
239
220
|
def query(region, beg = nil, end_ = nil, copy: false, &block)
|
|
240
221
|
check_closed
|
|
241
|
-
|
|
242
|
-
|
|
222
|
+
unless ensure_index_loaded
|
|
223
|
+
raise MissingIndexError, "Index file is required to call the query method for #{@file_name}"
|
|
224
|
+
end
|
|
243
225
|
|
|
244
226
|
case region
|
|
245
227
|
when Array
|
|
246
228
|
raise ArgumentError, "beg and end must not be specified when region is an Array" unless beg.nil? && end_.nil?
|
|
229
|
+
return to_enum(__method__, region, copy:) unless block
|
|
247
230
|
|
|
248
|
-
|
|
231
|
+
region.each { |item| query(item, copy:, &block) }
|
|
232
|
+
self
|
|
249
233
|
else
|
|
250
234
|
if beg && end_
|
|
251
|
-
|
|
252
|
-
|
|
235
|
+
iterate_query(@native.query_interval(read_header_native, header.name2id(region), beg, end_), copy, region,
|
|
236
|
+
&block)
|
|
253
237
|
elsif beg.nil? && end_.nil?
|
|
254
|
-
|
|
238
|
+
iterate_query(@native.query_region(read_header_native, region), copy, region, &block)
|
|
255
239
|
else
|
|
256
240
|
raise ArgumentError, "beg and end must be specified together"
|
|
257
241
|
end
|
|
@@ -260,241 +244,83 @@ module HTS
|
|
|
260
244
|
|
|
261
245
|
private
|
|
262
246
|
|
|
263
|
-
def
|
|
264
|
-
if
|
|
265
|
-
|
|
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?
|
|
247
|
+
def ensure_index_loaded
|
|
248
|
+
return true if index_loaded?
|
|
249
|
+
return false if @index_load_attempted
|
|
289
250
|
|
|
290
|
-
|
|
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
|
|
251
|
+
load_index(@index_name)
|
|
309
252
|
end
|
|
310
253
|
|
|
311
|
-
def
|
|
312
|
-
|
|
254
|
+
def native_handle = @native
|
|
255
|
+
def read_header_native = (@read_header || @header).__send__(:native_handle)
|
|
313
256
|
|
|
314
|
-
|
|
315
|
-
|
|
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.hts_itr_next(@hts_file[:fp][:bgzf], qiter, bcf1, ::FFI::Pointer::NULL)
|
|
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?
|
|
351
|
-
|
|
352
|
-
return querys_copy_vcf(region, &block) if tabix_index?
|
|
353
|
-
|
|
354
|
-
qiter = LibHTS.bcf_itr_querys(@idx, read_header, region)
|
|
355
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" if qiter.null?
|
|
356
|
-
|
|
357
|
-
query_copy_yield(qiter, &block)
|
|
358
|
-
self
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def query_regions_copy(regions, &block)
|
|
362
|
-
return to_enum(__method__, regions) unless block_given?
|
|
363
|
-
|
|
364
|
-
regions.each do |region|
|
|
365
|
-
querys_copy(region, &block)
|
|
366
|
-
end
|
|
367
|
-
self
|
|
368
|
-
end
|
|
257
|
+
def each_record_reuse
|
|
258
|
+
return to_enum(__method__) unless block_given?
|
|
369
259
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
record = Record.new(header, bcf1)
|
|
260
|
+
record = Record.new(header)
|
|
261
|
+
prepare_record(record)
|
|
373
262
|
loop do
|
|
374
|
-
|
|
375
|
-
break if
|
|
376
|
-
raise if
|
|
263
|
+
result = @native.read(read_header_native, record.__send__(:native_handle))
|
|
264
|
+
break if result == -1
|
|
265
|
+
raise QueryError, "Failed to read variant record from #{@file_name}" if result < -1
|
|
377
266
|
|
|
378
267
|
apply_subset!(record)
|
|
379
|
-
yield record
|
|
268
|
+
yield record
|
|
380
269
|
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
270
|
self
|
|
403
271
|
end
|
|
404
272
|
|
|
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?
|
|
273
|
+
def each_record_copy
|
|
274
|
+
return to_enum(__method__) unless block_given?
|
|
427
275
|
|
|
428
|
-
|
|
276
|
+
each_record_reuse { |record| yield record.dup }
|
|
429
277
|
self
|
|
430
278
|
end
|
|
431
279
|
|
|
432
|
-
def
|
|
433
|
-
|
|
434
|
-
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}"
|
|
280
|
+
def iterate_query(iterator, copy, region)
|
|
281
|
+
return to_enum(__method__, iterator, copy, region) unless block_given?
|
|
282
|
+
raise QueryError, "Failed to query region #{region.inspect} in #{@file_name}" unless iterator
|
|
435
283
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def query_copy_yield_vcf(qiter)
|
|
441
|
-
line = LibHTS::KString.new
|
|
284
|
+
record = Record.new(header)
|
|
285
|
+
prepare_record(record)
|
|
442
286
|
begin
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
287
|
+
loop do
|
|
288
|
+
result = iterator.next(record.__send__(:native_handle))
|
|
289
|
+
break if result == -1
|
|
290
|
+
raise QueryError, "Failed to parse/query record in #{@file_name}" if result < -1
|
|
447
291
|
|
|
448
|
-
record = Record.new(header, bcf1)
|
|
449
292
|
apply_subset!(record)
|
|
450
|
-
yield record
|
|
293
|
+
yield(copy ? record.dup : record)
|
|
451
294
|
end
|
|
452
|
-
raise if slen < -1
|
|
453
295
|
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
|
|
296
|
+
iterator.close
|
|
469
297
|
end
|
|
470
298
|
self
|
|
471
299
|
end
|
|
472
300
|
|
|
473
|
-
def
|
|
474
|
-
|
|
301
|
+
def apply_subset!(record)
|
|
302
|
+
return unless header&.subset?
|
|
475
303
|
|
|
476
|
-
|
|
304
|
+
map = header.__send__(:subset_imap)
|
|
305
|
+
result = record.__send__(:native_handle).subset(header.__send__(:native_handle), map)
|
|
306
|
+
return unless result.negative?
|
|
477
307
|
|
|
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
|
|
308
|
+
raise SubsetError,
|
|
309
|
+
"Failed to subset samples #{header.subset_samples.inspect} while reading #{@file_name}"
|
|
485
310
|
end
|
|
486
311
|
|
|
487
|
-
def
|
|
488
|
-
|
|
312
|
+
def resolve_max_unpack(level)
|
|
313
|
+
case level
|
|
314
|
+
when :all, :format then 15
|
|
315
|
+
when :site_only, :info then 4
|
|
316
|
+
when :filter then 2
|
|
317
|
+
when :string, :alleles then 1
|
|
318
|
+
else raise ArgumentError, "unknown unpack level: #{level.inspect}"
|
|
319
|
+
end
|
|
489
320
|
end
|
|
490
321
|
|
|
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}"
|
|
322
|
+
def prepare_record(record)
|
|
323
|
+
record.__send__(:native_handle).max_unpack = @max_unpack
|
|
498
324
|
end
|
|
499
325
|
end
|
|
500
326
|
end
|