htslib 0.4.2 → 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 -122
- 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 -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/bam.rb
CHANGED
|
@@ -17,6 +17,20 @@ module HTS
|
|
|
17
17
|
class Bam
|
|
18
18
|
include Enumerable
|
|
19
19
|
|
|
20
|
+
# Filter an owning batch of records in one native pass when available.
|
|
21
|
+
def self.filter_records(records, required_flags: 0, excluded_flags: 0,
|
|
22
|
+
min_mapq: 0, tid: nil, beg: nil, end_: nil)
|
|
23
|
+
required_flags = Integer(required_flags)
|
|
24
|
+
excluded_flags = Integer(excluded_flags)
|
|
25
|
+
min_mapq = Integer(min_mapq)
|
|
26
|
+
Array(records).select do |record|
|
|
27
|
+
flags = record.flag_value
|
|
28
|
+
(flags & required_flags) == required_flags && (flags & excluded_flags).zero? &&
|
|
29
|
+
record.mapq >= min_mapq && (tid.nil? || record.tid == Integer(tid)) &&
|
|
30
|
+
(beg.nil? || record.endpos > Integer(beg)) && (end_.nil? || record.pos < Integer(end_))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
20
34
|
attr_reader :file_name, :index_name, :mode, :header, :nthreads
|
|
21
35
|
|
|
22
36
|
def self.open(*args, **kw)
|
|
@@ -40,7 +54,7 @@ module HTS
|
|
|
40
54
|
end
|
|
41
55
|
end
|
|
42
56
|
|
|
43
|
-
case
|
|
57
|
+
case Native::BamFileHandle.build_index(file_name, index_name, min_shift, threads)
|
|
44
58
|
when 0 # successful
|
|
45
59
|
when -1 then raise "indexing failed"
|
|
46
60
|
when -2 then raise "opening #{file_name} failed"
|
|
@@ -63,9 +77,8 @@ module HTS
|
|
|
63
77
|
@index_name = index
|
|
64
78
|
@mode = mode
|
|
65
79
|
@nthreads = threads
|
|
66
|
-
@
|
|
67
|
-
|
|
68
|
-
raise Errno::ENOENT, "Failed to open #{@file_name}" if @hts_file.null?
|
|
80
|
+
@index_load_attempted = false
|
|
81
|
+
@native = Native::BamFileHandle.open(@file_name, mode)
|
|
69
82
|
|
|
70
83
|
# Auto-detect and set reference for CRAM files
|
|
71
84
|
if fai.nil? && @file_name.end_with?(".cram")
|
|
@@ -79,7 +92,7 @@ module HTS
|
|
|
79
92
|
end
|
|
80
93
|
|
|
81
94
|
if fai
|
|
82
|
-
r =
|
|
95
|
+
r = @native.set_fai(fai)
|
|
83
96
|
raise "Failed to load fasta index: #{fai}" if r < 0
|
|
84
97
|
end
|
|
85
98
|
|
|
@@ -87,9 +100,13 @@ module HTS
|
|
|
87
100
|
|
|
88
101
|
return if @mode[0] == "w"
|
|
89
102
|
|
|
90
|
-
@header = Bam::Header.new(@
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
@header = Bam::Header.new(@native.read_header)
|
|
104
|
+
if build_index
|
|
105
|
+
build_index(index)
|
|
106
|
+
load_index(index)
|
|
107
|
+
elsif index
|
|
108
|
+
load_index(index)
|
|
109
|
+
end
|
|
93
110
|
@start_position = tell
|
|
94
111
|
end
|
|
95
112
|
|
|
@@ -97,36 +114,69 @@ module HTS
|
|
|
97
114
|
check_closed
|
|
98
115
|
|
|
99
116
|
self.class.build_index(@file_name, index_name, min_shift, @nthreads || 0, verbose)
|
|
117
|
+
@index_name = index_name
|
|
118
|
+
@index_load_attempted = false
|
|
100
119
|
self # for method chaining
|
|
101
120
|
end
|
|
102
121
|
|
|
103
122
|
def load_index(index_name = nil)
|
|
104
123
|
check_closed
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
LibHTS.sam_index_load3(@hts_file, @file_name, nil, 2) # should be 3 ? (copy remote file to local?)
|
|
110
|
-
end
|
|
125
|
+
@index_name = index_name
|
|
126
|
+
@index_load_attempted = true
|
|
127
|
+
@native.load_index(index_name)
|
|
111
128
|
end
|
|
112
129
|
|
|
113
130
|
def index_loaded?
|
|
114
131
|
check_closed
|
|
115
132
|
|
|
116
|
-
|
|
133
|
+
@native.index_loaded?
|
|
117
134
|
end
|
|
118
135
|
|
|
119
136
|
def close
|
|
120
|
-
|
|
121
|
-
@idx = nil
|
|
122
|
-
super
|
|
137
|
+
@native&.close
|
|
123
138
|
end
|
|
124
139
|
|
|
140
|
+
def closed? = @native.nil? || @native.closed?
|
|
141
|
+
def file_format = @native.file_format
|
|
142
|
+
def file_format_version = @native.file_format_version
|
|
143
|
+
|
|
144
|
+
def set_threads(n = nil)
|
|
145
|
+
if n.nil?
|
|
146
|
+
require "etc"
|
|
147
|
+
n = [Etc.nprocessors - 1, 1].max
|
|
148
|
+
end
|
|
149
|
+
raise TypeError unless n.is_a?(Integer)
|
|
150
|
+
raise ArgumentError, "Number of threads must be positive" if n < 1
|
|
151
|
+
raise "Failed to set number of threads: #{n}" if @native.set_threads(n).negative?
|
|
152
|
+
|
|
153
|
+
@nthreads = n
|
|
154
|
+
self
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def seek(offset) = @native.seek(offset)
|
|
158
|
+
def tell = @native.tell
|
|
159
|
+
|
|
160
|
+
def rewind
|
|
161
|
+
raise "Cannot rewind: no start position" unless @start_position
|
|
162
|
+
|
|
163
|
+
result = seek(@start_position)
|
|
164
|
+
raise "Failed to rewind: #{result}" if result.negative?
|
|
165
|
+
|
|
166
|
+
tell
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
def native_handle = @native
|
|
172
|
+
|
|
173
|
+
public
|
|
174
|
+
|
|
125
175
|
def write_header(header)
|
|
126
176
|
check_closed
|
|
127
177
|
|
|
128
178
|
@header = header.dup
|
|
129
|
-
|
|
179
|
+
@native.write_header(header.__send__(:native_handle))
|
|
130
180
|
end
|
|
131
181
|
|
|
132
182
|
def header=(header)
|
|
@@ -136,7 +186,7 @@ module HTS
|
|
|
136
186
|
def write(record)
|
|
137
187
|
check_closed
|
|
138
188
|
|
|
139
|
-
r =
|
|
189
|
+
r = @native.write(header.__send__(:native_handle), record.__send__(:native_handle))
|
|
140
190
|
raise "Failed to write record" if r < 0
|
|
141
191
|
end
|
|
142
192
|
|
|
@@ -172,6 +222,13 @@ module HTS
|
|
|
172
222
|
seek(position) if position
|
|
173
223
|
ary
|
|
174
224
|
end
|
|
225
|
+
alias aux_array aux
|
|
226
|
+
|
|
227
|
+
# Materialize independent records from the current stream position.
|
|
228
|
+
# Unlike each.to_a, every element owns its bam1_t storage.
|
|
229
|
+
def collect_records
|
|
230
|
+
each(copy: true).to_a
|
|
231
|
+
end
|
|
175
232
|
|
|
176
233
|
# @!macro [attach] define_iterator
|
|
177
234
|
# @method each_$1
|
|
@@ -236,7 +293,7 @@ module HTS
|
|
|
236
293
|
# bam.query(["chr1:100-200", "chr2:500-600"]) { |r| puts r.qname }
|
|
237
294
|
def query(region, beg = nil, end_ = nil, copy: false, &block)
|
|
238
295
|
check_closed
|
|
239
|
-
raise "Index file is required to call the query method." unless
|
|
296
|
+
raise "Index file is required to call the query method." unless ensure_index_loaded
|
|
240
297
|
|
|
241
298
|
case region
|
|
242
299
|
when Array
|
|
@@ -280,6 +337,13 @@ module HTS
|
|
|
280
337
|
|
|
281
338
|
private
|
|
282
339
|
|
|
340
|
+
def ensure_index_loaded
|
|
341
|
+
return true if index_loaded?
|
|
342
|
+
return false if @index_load_attempted
|
|
343
|
+
|
|
344
|
+
load_index(@index_name)
|
|
345
|
+
end
|
|
346
|
+
|
|
283
347
|
def queryi(tid, beg, end_, copy: false, &block)
|
|
284
348
|
if copy
|
|
285
349
|
queryi_copy(tid, beg, end_, &block)
|
|
@@ -313,9 +377,8 @@ module HTS
|
|
|
313
377
|
# This is the common behavior of IO objects in Ruby.
|
|
314
378
|
return to_enum(__method__) unless block_given?
|
|
315
379
|
|
|
316
|
-
|
|
317
|
-
record
|
|
318
|
-
yield record while LibHTS.sam_read1(@hts_file, header, bam1) != -1
|
|
380
|
+
record = Record.new(header)
|
|
381
|
+
yield record while @native.read(header.__send__(:native_handle), record.__send__(:native_handle)) != -1
|
|
319
382
|
self
|
|
320
383
|
end
|
|
321
384
|
|
|
@@ -324,17 +387,16 @@ module HTS
|
|
|
324
387
|
check_closed
|
|
325
388
|
return to_enum(__method__) unless block_given?
|
|
326
389
|
|
|
327
|
-
|
|
328
|
-
record
|
|
329
|
-
yield record.dup while LibHTS.sam_read1(@hts_file, header, bam1) != -1
|
|
390
|
+
record = Record.new(header)
|
|
391
|
+
yield record.dup while @native.read(header.__send__(:native_handle), record.__send__(:native_handle)) != -1
|
|
330
392
|
self
|
|
331
393
|
end
|
|
332
394
|
|
|
333
395
|
def queryi_reuse(tid, beg, end_, &block)
|
|
334
396
|
return to_enum(__method__, tid, beg, end_) unless block_given?
|
|
335
397
|
|
|
336
|
-
qiter =
|
|
337
|
-
raise "Failed to query region: #{tid} #{beg} #{end_}"
|
|
398
|
+
qiter = @native.query_interval(tid, beg, end_)
|
|
399
|
+
raise "Failed to query region: #{tid} #{beg} #{end_}" unless qiter
|
|
338
400
|
|
|
339
401
|
query_reuse_yield(qiter, &block)
|
|
340
402
|
self
|
|
@@ -343,8 +405,8 @@ module HTS
|
|
|
343
405
|
def queryi_copy(tid, beg, end_, &block)
|
|
344
406
|
return to_enum(__method__, tid, beg, end_) unless block_given?
|
|
345
407
|
|
|
346
|
-
qiter =
|
|
347
|
-
raise "Failed to query region: #{tid} #{beg} #{end_}"
|
|
408
|
+
qiter = @native.query_interval(tid, beg, end_)
|
|
409
|
+
raise "Failed to query region: #{tid} #{beg} #{end_}" unless qiter
|
|
348
410
|
|
|
349
411
|
query_copy(qiter, &block)
|
|
350
412
|
self
|
|
@@ -353,8 +415,8 @@ module HTS
|
|
|
353
415
|
def querys_reuse(region, &block)
|
|
354
416
|
return to_enum(__method__, region) unless block_given?
|
|
355
417
|
|
|
356
|
-
qiter =
|
|
357
|
-
raise "Failed to query region: #{region}"
|
|
418
|
+
qiter = @native.query_region(header.__send__(:native_handle), region)
|
|
419
|
+
raise "Failed to query region: #{region}" unless qiter
|
|
358
420
|
|
|
359
421
|
query_reuse_yield(qiter, &block)
|
|
360
422
|
self
|
|
@@ -363,8 +425,8 @@ module HTS
|
|
|
363
425
|
def querys_copy(region, &block)
|
|
364
426
|
return to_enum(__method__, region) unless block_given?
|
|
365
427
|
|
|
366
|
-
qiter =
|
|
367
|
-
raise "Failed to query region: #{region}"
|
|
428
|
+
qiter = @native.query_region(header.__send__(:native_handle), region)
|
|
429
|
+
raise "Failed to query region: #{region}" unless qiter
|
|
368
430
|
|
|
369
431
|
query_copy(qiter, &block)
|
|
370
432
|
self
|
|
@@ -372,30 +434,28 @@ module HTS
|
|
|
372
434
|
|
|
373
435
|
# Internal: reused-Record iterator over a query iterator.
|
|
374
436
|
def query_reuse_yield(qiter)
|
|
375
|
-
|
|
376
|
-
record = Record.new(header, bam1)
|
|
437
|
+
record = Record.new(header)
|
|
377
438
|
begin
|
|
378
|
-
while (slen =
|
|
439
|
+
while (slen = qiter.next(record.__send__(:native_handle))) >= 0
|
|
379
440
|
yield record
|
|
380
441
|
end
|
|
381
442
|
raise if slen < -1
|
|
382
443
|
ensure
|
|
383
|
-
|
|
444
|
+
qiter.close
|
|
384
445
|
end
|
|
385
446
|
end
|
|
386
447
|
|
|
387
448
|
def query_copy(qiter)
|
|
388
|
-
|
|
389
|
-
record = Record.new(header, bam1)
|
|
449
|
+
record = Record.new(header)
|
|
390
450
|
loop do
|
|
391
|
-
slen =
|
|
451
|
+
slen = qiter.next(record.__send__(:native_handle))
|
|
392
452
|
break if slen == -1
|
|
393
453
|
raise if slen < -1
|
|
394
454
|
|
|
395
455
|
yield record.dup
|
|
396
456
|
end
|
|
397
457
|
ensure
|
|
398
|
-
|
|
458
|
+
qiter.close
|
|
399
459
|
end
|
|
400
460
|
|
|
401
461
|
# Multi-region query using sequential single-region queries
|
data/lib/hts/bcf/errors.rb
CHANGED
|
@@ -21,6 +21,7 @@ module HTS
|
|
|
21
21
|
class FormatDefinitionError < FormatError; end
|
|
22
22
|
class FormatTypeError < FormatError; end
|
|
23
23
|
class FormatReadError < FormatError; end
|
|
24
|
+
class InvalidBorrowedViewError < FormatError; end
|
|
24
25
|
class FormatUpdateError < FormatError; end
|
|
25
26
|
class UnsupportedFormatOperationError < FormatError; end
|
|
26
27
|
end
|