htslib 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -49
  3. data/TUTORIAL.md +9 -20
  4. data/ext/htslib_native/extconf.rb +41 -0
  5. data/ext/htslib_native/htslib_native.h +11 -0
  6. data/ext/htslib_native/htslib_native_ext.c +48 -0
  7. data/ext/htslib_native/native_bam.c +1108 -0
  8. data/ext/htslib_native/native_bcf.c +369 -0
  9. data/ext/htslib_native/native_faidx.c +155 -0
  10. data/ext/htslib_native/native_tabix.c +246 -0
  11. data/lib/hts/bam/auxi.rb +87 -342
  12. data/lib/hts/bam/base_mod.rb +37 -73
  13. data/lib/hts/bam/cigar.rb +9 -55
  14. data/lib/hts/bam/flag.rb +19 -27
  15. data/lib/hts/bam/header.rb +29 -56
  16. data/lib/hts/bam/mpileup.rb +158 -132
  17. data/lib/hts/bam/pileup.rb +120 -145
  18. data/lib/hts/bam/record.rb +233 -270
  19. data/lib/hts/bam.rb +102 -42
  20. data/lib/hts/bcf/errors.rb +1 -0
  21. data/lib/hts/bcf/format.rb +278 -379
  22. data/lib/hts/bcf/header.rb +38 -122
  23. data/lib/hts/bcf/header_record.rb +9 -35
  24. data/lib/hts/bcf/info.rb +72 -320
  25. data/lib/hts/bcf/record.rb +61 -102
  26. data/lib/hts/bcf.rb +149 -323
  27. data/lib/hts/faidx.rb +28 -87
  28. data/lib/hts/hts.rb +6 -94
  29. data/lib/hts/native.rb +13 -0
  30. data/lib/hts/tabix.rb +93 -48
  31. data/lib/hts/version.rb +1 -1
  32. data/lib/htslib.rb +6 -52
  33. metadata +13 -64
  34. data/lib/hts/ffi_ext/README.md +0 -8
  35. data/lib/hts/ffi_ext/pointer.rb +0 -18
  36. data/lib/hts/ffi_ext/struct.rb +0 -45
  37. data/lib/hts/libhts/bgzf.rb +0 -199
  38. data/lib/hts/libhts/constants.rb +0 -658
  39. data/lib/hts/libhts/cram.rb +0 -471
  40. data/lib/hts/libhts/fai.rb +0 -146
  41. data/lib/hts/libhts/hfile.rb +0 -121
  42. data/lib/hts/libhts/hts.rb +0 -477
  43. data/lib/hts/libhts/kfunc.rb +0 -38
  44. data/lib/hts/libhts/sam.rb +0 -760
  45. data/lib/hts/libhts/sam_funcs.rb +0 -155
  46. data/lib/hts/libhts/tbx.rb +0 -94
  47. data/lib/hts/libhts/tbx_funcs.rb +0 -36
  48. data/lib/hts/libhts/thread_pool.rb +0 -139
  49. data/lib/hts/libhts/vcf.rb +0 -567
  50. data/lib/hts/libhts/vcf_funcs.rb +0 -366
  51. data/lib/hts/libhts.rb +0 -47
