minimap2 0.2.30.3 → 1.2.31.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +48 -63
  3. data/ext/minimap2/Makefile +1 -1
  4. data/ext/minimap2/format.c +12 -2
  5. data/ext/minimap2/hit.c +19 -8
  6. data/ext/minimap2/ksw2_ll_sse.c +2 -2
  7. data/ext/minimap2/minimap.h +2 -1
  8. data/ext/minimap2/pe.c +7 -2
  9. data/ext/ruby_minimap2/extconf.rb +47 -0
  10. data/ext/ruby_minimap2/native_main.c +15 -0
  11. data/ext/ruby_minimap2/ruby_minimap2.c +1134 -0
  12. data/lib/minimap2/aligner.rb +66 -374
  13. data/lib/minimap2/alignment.rb +22 -11
  14. data/lib/minimap2/version.rb +2 -1
  15. data/lib/minimap2.rb +39 -110
  16. metadata +8 -89
  17. data/ext/Rakefile +0 -60
  18. data/ext/cmappy/cmappy.c +0 -134
  19. data/ext/cmappy/cmappy.h +0 -46
  20. data/ext/minimap2/FAQ.md +0 -46
  21. data/ext/minimap2/MANIFEST.in +0 -10
  22. data/ext/minimap2/Makefile.simde +0 -97
  23. data/ext/minimap2/NEWS.md +0 -989
  24. data/ext/minimap2/README.md +0 -428
  25. data/ext/minimap2/code_of_conduct.md +0 -30
  26. data/ext/minimap2/cookbook.md +0 -243
  27. data/ext/minimap2/minimap2.1 +0 -844
  28. data/ext/minimap2/misc/README.md +0 -180
  29. data/ext/minimap2/misc/pafcluster.js +0 -241
  30. data/ext/minimap2/misc/paftools.js +0 -3734
  31. data/ext/minimap2/pyproject.toml +0 -2
  32. data/ext/minimap2/python/README.rst +0 -198
  33. data/ext/minimap2/python/cmappy.h +0 -152
  34. data/ext/minimap2/python/cmappy.pxd +0 -156
  35. data/ext/minimap2/python/mappy.pyx +0 -289
  36. data/ext/minimap2/python/minimap2.py +0 -41
  37. data/ext/minimap2/setup.py +0 -55
  38. data/ext/minimap2/test/MT-human.fa +0 -278
  39. data/ext/minimap2/test/MT-orang.fa +0 -276
  40. data/ext/minimap2/test/q-inv.fa +0 -4
  41. data/ext/minimap2/test/q2.fa +0 -2
  42. data/ext/minimap2/test/t-inv.fa +0 -127
  43. data/ext/minimap2/test/t2.fa +0 -2
  44. data/ext/minimap2/test/x3s-aln.txt +0 -5
  45. data/ext/minimap2/test/x3s-qry.fa +0 -5
  46. data/ext/minimap2/test/x3s-ref.fa +0 -10
  47. data/ext/minimap2/tex/Makefile +0 -21
  48. data/ext/minimap2/tex/bioinfo.cls +0 -930
  49. data/ext/minimap2/tex/blasr-mc.eval +0 -17
  50. data/ext/minimap2/tex/bowtie2-s3.sam.eval +0 -28
  51. data/ext/minimap2/tex/bwa-s3.sam.eval +0 -52
  52. data/ext/minimap2/tex/bwa.eval +0 -55
  53. data/ext/minimap2/tex/eval2roc.pl +0 -33
  54. data/ext/minimap2/tex/graphmap.eval +0 -4
  55. data/ext/minimap2/tex/hs38-simu.sh +0 -10
  56. data/ext/minimap2/tex/minialign.eval +0 -49
  57. data/ext/minimap2/tex/minimap2.bib +0 -460
  58. data/ext/minimap2/tex/minimap2.tex +0 -724
  59. data/ext/minimap2/tex/mm2-s3.sam.eval +0 -62
  60. data/ext/minimap2/tex/mm2-update.tex +0 -240
  61. data/ext/minimap2/tex/mm2.approx.eval +0 -12
  62. data/ext/minimap2/tex/mm2.eval +0 -13
  63. data/ext/minimap2/tex/natbib.bst +0 -1288
  64. data/ext/minimap2/tex/natbib.sty +0 -803
  65. data/ext/minimap2/tex/ngmlr.eval +0 -38
  66. data/ext/minimap2/tex/roc.gp +0 -60
  67. data/ext/minimap2/tex/snap-s3.sam.eval +0 -62
  68. data/ext/minimap2.patch +0 -19
  69. data/lib/minimap2/ffi/constants.rb +0 -267
  70. data/lib/minimap2/ffi/functions.rb +0 -239
  71. data/lib/minimap2/ffi/mappy.rb +0 -104
  72. data/lib/minimap2/ffi.rb +0 -27
