minibwa 0.0.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +119 -0
- data/ext/minibwa/extconf.rb +105 -0
- data/ext/minibwa/mb_buffer.c +113 -0
- data/ext/minibwa/mb_hit.c +192 -0
- data/ext/minibwa/mb_index.c +462 -0
- data/ext/minibwa/mb_index_build.c +174 -0
- data/ext/minibwa/mb_options.c +301 -0
- data/ext/minibwa/minibwa/LICENSE.txt +37 -0
- data/ext/minibwa/minibwa/align.c +930 -0
- data/ext/minibwa/minibwa/bseq.h +45 -0
- data/ext/minibwa/minibwa/bwt.c +715 -0
- data/ext/minibwa/minibwa/bwt.h +86 -0
- data/ext/minibwa/minibwa/cs.c +161 -0
- data/ext/minibwa/minibwa/format.c +356 -0
- data/ext/minibwa/minibwa/index.c +342 -0
- data/ext/minibwa/minibwa/kalloc.c +224 -0
- data/ext/minibwa/minibwa/kalloc.h +54 -0
- data/ext/minibwa/minibwa/ketopt.h +123 -0
- data/ext/minibwa/minibwa/kommon.c +374 -0
- data/ext/minibwa/minibwa/kommon.h +85 -0
- data/ext/minibwa/minibwa/kseq.h +256 -0
- data/ext/minibwa/minibwa/ksort.h +163 -0
- data/ext/minibwa/minibwa/ksw2.h +220 -0
- data/ext/minibwa/minibwa/ksw2_extd2_sse.c +403 -0
- data/ext/minibwa/minibwa/ksw2_extz2_sse.c +296 -0
- data/ext/minibwa/minibwa/ksw2_ll_sse.c +341 -0
- data/ext/minibwa/minibwa/kthread.h +15 -0
- data/ext/minibwa/minibwa/l2bit.c +479 -0
- data/ext/minibwa/minibwa/l2bit.h +72 -0
- data/ext/minibwa/minibwa/lchain.c +231 -0
- data/ext/minibwa/minibwa/libsais.c +6985 -0
- data/ext/minibwa/minibwa/libsais.h +106 -0
- data/ext/minibwa/minibwa/libsais64.c +7064 -0
- data/ext/minibwa/minibwa/libsais64.h +81 -0
- data/ext/minibwa/minibwa/map-algo.c +769 -0
- data/ext/minibwa/minibwa/mbpriv.h +148 -0
- data/ext/minibwa/minibwa/minibwa.h +176 -0
- data/ext/minibwa/minibwa/options.c +116 -0
- data/ext/minibwa/minibwa/pe.c +559 -0
- data/ext/minibwa/minibwa/s2n-lite.h +59 -0
- data/ext/minibwa/minibwa/seed.c +354 -0
- data/ext/minibwa/minibwa.c +67 -0
- data/ext/minibwa/minibwa.h +51 -0
- data/lib/minibwa/hit.rb +110 -0
- data/lib/minibwa/index.rb +77 -0
- data/lib/minibwa/options.rb +235 -0
- data/lib/minibwa/sam.rb +85 -0
- data/lib/minibwa/version.rb +6 -0
- data/lib/minibwa.rb +11 -0
- metadata +88 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5d54a100aaa8439c2dc2fb9b686ca60f4898ea0873370f3dc5b58bfd5af1b7fa
|
|
4
|
+
data.tar.gz: 17911037009111f945b2f2621ccdc1e6b51928748585cc3753d6341250cca51e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0e3a98d1f7e2652f16d3537bbbe7a2e3b89ff05dad6b5f1c166db505a465cdb1f854bbd2970bca0c1c0152f8f5fa4bc10bf7ef4a716a4c9001663bf35ec77743
|
|
7
|
+
data.tar.gz: 0c99b08294dcaaff5f896da4516c4c52df07cb74e879e58c7eea40f795235ac95b92535e7a86dec93cd900ee6257d4ea4e5e42b199fe4705fe1faa851d0cdb39
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kojix2
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# ruby-minibwa
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/minibwa)
|
|
4
|
+
[](https://github.com/kojix2/ruby-minibwa/actions/workflows/test.yml)
|
|
5
|
+
[](https://tokei.kojix2.net/github/kojix2/ruby-minibwa)
|
|
6
|
+
|
|
7
|
+
Ruby bindings for [minibwa](https://github.com/lh3/minibwa), a short-read
|
|
8
|
+
aligner combining bwa-mem seeding with minimap2 chaining and alignment.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Requires a C compiler, zlib, and Ruby >= 3.2.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
git clone --recursive https://github.com/kojix2/ruby-minibwa
|
|
16
|
+
cd ruby-minibwa
|
|
17
|
+
bundle install
|
|
18
|
+
bundle exec rake compile
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The extension builds the vendored minibwa sources, including libsais for
|
|
22
|
+
index construction. Expect the first build to take a few minutes.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Build an index:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
require "minibwa"
|
|
30
|
+
|
|
31
|
+
Minibwa::Index.build("ref.fa", "ref", n_thread: 4)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This writes `ref.l2b` and `ref.mbw`. Indexes made by the `minibwa index`
|
|
35
|
+
command are also usable.
|
|
36
|
+
|
|
37
|
+
Map reads:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
opt = Minibwa::Options.new
|
|
41
|
+
opt.preset("sr") # or "adap", "lr"
|
|
42
|
+
|
|
43
|
+
idx = Minibwa::Index.load("ref")
|
|
44
|
+
|
|
45
|
+
hits = idx.map("ACGT...", name: "read1", opt: opt)
|
|
46
|
+
hits.each do |h|
|
|
47
|
+
puts [h.ctg, h.ts, h.te, h.strand, h.qs, h.qe, h.mapq, h.cigar_str].join("\t")
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Map many reads in one call:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
seqs = ["ACGT...", "TGCA..."]
|
|
55
|
+
names = ["read1", "read2"]
|
|
56
|
+
hits = idx.map_batch(seqs, names: names, opt: opt)
|
|
57
|
+
# => [[Hit, ...], [Hit, ...]]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use a reusable buffer for repeated mapping:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
buf = Minibwa::Buffer.new
|
|
64
|
+
idx.map(seq, buf: buf)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Generate SAM output:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
puts Minibwa::Sam.header(idx)
|
|
71
|
+
hits.each do |h|
|
|
72
|
+
puts Minibwa::Sam.format("read1", 0, h, seq)
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Note on reverse-strand alignments:** For hits where `hit.rev` is true,
|
|
77
|
+
the SAM spec requires SEQ to be the reverse complement of the original
|
|
78
|
+
query and the FLAG to have 0x10 set. `Sam.format` does not do this
|
|
79
|
+
automatically — pass the already-oriented sequence and the correct flag.
|
|
80
|
+
The library does not retain the original query after mapping.
|
|
81
|
+
|
|
82
|
+
With `pe: true`, `map_batch` treats consecutive sequences as read pairs.
|
|
83
|
+
|
|
84
|
+
### CIGAR
|
|
85
|
+
|
|
86
|
+
`Hit#cigar` and `#cigar_str` cover the aligned region only and contain no
|
|
87
|
+
clipping, which is how minibwa reports alignments. `#full_cigar_str`
|
|
88
|
+
takes the query length and adds soft clips derived from `qs` and `qe`.
|
|
89
|
+
|
|
90
|
+
### Threads
|
|
91
|
+
|
|
92
|
+
`Options#n_thread` affects only `Index.build` (via libsais + OpenMP).
|
|
93
|
+
Mapping itself is single-threaded per call, but releases the GVL, so Ruby
|
|
94
|
+
threads run in parallel. To map from several Ruby threads, give each one
|
|
95
|
+
its own buffer:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
buf = Minibwa::Buffer.new
|
|
99
|
+
idx.map(seq, opt: opt, buf: buf)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
A `Buffer` holds mutable scratch state and must not be shared between
|
|
103
|
+
threads mapping at the same time.
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
bundle exec rake compile
|
|
109
|
+
bundle exec rake test
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`bin/console` gives an IRB session with the extension loaded.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT. The vendored minibwa is MIT as well; its GPL-licensed files
|
|
117
|
+
(`bwtgen.c`, `QSufSort.c`, providing the low-memory BWT construction
|
|
118
|
+
option) are neither compiled nor distributed. See
|
|
119
|
+
`ext/minibwa/minibwa/LICENSE.txt`.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Builds the binding together with the minibwa sources from the submodule.
|
|
4
|
+
#
|
|
5
|
+
# There is no shared libminibwa to link against, so the C library is compiled
|
|
6
|
+
# into the extension. Upstream's Makefile is the reference for which files and
|
|
7
|
+
# flags are needed; api-test/Makefile shows that the public mapping API needs
|
|
8
|
+
# only libminibwa.a (the LOBJS group) plus -lz -lm.
|
|
9
|
+
|
|
10
|
+
require 'mkmf'
|
|
11
|
+
|
|
12
|
+
upstream = File.join(__dir__, 'minibwa')
|
|
13
|
+
|
|
14
|
+
unless File.exist?(File.join(upstream, 'minibwa.h'))
|
|
15
|
+
abort 'minibwa sources not found. Run: git submodule update --init --recursive'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# --- source selection ------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
# The LOBJS group of upstream's Makefile: everything behind the public API.
|
|
21
|
+
core = %w[
|
|
22
|
+
kommon kalloc bwt l2bit options seed map-algo lchain align pe cs format
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# SSE alignment kernels. On ARM these fall back to s2n-lite.h (NEON) on their
|
|
26
|
+
# own, so no extra flag is needed there.
|
|
27
|
+
ksw2 = %w[ksw2_extz2_sse ksw2_extd2_sse ksw2_ll_sse]
|
|
28
|
+
|
|
29
|
+
# Suffix array construction, needed only by Index.build.
|
|
30
|
+
sais = %w[libsais libsais64]
|
|
31
|
+
|
|
32
|
+
# Deliberately NOT compiled:
|
|
33
|
+
#
|
|
34
|
+
# index.c mb_index_build.c #includes it to reach the static
|
|
35
|
+
# mb_bwt_libsais(); compiling it too would define every
|
|
36
|
+
# symbol in it twice.
|
|
37
|
+
# bwtgen.c GPL, and the low-memory BWT construction they implement is
|
|
38
|
+
# QSufSort.c not offered by this gem. Keeping them out keeps it MIT.
|
|
39
|
+
# mimalloc/ replaces malloc process-wide, which is not something a Ruby
|
|
40
|
+
# extension may do to its host. See HAVE_KALLOC below.
|
|
41
|
+
# main.c the command line tool.
|
|
42
|
+
# fastmap.c
|
|
43
|
+
# map-main.c
|
|
44
|
+
# bseq.c FASTX reading and thread pool for the CLI; reading sequences
|
|
45
|
+
# kthread.c is the caller's job in Ruby.
|
|
46
|
+
|
|
47
|
+
binding_srcs = %w[minibwa mb_options mb_index mb_index_build mb_buffer mb_hit]
|
|
48
|
+
|
|
49
|
+
$srcs = binding_srcs.map { |f| "#{f}.c" } +
|
|
50
|
+
(core + ksw2 + sais).map { |f| "minibwa/#{f}.c" }
|
|
51
|
+
$objs = $srcs.map { |f| "#{File.basename(f, '.c')}.o" }
|
|
52
|
+
$VPATH << '$(srcdir)/minibwa'
|
|
53
|
+
|
|
54
|
+
# So that our own sources can say #include "minibwa/minibwa.h".
|
|
55
|
+
$INCFLAGS << ' -I$(srcdir)'
|
|
56
|
+
|
|
57
|
+
# --- libraries -------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
# l2bit.c reads FASTA through kseq/gzip.
|
|
60
|
+
abort 'zlib is required' unless have_header('zlib.h') && have_library('z', 'gzopen')
|
|
61
|
+
have_library('m', 'log')
|
|
62
|
+
|
|
63
|
+
# --- flags -----------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
# Required, and quietly consequential: without it mb_opt_init() sets
|
|
66
|
+
# MB_F_NO_KALLOC and every allocation goes to the system allocator. Upstream
|
|
67
|
+
# defines it whenever mimalloc is not linked, which for us is always. The
|
|
68
|
+
# build succeeds either way, so a missing -DHAVE_KALLOC shows up only as lost
|
|
69
|
+
# performance.
|
|
70
|
+
$defs << '-DHAVE_KALLOC'
|
|
71
|
+
|
|
72
|
+
# Keep optional CPU instructions local to the alignment kernels so the rest
|
|
73
|
+
# of a precompiled extension remains compatible with baseline x86_64 CPUs.
|
|
74
|
+
sse_cflags = if RbConfig::CONFIG['host_cpu'].match?(/x86_64|x64|amd64/i)
|
|
75
|
+
%w[-msse4.2 -mpopcnt].select { |flag| try_cflags(flag) }
|
|
76
|
+
else
|
|
77
|
+
[]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# libsais parallelises index construction with OpenMP; Options#n_thread has no
|
|
81
|
+
# effect on Index.build without it. Everything else is single-threaded here,
|
|
82
|
+
# so this is optional. Disable with --disable-openmp.
|
|
83
|
+
if enable_config('openmp', true) && try_compile("#include <omp.h>\nint main(void){return 0;}", '-fopenmp')
|
|
84
|
+
$defs << '-DLIBSAIS_OPENMP'
|
|
85
|
+
append_cflags('-fopenmp')
|
|
86
|
+
append_ldflags('-fopenmp')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Hide upstream's symbols. ksw2 and kalloc names are shared with minimap2 and
|
|
90
|
+
# other lh3 libraries, which may be loaded into the same process by another
|
|
91
|
+
# gem.
|
|
92
|
+
append_cflags('-fvisibility=hidden')
|
|
93
|
+
|
|
94
|
+
create_makefile('minibwa/minibwa')
|
|
95
|
+
|
|
96
|
+
unless sse_cflags.empty?
|
|
97
|
+
File.open('Makefile', 'a') do |makefile|
|
|
98
|
+
ksw2.each do |source|
|
|
99
|
+
makefile.puts
|
|
100
|
+
makefile.puts "#{source}.o: $(srcdir)/minibwa/#{source}.c"
|
|
101
|
+
makefile.puts "\t$(ECHO) compiling $<"
|
|
102
|
+
makefile.puts "\t$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{sse_cflags.join(' ')} $(COUTFLAG)$@ -c $(CSRCFLAG)$<"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document-class: Minibwa::Buffer
|
|
3
|
+
*
|
|
4
|
+
* Minibwa::Buffer -- wraps mb_tbuf_t, the reusable per-thread scratch buffer.
|
|
5
|
+
*
|
|
6
|
+
* Passing a Buffer to Index#map saves an mb_tbuf_init/mb_tbuf_destroy pair on
|
|
7
|
+
* every call; passing nil lets mb_map() allocate one internally. A Buffer
|
|
8
|
+
* carries mutable working state and must never be shared between threads that
|
|
9
|
+
* map concurrently -- one Buffer per thread.
|
|
10
|
+
*
|
|
11
|
+
* Freed with mb_tbuf_destroy().
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#include "minibwa.h"
|
|
15
|
+
|
|
16
|
+
static void
|
|
17
|
+
rb_minibwa_buffer_free(void *ptr)
|
|
18
|
+
{
|
|
19
|
+
mb_tbuf_t *b = (mb_tbuf_t *)ptr;
|
|
20
|
+
if (b) mb_tbuf_destroy(b);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static size_t
|
|
24
|
+
rb_minibwa_buffer_memsize(const void *ptr)
|
|
25
|
+
{
|
|
26
|
+
/* mb_tbuf_t is an opaque type; report the pointer size only. */
|
|
27
|
+
(void)ptr;
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rb_data_type_t rb_minibwa_buffer_type = {
|
|
32
|
+
.wrap_struct_name = "Minibwa::Buffer",
|
|
33
|
+
.function = {
|
|
34
|
+
NULL,
|
|
35
|
+
rb_minibwa_buffer_free,
|
|
36
|
+
rb_minibwa_buffer_memsize,
|
|
37
|
+
},
|
|
38
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/* ------------------------------------------------------------------ */
|
|
42
|
+
/* Constructor */
|
|
43
|
+
/* ------------------------------------------------------------------ */
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* call-seq:
|
|
47
|
+
* Buffer.new(no_kalloc: false) -> Buffer
|
|
48
|
+
*
|
|
49
|
+
* Allocates a new thread-local scratch buffer. Set +no_kalloc:+ to +true+
|
|
50
|
+
* to disable the internal allocator.
|
|
51
|
+
*/
|
|
52
|
+
static VALUE
|
|
53
|
+
rb_minibwa_buffer_initialize(int argc, VALUE *argv, VALUE self)
|
|
54
|
+
{
|
|
55
|
+
VALUE kwargs;
|
|
56
|
+
rb_scan_args(argc, argv, ":", &kwargs);
|
|
57
|
+
|
|
58
|
+
int no_kalloc = 0;
|
|
59
|
+
if (!NIL_P(kwargs)) {
|
|
60
|
+
VALUE v = rb_hash_aref(kwargs, ID2SYM(rb_intern("no_kalloc")));
|
|
61
|
+
if (RTEST(v)) no_kalloc = 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* The buffer was allocated by alloc with the default (kalloc enabled).
|
|
65
|
+
* If the caller wants no_kalloc, rebuild it through the public API. */
|
|
66
|
+
if (no_kalloc) {
|
|
67
|
+
mb_tbuf_t *b = rb_minibwa_get_tbuf(self);
|
|
68
|
+
if (b) mb_tbuf_destroy(b);
|
|
69
|
+
b = mb_tbuf_init(1);
|
|
70
|
+
if (!b) rb_raise(rb_eMinibwaError, "mb_tbuf_init failed");
|
|
71
|
+
DATA_PTR(self) = b;
|
|
72
|
+
}
|
|
73
|
+
return Qnil;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static VALUE
|
|
77
|
+
rb_minibwa_buffer_alloc(VALUE klass)
|
|
78
|
+
{
|
|
79
|
+
mb_tbuf_t *b = mb_tbuf_init(0);
|
|
80
|
+
VALUE obj = TypedData_Wrap_Struct(klass, &rb_minibwa_buffer_type, b);
|
|
81
|
+
return obj;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* call-seq:
|
|
86
|
+
* reset!(max_block_size) -> Integer
|
|
87
|
+
*
|
|
88
|
+
* Resets the buffer for reuse, optionally setting a new maximum block size.
|
|
89
|
+
* Returns the new capacity.
|
|
90
|
+
*/
|
|
91
|
+
static VALUE
|
|
92
|
+
rb_minibwa_buffer_reset(VALUE self, VALUE max_block_size)
|
|
93
|
+
{
|
|
94
|
+
mb_tbuf_t *b = rb_minibwa_get_tbuf(self);
|
|
95
|
+
int64_t mbs = NUM2LL(max_block_size);
|
|
96
|
+
int32_t cap = mb_tbuf_reset(b, mbs);
|
|
97
|
+
return INT2NUM(cap);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ------------------------------------------------------------------ */
|
|
101
|
+
/* Registration */
|
|
102
|
+
/* ------------------------------------------------------------------ */
|
|
103
|
+
|
|
104
|
+
void
|
|
105
|
+
rb_minibwa_init_buffer(void)
|
|
106
|
+
{
|
|
107
|
+
VALUE klass = rb_define_class_under(rb_mMinibwa, "Buffer", rb_cObject);
|
|
108
|
+
rb_cMinibwaBuffer = klass;
|
|
109
|
+
|
|
110
|
+
rb_define_alloc_func(klass, rb_minibwa_buffer_alloc);
|
|
111
|
+
rb_define_method(klass, "initialize", rb_minibwa_buffer_initialize, -1);
|
|
112
|
+
rb_define_method(klass, "reset!", rb_minibwa_buffer_reset, 1);
|
|
113
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Minibwa::Hit -- one alignment produced by mb_map() / mb_map_batch().
|
|
3
|
+
*
|
|
4
|
+
* mb_hit_t is converted eagerly into a plain Ruby object and the C memory is
|
|
5
|
+
* released immediately, because the caller owns both hit[i].p and the hit
|
|
6
|
+
* array. Copying up front costs one pass but removes every question about
|
|
7
|
+
* object lifetime, and keeps a Hit usable after its Index is gone.
|
|
8
|
+
*
|
|
9
|
+
* The CIGAR in mb_extra_t is unpacked into an array of [length, op] pairs.
|
|
10
|
+
* It describes the aligned region only and contains no soft or hard clipping;
|
|
11
|
+
* clips have to be reconstructed from qs/qe (see lib/minibwa/hit.rb).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#include "minibwa.h"
|
|
15
|
+
|
|
16
|
+
/* ------------------------------------------------------------------ */
|
|
17
|
+
/* Single hit conversion */
|
|
18
|
+
/* ------------------------------------------------------------------ */
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Builds a Minibwa::Hit from an mb_hit_t. Takes ownership of hit->p
|
|
22
|
+
* (frees it). The idx is needed for ctg_name lookups.
|
|
23
|
+
*/
|
|
24
|
+
VALUE
|
|
25
|
+
rb_minibwa_hit_new(const mb_idx_t *idx, mb_hit_t *hit)
|
|
26
|
+
{
|
|
27
|
+
VALUE klass = rb_cMinibwaHit;
|
|
28
|
+
VALUE obj = rb_obj_alloc(klass);
|
|
29
|
+
|
|
30
|
+
/* --- scalar fields --- */
|
|
31
|
+
rb_ivar_set(obj, rb_intern("@tid"), LL2NUM(hit->tid));
|
|
32
|
+
rb_ivar_set(obj, rb_intern("@ts"), LL2NUM(hit->ts));
|
|
33
|
+
rb_ivar_set(obj, rb_intern("@te"), LL2NUM(hit->te));
|
|
34
|
+
rb_ivar_set(obj, rb_intern("@qs"), INT2NUM(hit->qs));
|
|
35
|
+
rb_ivar_set(obj, rb_intern("@qe"), INT2NUM(hit->qe));
|
|
36
|
+
rb_ivar_set(obj, rb_intern("@score"), INT2NUM(hit->score));
|
|
37
|
+
rb_ivar_set(obj, rb_intern("@score0"), INT2NUM(hit->score0));
|
|
38
|
+
rb_ivar_set(obj, rb_intern("@mlen"), INT2NUM(hit->mlen));
|
|
39
|
+
rb_ivar_set(obj, rb_intern("@blen"), INT2NUM(hit->blen));
|
|
40
|
+
rb_ivar_set(obj, rb_intern("@mapq"), INT2NUM(hit->mapq));
|
|
41
|
+
rb_ivar_set(obj, rb_intern("@cnt"), INT2NUM(hit->cnt));
|
|
42
|
+
rb_ivar_set(obj, rb_intern("@n_sub"), INT2NUM(hit->n_sub));
|
|
43
|
+
rb_ivar_set(obj, rb_intern("@subsc"), INT2NUM(hit->subsc));
|
|
44
|
+
rb_ivar_set(obj, rb_intern("@hash"), UINT2NUM(hit->hash));
|
|
45
|
+
|
|
46
|
+
/* --- bit fields --- */
|
|
47
|
+
rb_ivar_set(obj, rb_intern("@rev"), hit->rev ? Qtrue : Qfalse);
|
|
48
|
+
rb_ivar_set(obj, rb_intern("@proper_pair"), hit->proper_pair ? Qtrue : Qfalse);
|
|
49
|
+
rb_ivar_set(obj, rb_intern("@sam_pri"), hit->sam_pri ? Qtrue : Qfalse);
|
|
50
|
+
rb_ivar_set(obj, rb_intern("@flt"), hit->flt ? Qtrue : Qfalse);
|
|
51
|
+
rb_ivar_set(obj, rb_intern("@inv"), hit->inv ? Qtrue : Qfalse);
|
|
52
|
+
rb_ivar_set(obj, rb_intern("@split"), INT2NUM(hit->split));
|
|
53
|
+
rb_ivar_set(obj, rb_intern("@split_inv"), hit->split_inv ? Qtrue : Qfalse);
|
|
54
|
+
rb_ivar_set(obj, rb_intern("@rescued"), hit->rescued ? Qtrue : Qfalse);
|
|
55
|
+
rb_ivar_set(obj, rb_intern("@frac_high"), INT2NUM(hit->frac_high));
|
|
56
|
+
rb_ivar_set(obj, rb_intern("@seed_ratio"), INT2NUM(hit->seed_ratio));
|
|
57
|
+
|
|
58
|
+
/* --- contig name (lazy: store tid, resolve in Ruby) --- */
|
|
59
|
+
const char *ctg = mb_idx_ctg_name(idx, (int32_t)hit->tid);
|
|
60
|
+
rb_ivar_set(obj, rb_intern("@ctg"),
|
|
61
|
+
ctg ? rb_str_new_cstr(ctg) : Qnil);
|
|
62
|
+
|
|
63
|
+
/* --- extra (CIGAR, DP scores) --- */
|
|
64
|
+
if (hit->p) {
|
|
65
|
+
mb_extra_t *p = hit->p;
|
|
66
|
+
|
|
67
|
+
rb_ivar_set(obj, rb_intern("@dp_score"), INT2NUM(p->dp_score));
|
|
68
|
+
rb_ivar_set(obj, rb_intern("@dp_max0"), INT2NUM(p->dp_max0));
|
|
69
|
+
rb_ivar_set(obj, rb_intern("@dp_max"), INT2NUM(p->dp_max));
|
|
70
|
+
rb_ivar_set(obj, rb_intern("@dp_max2"), INT2NUM(p->dp_max2));
|
|
71
|
+
rb_ivar_set(obj, rb_intern("@n_ambi"), UINT2NUM(p->n_ambi));
|
|
72
|
+
rb_ivar_set(obj, rb_intern("@cs_flag"), p->cs ? Qtrue : Qfalse);
|
|
73
|
+
|
|
74
|
+
/* CIGAR: array of [length, op] pairs */
|
|
75
|
+
VALUE cigar = rb_ary_new_capa(p->n_cigar);
|
|
76
|
+
for (int32_t i = 0; i < p->n_cigar; i++) {
|
|
77
|
+
uint32_t c = p->cigar[i];
|
|
78
|
+
VALUE pair = rb_ary_new_from_args(2,
|
|
79
|
+
UINT2NUM(c >> 4),
|
|
80
|
+
INT2NUM(c & 0xf));
|
|
81
|
+
rb_ary_push(cigar, pair);
|
|
82
|
+
}
|
|
83
|
+
rb_ivar_set(obj, rb_intern("@cigar"), cigar);
|
|
84
|
+
|
|
85
|
+
free(p);
|
|
86
|
+
hit->p = NULL; /* mark as consumed for the ensure handler */
|
|
87
|
+
} else {
|
|
88
|
+
rb_ivar_set(obj, rb_intern("@dp_score"), INT2NUM(0));
|
|
89
|
+
rb_ivar_set(obj, rb_intern("@dp_max0"), INT2NUM(0));
|
|
90
|
+
rb_ivar_set(obj, rb_intern("@dp_max"), INT2NUM(0));
|
|
91
|
+
rb_ivar_set(obj, rb_intern("@dp_max2"), INT2NUM(0));
|
|
92
|
+
rb_ivar_set(obj, rb_intern("@n_ambi"), INT2NUM(0));
|
|
93
|
+
rb_ivar_set(obj, rb_intern("@cs_flag"), Qfalse);
|
|
94
|
+
rb_ivar_set(obj, rb_intern("@cigar"), rb_ary_new());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return obj;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ------------------------------------------------------------------ */
|
|
101
|
+
/* Hit array conversion */
|
|
102
|
+
/* ------------------------------------------------------------------ */
|
|
103
|
+
|
|
104
|
+
struct hit_ary_ctx {
|
|
105
|
+
const mb_idx_t *idx;
|
|
106
|
+
mb_hit_t *hits;
|
|
107
|
+
int32_t n_hit;
|
|
108
|
+
VALUE ary;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
static VALUE
|
|
112
|
+
rb_minibwa_hit_ary_convert(VALUE ptr)
|
|
113
|
+
{
|
|
114
|
+
struct hit_ary_ctx *ctx = (struct hit_ary_ctx *)ptr;
|
|
115
|
+
ctx->ary = rb_ary_new_capa(ctx->n_hit);
|
|
116
|
+
for (int32_t i = 0; i < ctx->n_hit; i++) {
|
|
117
|
+
VALUE hit = rb_minibwa_hit_new(ctx->idx, &ctx->hits[i]);
|
|
118
|
+
rb_ary_push(ctx->ary, hit);
|
|
119
|
+
}
|
|
120
|
+
return ctx->ary;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static VALUE
|
|
124
|
+
rb_minibwa_hit_ary_free_hits(VALUE ptr)
|
|
125
|
+
{
|
|
126
|
+
struct hit_ary_ctx *ctx = (struct hit_ary_ctx *)ptr;
|
|
127
|
+
/* If conversion raised, free any remaining hit[i].p and the array.
|
|
128
|
+
* rb_minibwa_hit_new() sets hit->p to NULL after freeing, so already-
|
|
129
|
+
* converted hits are skipped. */
|
|
130
|
+
for (int32_t i = 0; i < ctx->n_hit; i++)
|
|
131
|
+
free(ctx->hits[i].p);
|
|
132
|
+
free(ctx->hits);
|
|
133
|
+
return Qnil;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/*
|
|
137
|
+
* Converts an array of mb_hit_t to a Ruby Array of Minibwa::Hit.
|
|
138
|
+
* Takes ownership of the hit array and all hit[i].p (frees them).
|
|
139
|
+
* If conversion raises, the array is still freed before re-raising.
|
|
140
|
+
*/
|
|
141
|
+
VALUE
|
|
142
|
+
rb_minibwa_hit_ary_new(const mb_idx_t *idx, mb_hit_t *hits, int32_t n_hit)
|
|
143
|
+
{
|
|
144
|
+
struct hit_ary_ctx ctx = { idx, hits, n_hit, Qnil };
|
|
145
|
+
return rb_ensure(rb_minibwa_hit_ary_convert, (VALUE)&ctx,
|
|
146
|
+
rb_minibwa_hit_ary_free_hits, (VALUE)&ctx);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* ------------------------------------------------------------------ */
|
|
150
|
+
/* Registration */
|
|
151
|
+
/* ------------------------------------------------------------------ */
|
|
152
|
+
|
|
153
|
+
void
|
|
154
|
+
rb_minibwa_init_hit(void)
|
|
155
|
+
{
|
|
156
|
+
VALUE klass = rb_define_class_under(rb_mMinibwa, "Hit", rb_cObject);
|
|
157
|
+
rb_cMinibwaHit = klass;
|
|
158
|
+
|
|
159
|
+
/* attr_readers for all fields */
|
|
160
|
+
rb_define_attr(klass, "tid", 1, 0);
|
|
161
|
+
rb_define_attr(klass, "ts", 1, 0);
|
|
162
|
+
rb_define_attr(klass, "te", 1, 0);
|
|
163
|
+
rb_define_attr(klass, "qs", 1, 0);
|
|
164
|
+
rb_define_attr(klass, "qe", 1, 0);
|
|
165
|
+
rb_define_attr(klass, "score", 1, 0);
|
|
166
|
+
rb_define_attr(klass, "score0", 1, 0);
|
|
167
|
+
rb_define_attr(klass, "mlen", 1, 0);
|
|
168
|
+
rb_define_attr(klass, "blen", 1, 0);
|
|
169
|
+
rb_define_attr(klass, "mapq", 1, 0);
|
|
170
|
+
rb_define_attr(klass, "cnt", 1, 0);
|
|
171
|
+
rb_define_attr(klass, "n_sub", 1, 0);
|
|
172
|
+
rb_define_attr(klass, "subsc", 1, 0);
|
|
173
|
+
rb_define_attr(klass, "hash", 1, 0);
|
|
174
|
+
rb_define_attr(klass, "rev", 1, 0);
|
|
175
|
+
rb_define_attr(klass, "proper_pair", 1, 0);
|
|
176
|
+
rb_define_attr(klass, "sam_pri", 1, 0);
|
|
177
|
+
rb_define_attr(klass, "flt", 1, 0);
|
|
178
|
+
rb_define_attr(klass, "inv", 1, 0);
|
|
179
|
+
rb_define_attr(klass, "split", 1, 0);
|
|
180
|
+
rb_define_attr(klass, "split_inv", 1, 0);
|
|
181
|
+
rb_define_attr(klass, "rescued", 1, 0);
|
|
182
|
+
rb_define_attr(klass, "frac_high", 1, 0);
|
|
183
|
+
rb_define_attr(klass, "seed_ratio", 1, 0);
|
|
184
|
+
rb_define_attr(klass, "ctg", 1, 0);
|
|
185
|
+
rb_define_attr(klass, "dp_score", 1, 0);
|
|
186
|
+
rb_define_attr(klass, "dp_max0", 1, 0);
|
|
187
|
+
rb_define_attr(klass, "dp_max", 1, 0);
|
|
188
|
+
rb_define_attr(klass, "dp_max2", 1, 0);
|
|
189
|
+
rb_define_attr(klass, "n_ambi", 1, 0);
|
|
190
|
+
rb_define_attr(klass, "cs_flag", 1, 0);
|
|
191
|
+
rb_define_attr(klass, "cigar", 1, 0);
|
|
192
|
+
}
|