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 +4 -4
- data/README.md +60 -20
- data/lib/hts/bam/header.rb +16 -5
- data/lib/hts/bam/record.rb +12 -4
- data/lib/hts/bam.rb +80 -45
- data/lib/hts/bcf/format.rb +26 -5
- data/lib/hts/bcf/header.rb +23 -2
- data/lib/hts/bcf/info.rb +26 -5
- data/lib/hts/bcf/record.rb +13 -9
- data/lib/hts/bcf.rb +53 -27
- data/lib/hts/libhts/constants.rb +1 -1
- data/lib/hts/libhts/hts.rb +1 -1
- data/lib/hts/libhts/thread_pool.rb +139 -0
- data/lib/hts/libhts.rb +1 -0
- data/lib/hts/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd81ebe87741d1019acd341189ee0f8fb6ef09d3a10690686b3d40bdfea73076
|
4
|
+
data.tar.gz: 39a7fa296cc1797c7cd6317923b3336c67f0cf16e8c9f7eb8ae8dbef9e6c7f56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
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
|
-
|
44
|
+
A high-level API is under development.
|
45
|
+
Classes such as `Cram` `Bam` `Bcf` `Faidx` `Tabix` are partially implemented.
|
56
46
|
|
57
|
-
|
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
|
-
|
72
|
-
mpos: r.
|
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)
|
data/lib/hts/bam/header.rb
CHANGED
@@ -6,8 +6,8 @@
|
|
6
6
|
module HTS
|
7
7
|
class Bam
|
8
8
|
class Header
|
9
|
-
def initialize(
|
10
|
-
@sam_hdr =
|
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
|
-
|
26
|
-
|
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
|
data/lib/hts/bam/record.rb
CHANGED
@@ -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
|
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
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
20
|
-
|
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(
|
24
|
-
|
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
|
-
|
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
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
62
|
-
|
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
|
66
|
-
|
70
|
+
def struct
|
71
|
+
@hts_file
|
67
72
|
end
|
68
73
|
|
69
|
-
def
|
70
|
-
|
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.
|
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(
|
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
|
-
|
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(
|
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(
|
142
|
+
slen = LibHTS.sam_itr_next(@hts_file, qiter, bam1)
|
108
143
|
end
|
109
144
|
ensure
|
110
145
|
LibHTS.hts_itr_destroy(qiter)
|
data/lib/hts/bcf/format.rb
CHANGED
@@ -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.
|
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
|
-
|
37
|
-
|
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
|
-
|
61
|
+
# format_values.call(LibHTS::BCF_HT_STR)
|
62
|
+
# .read_string
|
42
63
|
end
|
43
64
|
end
|
44
65
|
|
data/lib/hts/bcf/header.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module HTS
|
4
4
|
class Bcf
|
5
5
|
class Header
|
6
|
-
def initialize(
|
7
|
-
@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 = @
|
14
|
-
h = @record.
|
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.
|
78
|
+
@record.header.struct, LibHTS::BCF_DT_ID, fld[:key]
|
58
79
|
),
|
59
80
|
n: LibHTS.bcf_hdr_id2number(
|
60
|
-
@record.
|
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.
|
97
|
+
@record.header.struct, LibHTS::BCF_DT_ID, fld[:key]
|
77
98
|
)
|
78
99
|
return fld[:type] if id == key
|
79
100
|
end
|
data/lib/hts/bcf/record.rb
CHANGED
@@ -3,13 +3,12 @@
|
|
3
3
|
module HTS
|
4
4
|
class Bcf
|
5
5
|
class Record
|
6
|
-
def initialize(bcf_t,
|
6
|
+
def initialize(bcf_t, header)
|
7
7
|
@bcf1 = bcf_t
|
8
|
-
@
|
9
|
-
@p1 = FFI::MemoryPointer.new(:pointer) # FIXME: naming
|
8
|
+
@header = header
|
10
9
|
end
|
11
10
|
|
12
|
-
attr_reader :
|
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(
|
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(@
|
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(@
|
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(@
|
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
|
-
|
20
|
-
|
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(
|
24
|
-
|
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
|
-
|
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
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
53
|
+
@hts_file
|
48
54
|
end
|
49
55
|
|
50
56
|
def to_ptr
|
51
|
-
|
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(
|
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(
|
63
|
-
record = Record.new(bcf1,
|
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
|
data/lib/hts/libhts/constants.rb
CHANGED
data/lib/hts/libhts/hts.rb
CHANGED
@@ -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,
|
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
data/lib/hts/version.rb
CHANGED
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.
|
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-
|
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.
|
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: []
|