@@ -0,0 +1,1134 @@
1
+ #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include <stdint.h>
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+ #include <zlib.h>
8
+
9
+ #ifdef _WIN32
10
+ #include <fcntl.h>
11
+ #include <io.h>
12
+ #else
13
+ #include <unistd.h>
14
+ #endif
15
+
16
+ #include "minimap.h"
17
+ #include "kseq.h"
18
+
19
+ #ifdef __clang__
20
+ #pragma clang diagnostic push
21
+ #pragma clang diagnostic ignored "-Wshorten-64-to-32"
22
+ #endif
23
+ KSEQ_INIT(gzFile, gzread)
24
+ #ifdef __clang__
25
+ #pragma clang diagnostic pop
26
+ #endif
27
+
28
+ extern int mm2_embedded_main(int argc, char **argv);
29
+ extern unsigned char seq_comp_table[256];
30
+ extern double realtime(void);
31
+
32
+ typedef struct {
33
+ mm_idxopt_t idx_opt;
34
+ mm_mapopt_t map_opt;
35
+ mm_idx_t **indexes;
36
+ size_t index_count;
37
+ size_t index_capacity;
38
+ int released;
39
+ VALUE mutex;
40
+ } rb_mm2_aligner_t;
41
+
42
+ typedef struct {
43
+ kseq_t *seq;
44
+ gzFile file;
45
+ int closed;
46
+ } rb_mm2_fastx_t;
47
+
48
+ static VALUE mMinimap2;
49
+ static VALUE mNative;
50
+ static VALUE cAligner;
51
+ static VALUE cAlignment;
52
+ static VALUE cFastxReader;
53
+ static VALUE eMinimap2Error;
54
+ static VALUE execute_mutex;
55
+
56
+ #ifdef RUBY_MINIMAP2_TESTING
57
+ static size_t live_indexes = 0;
58
+ static size_t live_fastx_readers = 0;
59
+ static size_t live_pair_buffers = 0;
60
+ #endif
61
+
62
+ static ID id_new;
63
+ static ID id_sort_by;
64
+
65
+ static void aligner_release_indexes(rb_mm2_aligner_t *aligner)
66
+ {
67
+ size_t i;
68
+ if (aligner->released) return;
69
+ aligner->released = 1;
70
+ for (i = 0; i < aligner->index_count; ++i) {
71
+ if (aligner->indexes[i]) {
72
+ mm_idx_destroy(aligner->indexes[i]);
73
+ #ifdef RUBY_MINIMAP2_TESTING
74
+ --live_indexes;
75
+ #endif
76
+ }
77
+ }
78
+ free(aligner->indexes);
79
+ aligner->indexes = NULL;
80
+ aligner->index_count = 0;
81
+ aligner->index_capacity = 0;
82
+ }
83
+
84
+ static void aligner_mark(void *ptr)
85
+ {
86
+ rb_mm2_aligner_t *aligner = ptr;
87
+ if (aligner) rb_gc_mark(aligner->mutex);
88
+ }
89
+
90
+ static void aligner_free(void *ptr)
91
+ {
92
+ rb_mm2_aligner_t *aligner = ptr;
93
+ if (!aligner) return;
94
+ aligner_release_indexes(aligner);
95
+ xfree(aligner);
96
+ }
97
+
98
+ static size_t aligner_memsize(const void *ptr)
99
+ {
100
+ const rb_mm2_aligner_t *aligner = ptr;
101
+ if (!aligner) return 0;
102
+ return sizeof(*aligner) + aligner->index_capacity * sizeof(mm_idx_t *);
103
+ }
104
+
105
+ static const rb_data_type_t aligner_type = {
106
+ "Minimap2::Aligner",
107
+ {aligner_mark, aligner_free, aligner_memsize, NULL},
108
+ NULL, NULL,
109
+ RUBY_TYPED_FREE_IMMEDIATELY
110
+ };
111
+
112
+ static VALUE aligner_alloc(VALUE klass)
113
+ {
114
+ rb_mm2_aligner_t *aligner;
115
+ VALUE object = TypedData_Make_Struct(klass, rb_mm2_aligner_t, &aligner_type, aligner);
116
+ memset(aligner, 0, sizeof(*aligner));
117
+ aligner->released = 1;
118
+ aligner->mutex = rb_mutex_new();
119
+ return object;
120
+ }
121
+
122
+ static rb_mm2_aligner_t *get_aligner(VALUE self)
123
+ {
124
+ rb_mm2_aligner_t *aligner;
125
+ TypedData_Get_Struct(self, rb_mm2_aligner_t, &aligner_type, aligner);
126
+ return aligner;
127
+ }
128
+
129
+ static void aligner_add_index(rb_mm2_aligner_t *aligner, mm_idx_t *index)
130
+ {
131
+ if (!index) return;
132
+ if (aligner->index_count == aligner->index_capacity) {
133
+ size_t capacity = aligner->index_capacity == 0 ? 4 : aligner->index_capacity * 2;
134
+ mm_idx_t **indexes = realloc(aligner->indexes, capacity * sizeof(*indexes));
135
+ if (!indexes) rb_memerror();
136
+ aligner->indexes = indexes;
137
+ aligner->index_capacity = capacity;
138
+ }
139
+ aligner->indexes[aligner->index_count++] = index;
140
+ #ifdef RUBY_MINIMAP2_TESTING
141
+ ++live_indexes;
142
+ #endif
143
+ }
144
+
145
+ static int keyword_given(VALUE value)
146
+ {
147
+ return value != Qundef && !NIL_P(value);
148
+ }
149
+
150
+ static int keyword_int(VALUE value)
151
+ {
152
+ return NUM2INT(value);
153
+ }
154
+
155
+ typedef struct {
156
+ const char *path;
157
+ const char *output_path;
158
+ mm_idxopt_t options;
159
+ int threads;
160
+ mm_idx_t **indexes;
161
+ size_t count;
162
+ size_t capacity;
163
+ int open_failed;
164
+ int allocation_failed;
165
+ } index_file_job_t;
166
+
167
+ static void *load_indexes_without_gvl(void *argument)
168
+ {
169
+ index_file_job_t *job = argument;
170
+ mm_idx_reader_t *reader = mm_idx_reader_open(job->path, &job->options, job->output_path);
171
+ if (!reader) {
172
+ job->open_failed = 1;
173
+ return NULL;
174
+ }
175
+ for (;;) {
176
+ mm_idx_t *index = mm_idx_reader_read(reader, job->threads);
177
+ if (!index) break;
178
+ mm_idx_index_name(index);
179
+ if (job->count == job->capacity) {
180
+ size_t capacity = job->capacity == 0 ? 4 : job->capacity * 2;
181
+ mm_idx_t **indexes = realloc(job->indexes, capacity * sizeof(*indexes));
182
+ if (!indexes) {
183
+ mm_idx_destroy(index);
184
+ job->allocation_failed = 1;
185
+ break;
186
+ }
187
+ job->indexes = indexes;
188
+ job->capacity = capacity;
189
+ }
190
+ job->indexes[job->count++] = index;
191
+ }
192
+ mm_idx_reader_close(reader);
193
+ return NULL;
194
+ }
195
+
196
+ typedef struct {
197
+ const char *sequence;
198
+ mm_idxopt_t options;
199
+ mm_idx_t *index;
200
+ } index_sequence_job_t;
201
+
202
+ static void *build_sequence_index_without_gvl(void *argument)
203
+ {
204
+ index_sequence_job_t *job = argument;
205
+ const char *name = "N/A";
206
+ job->index = mm_idx_str(job->options.w, job->options.k, job->options.flag & 1,
207
+ job->options.bucket_bits, 1, &job->sequence, &name);
208
+ if (job->index) mm_idx_index_name(job->index);
209
+ return NULL;
210
+ }
211
+
212
+ static VALUE aligner_initialize(int argc, VALUE *argv, VALUE self)
213
+ {
214
+ static ID keywords[18];
215
+ VALUE path = Qnil, options = Qnil, values[18];
216
+ VALUE preset, seq, fn_idx_out, scoring;
217
+ rb_mm2_aligner_t *aligner = get_aligner(self);
218
+ const char *preset_ptr = NULL;
219
+ int result;
220
+
221
+ if (!keywords[0]) {
222
+ const char *names[] = {
223
+ "seq", "preset", "k", "w", "min_cnt", "min_chain_score", "min_dp_score",
224
+ "bw", "bw_long", "best_n", "n_threads", "fn_idx_out", "max_frag_len",
225
+ "extra_flags", "scoring", "sc_ambi", "max_chain_skip", "batch_size"
226
+ };
227
+ int i;
228
+ for (i = 0; i < 18; ++i) keywords[i] = rb_intern(names[i]);
229
+ }
230
+
231
+ rb_scan_args(argc, argv, "01:", &path, &options);
232
+ for (int i = 0; i < 18; ++i) values[i] = Qundef;
233
+ if (!NIL_P(options)) rb_get_kwargs(options, keywords, 0, 18, values);
234
+
235
+ seq = values[0];
236
+ preset = values[1];
237
+ fn_idx_out = values[11];
238
+ scoring = values[14];
239
+
240
+ mm_set_opt(NULL, &aligner->idx_opt, &aligner->map_opt);
241
+ if (keyword_given(preset)) {
242
+ VALUE preset_string = rb_obj_as_string(preset);
243
+ preset_ptr = StringValueCStr(preset_string);
244
+ result = mm_set_opt(preset_ptr, &aligner->idx_opt, &aligner->map_opt);
245
+ if (result == -1) rb_raise(rb_eArgError, "Unknown preset name: %s", preset_ptr);
246
+ }
247
+ aligner->map_opt.flag |= MM_F_CIGAR;
248
+ aligner->idx_opt.batch_size = UINT64_MAX >> 1;
249
+
250
+ if (keyword_given(values[2])) {
251
+ int k = keyword_int(values[2]);
252
+ if (k < 1 || k > 28) rb_raise(rb_eArgError, "k must be between 1 and 28");
253
+ aligner->idx_opt.k = (short)k;
254
+ }
255
+ if (keyword_given(values[3])) {
256
+ int w = keyword_int(values[3]);
257
+ if (w < 1 || w > 255) rb_raise(rb_eArgError, "w must be between 1 and 255");
258
+ aligner->idx_opt.w = (short)w;
259
+ }
260
+ if (keyword_given(values[4])) aligner->map_opt.min_cnt = keyword_int(values[4]);
261
+ if (keyword_given(values[5])) aligner->map_opt.min_chain_score = keyword_int(values[5]);
262
+ if (keyword_given(values[6])) aligner->map_opt.min_dp_max = keyword_int(values[6]);
263
+ if (keyword_given(values[7])) aligner->map_opt.bw = keyword_int(values[7]);
264
+ if (keyword_given(values[8])) aligner->map_opt.bw_long = keyword_int(values[8]);
265
+ if (keyword_given(values[9])) aligner->map_opt.best_n = keyword_int(values[9]);
266
+ if (keyword_given(values[12])) aligner->map_opt.max_frag_len = keyword_int(values[12]);
267
+ if (keyword_given(values[13])) aligner->map_opt.flag |= NUM2LL(values[13]);
268
+ if (keyword_given(values[15])) aligner->map_opt.sc_ambi = keyword_int(values[15]);
269
+ if (keyword_given(values[16])) aligner->map_opt.max_chain_skip = keyword_int(values[16]);
270
+ if (keyword_given(values[17])) aligner->idx_opt.batch_size = NUM2ULL(values[17]);
271
+
272
+ if (keyword_given(scoring)) {
273
+ long length;
274
+ Check_Type(scoring, T_ARRAY);
275
+ length = RARRAY_LEN(scoring);
276
+ if (length >= 4) {
277
+ aligner->map_opt.a = NUM2INT(rb_ary_entry(scoring, 0));
278
+ aligner->map_opt.b = NUM2INT(rb_ary_entry(scoring, 1));
279
+ aligner->map_opt.q = NUM2INT(rb_ary_entry(scoring, 2));
280
+ aligner->map_opt.e = NUM2INT(rb_ary_entry(scoring, 3));
281
+ aligner->map_opt.q2 = aligner->map_opt.q;
282
+ aligner->map_opt.e2 = aligner->map_opt.e;
283
+ if (length >= 6) {
284
+ aligner->map_opt.q2 = NUM2INT(rb_ary_entry(scoring, 4));
285
+ aligner->map_opt.e2 = NUM2INT(rb_ary_entry(scoring, 5));
286
+ if (length >= 7) aligner->map_opt.sc_ambi = NUM2INT(rb_ary_entry(scoring, 6));
287
+ }
288
+ }
289
+ }
290
+
291
+ aligner->released = 0;
292
+ if (!NIL_P(path)) {
293
+ VALUE path_string = rb_get_path(path);
294
+ index_file_job_t job;
295
+ char *path_copy;
296
+ char *output_copy = NULL;
297
+ int threads = keyword_given(values[10]) ? keyword_int(values[10]) : 3;
298
+
299
+ if (!RTEST(rb_funcall(rb_cFile, rb_intern("file?"), 1, path_string)))
300
+ rb_raise(eMinimap2Error, "Cannot open index file: %s", StringValueCStr(path_string));
301
+ path_copy = strdup(StringValueCStr(path_string));
302
+
303
+ if (keyword_given(seq)) rb_warn("Since fn_idx_in is specified, the seq argument will be ignored.");
304
+ if (keyword_given(fn_idx_out)) {
305
+ fn_idx_out = rb_get_path(fn_idx_out);
306
+ output_copy = strdup(StringValueCStr(fn_idx_out));
307
+ }
308
+ if (!path_copy || (keyword_given(fn_idx_out) && !output_copy)) {
309
+ free(path_copy);
310
+ free(output_copy);
311
+ rb_memerror();
312
+ }
313
+ memset(&job, 0, sizeof(job));
314
+ job.path = path_copy;
315
+ job.output_path = output_copy;
316
+ job.options = aligner->idx_opt;
317
+ job.threads = threads;
318
+ rb_thread_call_without_gvl(load_indexes_without_gvl, &job, NULL, NULL);
319
+ free(path_copy);
320
+ free(output_copy);
321
+ if (job.allocation_failed) {
322
+ for (size_t i = 0; i < job.count; ++i) mm_idx_destroy(job.indexes[i]);
323
+ free(job.indexes);
324
+ rb_memerror();
325
+ }
326
+ if (job.open_failed) rb_raise(eMinimap2Error, "Cannot open: %s", StringValueCStr(path_string));
327
+ uint64_t sequence_count = 0;
328
+ int sequence_data_missing = 0;
329
+ for (size_t i = 0; i < job.count; ++i) sequence_count += job.indexes[i]->n_seq;
330
+ for (size_t i = 0; i < job.count; ++i) {
331
+ if (job.indexes[i]->flag & MM_I_NO_SEQ) sequence_data_missing = 1;
332
+ }
333
+ if (job.count == 0 || sequence_count == 0 || sequence_data_missing) {
334
+ for (size_t i = 0; i < job.count; ++i) mm_idx_destroy(job.indexes[i]);
335
+ free(job.indexes);
336
+ if (sequence_data_missing)
337
+ rb_raise(eMinimap2Error, "Index does not contain sequence data: %s", StringValueCStr(path_string));
338
+ rb_raise(eMinimap2Error, "Failed to read index parts from: %s", StringValueCStr(path_string));
339
+ }
340
+ aligner->indexes = job.indexes;
341
+ aligner->index_count = job.count;
342
+ aligner->index_capacity = job.capacity;
343
+ #ifdef RUBY_MINIMAP2_TESTING
344
+ live_indexes += job.count;
345
+ #endif
346
+ mm_mapopt_update(&aligner->map_opt, aligner->indexes[0]);
347
+ RB_GC_GUARD(path_string);
348
+ RB_GC_GUARD(fn_idx_out);
349
+ } else if (keyword_given(seq)) {
350
+ VALUE sequence = StringValue(seq);
351
+ index_sequence_job_t job;
352
+ char *sequence_copy = malloc((size_t)RSTRING_LEN(sequence) + 1);
353
+ if (!sequence_copy) rb_memerror();
354
+ memcpy(sequence_copy, RSTRING_PTR(sequence), (size_t)RSTRING_LEN(sequence));
355
+ sequence_copy[RSTRING_LEN(sequence)] = '\0';
356
+ memset(&job, 0, sizeof(job));
357
+ job.sequence = sequence_copy;
358
+ job.options = aligner->idx_opt;
359
+ rb_thread_call_without_gvl(build_sequence_index_without_gvl, &job, RUBY_UBF_IO, NULL);
360
+ free(sequence_copy);
361
+ if (!job.index) rb_raise(eMinimap2Error, "Failed to build index from sequence");
362
+ aligner_add_index(aligner, job.index);
363
+ mm_mapopt_update(&aligner->map_opt, job.index);
364
+ aligner->map_opt.mid_occ = 1000;
365
+ RB_GC_GUARD(sequence);
366
+ }
367
+
368
+ return self;
369
+ }
370
+
371
+ static VALUE aligner_free_index_locked(VALUE argument)
372
+ {
373
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
374
+ aligner_release_indexes(aligner);
375
+ return Qnil;
376
+ }
377
+
378
+ static VALUE aligner_free_index(VALUE self)
379
+ {
380
+ rb_mm2_aligner_t *aligner = get_aligner(self);
381
+ return rb_mutex_synchronize(aligner->mutex, aligner_free_index_locked, (VALUE)(uintptr_t)aligner);
382
+ }
383
+
384
+ static void require_live_index(rb_mm2_aligner_t *aligner)
385
+ {
386
+ if (aligner->released) rb_raise(eMinimap2Error, "index has been released");
387
+ if (aligner->index_count == 0) rb_raise(eMinimap2Error, "index is not initialized");
388
+ }
389
+
390
+ static VALUE aligner_index_p_locked(VALUE argument)
391
+ {
392
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
393
+ return (!aligner->released && aligner->index_count > 0) ? Qtrue : Qfalse;
394
+ }
395
+
396
+ static VALUE aligner_index_p(VALUE self)
397
+ {
398
+ rb_mm2_aligner_t *aligner = get_aligner(self);
399
+ return rb_mutex_synchronize(aligner->mutex, aligner_index_p_locked, (VALUE)(uintptr_t)aligner);
400
+ }
401
+
402
+ static VALUE aligner_k_locked(VALUE argument)
403
+ {
404
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
405
+ require_live_index(aligner);
406
+ return INT2NUM(aligner->indexes[0]->k);
407
+ }
408
+
409
+ static VALUE aligner_k(VALUE self)
410
+ {
411
+ rb_mm2_aligner_t *aligner = get_aligner(self);
412
+ return rb_mutex_synchronize(aligner->mutex, aligner_k_locked, (VALUE)(uintptr_t)aligner);
413
+ }
414
+
415
+ static VALUE aligner_w_locked(VALUE argument)
416
+ {
417
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
418
+ require_live_index(aligner);
419
+ return INT2NUM(aligner->indexes[0]->w);
420
+ }
421
+
422
+ static VALUE aligner_w(VALUE self)
423
+ {
424
+ rb_mm2_aligner_t *aligner = get_aligner(self);
425
+ return rb_mutex_synchronize(aligner->mutex, aligner_w_locked, (VALUE)(uintptr_t)aligner);
426
+ }
427
+
428
+ static VALUE aligner_n_seq_locked(VALUE argument)
429
+ {
430
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
431
+ size_t i;
432
+ uint64_t count = 0;
433
+ if (aligner->released) return INT2FIX(0);
434
+ for (i = 0; i < aligner->index_count; ++i) count += aligner->indexes[i]->n_seq;
435
+ return ULL2NUM(count);
436
+ }
437
+
438
+ static VALUE aligner_n_seq(VALUE self)
439
+ {
440
+ rb_mm2_aligner_t *aligner = get_aligner(self);
441
+ return rb_mutex_synchronize(aligner->mutex, aligner_n_seq_locked, (VALUE)(uintptr_t)aligner);
442
+ }
443
+
444
+ static VALUE aligner_seq_names_locked(VALUE argument)
445
+ {
446
+ rb_mm2_aligner_t *aligner = (rb_mm2_aligner_t *)(uintptr_t)argument;
447
+ VALUE result = rb_ary_new();
448
+ size_t i;
449
+ if (aligner->released) return result;
450
+ for (i = 0; i < aligner->index_count; ++i) {
451
+ mm_idx_t *index = aligner->indexes[i];
452
+ uint32_t j;
453
+ for (j = 0; j < index->n_seq; ++j) {
454
+ VALUE name = rb_utf8_str_new_cstr(index->seq[j].name);
455
+ rb_ary_push(result, name);
456
+ }
457
+ }
458
+ return result;
459
+ }
460
+
461
+ static VALUE aligner_seq_names(VALUE self)
462
+ {
463
+ rb_mm2_aligner_t *aligner = get_aligner(self);
464
+ return rb_mutex_synchronize(aligner->mutex, aligner_seq_names_locked, (VALUE)(uintptr_t)aligner);
465
+ }
466
+
467
+ typedef struct {
468
+ rb_mm2_aligner_t *aligner;
469
+ const char *name;
470
+ int start;
471
+ int stop;
472
+ } seq_args_t;
473
+
474
+ static VALUE aligner_seq_locked(VALUE argument)
475
+ {
476
+ seq_args_t *args = (seq_args_t *)(uintptr_t)argument;
477
+ size_t i;
478
+ if (args->aligner->released) return Qnil;
479
+ for (i = 0; i < args->aligner->index_count; ++i) {
480
+ mm_idx_t *index = args->aligner->indexes[i];
481
+ int rid = mm_idx_name2id(index, args->name);
482
+ int start = args->start;
483
+ int stop = args->stop;
484
+ int length, j;
485
+ uint8_t *buffer;
486
+ VALUE result;
487
+ if (rid < 0) continue;
488
+ if (start < 0) start = 0;
489
+ if ((uint32_t)start >= index->seq[rid].len || start >= stop) return Qnil;
490
+ if ((uint32_t)stop > index->seq[rid].len) stop = (int)index->seq[rid].len;
491
+ buffer = malloc((size_t)(stop - start));
492
+ if (!buffer) rb_memerror();
493
+ length = mm_idx_getseq(index, (uint32_t)rid, (uint32_t)start, (uint32_t)stop, buffer);
494
+ for (j = 0; j < length; ++j) buffer[j] = (uint8_t)"ACGTN"[buffer[j]];
495
+ result = rb_str_new((const char *)buffer, length);
496
+ free(buffer);
497
+ return result;
498
+ }
499
+ return Qnil;
500
+ }
501
+
502
+ static VALUE aligner_seq(int argc, VALUE *argv, VALUE self)
503
+ {
504
+ VALUE name, start_value = Qnil, stop_value = Qnil;
505
+ VALUE name_string;
506
+ rb_mm2_aligner_t *aligner = get_aligner(self);
507
+ seq_args_t args;
508
+ rb_scan_args(argc, argv, "12", &name, &start_value, &stop_value);
509
+ if (NIL_P(start_value)) start_value = INT2FIX(0);
510
+ if (NIL_P(stop_value)) stop_value = INT2NUM(0x7fffffff);
511
+ name_string = rb_obj_as_string(name);
512
+ args.aligner = aligner;
513
+ args.name = StringValueCStr(name_string);
514
+ args.start = NUM2INT(start_value);
515
+ args.stop = NUM2INT(stop_value);
516
+ VALUE result = rb_mutex_synchronize(aligner->mutex, aligner_seq_locked, (VALUE)(uintptr_t)&args);
517
+ RB_GC_GUARD(name_string);
518
+ return result;
519
+ }
520
+
521
+ typedef struct {
522
+ const mm_idx_t *index;
523
+ const char *name;
524
+ const char *seq1;
525
+ const char *seq2;
526
+ mm_mapopt_t options;
527
+ mm_tbuf_t *buffer;
528
+ mm_reg1_t *regs;
529
+ int n_regs;
530
+ int failed;
531
+ } map_job_t;
532
+
533
+ static void *map_without_gvl(void *argument)
534
+ {
535
+ map_job_t *job = argument;
536
+ if (!job->seq2) {
537
+ job->regs = mm_map(job->index, (int)strlen(job->seq1), job->seq1,
538
+ &job->n_regs, job->buffer, &job->options, job->name);
539
+ } else {
540
+ int lengths[2], counts[2] = {0, 0};
541
+ mm_reg1_t *parts[2] = {NULL, NULL};
542
+ const char *sequences[2];
543
+ char *mate = strdup(job->seq2);
544
+ int i;
545
+ if (!mate) {
546
+ job->failed = 1;
547
+ return NULL;
548
+ }
549
+ #ifdef RUBY_MINIMAP2_TESTING
550
+ ++live_pair_buffers;
551
+ #endif
552
+ lengths[0] = (int)strlen(job->seq1);
553
+ lengths[1] = (int)strlen(job->seq2);
554
+ sequences[0] = job->seq1;
555
+ sequences[1] = mate;
556
+ for (i = 0; i < lengths[1] / 2; ++i) {
557
+ int right = mate[lengths[1] - i - 1];
558
+ mate[lengths[1] - i - 1] = (char)seq_comp_table[(uint8_t)mate[i]];
559
+ mate[i] = (char)seq_comp_table[(uint8_t)right];
560
+ }
561
+ if (lengths[1] & 1)
562
+ mate[lengths[1] / 2] = (char)seq_comp_table[(uint8_t)mate[lengths[1] / 2]];
563
+ mm_map_frag(job->index, 2, lengths, sequences, counts, parts,
564
+ job->buffer, &job->options, job->name);
565
+ free(mate);
566
+ #ifdef RUBY_MINIMAP2_TESTING
567
+ --live_pair_buffers;
568
+ #endif
569
+ for (i = 0; i < counts[1]; ++i) parts[1][i].rev = !parts[1][i].rev;
570
+ job->n_regs = counts[0] + counts[1];
571
+ if (job->n_regs > 0) {
572
+ mm_reg1_t *combined = realloc(parts[0], sizeof(mm_reg1_t) * (size_t)job->n_regs);
573
+ if (!combined) {
574
+ for (i = 0; i < counts[0]; ++i) free(parts[0][i].p);
575
+ for (i = 0; i < counts[1]; ++i) free(parts[1][i].p);
576
+ free(parts[0]);
577
+ free(parts[1]);
578
+ job->failed = 1;
579
+ return NULL;
580
+ }
581
+ parts[0] = combined;
582
+ if (counts[1] > 0) memcpy(parts[0] + counts[0], parts[1], sizeof(mm_reg1_t) * (size_t)counts[1]);
583
+ }
584
+ free(parts[1]);
585
+ job->regs = parts[0];
586
+ }
587
+ return NULL;
588
+ }
589
+
590
+ typedef struct {
591
+ map_job_t *job;
592
+ VALUE entries;
593
+ VALUE seq1;
594
+ VALUE seq2;
595
+ VALUE name;
596
+ int want_cs;
597
+ int want_ds;
598
+ int want_md;
599
+ long *ordinal;
600
+ int converted;
601
+ char *tag_buffer;
602
+ int tag_capacity;
603
+ } convert_ctx_t;
604
+
605
+ static VALUE alignment_hash(const mm_idx_t *index, const mm_reg1_t *reg, VALUE name, long query_length)
606
+ {
607
+ VALUE hash = rb_hash_new();
608
+ int trans_strand = reg->p->trans_strand == 1 ? 1 : reg->p->trans_strand == 2 ? -1 : 0;
609
+ rb_hash_aset(hash, ID2SYM(rb_intern("qname")), NIL_P(name) ? rb_str_new_cstr("*") : rb_str_dup(name));
610
+ rb_hash_aset(hash, ID2SYM(rb_intern("qlen")), LONG2NUM(query_length));
611
+ rb_hash_aset(hash, ID2SYM(rb_intern("ctg")), rb_utf8_str_new_cstr(index->seq[reg->rid].name));
612
+ rb_hash_aset(hash, ID2SYM(rb_intern("ctg_len")), UINT2NUM(index->seq[reg->rid].len));
613
+ rb_hash_aset(hash, ID2SYM(rb_intern("ctg_start")), INT2NUM(reg->rs));
614
+ rb_hash_aset(hash, ID2SYM(rb_intern("ctg_end")), INT2NUM(reg->re));
615
+ rb_hash_aset(hash, ID2SYM(rb_intern("qry_start")), INT2NUM(reg->qs));
616
+ rb_hash_aset(hash, ID2SYM(rb_intern("qry_end")), INT2NUM(reg->qe));
617
+ rb_hash_aset(hash, ID2SYM(rb_intern("blen")), INT2NUM(reg->blen));
618
+ rb_hash_aset(hash, ID2SYM(rb_intern("mlen")), INT2NUM(reg->mlen));
619
+ rb_hash_aset(hash, ID2SYM(rb_intern("NM")), INT2NUM(reg->blen - reg->mlen + reg->p->n_ambi));
620
+ rb_hash_aset(hash, ID2SYM(rb_intern("mapq")), INT2NUM(reg->mapq));
621
+ rb_hash_aset(hash, ID2SYM(rb_intern("is_primary")), reg->id == reg->parent ? INT2FIX(1) : INT2FIX(0));
622
+ rb_hash_aset(hash, ID2SYM(rb_intern("strand")), reg->rev ? INT2FIX(-1) : INT2FIX(1));
623
+ rb_hash_aset(hash, ID2SYM(rb_intern("trans_strand")), INT2NUM(trans_strand));
624
+ rb_hash_aset(hash, ID2SYM(rb_intern("seg_id")), INT2NUM(reg->seg_id));
625
+ return hash;
626
+ }
627
+
628
+ static VALUE convert_regs(VALUE argument)
629
+ {
630
+ convert_ctx_t *ctx = (convert_ctx_t *)(uintptr_t)argument;
631
+ map_job_t *job = ctx->job;
632
+ int i;
633
+ void *km = mm_tbuf_get_km(job->buffer);
634
+ for (i = 0; i < job->n_regs; ++i) {
635
+ mm_reg1_t *reg = &job->regs[i];
636
+ if (!reg->p) {
637
+ ctx->converted = i + 1;
638
+ continue;
639
+ }
640
+ VALUE cigar = rb_ary_new_capa(reg->p->n_cigar);
641
+ VALUE cs = Qnil;
642
+ VALUE ds = Qnil;
643
+ VALUE md = Qnil;
644
+ VALUE hash, alignment, entry;
645
+ uint32_t j;
646
+ VALUE query_value = reg->seg_id > 0 && !NIL_P(ctx->seq2) ? ctx->seq2 : ctx->seq1;
647
+ const char *query = RSTRING_PTR(query_value);
648
+ for (j = 0; j < reg->p->n_cigar; ++j) {
649
+ VALUE pair = rb_ary_new_capa(2);
650
+ rb_ary_push(pair, UINT2NUM(reg->p->cigar[j] >> 4));
651
+ rb_ary_push(pair, UINT2NUM(reg->p->cigar[j] & 0xf));
652
+ rb_ary_push(cigar, pair);
653
+ }
654
+ if (ctx->want_cs) {
655
+ int length = mm_gen_cs(km, &ctx->tag_buffer, &ctx->tag_capacity, job->index, reg, query, 1);
656
+ cs = length > 0 ? rb_str_new(ctx->tag_buffer, length) : rb_str_new_cstr("");
657
+ }
658
+ if (ctx->want_ds) {
659
+ int length = mm_gen_ds(km, &ctx->tag_buffer, &ctx->tag_capacity, job->index, reg, query, 1);
660
+ ds = length > 0 ? rb_str_new(ctx->tag_buffer, length) : rb_str_new_cstr("");
661
+ }
662
+ if (ctx->want_md) {
663
+ int length = mm_gen_MD(km, &ctx->tag_buffer, &ctx->tag_capacity, job->index, reg, query);
664
+ md = length > 0 ? rb_str_new(ctx->tag_buffer, length) : rb_str_new_cstr("");
665
+ }
666
+ hash = alignment_hash(job->index, reg, ctx->name, RSTRING_LEN(query_value));
667
+ alignment = rb_funcall(cAlignment, id_new, 5, hash, cigar, cs, ds, md);
668
+ entry = rb_ary_new_capa(7);
669
+ rb_ary_push(entry, alignment);
670
+ rb_ary_push(entry, reg->id == reg->parent ? INT2FIX(1) : INT2FIX(0));
671
+ rb_ary_push(entry, INT2NUM(reg->mapq));
672
+ rb_ary_push(entry, INT2NUM(reg->mlen));
673
+ rb_ary_push(entry, INT2NUM(reg->blen));
674
+ rb_ary_push(entry, INT2NUM(reg->blen - reg->mlen + reg->p->n_ambi));
675
+ rb_ary_push(entry, LONG2NUM((*ctx->ordinal)++));
676
+ rb_ary_push(ctx->entries, entry);
677
+ free(reg->p);
678
+ reg->p = NULL;
679
+ ctx->converted = i + 1;
680
+ }
681
+ return Qnil;
682
+ }
683
+
684
+ static VALUE cleanup_regs(VALUE argument)
685
+ {
686
+ convert_ctx_t *ctx = (convert_ctx_t *)(uintptr_t)argument;
687
+ int i;
688
+ if (ctx->job->regs) {
689
+ for (i = ctx->converted; i < ctx->job->n_regs; ++i) free(ctx->job->regs[i].p);
690
+ free(ctx->job->regs);
691
+ ctx->job->regs = NULL;
692
+ }
693
+ free(ctx->tag_buffer);
694
+ ctx->tag_buffer = NULL;
695
+ return Qnil;
696
+ }
697
+
698
+ static VALUE sort_key(VALUE entry, VALUE ignored, int argc, const VALUE *argv, VALUE blockarg)
699
+ {
700
+ VALUE key = rb_ary_new_capa(6);
701
+ (void)ignored; (void)argc; (void)argv; (void)blockarg;
702
+ rb_ary_push(key, LONG2NUM(-NUM2LONG(rb_ary_entry(entry, 1))));
703
+ rb_ary_push(key, LONG2NUM(-NUM2LONG(rb_ary_entry(entry, 2))));
704
+ rb_ary_push(key, LONG2NUM(-NUM2LONG(rb_ary_entry(entry, 3))));
705
+ rb_ary_push(key, LONG2NUM(-NUM2LONG(rb_ary_entry(entry, 4))));
706
+ rb_ary_push(key, rb_ary_entry(entry, 5));
707
+ rb_ary_push(key, rb_ary_entry(entry, 6));
708
+ return key;
709
+ }
710
+
711
+ typedef struct {
712
+ rb_mm2_aligner_t *aligner;
713
+ VALUE seq1;
714
+ VALUE seq2;
715
+ VALUE name;
716
+ int want_cs;
717
+ int want_ds;
718
+ int want_md;
719
+ int has_max_frag_len;
720
+ int max_frag_len;
721
+ int has_extra_flags;
722
+ int64_t extra_flags;
723
+ mm_tbuf_t *buffer;
724
+ char *seq1_copy;
725
+ char *seq2_copy;
726
+ char *name_copy;
727
+ } align_args_t;
728
+
729
+ static VALUE align_body(VALUE argument)
730
+ {
731
+ align_args_t *args = (align_args_t *)(uintptr_t)argument;
732
+ VALUE entries = rb_ary_new();
733
+ VALUE result;
734
+ const char *seq1 = args->seq1_copy;
735
+ const char *seq2 = args->seq2_copy;
736
+ const char *name = args->name_copy;
737
+ long ordinal = 0;
738
+ size_t part;
739
+
740
+ for (part = 0; part < args->aligner->index_count; ++part) {
741
+ map_job_t job;
742
+ convert_ctx_t convert;
743
+ memset(&job, 0, sizeof(job));
744
+ job.index = args->aligner->indexes[part];
745
+ job.name = name;
746
+ job.seq1 = seq1;
747
+ job.seq2 = seq2;
748
+ job.options = args->aligner->map_opt;
749
+ mm_mapopt_update(&job.options, job.index);
750
+ job.options.flag |= MM_F_CIGAR;
751
+ if (args->has_max_frag_len) job.options.max_frag_len = args->max_frag_len;
752
+ if (args->has_extra_flags) job.options.flag |= args->extra_flags;
753
+ job.buffer = args->buffer;
754
+ rb_thread_call_without_gvl(map_without_gvl, &job, RUBY_UBF_IO, NULL);
755
+ if (job.failed) rb_memerror();
756
+ if (!job.regs || job.n_regs <= 0) {
757
+ free(job.regs);
758
+ continue;
759
+ }
760
+ memset(&convert, 0, sizeof(convert));
761
+ convert.job = &job;
762
+ convert.entries = entries;
763
+ convert.seq1 = args->seq1;
764
+ convert.seq2 = args->seq2;
765
+ convert.name = args->name;
766
+ convert.want_cs = args->want_cs;
767
+ convert.want_ds = args->want_ds;
768
+ convert.want_md = args->want_md;
769
+ convert.ordinal = &ordinal;
770
+ rb_ensure(convert_regs, (VALUE)(uintptr_t)&convert, cleanup_regs, (VALUE)(uintptr_t)&convert);
771
+ }
772
+
773
+ if (args->aligner->map_opt.best_n > 0 && RARRAY_LEN(entries) > args->aligner->map_opt.best_n) {
774
+ entries = rb_block_call(entries, id_sort_by, 0, NULL, sort_key, Qnil);
775
+ entries = rb_ary_subseq(entries, 0, args->aligner->map_opt.best_n);
776
+ }
777
+ result = rb_ary_new_capa(RARRAY_LEN(entries));
778
+ for (long i = 0; i < RARRAY_LEN(entries); ++i)
779
+ rb_ary_push(result, rb_ary_entry(rb_ary_entry(entries, i), 0));
780
+ return result;
781
+ }
782
+
783
+ static VALUE align_cleanup(VALUE argument)
784
+ {
785
+ align_args_t *args = (align_args_t *)(uintptr_t)argument;
786
+ if (args->buffer) {
787
+ mm_tbuf_destroy(args->buffer);
788
+ args->buffer = NULL;
789
+ }
790
+ free(args->seq1_copy);
791
+ free(args->seq2_copy);
792
+ free(args->name_copy);
793
+ args->seq1_copy = NULL;
794
+ args->seq2_copy = NULL;
795
+ args->name_copy = NULL;
796
+ return Qnil;
797
+ }
798
+
799
+ static VALUE align_locked(VALUE argument)
800
+ {
801
+ align_args_t *args = (align_args_t *)(uintptr_t)argument;
802
+ if (args->aligner->released || args->aligner->index_count == 0) return rb_ary_new();
803
+ args->seq1_copy = strdup(StringValueCStr(args->seq1));
804
+ if (!NIL_P(args->seq2)) args->seq2_copy = strdup(StringValueCStr(args->seq2));
805
+ if (!NIL_P(args->name)) args->name_copy = strdup(StringValueCStr(args->name));
806
+ if (!args->seq1_copy || (!NIL_P(args->seq2) && !args->seq2_copy) ||
807
+ (!NIL_P(args->name) && !args->name_copy)) {
808
+ align_cleanup(argument);
809
+ rb_memerror();
810
+ }
811
+ args->buffer = mm_tbuf_init();
812
+ if (!args->buffer) {
813
+ align_cleanup(argument);
814
+ rb_memerror();
815
+ }
816
+ return rb_ensure(align_body, argument, align_cleanup, argument);
817
+ }
818
+
819
+ static VALUE align_synchronized(VALUE argument)
820
+ {
821
+ align_args_t *args = (align_args_t *)(uintptr_t)argument;
822
+ return rb_mutex_synchronize(args->aligner->mutex, align_locked, argument);
823
+ }
824
+
825
+ static VALUE aligner_align(int argc, VALUE *argv, VALUE self)
826
+ {
827
+ static ID keywords[6];
828
+ VALUE seq1, seq2 = Qnil, options = Qnil, values[6];
829
+ align_args_t args;
830
+ rb_mm2_aligner_t *aligner = get_aligner(self);
831
+ if (!keywords[0]) {
832
+ const char *names[] = {"name", "cs", "ds", "md", "max_frag_len", "extra_flags"};
833
+ for (int i = 0; i < 6; ++i) keywords[i] = rb_intern(names[i]);
834
+ }
835
+ rb_scan_args(argc, argv, "11:", &seq1, &seq2, &options);
836
+ for (int i = 0; i < 6; ++i) values[i] = Qundef;
837
+ if (!NIL_P(options)) rb_get_kwargs(options, keywords, 0, 6, values);
838
+ seq1 = StringValue(seq1);
839
+ if (!NIL_P(seq2)) seq2 = StringValue(seq2);
840
+ memset(&args, 0, sizeof(args));
841
+ args.aligner = aligner;
842
+ args.seq1 = seq1;
843
+ args.seq2 = seq2;
844
+ args.name = keyword_given(values[0]) ? rb_obj_as_string(values[0]) : Qnil;
845
+ args.want_cs = keyword_given(values[1]) && RTEST(values[1]);
846
+ args.want_ds = keyword_given(values[2]) && RTEST(values[2]);
847
+ args.want_md = keyword_given(values[3]) && RTEST(values[3]);
848
+ args.has_max_frag_len = keyword_given(values[4]);
849
+ if (args.has_max_frag_len) args.max_frag_len = NUM2INT(values[4]);
850
+ args.has_extra_flags = keyword_given(values[5]);
851
+ if (args.has_extra_flags) args.extra_flags = NUM2LL(values[5]);
852
+ VALUE result = align_synchronized((VALUE)(uintptr_t)&args);
853
+ RB_GC_GUARD(seq1);
854
+ RB_GC_GUARD(seq2);
855
+ RB_GC_GUARD(args.name);
856
+ return result;
857
+ }
858
+
859
+ static void fastx_close_native(rb_mm2_fastx_t *reader)
860
+ {
861
+ if (!reader || reader->closed) return;
862
+ reader->closed = 1;
863
+ if (reader->seq) kseq_destroy(reader->seq);
864
+ if (reader->file) gzclose(reader->file);
865
+ reader->seq = NULL;
866
+ reader->file = NULL;
867
+ #ifdef RUBY_MINIMAP2_TESTING
868
+ --live_fastx_readers;
869
+ #endif
870
+ }
871
+
872
+ static void fastx_free(void *ptr)
873
+ {
874
+ rb_mm2_fastx_t *reader = ptr;
875
+ fastx_close_native(reader);
876
+ xfree(reader);
877
+ }
878
+
879
+ static size_t fastx_memsize(const void *ptr)
880
+ {
881
+ return ptr ? sizeof(rb_mm2_fastx_t) : 0;
882
+ }
883
+
884
+ static const rb_data_type_t fastx_type = {
885
+ "Minimap2::Native::FastxReader",
886
+ {NULL, fastx_free, fastx_memsize, NULL},
887
+ NULL, NULL,
888
+ RUBY_TYPED_FREE_IMMEDIATELY
889
+ };
890
+
891
+ static VALUE fastx_alloc(VALUE klass)
892
+ {
893
+ rb_mm2_fastx_t *reader;
894
+ VALUE object = TypedData_Make_Struct(klass, rb_mm2_fastx_t, &fastx_type, reader);
895
+ memset(reader, 0, sizeof(*reader));
896
+ reader->closed = 1;
897
+ return object;
898
+ }
899
+
900
+ static VALUE fastx_initialize(VALUE self, VALUE path)
901
+ {
902
+ rb_mm2_fastx_t *reader;
903
+ VALUE path_string = rb_get_path(path);
904
+ const char *path_ptr = StringValueCStr(path_string);
905
+ int first;
906
+ TypedData_Get_Struct(self, rb_mm2_fastx_t, &fastx_type, reader);
907
+ if (strcmp(path_ptr, "-") == 0) {
908
+ #ifdef _WIN32
909
+ int fd = _dup(_fileno(stdin));
910
+ if (fd >= 0) _setmode(fd, _O_BINARY);
911
+ #else
912
+ int fd = dup(fileno(stdin));
913
+ #endif
914
+ if (fd >= 0) {
915
+ reader->file = gzdopen(fd, "r");
916
+ if (!reader->file) {
917
+ #ifdef _WIN32
918
+ _close(fd);
919
+ #else
920
+ close(fd);
921
+ #endif
922
+ }
923
+ }
924
+ } else {
925
+ reader->file = gzopen(path_ptr, "r");
926
+ }
927
+ if (!reader->file) rb_raise(eMinimap2Error, "Cannot open FASTA/FASTQ file: %s", StringValueCStr(path_string));
928
+ first = gzgetc(reader->file);
929
+ if (first != '>' && first != '@') {
930
+ gzclose(reader->file);
931
+ reader->file = NULL;
932
+ rb_raise(eMinimap2Error, "Invalid FASTA/FASTQ file: %s", path_ptr);
933
+ }
934
+ if (gzungetc(first, reader->file) == -1) {
935
+ gzclose(reader->file);
936
+ reader->file = NULL;
937
+ rb_raise(eMinimap2Error, "Cannot read FASTA/FASTQ file: %s", path_ptr);
938
+ }
939
+ reader->seq = kseq_init(reader->file);
940
+ if (!reader->seq) {
941
+ gzclose(reader->file);
942
+ reader->file = NULL;
943
+ rb_raise(eMinimap2Error, "Cannot initialize FASTA/FASTQ reader");
944
+ }
945
+ reader->closed = 0;
946
+ #ifdef RUBY_MINIMAP2_TESTING
947
+ ++live_fastx_readers;
948
+ #endif
949
+ return self;
950
+ }
951
+
952
+ static VALUE fastx_next(VALUE self, VALUE read_comment)
953
+ {
954
+ rb_mm2_fastx_t *reader;
955
+ VALUE result;
956
+ int status;
957
+ TypedData_Get_Struct(self, rb_mm2_fastx_t, &fastx_type, reader);
958
+ if (reader->closed) return Qnil;
959
+ status = kseq_read(reader->seq);
960
+ if (status == -1) {
961
+ fastx_close_native(reader);
962
+ return Qnil;
963
+ }
964
+ if (status < -1) {
965
+ fastx_close_native(reader);
966
+ rb_raise(eMinimap2Error, "Malformed FASTA/FASTQ record");
967
+ }
968
+ if (reader->seq->name.l == 0 || reader->seq->seq.l == 0) {
969
+ fastx_close_native(reader);
970
+ rb_raise(eMinimap2Error, "Malformed FASTA/FASTQ record");
971
+ }
972
+ result = rb_ary_new_capa(RTEST(read_comment) ? 4 : 3);
973
+ rb_ary_push(result, rb_str_new(reader->seq->name.s, reader->seq->name.l));
974
+ rb_ary_push(result, rb_str_new(reader->seq->seq.s, reader->seq->seq.l));
975
+ rb_ary_push(result, reader->seq->qual.l > 0 ? rb_str_new(reader->seq->qual.s, reader->seq->qual.l) : Qnil);
976
+ if (RTEST(read_comment))
977
+ rb_ary_push(result, reader->seq->comment.l > 0 ? rb_str_new(reader->seq->comment.s, reader->seq->comment.l) : Qnil);
978
+ return result;
979
+ }
980
+
981
+ static VALUE fastx_close(VALUE self)
982
+ {
983
+ rb_mm2_fastx_t *reader;
984
+ TypedData_Get_Struct(self, rb_mm2_fastx_t, &fastx_type, reader);
985
+ fastx_close_native(reader);
986
+ return Qnil;
987
+ }
988
+
989
+ static VALUE native_revcomp(VALUE module, VALUE sequence)
990
+ {
991
+ VALUE input = StringValue(sequence);
992
+ long length = RSTRING_LEN(input), i;
993
+ VALUE output = rb_str_new(NULL, length);
994
+ const unsigned char *source = (const unsigned char *)RSTRING_PTR(input);
995
+ char *target = RSTRING_PTR(output);
996
+ (void)module;
997
+ for (i = 0; i < length; ++i) target[length - i - 1] = (char)seq_comp_table[source[i]];
998
+ return output;
999
+ }
1000
+
1001
+ static VALUE native_verbose(VALUE module)
1002
+ {
1003
+ (void)module;
1004
+ return INT2NUM(mm_verbose);
1005
+ }
1006
+
1007
+ static VALUE native_set_verbose(VALUE module, VALUE value)
1008
+ {
1009
+ (void)module;
1010
+ mm_verbose = NUM2INT(value);
1011
+ return value;
1012
+ }
1013
+
1014
+ typedef struct {
1015
+ int argc;
1016
+ char **argv;
1017
+ int result;
1018
+ int allocated;
1019
+ } execute_job_t;
1020
+
1021
+ static void *execute_without_gvl(void *argument)
1022
+ {
1023
+ execute_job_t *job = argument;
1024
+ job->result = mm2_embedded_main(job->argc, job->argv);
1025
+ return NULL;
1026
+ }
1027
+
1028
+ static VALUE native_execute_locked(VALUE argument)
1029
+ {
1030
+ execute_job_t *job = (execute_job_t *)(uintptr_t)argument;
1031
+ int old_verbose = mm_verbose;
1032
+ int old_debug = mm_dbg_flag;
1033
+ double old_timer = mm_realtime0;
1034
+ rb_thread_call_without_gvl(execute_without_gvl, job, RUBY_UBF_IO, NULL);
1035
+ mm_verbose = old_verbose;
1036
+ mm_dbg_flag = old_debug;
1037
+ mm_realtime0 = old_timer;
1038
+ return INT2NUM(job->result);
1039
+ }
1040
+
1041
+ static VALUE native_execute_synchronized(VALUE argument)
1042
+ {
1043
+ return rb_mutex_synchronize(execute_mutex, native_execute_locked, argument);
1044
+ }
1045
+
1046
+ static VALUE native_execute_cleanup(VALUE argument)
1047
+ {
1048
+ execute_job_t *job = (execute_job_t *)(uintptr_t)argument;
1049
+ int i;
1050
+ for (i = 0; i < job->allocated; ++i) free(job->argv[i]);
1051
+ xfree(job->argv);
1052
+ job->argv = NULL;
1053
+ return Qnil;
1054
+ }
1055
+
1056
+ static VALUE native_execute(int argc, VALUE *argv, VALUE module)
1057
+ {
1058
+ execute_job_t job;
1059
+ VALUE result;
1060
+ int i;
1061
+ (void)module;
1062
+ job.argc = argc + 1;
1063
+ job.allocated = 0;
1064
+ job.argv = ALLOC_N(char *, (size_t)job.argc + 1);
1065
+ job.argv[0] = strdup("minimap2");
1066
+ if (!job.argv[0]) {
1067
+ xfree(job.argv);
1068
+ rb_memerror();
1069
+ }
1070
+ job.allocated = 1;
1071
+ for (i = 0; i < argc; ++i) {
1072
+ VALUE string = rb_obj_as_string(argv[i]);
1073
+ job.argv[i + 1] = strdup(StringValueCStr(string));
1074
+ if (!job.argv[i + 1]) {
1075
+ native_execute_cleanup((VALUE)(uintptr_t)&job);
1076
+ rb_memerror();
1077
+ }
1078
+ ++job.allocated;
1079
+ }
1080
+ job.argv[job.argc] = NULL;
1081
+ result = rb_ensure(native_execute_synchronized, (VALUE)(uintptr_t)&job,
1082
+ native_execute_cleanup, (VALUE)(uintptr_t)&job);
1083
+ return result;
1084
+ }
1085
+
1086
+ #ifdef RUBY_MINIMAP2_TESTING
1087
+ static VALUE native_resource_counts(VALUE module)
1088
+ {
1089
+ VALUE result = rb_hash_new();
1090
+ (void)module;
1091
+ rb_hash_aset(result, ID2SYM(rb_intern("indexes")), SIZET2NUM(live_indexes));
1092
+ rb_hash_aset(result, ID2SYM(rb_intern("fastx_readers")), SIZET2NUM(live_fastx_readers));
1093
+ rb_hash_aset(result, ID2SYM(rb_intern("pair_buffers")), SIZET2NUM(live_pair_buffers));
1094
+ return result;
1095
+ }
1096
+ #endif
1097
+
1098
+ void Init_minimap2_ext(void)
1099
+ {
1100
+ mm_realtime0 = realtime();
1101
+ id_new = rb_intern("new");
1102
+ id_sort_by = rb_intern("sort_by");
1103
+ mMinimap2 = rb_define_module("Minimap2");
1104
+ eMinimap2Error = rb_define_class_under(mMinimap2, "Error", rb_eStandardError);
1105
+ mNative = rb_define_module_under(mMinimap2, "Native");
1106
+ cAlignment = rb_define_class_under(mMinimap2, "Alignment", rb_cObject);
1107
+ cAligner = rb_define_class_under(mMinimap2, "Aligner", rb_cObject);
1108
+ rb_define_alloc_func(cAligner, aligner_alloc);
1109
+ rb_define_method(cAligner, "initialize", aligner_initialize, -1);
1110
+ rb_define_method(cAligner, "free_index", aligner_free_index, 0);
1111
+ rb_define_method(cAligner, "align", aligner_align, -1);
1112
+ rb_define_method(cAligner, "seq", aligner_seq, -1);
1113
+ rb_define_method(cAligner, "k", aligner_k, 0);
1114
+ rb_define_method(cAligner, "w", aligner_w, 0);
1115
+ rb_define_method(cAligner, "n_seq", aligner_n_seq, 0);
1116
+ rb_define_method(cAligner, "seq_names", aligner_seq_names, 0);
1117
+ rb_define_method(cAligner, "index?", aligner_index_p, 0);
1118
+
1119
+ cFastxReader = rb_define_class_under(mNative, "FastxReader", rb_cObject);
1120
+ rb_define_alloc_func(cFastxReader, fastx_alloc);
1121
+ rb_define_method(cFastxReader, "initialize", fastx_initialize, 1);
1122
+ rb_define_method(cFastxReader, "next_record", fastx_next, 1);
1123
+ rb_define_method(cFastxReader, "close", fastx_close, 0);
1124
+
1125
+ execute_mutex = rb_mutex_new();
1126
+ rb_global_variable(&execute_mutex);
1127
+ rb_define_singleton_method(mNative, "revcomp", native_revcomp, 1);
1128
+ rb_define_singleton_method(mNative, "verbose", native_verbose, 0);
1129
+ rb_define_singleton_method(mNative, "verbose=", native_set_verbose, 1);
1130
+ rb_define_singleton_method(mNative, "execute", native_execute, -1);
1131
+ #ifdef RUBY_MINIMAP2_TESTING
1132
+ rb_define_singleton_method(mNative, "resource_counts", native_resource_counts, 0);
1133
+ #endif
1134
+ }