@@ -0,0 +1,369 @@
1
+ #include "htslib_native.h"
2
+ #include <ruby/thread.h>
3
+
4
+ #include <htslib/bgzf.h>
5
+ #include <htslib/hfile.h>
6
+ #include <htslib/hts.h>
7
+ #include <htslib/kstring.h>
8
+ #include <htslib/tbx.h>
9
+ #include <htslib/vcf.h>
10
+ #include <errno.h>
11
+ #include <stdint.h>
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+ #include <strings.h>
15
+
16
+ typedef struct { bcf_hdr_t *pointer; } ruby_bcf_header_t;
17
+ typedef struct { bcf1_t *pointer; } ruby_bcf_record_t;
18
+ typedef struct { bcf_hrec_t *pointer; } ruby_bcf_hrec_t;
19
+ typedef struct {
20
+ htsFile *file;
21
+ hts_idx_t *index;
22
+ tbx_t *tabix;
23
+ VALUE path;
24
+ int active_io;
25
+ } ruby_bcf_file_t;
26
+ typedef struct {
27
+ hts_itr_t *iterator;
28
+ VALUE file;
29
+ VALUE header;
30
+ int tabix;
31
+ kstring_t line;
32
+ } ruby_bcf_iterator_t;
33
+
34
+ static VALUE cBcfHeader, cBcfRecord, cBcfHrec, cBcfFile, cBcfIterator;
35
+
36
+ static void bcf_header_free(void *data) { ruby_bcf_header_t *v=data; if(v->pointer) bcf_hdr_destroy(v->pointer); xfree(v); }
37
+ static size_t bcf_header_size(const void *data) { return data ? sizeof(ruby_bcf_header_t) : 0; }
38
+ static const rb_data_type_t bcf_header_type = {
39
+ .wrap_struct_name="HTS::Native::BcfHeaderHandle",
40
+ .function={.dfree=bcf_header_free,.dsize=bcf_header_size}, .flags=RUBY_TYPED_FREE_IMMEDIATELY
41
+ };
42
+ static ruby_bcf_header_t *get_bcf_header(VALUE self) {
43
+ ruby_bcf_header_t *v; TypedData_Get_Struct(self,ruby_bcf_header_t,&bcf_header_type,v);
44
+ if(!v->pointer) rb_raise(rb_eIOError,"closed BCF header");
45
+ return v;
46
+ }
47
+ static VALUE wrap_bcf_header(bcf_hdr_t *pointer) {
48
+ ruby_bcf_header_t *v; VALUE object;
49
+ if(!pointer) rb_raise(rb_eRuntimeError,"failed to create BCF header");
50
+ object=TypedData_Make_Struct(cBcfHeader,ruby_bcf_header_t,&bcf_header_type,v); v->pointer=pointer; return object;
51
+ }
52
+
53
+ static void bcf_record_free(void *data) { ruby_bcf_record_t *v=data; if(v->pointer) bcf_destroy(v->pointer); xfree(v); }
54
+ static size_t bcf_record_size(const void *data) { return data ? sizeof(ruby_bcf_record_t) : 0; }
55
+ static const rb_data_type_t bcf_record_type = {
56
+ .wrap_struct_name="HTS::Native::BcfRecordHandle",
57
+ .function={.dfree=bcf_record_free,.dsize=bcf_record_size}, .flags=RUBY_TYPED_FREE_IMMEDIATELY
58
+ };
59
+ static ruby_bcf_record_t *get_bcf_record(VALUE self) {
60
+ ruby_bcf_record_t *v; TypedData_Get_Struct(self,ruby_bcf_record_t,&bcf_record_type,v);
61
+ if(!v->pointer) rb_raise(rb_eIOError,"closed BCF record");
62
+ return v;
63
+ }
64
+ static VALUE wrap_bcf_record(bcf1_t *pointer) {
65
+ ruby_bcf_record_t *v; VALUE object;
66
+ if(!pointer) rb_raise(rb_eNoMemError,"bcf_init failed");
67
+ object=TypedData_Make_Struct(cBcfRecord,ruby_bcf_record_t,&bcf_record_type,v); v->pointer=pointer; return object;
68
+ }
69
+
70
+ static void bcf_hrec_free(void *data) { ruby_bcf_hrec_t *v=data; if(v->pointer) bcf_hrec_destroy(v->pointer); xfree(v); }
71
+ static size_t bcf_hrec_size(const void *data) { return data ? sizeof(ruby_bcf_hrec_t) : 0; }
72
+ static const rb_data_type_t bcf_hrec_type = {
73
+ .wrap_struct_name="HTS::Native::BcfHeaderRecordHandle",
74
+ .function={.dfree=bcf_hrec_free,.dsize=bcf_hrec_size}, .flags=RUBY_TYPED_FREE_IMMEDIATELY
75
+ };
76
+ static ruby_bcf_hrec_t *get_bcf_hrec(VALUE self) {
77
+ ruby_bcf_hrec_t *v; TypedData_Get_Struct(self,ruby_bcf_hrec_t,&bcf_hrec_type,v);
78
+ if(!v->pointer) rb_raise(rb_eIOError,"closed BCF header record");
79
+ return v;
80
+ }
81
+ static VALUE wrap_bcf_hrec(bcf_hrec_t *pointer) {
82
+ ruby_bcf_hrec_t *v; VALUE object;
83
+ if(!pointer) return Qnil;
84
+ object=TypedData_Make_Struct(cBcfHrec,ruby_bcf_hrec_t,&bcf_hrec_type,v); v->pointer=pointer; return object;
85
+ }
86
+
87
+ static void bcf_file_mark(void *data) { rb_gc_mark_movable(((ruby_bcf_file_t*)data)->path); }
88
+ static void bcf_file_compact(void *data) { ruby_bcf_file_t *v=data; v->path=rb_gc_location(v->path); }
89
+ static void bcf_file_free(void *data) {
90
+ ruby_bcf_file_t *v=data; if(v->index) hts_idx_destroy(v->index); if(v->tabix) tbx_destroy(v->tabix);
91
+ if(v->file) hts_close(v->file);
92
+ xfree(v);
93
+ }
94
+ static size_t bcf_file_size(const void *data) { return data ? sizeof(ruby_bcf_file_t) : 0; }
95
+ static const rb_data_type_t bcf_file_type = {
96
+ .wrap_struct_name="HTS::Native::BcfFileHandle",
97
+ .function={.dmark=bcf_file_mark,.dfree=bcf_file_free,.dsize=bcf_file_size,.dcompact=bcf_file_compact},
98
+ .flags=RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
99
+ };
100
+ static ruby_bcf_file_t *get_bcf_file(VALUE self,int allow_closed) {
101
+ ruby_bcf_file_t *v; TypedData_Get_Struct(self,ruby_bcf_file_t,&bcf_file_type,v);
102
+ if(!allow_closed && !v->file) rb_raise(rb_eIOError,"closed stream");
103
+ return v;
104
+ }
105
+
106
+ static void bcf_iterator_mark(void *data) { ruby_bcf_iterator_t *v=data; rb_gc_mark_movable(v->file); rb_gc_mark_movable(v->header); }
107
+ static void bcf_iterator_compact(void *data) { ruby_bcf_iterator_t *v=data; v->file=rb_gc_location(v->file); v->header=rb_gc_location(v->header); }
108
+ static void bcf_iterator_free(void *data) { ruby_bcf_iterator_t *v=data; if(v->iterator) hts_itr_destroy(v->iterator); free(v->line.s); xfree(v); }
109
+ static size_t bcf_iterator_size(const void *data) { return data ? sizeof(ruby_bcf_iterator_t) : 0; }
110
+ static const rb_data_type_t bcf_iterator_type = {
111
+ .wrap_struct_name="HTS::Native::BcfIteratorHandle",
112
+ .function={.dmark=bcf_iterator_mark,.dfree=bcf_iterator_free,.dsize=bcf_iterator_size,.dcompact=bcf_iterator_compact},
113
+ .flags=RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
114
+ };
115
+ static ruby_bcf_iterator_t *get_bcf_iterator(VALUE self) {
116
+ ruby_bcf_iterator_t *v; TypedData_Get_Struct(self,ruby_bcf_iterator_t,&bcf_iterator_type,v);
117
+ if(!v->iterator) rb_raise(rb_eIOError,"closed BCF iterator");
118
+ return v;
119
+ }
120
+ static VALUE wrap_bcf_iterator(VALUE file,VALUE header,hts_itr_t *iterator,int tabix) {
121
+ ruby_bcf_iterator_t *v; VALUE object; if(!iterator) return Qnil;
122
+ object=TypedData_Make_Struct(cBcfIterator,ruby_bcf_iterator_t,&bcf_iterator_type,v);
123
+ v->iterator=iterator; v->file=file; v->header=header; v->tabix=tabix; v->line=(kstring_t)KS_INITIALIZE;
124
+ RB_OBJ_WRITE(object,&v->file,file); RB_OBJ_WRITE(object,&v->header,header); return object;
125
+ }
126
+
127
+ /* Header */
128
+ static VALUE native_bcf_header_create(VALUE klass) { return wrap_bcf_header(bcf_hdr_init("w")); }
129
+ static VALUE native_bcf_header_duplicate(VALUE self) { return wrap_bcf_header(bcf_hdr_dup(get_bcf_header(self)->pointer)); }
130
+ static VALUE native_bcf_header_version(VALUE self) { const char *v=bcf_hdr_get_version(get_bcf_header(self)->pointer); return v?rb_str_new_cstr(v):Qnil; }
131
+ static VALUE native_bcf_header_set_version(VALUE self,VALUE value) { return INT2NUM(bcf_hdr_set_version(get_bcf_header(self)->pointer,StringValueCStr(value))); }
132
+ static VALUE native_bcf_header_read_file(VALUE self,VALUE path) { return INT2NUM(bcf_hdr_set(get_bcf_header(self)->pointer,StringValueCStr(path))); }
133
+ static VALUE native_bcf_header_nsamples(VALUE self) { return INT2NUM(bcf_hdr_nsamples(get_bcf_header(self)->pointer)); }
134
+ static VALUE native_bcf_header_samples(VALUE self) {
135
+ bcf_hdr_t *h=get_bcf_header(self)->pointer; int count=bcf_hdr_nsamples(h),i; VALUE a=rb_ary_new_capa(count);
136
+ for(i=0;i<count;i++) rb_ary_push(a,rb_str_new_cstr(h->samples[i]));
137
+ return a;
138
+ }
139
+ static VALUE native_bcf_header_seqnames(VALUE self) {
140
+ int count=0,i; const char **names=bcf_hdr_seqnames(get_bcf_header(self)->pointer,&count); VALUE a=rb_ary_new_capa(count);
141
+ for(i=0;i<count;i++) rb_ary_push(a,rb_str_new_cstr(names[i]));
142
+ free(names);
143
+ return a;
144
+ }
145
+ static VALUE native_bcf_header_name2id(VALUE self,VALUE name) { return INT2NUM(bcf_hdr_name2id(get_bcf_header(self)->pointer,StringValueCStr(name))); }
146
+ static VALUE native_bcf_header_id2name(VALUE self,VALUE id) { const char *v=bcf_hdr_id2name(get_bcf_header(self)->pointer,NUM2INT(id)); return v?rb_str_new_cstr(v):Qnil; }
147
+ static VALUE native_bcf_header_add_sample(VALUE self,VALUE sample) { return INT2NUM(bcf_hdr_add_sample(get_bcf_header(self)->pointer,StringValueCStr(sample))); }
148
+ static VALUE native_bcf_header_sync(VALUE self) { return INT2NUM(bcf_hdr_sync(get_bcf_header(self)->pointer)); }
149
+ static VALUE native_bcf_header_append(VALUE self,VALUE line) { return INT2NUM(bcf_hdr_append(get_bcf_header(self)->pointer,StringValueCStr(line))); }
150
+ static VALUE native_bcf_header_merge(VALUE self,VALUE other) {
151
+ bcf_hdr_t *result=bcf_hdr_merge(get_bcf_header(self)->pointer,get_bcf_header(other)->pointer);
152
+ if(!result) rb_raise(rb_eRuntimeError,"failed to merge BCF headers");
153
+ return self;
154
+ }
155
+ static VALUE native_bcf_header_to_s(VALUE self) {
156
+ kstring_t str=KS_INITIALIZE; VALUE result;
157
+ if(bcf_hdr_format(get_bcf_header(self)->pointer,0,&str)<0) { free(str.s); rb_raise(rb_eRuntimeError,"failed to format BCF header"); }
158
+ result=rb_str_new(str.s,str.l); free(str.s); return result;
159
+ }
160
+ static int header_kind(VALUE kind) {
161
+ const char *s=StringValueCStr(kind);
162
+ if(!strcasecmp(s,"FILTER")||!strcasecmp(s,"FIL")) return BCF_HL_FLT;
163
+ if(!strcasecmp(s,"INFO")) return BCF_HL_INFO;
164
+ if(!strcasecmp(s,"FORMAT")||!strcasecmp(s,"FMT")) return BCF_HL_FMT;
165
+ if(!strcasecmp(s,"CONTIG")||!strcasecmp(s,"CTG")) return BCF_HL_CTG;
166
+ if(!strcasecmp(s,"STRUCTURED")||!strcasecmp(s,"STR")) return BCF_HL_STR;
167
+ if(!strcasecmp(s,"GENOTYPE")||!strcasecmp(s,"GEN")) return BCF_HL_GEN;
168
+ rb_raise(rb_eTypeError,"invalid BCF header kind");
169
+ }
170
+ static VALUE native_bcf_header_remove(VALUE self,VALUE kind,VALUE key) {
171
+ bcf_hdr_remove(get_bcf_header(self)->pointer,header_kind(kind),NIL_P(key)?NULL:StringValueCStr(key)); return Qnil;
172
+ }
173
+ static VALUE native_bcf_header_get_hrec(VALUE self,VALUE kind,VALUE key,VALUE value,VALUE str_class) {
174
+ bcf_hrec_t *record=bcf_hdr_get_hrec(get_bcf_header(self)->pointer,header_kind(kind),
175
+ NIL_P(key)?NULL:StringValueCStr(key),NIL_P(value)?NULL:StringValueCStr(value),
176
+ NIL_P(str_class)?NULL:StringValueCStr(str_class));
177
+ return record?wrap_bcf_hrec(bcf_hrec_dup(record)):Qnil;
178
+ }
179
+ static VALUE type_symbol(int type) {
180
+ switch(type) { case BCF_HT_FLAG:return ID2SYM(rb_intern("flag")); case BCF_HT_INT:return ID2SYM(rb_intern("int"));
181
+ case BCF_HT_REAL:return ID2SYM(rb_intern("float")); case BCF_HT_STR:return ID2SYM(rb_intern("string"));
182
+ case BCF_HT_LONG:return ID2SYM(rb_intern("int64")); default:return Qnil; }
183
+ }
184
+ static VALUE native_bcf_header_schema(VALUE self,VALUE kind_value,VALUE key_value) {
185
+ bcf_hdr_t *h=get_bcf_header(self)->pointer; int kind=header_kind(kind_value), id=bcf_hdr_id2int(h,BCF_DT_ID,StringValueCStr(key_value));
186
+ VALUE result;
187
+ if(id<0 || !bcf_hdr_idinfo_exists(h,kind,id)) return Qnil;
188
+ result=rb_ary_new_capa(3); rb_ary_push(result,type_symbol(bcf_hdr_id2type(h,kind,id)));
189
+ rb_ary_push(result,INT2NUM(bcf_hdr_id2number(h,kind,id))); rb_ary_push(result,INT2NUM(id)); return result;
190
+ }
191
+ static VALUE native_bcf_header_subset(VALUE self,VALUE samples) {
192
+ bcf_hdr_t *h=get_bcf_header(self)->pointer,*subset; int count=RARRAY_LEN(samples),i; char **names=NULL; int *imap=NULL; VALUE map;
193
+ if(count>0) { names=ALLOC_N(char*,count); imap=ALLOC_N(int,count); for(i=0;i<count;i++) { VALUE sample=rb_ary_entry(samples,i); names[i]=StringValueCStr(sample); } }
194
+ subset=bcf_hdr_subset(h,count,names,imap); xfree(names); if(!subset){xfree(imap);return Qnil;}
195
+ map=rb_ary_new_capa(count); for(i=0;i<count;i++)rb_ary_push(map,INT2NUM(imap[i])); xfree(imap);
196
+ return rb_ary_new_from_args(2,wrap_bcf_header(subset),map);
197
+ }
198
+
199
+ /* Header record */
200
+ static VALUE native_bcf_hrec_duplicate(VALUE self) { return wrap_bcf_hrec(bcf_hrec_dup(get_bcf_hrec(self)->pointer)); }
201
+ static VALUE native_bcf_hrec_add_key(VALUE self,VALUE key) { return INT2NUM(bcf_hrec_add_key(get_bcf_hrec(self)->pointer,StringValueCStr(key),RSTRING_LEN(key))); }
202
+ static VALUE native_bcf_hrec_set_value(VALUE self,VALUE index,VALUE value,VALUE quote) { StringValue(value); return INT2NUM(bcf_hrec_set_val(get_bcf_hrec(self)->pointer,NUM2INT(index),RSTRING_PTR(value),RSTRING_LEN(value),RTEST(quote))); }
203
+ static VALUE native_bcf_hrec_find_key(VALUE self,VALUE key) { return INT2NUM(bcf_hrec_find_key(get_bcf_hrec(self)->pointer,StringValueCStr(key))); }
204
+ static VALUE native_bcf_hrec_to_s(VALUE self) { kstring_t s=KS_INITIALIZE; VALUE r; bcf_hrec_format(get_bcf_hrec(self)->pointer,&s); r=rb_str_new(s.s,s.l); free(s.s); return r; }
205
+
206
+ /* Record core */
207
+ static VALUE native_bcf_record_create(VALUE klass) { return wrap_bcf_record(bcf_init()); }
208
+ static VALUE native_bcf_record_duplicate(VALUE self) { return wrap_bcf_record(bcf_dup(get_bcf_record(self)->pointer)); }
209
+ static VALUE native_bcf_record_core_get(VALUE self,VALUE field) {
210
+ bcf1_t *r=get_bcf_record(self)->pointer; ID id=SYM2ID(field);
211
+ if(id==rb_intern("rid"))return INT2NUM(r->rid);
212
+ if(id==rb_intern("pos"))return LL2NUM(r->pos);
213
+ if(id==rb_intern("rlen"))return LL2NUM(r->rlen);
214
+ if(id==rb_intern("qual"))return DBL2NUM(r->qual);
215
+ rb_raise(rb_eArgError,"unknown BCF field");
216
+ }
217
+ static VALUE native_bcf_record_core_set(VALUE self,VALUE field,VALUE value) {
218
+ bcf1_t *r=get_bcf_record(self)->pointer; ID id=SYM2ID(field);
219
+ if(id==rb_intern("rid"))r->rid=NUM2INT(value); else if(id==rb_intern("pos"))r->pos=NUM2LL(value);
220
+ else if(id==rb_intern("qual"))r->qual=(float)NUM2DBL(value);
221
+ else rb_raise(rb_eArgError,"unknown BCF field");
222
+ return value;
223
+ }
224
+ static VALUE native_bcf_record_id(VALUE self) { bcf1_t *r=get_bcf_record(self)->pointer; bcf_unpack(r,BCF_UN_INFO); return rb_str_new_cstr(r->d.id); }
225
+ static VALUE native_bcf_record_set_id(VALUE self,VALUE header,VALUE id) { return INT2NUM(bcf_update_id(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,StringValueCStr(id))); }
226
+ static VALUE native_bcf_record_alleles(VALUE self) { bcf1_t *r=get_bcf_record(self)->pointer; int i; VALUE a; bcf_unpack(r,BCF_UN_STR); a=rb_ary_new_capa(r->n_allele); for(i=0;i<r->n_allele;i++)rb_ary_push(a,rb_str_new_cstr(r->d.allele[i])); return a; }
227
+ static VALUE native_bcf_record_set_alleles(VALUE self,VALUE header,VALUE value) { return INT2NUM(bcf_update_alleles_str(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,StringValueCStr(value))); }
228
+ static VALUE native_bcf_record_filter_ids(VALUE self) { bcf1_t *r=get_bcf_record(self)->pointer; int i; VALUE a; bcf_unpack(r,BCF_UN_FLT); a=rb_ary_new_capa(r->d.n_flt); for(i=0;i<r->d.n_flt;i++)rb_ary_push(a,INT2NUM(r->d.flt[i])); return a; }
229
+ static VALUE native_bcf_record_filter_names(VALUE self,VALUE header) { bcf1_t *r=get_bcf_record(self)->pointer; bcf_hdr_t *h=get_bcf_header(header)->pointer; int i; VALUE a; bcf_unpack(r,BCF_UN_FLT); a=rb_ary_new_capa(r->d.n_flt); for(i=0;i<r->d.n_flt;i++)rb_ary_push(a,rb_str_new_cstr(bcf_hdr_int2id(h,BCF_DT_ID,r->d.flt[i]))); return a; }
230
+ static VALUE native_bcf_record_to_s(VALUE self,VALUE header) { kstring_t s=KS_INITIALIZE; VALUE v; if(vcf_format(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,&s)<0){free(s.s);rb_raise(rb_eRuntimeError,"failed to format BCF record");}v=rb_str_new(s.s,s.l);free(s.s);return v; }
231
+ static VALUE native_bcf_record_set_unpack(VALUE self,VALUE level){get_bcf_record(self)->pointer->max_unpack=NUM2INT(level);return level;}
232
+ static VALUE native_bcf_record_subset(VALUE self,VALUE header,VALUE map){int count=RARRAY_LEN(map),i,*imap=ALLOC_N(int,count?count:1);for(i=0;i<count;i++)imap[i]=NUM2INT(rb_ary_entry(map,i));int result=bcf_subset(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,count,imap);xfree(imap);return INT2NUM(result);}
233
+
234
+ static VALUE field_rows(bcf_hdr_t *h,bcf1_t *r,int format) {
235
+ int i,count; VALUE rows; if(format){bcf_unpack(r,BCF_UN_FMT);count=r->n_fmt;}else{bcf_unpack(r,BCF_UN_INFO);count=r->n_info;}
236
+ rows=rb_ary_new_capa(count);
237
+ for(i=0;i<count;i++) { int id=format?r->d.fmt[i].id:r->d.info[i].key, kind=format?BCF_HL_FMT:BCF_HL_INFO; VALUE row=rb_hash_new();
238
+ rb_hash_aset(row,ID2SYM(rb_intern("name")),rb_str_new_cstr(bcf_hdr_int2id(h,BCF_DT_ID,id)));
239
+ rb_hash_aset(row,ID2SYM(rb_intern("n")),INT2NUM(bcf_hdr_id2number(h,kind,id)));
240
+ rb_hash_aset(row,ID2SYM(rb_intern("type")),type_symbol(bcf_hdr_id2type(h,kind,id)));
241
+ rb_hash_aset(row,ID2SYM(rb_intern(format?"id":"key")),INT2NUM(id)); rb_ary_push(rows,row); }
242
+ return rows;
243
+ }
244
+ static VALUE native_bcf_record_info_fields(VALUE self,VALUE header) { return field_rows(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,0); }
245
+ static VALUE native_bcf_record_format_fields(VALUE self,VALUE header) { return field_rows(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,1); }
246
+
247
+ static VALUE info_get(VALUE self,VALUE header_value,VALUE key_value,VALUE type_value) {
248
+ bcf_hdr_t *h=get_bcf_header(header_value)->pointer; bcf1_t *r=get_bcf_record(self)->pointer; int type=NUM2INT(type_value),cap=0,count,i; void *dst=NULL; VALUE value;
249
+ count=bcf_get_info_values(h,r,StringValueCStr(key_value),&dst,&cap,type);
250
+ if(count<0){free(dst);return Qnil;}
251
+ if(type==BCF_HT_FLAG){free(dst);return count==1?Qtrue:Qnil;}
252
+ if(type==BCF_HT_STR)value=rb_str_new_cstr((char*)dst);
253
+ else { value=rb_ary_new_capa(count); for(i=0;i<count;i++) {
254
+ if(type==BCF_HT_INT){
255
+ int32_t item=((int32_t*)dst)[i];
256
+ if(item==bcf_int32_vector_end)break;
257
+ rb_ary_push(value,item==bcf_int32_missing?Qnil:INT2NUM(item));
258
+ }
259
+ else if(type==BCF_HT_LONG){
260
+ int64_t item=((int64_t*)dst)[i];
261
+ if(item==bcf_int64_vector_end)break;
262
+ rb_ary_push(value,item==bcf_int64_missing?Qnil:LL2NUM(item));
263
+ }
264
+ else {
265
+ float item=((float*)dst)[i];
266
+ if(bcf_float_is_vector_end(item))break;
267
+ rb_ary_push(value,bcf_float_is_missing(item)?Qnil:DBL2NUM(item));
268
+ } } }
269
+ free(dst);return value;
270
+ }
271
+ static VALUE native_bcf_info_get(VALUE self,VALUE header,VALUE key,VALUE type) { return info_get(self,header,key,type); }
272
+ static VALUE native_bcf_info_present(VALUE self,VALUE header,VALUE key,VALUE type) { return NIL_P(info_get(self,header,key,type))?Qfalse:Qtrue; }
273
+ static int fill_i32(VALUE array,int32_t **out) { int n=RARRAY_LEN(array),i; *out=ALLOC_N(int32_t,n?n:1);for(i=0;i<n;i++)(*out)[i]=NUM2INT(rb_ary_entry(array,i));return n; }
274
+ static int fill_float(VALUE array,float **out) { int n=RARRAY_LEN(array),i; *out=ALLOC_N(float,n?n:1);for(i=0;i<n;i++)(*out)[i]=(float)NUM2DBL(rb_ary_entry(array,i));return n; }
275
+ static VALUE native_bcf_info_update(VALUE self,VALUE header,VALUE key,VALUE type_value,VALUE values) {
276
+ bcf_hdr_t*h=get_bcf_header(header)->pointer;bcf1_t*r=get_bcf_record(self)->pointer;int type=NUM2INT(type_value),n,result;void*data=NULL;
277
+ if(type!=BCF_HT_FLAG && (NIL_P(values) || values==Qfalse)){
278
+ return INT2NUM(bcf_update_info(h,r,StringValueCStr(key),NULL,0,type));
279
+ }
280
+ if(type==BCF_HT_INT){int32_t*p;n=fill_i32(values,&p);data=p;result=bcf_update_info(h,r,StringValueCStr(key),data,n,type);xfree(p);}
281
+ else if(type==BCF_HT_REAL){float*p;n=fill_float(values,&p);data=p;result=bcf_update_info(h,r,StringValueCStr(key),data,n,type);xfree(p);}
282
+ else if(type==BCF_HT_STR){result=bcf_update_info(h,r,StringValueCStr(key),StringValueCStr(values),1,type);}
283
+ else { n=RTEST(values)?1:0; result=bcf_update_info(h,r,StringValueCStr(key),NULL,n,type); }
284
+ return INT2NUM(result);
285
+ }
286
+
287
+ static VALUE native_bcf_format_get(VALUE self,VALUE header_value,VALUE key_value,VALUE type_value,VALUE raw_value) {
288
+ bcf_hdr_t*h=get_bcf_header(header_value)->pointer;bcf1_t*r=get_bcf_record(self)->pointer;int type=NUM2INT(type_value),cap=0,count,i;void*dst=NULL;VALUE value;
289
+ if(type==BCF_HT_STR){
290
+ char **strings=NULL;
291
+ int sample_count=bcf_hdr_nsamples(h);
292
+ count=bcf_get_format_string(h,r,StringValueCStr(key_value),&strings,&cap);
293
+ if(count<0){free(strings);return Qnil;}
294
+ value=rb_ary_new_capa(sample_count);
295
+ for(i=0;i<sample_count;i++)rb_ary_push(value,rb_str_new_cstr(strings[i]));
296
+ if(strings){if(sample_count>0)free(strings[0]);free(strings);}
297
+ return value;
298
+ }
299
+ count=bcf_get_format_values(h,r,StringValueCStr(key_value),&dst,&cap,type);if(count<0){free(dst);return Qnil;}value=rb_ary_new_capa(count);
300
+ for(i=0;i<count;i++){if(type==BCF_HT_INT)rb_ary_push(value,INT2NUM(((int32_t*)dst)[i]));else {float f=((float*)dst)[i];uint32_t word;memcpy(&word,&f,4);if(RTEST(raw_value))rb_ary_push(value,UINT2NUM(word));else if(bcf_float_is_missing(f)||bcf_float_is_vector_end(f))rb_ary_push(value,Qnil);else rb_ary_push(value,DBL2NUM(f));}}
301
+ free(dst);return value;
302
+ }
303
+ static VALUE native_bcf_format_update(VALUE self,VALUE header,VALUE key,VALUE type_value,VALUE values) {
304
+ bcf_hdr_t*h=get_bcf_header(header)->pointer;bcf1_t*r=get_bcf_record(self)->pointer;int type=NUM2INT(type_value),n,result;
305
+ if(type==BCF_HT_INT){int32_t*p;n=fill_i32(values,&p);result=bcf_update_format_int32(h,r,StringValueCStr(key),p,n);xfree(p);}
306
+ else if(type==BCF_HT_REAL){float*p;n=fill_float(values,&p);result=bcf_update_format_float(h,r,StringValueCStr(key),p,n);xfree(p);}
307
+ else {int i;char**strings;n=RARRAY_LEN(values);strings=ALLOC_N(char*,n?n:1);for(i=0;i<n;i++){VALUE string=rb_ary_entry(values,i);strings[i]=StringValueCStr(string);}result=bcf_update_format_string(h,r,StringValueCStr(key),(const char**)strings,n);xfree(strings);}return INT2NUM(result);
308
+ }
309
+ static VALUE native_bcf_format_update_float_words(VALUE self,VALUE header,VALUE key,VALUE values) {
310
+ bcf_hdr_t *h=get_bcf_header(header)->pointer;
311
+ bcf1_t *r=get_bcf_record(self)->pointer;
312
+ int n=RARRAY_LEN(values),i,result;
313
+ float *floats=ALLOC_N(float,n?n:1);
314
+ for(i=0;i<n;i++){
315
+ uint32_t word=NUM2UINT(rb_ary_entry(values,i));
316
+ memcpy(&floats[i],&word,sizeof(word));
317
+ }
318
+ result=bcf_update_format_float(h,r,StringValueCStr(key),floats,n);
319
+ xfree(floats);
320
+ return INT2NUM(result);
321
+ }
322
+ static VALUE native_bcf_genotype_update(VALUE self,VALUE header,VALUE values) {int32_t*p;int n=fill_i32(values,&p);int result=bcf_update_genotypes(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,p,n);xfree(p);return INT2NUM(result);}
323
+ static VALUE native_bcf_format_delete(VALUE self,VALUE header,VALUE key,VALUE type) {return INT2NUM(bcf_update_format(get_bcf_header(header)->pointer,get_bcf_record(self)->pointer,StringValueCStr(key),NULL,0,NUM2INT(type)));}
324
+
325
+ /* File */
326
+ typedef struct {htsFile*file;bcf_hdr_t*header;bcf1_t*record;hts_itr_t*iterator;tbx_t*tabix;kstring_t*line;int result;} bcf_io_args_t;
327
+ typedef struct {htsFile*file;bcf_hdr_t*result;} bcf_header_read_args_t;
328
+ static void *bcf_read_header_without_gvl(void*data){bcf_header_read_args_t*a=data;a->result=bcf_hdr_read(a->file);return NULL;}
329
+ static void *bcf_read_without_gvl(void*data){bcf_io_args_t*a=data;a->result=bcf_read(a->file,a->header,a->record);return NULL;}
330
+ static void *bcf_write_header_without_gvl(void*data){bcf_io_args_t*a=data;a->result=bcf_hdr_write(a->file,a->header);return NULL;}
331
+ static void *bcf_write_without_gvl(void*data){bcf_io_args_t*a=data;a->result=bcf_write(a->file,a->header,a->record);return NULL;}
332
+ static void *bcf_iterator_without_gvl(void*data){bcf_io_args_t*a=data;if(a->tabix){int n=tbx_itr_next(a->file,a->tabix,a->iterator,a->line);a->result=n<0?n:vcf_parse(a->line,a->header,a->record);}else a->result=bcf_itr_next(a->file,a->iterator,a->record);return NULL;}
333
+ static void bcf_io_begin(ruby_bcf_file_t*v){if(v->active_io)rb_raise(rb_eIOError,"concurrent BCF I/O is not supported");v->active_io=1;}
334
+ static VALUE native_bcf_file_open(VALUE klass,VALUE path,VALUE mode) {ruby_bcf_file_t*v;VALUE o=TypedData_Make_Struct(klass,ruby_bcf_file_t,&bcf_file_type,v);v->index=NULL;v->tabix=NULL;v->active_io=0;v->path=rb_str_dup(StringValue(path));RB_OBJ_WRITE(o,&v->path,v->path);v->file=hts_open(StringValueCStr(v->path),StringValueCStr(mode));if(!v->file)rb_syserr_fail_str(ENOENT,path);return o;}
335
+ static VALUE native_bcf_file_close(VALUE self){ruby_bcf_file_t*v=get_bcf_file(self,1);if(v->active_io)rb_raise(rb_eIOError,"cannot close BCF during active I/O");if(v->index){hts_idx_destroy(v->index);v->index=NULL;}if(v->tabix){tbx_destroy(v->tabix);v->tabix=NULL;}if(v->file){hts_close(v->file);v->file=NULL;}return Qnil;}
336
+ static VALUE native_bcf_file_closed(VALUE self){return get_bcf_file(self,1)->file?Qfalse:Qtrue;}
337
+ static VALUE native_bcf_file_read_header(VALUE self){ruby_bcf_file_t*v=get_bcf_file(self,0);bcf_header_read_args_t a={v->file,NULL};bcf_io_begin(v);rb_thread_call_without_gvl(bcf_read_header_without_gvl,&a,RUBY_UBF_IO,NULL);v->active_io=0;return wrap_bcf_header(a.result);}
338
+ static VALUE native_bcf_file_read(VALUE self,VALUE header,VALUE record){ruby_bcf_file_t*v=get_bcf_file(self,0);bcf_io_args_t a={v->file,get_bcf_header(header)->pointer,get_bcf_record(record)->pointer,NULL,NULL,NULL,0};bcf_io_begin(v);rb_thread_call_without_gvl(bcf_read_without_gvl,&a,RUBY_UBF_IO,NULL);v->active_io=0;return INT2NUM(a.result);}
339
+ static VALUE native_bcf_file_write_header(VALUE self,VALUE header){ruby_bcf_file_t*v=get_bcf_file(self,0);bcf_io_args_t a={v->file,get_bcf_header(header)->pointer,NULL,NULL,NULL,NULL,0};bcf_io_begin(v);rb_thread_call_without_gvl(bcf_write_header_without_gvl,&a,RUBY_UBF_IO,NULL);v->active_io=0;return INT2NUM(a.result);}
340
+ static VALUE native_bcf_file_write(VALUE self,VALUE header,VALUE record){ruby_bcf_file_t*v=get_bcf_file(self,0);bcf_io_args_t a={v->file,get_bcf_header(header)->pointer,get_bcf_record(record)->pointer,NULL,NULL,NULL,0};bcf_io_begin(v);rb_thread_call_without_gvl(bcf_write_without_gvl,&a,RUBY_UBF_IO,NULL);v->active_io=0;return INT2NUM(a.result);}
341
+ static VALUE native_bcf_file_set_threads(VALUE self,VALUE n){return INT2NUM(hts_set_threads(get_bcf_file(self,0)->file,NUM2INT(n)));}
342
+ static VALUE native_bcf_file_format(VALUE self){const htsFormat*f=hts_get_format(get_bcf_file(self,0)->file);if(!f)return Qnil;switch(f->format){case vcf:return rb_str_new_cstr("vcf");case bcf:return rb_str_new_cstr("bcf");default:return rb_str_new_cstr("unknown_format");}}
343
+ static VALUE native_bcf_file_version(VALUE self){const htsFormat*f=hts_get_format(get_bcf_file(self,0)->file);if(!f)return Qnil;if(f->version.minor==-1)return rb_sprintf("%d",f->version.major);return rb_sprintf("%d.%d",f->version.major,f->version.minor);}
344
+ static VALUE native_bcf_file_seek(VALUE self,VALUE offset){htsFile*f=get_bcf_file(self,0)->file;return LL2NUM(f->is_bgzf?bgzf_seek(f->fp.bgzf,NUM2LL(offset),SEEK_SET):hseek(f->fp.hfile,NUM2LL(offset),SEEK_SET));}
345
+ static VALUE native_bcf_file_tell(VALUE self){htsFile*f=get_bcf_file(self,0)->file;return LL2NUM(f->is_bgzf?bgzf_tell(f->fp.bgzf):htell(f->fp.hfile));}
346
+ static VALUE native_bcf_file_load_index(VALUE self,VALUE index){ruby_bcf_file_t*v=get_bcf_file(self,0);const htsFormat*f=hts_get_format(v->file);if(v->index){hts_idx_destroy(v->index);v->index=NULL;}if(v->tabix){tbx_destroy(v->tabix);v->tabix=NULL;}if(f&&f->format==vcf)v->tabix=NIL_P(index)?tbx_index_load3(StringValueCStr(v->path),NULL,HTS_IDX_SAVE_REMOTE):tbx_index_load2(StringValueCStr(v->path),StringValueCStr(index));else v->index=NIL_P(index)?bcf_index_load3(StringValueCStr(v->path),NULL,HTS_IDX_SAVE_REMOTE):bcf_index_load2(StringValueCStr(v->path),StringValueCStr(index));return(v->index||v->tabix)?Qtrue:Qfalse;}
347
+ static VALUE native_bcf_file_index_loaded(VALUE self){ruby_bcf_file_t*v=get_bcf_file(self,0);return(v->index||v->tabix)?Qtrue:Qfalse;}
348
+ static VALUE native_bcf_file_build_index(VALUE klass,VALUE path,VALUE index,VALUE shift,VALUE threads){return INT2NUM(bcf_index_build3(StringValueCStr(path),NIL_P(index)?NULL:StringValueCStr(index),NUM2INT(shift),NUM2INT(threads)));}
349
+ static VALUE native_bcf_file_query_region(VALUE self,VALUE header,VALUE region){ruby_bcf_file_t*v=get_bcf_file(self,0);hts_itr_t*i;if(v->tabix)i=tbx_itr_querys(v->tabix,StringValueCStr(region));else i=bcf_itr_querys(v->index,get_bcf_header(header)->pointer,StringValueCStr(region));return wrap_bcf_iterator(self,header,i,v->tabix!=NULL);}
350
+ static VALUE native_bcf_file_query_interval(VALUE self,VALUE header,VALUE rid,VALUE beg,VALUE end){ruby_bcf_file_t*v=get_bcf_file(self,0);hts_itr_t*i=v->tabix?tbx_itr_queryi(v->tabix,NUM2INT(rid),NUM2LL(beg),NUM2LL(end)):bcf_itr_queryi(v->index,NUM2INT(rid),NUM2LL(beg),NUM2LL(end));return wrap_bcf_iterator(self,header,i,v->tabix!=NULL);}
351
+ static VALUE native_bcf_iterator_next(VALUE self,VALUE record){ruby_bcf_iterator_t*i=get_bcf_iterator(self);ruby_bcf_file_t*f=get_bcf_file(i->file,0);bcf_io_args_t a={f->file,get_bcf_header(i->header)->pointer,get_bcf_record(record)->pointer,i->iterator,i->tabix?f->tabix:NULL,&i->line,0};bcf_io_begin(f);rb_thread_call_without_gvl(bcf_iterator_without_gvl,&a,RUBY_UBF_IO,NULL);f->active_io=0;return INT2NUM(a.result);}
352
+ static VALUE native_bcf_iterator_close(VALUE self){ruby_bcf_iterator_t*v;TypedData_Get_Struct(self,ruby_bcf_iterator_t,&bcf_iterator_type,v);if(v->iterator){hts_itr_destroy(v->iterator);v->iterator=NULL;}return Qnil;}
353
+
354
+ void Init_htslib_native_bcf(VALUE native) {
355
+ cBcfHeader=rb_define_class_under(native,"BcfHeaderHandle",rb_cObject);rb_undef_alloc_func(cBcfHeader);
356
+ rb_define_singleton_method(cBcfHeader,"create",native_bcf_header_create,0);rb_define_method(cBcfHeader,"duplicate",native_bcf_header_duplicate,0);
357
+ rb_define_method(cBcfHeader,"version",native_bcf_header_version,0);rb_define_method(cBcfHeader,"set_version",native_bcf_header_set_version,1);rb_define_method(cBcfHeader,"read_file",native_bcf_header_read_file,1);
358
+ rb_define_method(cBcfHeader,"nsamples",native_bcf_header_nsamples,0);rb_define_method(cBcfHeader,"samples",native_bcf_header_samples,0);
359
+ rb_define_method(cBcfHeader,"seqnames",native_bcf_header_seqnames,0);rb_define_method(cBcfHeader,"name2id",native_bcf_header_name2id,1);rb_define_method(cBcfHeader,"id2name",native_bcf_header_id2name,1);
360
+ rb_define_method(cBcfHeader,"add_sample",native_bcf_header_add_sample,1);rb_define_method(cBcfHeader,"sync",native_bcf_header_sync,0);rb_define_method(cBcfHeader,"append",native_bcf_header_append,1);
361
+ rb_define_method(cBcfHeader,"merge",native_bcf_header_merge,1);rb_define_method(cBcfHeader,"to_s",native_bcf_header_to_s,0);rb_define_method(cBcfHeader,"remove",native_bcf_header_remove,2);
362
+ rb_define_method(cBcfHeader,"get_hrec",native_bcf_header_get_hrec,4);rb_define_method(cBcfHeader,"schema",native_bcf_header_schema,2);rb_define_method(cBcfHeader,"subset",native_bcf_header_subset,1);
363
+ cBcfHrec=rb_define_class_under(native,"BcfHeaderRecordHandle",rb_cObject);rb_undef_alloc_func(cBcfHrec);rb_define_method(cBcfHrec,"duplicate",native_bcf_hrec_duplicate,0);rb_define_method(cBcfHrec,"add_key",native_bcf_hrec_add_key,1);rb_define_method(cBcfHrec,"set_value",native_bcf_hrec_set_value,3);rb_define_method(cBcfHrec,"find_key",native_bcf_hrec_find_key,1);rb_define_method(cBcfHrec,"to_s",native_bcf_hrec_to_s,0);
364
+ cBcfRecord=rb_define_class_under(native,"BcfRecordHandle",rb_cObject);rb_undef_alloc_func(cBcfRecord);rb_define_singleton_method(cBcfRecord,"create",native_bcf_record_create,0);rb_define_method(cBcfRecord,"duplicate",native_bcf_record_duplicate,0);rb_define_method(cBcfRecord,"core_get",native_bcf_record_core_get,1);rb_define_method(cBcfRecord,"core_set",native_bcf_record_core_set,2);rb_define_method(cBcfRecord,"id",native_bcf_record_id,0);rb_define_method(cBcfRecord,"set_id",native_bcf_record_set_id,2);rb_define_method(cBcfRecord,"alleles",native_bcf_record_alleles,0);rb_define_method(cBcfRecord,"set_alleles",native_bcf_record_set_alleles,2);rb_define_method(cBcfRecord,"filter_ids",native_bcf_record_filter_ids,0);rb_define_method(cBcfRecord,"filter_names",native_bcf_record_filter_names,1);rb_define_method(cBcfRecord,"format_record",native_bcf_record_to_s,1);rb_define_method(cBcfRecord,"info_fields",native_bcf_record_info_fields,1);rb_define_method(cBcfRecord,"format_fields",native_bcf_record_format_fields,1);rb_define_method(cBcfRecord,"info_get",native_bcf_info_get,3);rb_define_method(cBcfRecord,"info_present?",native_bcf_info_present,3);rb_define_method(cBcfRecord,"info_update",native_bcf_info_update,4);rb_define_method(cBcfRecord,"format_get",native_bcf_format_get,4);rb_define_method(cBcfRecord,"format_update",native_bcf_format_update,4);rb_define_method(cBcfRecord,"format_update_float_words",native_bcf_format_update_float_words,3);rb_define_method(cBcfRecord,"genotype_update",native_bcf_genotype_update,2);rb_define_method(cBcfRecord,"format_delete",native_bcf_format_delete,3);
365
+ rb_define_method(cBcfRecord,"max_unpack=",native_bcf_record_set_unpack,1);rb_define_method(cBcfRecord,"subset",native_bcf_record_subset,2);
366
+ cBcfFile=rb_define_class_under(native,"BcfFileHandle",rb_cObject);rb_undef_alloc_func(cBcfFile);rb_define_singleton_method(cBcfFile,"open",native_bcf_file_open,2);rb_define_singleton_method(cBcfFile,"build_index",native_bcf_file_build_index,4);rb_define_method(cBcfFile,"close",native_bcf_file_close,0);rb_define_method(cBcfFile,"closed?",native_bcf_file_closed,0);rb_define_method(cBcfFile,"read_header",native_bcf_file_read_header,0);rb_define_method(cBcfFile,"read",native_bcf_file_read,2);rb_define_method(cBcfFile,"write_header",native_bcf_file_write_header,1);rb_define_method(cBcfFile,"write",native_bcf_file_write,2);rb_define_method(cBcfFile,"set_threads",native_bcf_file_set_threads,1);rb_define_method(cBcfFile,"file_format",native_bcf_file_format,0);rb_define_method(cBcfFile,"file_format_version",native_bcf_file_version,0);rb_define_method(cBcfFile,"seek",native_bcf_file_seek,1);rb_define_method(cBcfFile,"tell",native_bcf_file_tell,0);rb_define_method(cBcfFile,"load_index",native_bcf_file_load_index,1);rb_define_method(cBcfFile,"index_loaded?",native_bcf_file_index_loaded,0);rb_define_method(cBcfFile,"query_region",native_bcf_file_query_region,2);rb_define_method(cBcfFile,"query_interval",native_bcf_file_query_interval,4);
367
+ cBcfIterator=rb_define_class_under(native,"BcfIteratorHandle",rb_cObject);rb_undef_alloc_func(cBcfIterator);rb_define_method(cBcfIterator,"next",native_bcf_iterator_next,1);rb_define_method(cBcfIterator,"close",native_bcf_iterator_close,0);
368
+ rb_define_const(native,"BCF_HT_FLAG",INT2NUM(BCF_HT_FLAG));rb_define_const(native,"BCF_HT_INT",INT2NUM(BCF_HT_INT));rb_define_const(native,"BCF_HT_REAL",INT2NUM(BCF_HT_REAL));rb_define_const(native,"BCF_HT_STR",INT2NUM(BCF_HT_STR));rb_define_const(native,"BCF_HT_LONG",INT2NUM(BCF_HT_LONG));rb_define_const(native,"BCF_INT32_MISSING",INT2NUM(bcf_int32_missing));rb_define_const(native,"BCF_INT32_VECTOR_END",INT2NUM(bcf_int32_vector_end));rb_define_const(native,"BCF_FLOAT_MISSING",UINT2NUM(bcf_float_missing));rb_define_const(native,"BCF_FLOAT_VECTOR_END",UINT2NUM(bcf_float_vector_end));
369
+ }
@@ -0,0 +1,155 @@
1
+ #include "htslib_native.h"
2
+
3
+ #include <htslib/faidx.h>
4
+ #include <stdlib.h>
5
+
6
+ typedef struct {
7
+ faidx_t *fai;
8
+ } ruby_faidx_t;
9
+
10
+ static VALUE cNativeFaidx;
11
+
12
+ static void ruby_faidx_free(void *pointer)
13
+ {
14
+ ruby_faidx_t *handle = pointer;
15
+ if (handle->fai != NULL) fai_destroy(handle->fai);
16
+ xfree(handle);
17
+ }
18
+
19
+ static size_t ruby_faidx_size(const void *pointer)
20
+ {
21
+ return pointer == NULL ? 0 : sizeof(ruby_faidx_t);
22
+ }
23
+
24
+ static const rb_data_type_t ruby_faidx_type = {
25
+ .wrap_struct_name = "HTS::Native::FaidxHandle",
26
+ .function = {
27
+ .dmark = NULL,
28
+ .dfree = ruby_faidx_free,
29
+ .dsize = ruby_faidx_size,
30
+ },
31
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
32
+ };
33
+
34
+ static ruby_faidx_t *get_faidx(VALUE self, int allow_closed)
35
+ {
36
+ ruby_faidx_t *handle;
37
+ TypedData_Get_Struct(self, ruby_faidx_t, &ruby_faidx_type, handle);
38
+ if (!allow_closed && handle->fai == NULL) rb_raise(rb_eIOError, "closed Faidx");
39
+ return handle;
40
+ }
41
+
42
+ static VALUE native_faidx_open(VALUE klass, VALUE path_value, VALUE format_value, VALUE auto_build_value)
43
+ {
44
+ ruby_faidx_t *handle;
45
+ VALUE object;
46
+ const char *path = StringValueCStr(path_value);
47
+ enum fai_format_options format = NUM2INT(format_value) == 1 ? FAI_FASTQ : FAI_FASTA;
48
+
49
+ object = TypedData_Make_Struct(klass, ruby_faidx_t, &ruby_faidx_type, handle);
50
+ handle->fai = RTEST(auto_build_value)
51
+ ? fai_load_format(path, format)
52
+ : fai_load3_format(path, NULL, NULL, 0, format);
53
+ if (handle->fai == NULL) rb_syserr_fail_str(ENOENT, path_value);
54
+ return object;
55
+ }
56
+
57
+ static VALUE native_faidx_build(VALUE klass, VALUE path_value, VALUE fai_value, VALUE gzi_value)
58
+ {
59
+ const char *path = StringValueCStr(path_value);
60
+ const char *fai_path = NIL_P(fai_value) ? NULL : StringValueCStr(fai_value);
61
+ const char *gzi_path = NIL_P(gzi_value) ? NULL : StringValueCStr(gzi_value);
62
+ return INT2NUM(fai_build3(path, fai_path, gzi_path));
63
+ }
64
+
65
+ static VALUE native_faidx_close(VALUE self)
66
+ {
67
+ ruby_faidx_t *handle = get_faidx(self, 1);
68
+ if (handle->fai != NULL) {
69
+ fai_destroy(handle->fai);
70
+ handle->fai = NULL;
71
+ }
72
+ return Qnil;
73
+ }
74
+
75
+ static VALUE native_faidx_closed(VALUE self)
76
+ {
77
+ return get_faidx(self, 1)->fai == NULL ? Qtrue : Qfalse;
78
+ }
79
+
80
+ static VALUE native_faidx_size_method(VALUE self)
81
+ {
82
+ return INT2NUM(faidx_nseq(get_faidx(self, 0)->fai));
83
+ }
84
+
85
+ static VALUE native_faidx_names(VALUE self)
86
+ {
87
+ faidx_t *fai = get_faidx(self, 0)->fai;
88
+ int count = faidx_nseq(fai), index;
89
+ VALUE result = rb_ary_new_capa(count);
90
+ for (index = 0; index < count; index++) {
91
+ const char *name = faidx_iseq(fai, index);
92
+ rb_ary_push(result, name == NULL ? Qnil : rb_str_new_cstr(name));
93
+ }
94
+ return result;
95
+ }
96
+
97
+ static VALUE native_faidx_has_seq(VALUE self, VALUE name_value)
98
+ {
99
+ faidx_t *fai = get_faidx(self, 0)->fai;
100
+ int result = faidx_has_seq(fai, StringValueCStr(name_value));
101
+ if (result == 1) return Qtrue;
102
+ if (result == 0) return Qfalse;
103
+ rb_raise(rb_eRuntimeError, "unexpected return value from faidx_has_seq");
104
+ }
105
+
106
+ static VALUE native_faidx_seq_len(VALUE self, VALUE name_value)
107
+ {
108
+ faidx_t *fai = get_faidx(self, 0)->fai;
109
+ return LL2NUM(faidx_seq_len64(fai, StringValueCStr(name_value)));
110
+ }
111
+
112
+ static VALUE faidx_fetch_result(char *result, hts_pos_t length)
113
+ {
114
+ VALUE string = Qnil, pair;
115
+ if (result != NULL && length >= 0) string = rb_str_new(result, length);
116
+ free(result);
117
+ pair = rb_ary_new_capa(2);
118
+ rb_ary_push(pair, LL2NUM(length));
119
+ rb_ary_push(pair, string);
120
+ return pair;
121
+ }
122
+
123
+ static VALUE native_faidx_fetch_seq(VALUE self, VALUE name_value, VALUE start_value, VALUE stop_value)
124
+ {
125
+ faidx_t *fai = get_faidx(self, 0)->fai;
126
+ hts_pos_t length = 0;
127
+ char *result = faidx_fetch_seq64(fai, StringValueCStr(name_value),
128
+ NUM2LL(start_value), NUM2LL(stop_value), &length);
129
+ return faidx_fetch_result(result, length);
130
+ }
131
+
132
+ static VALUE native_faidx_fetch_qual(VALUE self, VALUE name_value, VALUE start_value, VALUE stop_value)
133
+ {
134
+ faidx_t *fai = get_faidx(self, 0)->fai;
135
+ hts_pos_t length = 0;
136
+ char *result = faidx_fetch_qual64(fai, StringValueCStr(name_value),
137
+ NUM2LL(start_value), NUM2LL(stop_value), &length);
138
+ return faidx_fetch_result(result, length);
139
+ }
140
+
141
+ void Init_htslib_native_faidx(VALUE native)
142
+ {
143
+ cNativeFaidx = rb_define_class_under(native, "FaidxHandle", rb_cObject);
144
+ rb_undef_alloc_func(cNativeFaidx);
145
+ rb_define_singleton_method(cNativeFaidx, "open", native_faidx_open, 3);
146
+ rb_define_singleton_method(cNativeFaidx, "build", native_faidx_build, 3);
147
+ rb_define_method(cNativeFaidx, "close", native_faidx_close, 0);
148
+ rb_define_method(cNativeFaidx, "closed?", native_faidx_closed, 0);
149
+ rb_define_method(cNativeFaidx, "size", native_faidx_size_method, 0);
150
+ rb_define_method(cNativeFaidx, "names", native_faidx_names, 0);
151
+ rb_define_method(cNativeFaidx, "has_seq?", native_faidx_has_seq, 1);
152
+ rb_define_method(cNativeFaidx, "seq_len", native_faidx_seq_len, 1);
153
+ rb_define_method(cNativeFaidx, "fetch_seq", native_faidx_fetch_seq, 3);
154
+ rb_define_method(cNativeFaidx, "fetch_qual", native_faidx_fetch_qual, 3);
155
+ }