htslib 0.4.2 → 0.6.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 +83 -51
  3. data/TUTORIAL.md +11 -22
  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 +1167 -0
  8. data/ext/htslib_native/native_bcf.c +447 -0
  9. data/ext/htslib_native/native_faidx.c +164 -0
  10. data/ext/htslib_native/native_tabix.c +255 -0
  11. data/lib/hts/bam/auxi.rb +87 -342
  12. data/lib/hts/bam/base_mod.rb +38 -73
  13. data/lib/hts/bam/cigar.rb +9 -55
  14. data/lib/hts/bam/flag.rb +24 -30
  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 +154 -45
  20. data/lib/hts/bcf/errors.rb +4 -0
  21. data/lib/hts/bcf/format.rb +283 -377
  22. data/lib/hts/bcf/header.rb +40 -122
  23. data/lib/hts/bcf/header_record.rb +9 -35
  24. data/lib/hts/bcf/info.rb +73 -320
  25. data/lib/hts/bcf/record.rb +73 -101
  26. data/lib/hts/bcf.rb +178 -321
  27. data/lib/hts/faidx.rb +30 -87
  28. data/lib/hts/hts.rb +6 -94
  29. data/lib/hts/native.rb +13 -0
  30. data/lib/hts/tabix.rb +109 -51
  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
@@ -23,18 +23,17 @@ module HTS
23
23
 
24
24
  def initialize(arg = nil)
25
25
  case arg
26
- when LibHTS::HtsFile
27
- @bcf_hdr = LibHTS.bcf_hdr_read(arg)
28
- when LibHTS::BcfHdr
29
- @bcf_hdr = arg
26
+ when Native::BcfHeaderHandle
27
+ @native = arg
30
28
  when nil
31
- @bcf_hdr = LibHTS.bcf_hdr_init("w")
29
+ @native = Native::BcfHeaderHandle.create
32
30
  else
33
31
  raise TypeError, "Invalid argument"
34
32
  end
35
33
 
36
34
  @sync_depth = 0
37
35
  @sync_needed = false
36
+ @schema_version = 0
38
37
  @subset_samples = nil
39
38
  @subset_imap = nil
40
39
  @subset_imap_pointer = nil
@@ -42,29 +41,23 @@ module HTS
42
41
  yield self if block_given?
43
42
  end
44
43
 
45
- def struct
46
- @bcf_hdr
47
- end
48
-
49
- def to_ptr
50
- @bcf_hdr.to_ptr
51
- end
52
-
53
44
  def get_version
54
- LibHTS.bcf_hdr_get_version(@bcf_hdr)
45
+ @native.version
55
46
  end
47
+ alias version get_version
56
48
 
57
49
  def set_version(version)
58
- rc = LibHTS.bcf_hdr_set_version(@bcf_hdr, version)
50
+ rc = @native.set_version(version)
59
51
  raise "Failed to set VCF header version" if rc.negative?
60
52
 
61
53
  mark_sync_needed!
62
54
  sync_if_needed!
63
55
  self
64
56
  end
57
+ alias version= set_version
65
58
 
66
59
  def nsamples
67
- LibHTS.bcf_hdr_nsamples(@bcf_hdr)
60
+ @native.nsamples
68
61
  end
69
62
 
70
63
  def target_count
@@ -84,13 +77,10 @@ module HTS
84
77
  end
85
78
 
86
79
  def samples
87
- # bcf_hdr_id2name is macro function
88
- @bcf_hdr[:samples]
89
- .read_array_of_pointer(nsamples)
90
- .map(&:read_string)
80
+ @native.samples
91
81
  end
92
82
 
93
- attr_reader :subset_samples, :subset_imap_pointer
83
+ attr_reader :subset_samples, :subset_imap_pointer, :schema_version
94
84
 
95
85
  def subset?
96
86
  !@subset_imap.nil?
@@ -104,27 +94,18 @@ module HTS
104
94
  subset_samples = normalize_subset_samples(samples)
105
95
  validate_subset_samples!(subset_samples)
106
96
 
107
- imap_pointer = nil
108
- if subset_samples.empty?
109
- subset_hdr = LibHTS.bcf_hdr_subset(@bcf_hdr, 0, ::FFI::Pointer::NULL, ::FFI::Pointer::NULL)
110
- else
111
- encoded_samples = subset_samples.map { |name| FFI::MemoryPointer.from_string(name) }
112
- sample_pointers = FFI::MemoryPointer.new(:pointer, subset_samples.length)
113
- sample_pointers.write_array_of_pointer(encoded_samples)
114
- imap_pointer = FFI::MemoryPointer.new(:int, subset_samples.length)
115
- subset_hdr = LibHTS.bcf_hdr_subset(@bcf_hdr, subset_samples.length, sample_pointers, imap_pointer)
116
- end
97
+ result = @native.subset(subset_samples)
98
+ raise SubsetError, "Failed to subset BCF header samples #{subset_samples.inspect}" unless result
117
99
 
118
- raise SubsetError, "Failed to subset BCF header samples #{subset_samples.inspect}" if subset_hdr.to_ptr.null?
119
-
120
- composed_imap = compose_subset_imap(read_subset_imap(imap_pointer, subset_samples.length))
121
- self.class.new(subset_hdr).tap do |header|
100
+ subset_header, imap = result
101
+ composed_imap = compose_subset_imap(imap)
102
+ self.class.new(subset_header).tap do |header|
122
103
  header.send(:set_subset_state, subset_samples, composed_imap)
123
104
  end
124
105
  end
125
106
 
126
107
  def add_sample(sample, sync: true)
127
- rc = LibHTS.bcf_hdr_add_sample(@bcf_hdr, sample)
108
+ rc = @native.add_sample(sample)
128
109
  raise "Failed to add sample #{sample}" if rc.negative?
129
110
 
130
111
  mark_sync_needed!
@@ -133,17 +114,14 @@ module HTS
133
114
  end
134
115
 
135
116
  def merge(hdr)
136
- merged = LibHTS.bcf_hdr_merge(@bcf_hdr, hdr.struct)
137
- raise "Failed to merge BCF headers" if merged.to_ptr.null?
138
-
139
- @bcf_hdr = merged
117
+ @native.merge(hdr.__send__(:native_handle))
140
118
  mark_sync_needed!
141
119
  sync_if_needed!
142
120
  self
143
121
  end
144
122
 
145
123
  def sync
146
- rc = LibHTS.bcf_hdr_sync(@bcf_hdr)
124
+ rc = @native.sync
147
125
  raise "Failed to sync BCF header" if rc.negative?
148
126
 
149
127
  @sync_needed = false
@@ -151,11 +129,13 @@ module HTS
151
129
  end
152
130
 
153
131
  def read_bcf(fname)
154
- LibHTS.bcf_hdr_set(@bcf_hdr, fname)
132
+ result = @native.read_file(fname)
133
+ @schema_version += 1 unless result.negative?
134
+ result
155
135
  end
156
136
 
157
137
  def append(line)
158
- rc = LibHTS.bcf_hdr_append(@bcf_hdr, line)
138
+ rc = @native.append(line)
159
139
  raise "Failed to append VCF header line" if rc.negative?
160
140
 
161
141
  mark_sync_needed!
@@ -164,18 +144,14 @@ module HTS
164
144
 
165
145
  def delete(bcf_hl_type, key = nil) # FIXME
166
146
  existed = hrec_exists?(bcf_hl_type, key)
167
- type = bcf_hl_type_to_int(bcf_hl_type)
168
- LibHTS.bcf_hdr_remove(@bcf_hdr, type, key)
147
+ @native.remove(bcf_hl_type.to_s, key)
169
148
  mark_sync_needed! if existed
170
149
  existed
171
150
  end
172
151
 
173
152
  def get_hrec(bcf_hl_type, key, value, str_class = nil)
174
- type = bcf_hl_type_to_int(bcf_hl_type)
175
- hrec = borrowed_hrec(type, key, value, str_class)
176
- return nil if hrec.to_ptr.null?
177
-
178
- HeaderRecord.new(owned_hrec(hrec))
153
+ hrec = @native.get_hrec(bcf_hl_type.to_s, key, value, str_class)
154
+ hrec ? HeaderRecord.new(hrec) : nil
179
155
  end
180
156
 
181
157
  def edit
@@ -251,33 +227,19 @@ module HTS
251
227
  end
252
228
 
253
229
  def seqnames
254
- n = FFI::MemoryPointer.new(:int)
255
- names = LibHTS.bcf_hdr_seqnames(@bcf_hdr, n)
256
- begin
257
- names.read_array_of_pointer(n.read_int)
258
- .map(&:read_string)
259
- ensure
260
- LibHTS.hts_free(names) unless names.null?
261
- end
230
+ @native.seqnames
262
231
  end
263
232
 
264
233
  def to_s
265
- kstr = LibHTS::KString.new
266
- begin
267
- raise "Failed to get header string" if LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr).negative?
268
-
269
- kstr.read_string_copy
270
- ensure
271
- kstr.free_buffer
272
- end
234
+ @native.to_s
273
235
  end
274
236
 
275
237
  def name2id(name)
276
- LibHTS.bcf_hdr_name2id(@bcf_hdr, name)
238
+ @native.name2id(name)
277
239
  end
278
240
 
279
241
  def id2name(id)
280
- LibHTS.bcf_hdr_id2name(@bcf_hdr, id)
242
+ @native.id2name(id)
281
243
  end
282
244
 
283
245
  private
@@ -324,6 +286,7 @@ module HTS
324
286
 
325
287
  def mark_sync_needed!
326
288
  @sync_needed = true
289
+ @schema_version += 1
327
290
  end
328
291
 
329
292
  def sync_if_needed!
@@ -331,58 +294,26 @@ module HTS
331
294
  end
332
295
 
333
296
  def hrec_exists?(bcf_hl_type, key)
334
- type = bcf_hl_type_to_int(bcf_hl_type)
335
- lookup_key, lookup_value, str_class = hrec_lookup_args(type, key)
336
- hrec = borrowed_hrec(type, lookup_key, lookup_value, str_class)
337
- !hrec.to_ptr.null?
338
- end
339
-
340
- def borrowed_hrec(type, key, value, str_class)
341
- LibHTS.bcf_hdr_get_hrec(@bcf_hdr, type, key, value, str_class)
342
- end
343
-
344
- def owned_hrec(hrec)
345
- LibHTS.bcf_hrec_dup(hrec).tap do |owned|
346
- raise "Failed to duplicate BCF header record" if owned.to_ptr.null?
347
- end
297
+ lookup_key, lookup_value, str_class = hrec_lookup_args(bcf_hl_type, key)
298
+ !@native.get_hrec(bcf_hl_type.to_s, lookup_key, lookup_value, str_class).nil?
348
299
  end
349
300
 
350
301
  def hrec_lookup_args(type, key)
351
- case type
352
- when LibHTS::BCF_HL_FLT, LibHTS::BCF_HL_INFO, LibHTS::BCF_HL_FMT, LibHTS::BCF_HL_CTG
302
+ case type.to_s.upcase
303
+ when "FILTER", "FIL", "INFO", "FORMAT", "FMT", "CONTIG", "CTG"
353
304
  ["ID", key, nil]
354
- when LibHTS::BCF_HL_GEN
305
+ when "GENOTYPE", "GEN"
355
306
  [key, nil, nil]
356
307
  else
357
308
  ["ID", key, nil]
358
309
  end
359
310
  end
360
311
 
361
- def bcf_hl_type_to_int(bcf_hl_type)
362
- return bcf_hl_type if bcf_hl_type.is_a?(Integer)
363
-
364
- case bcf_hl_type.to_s.upcase
365
- when "FILTER", "FIL"
366
- LibHTS::BCF_HL_FLT
367
- when "INFO"
368
- LibHTS::BCF_HL_INFO
369
- when "FORMAT", "FMT"
370
- LibHTS::BCF_HL_FMT
371
- when "CONTIG", "CTG"
372
- LibHTS::BCF_HL_CTG
373
- when "STRUCTURED", "STR"
374
- LibHTS::BCF_HL_STR
375
- when "GENOTYPE", "GEN"
376
- LibHTS::BCF_HL_GEN
377
- else
378
- raise TypeError, "Invalid argument"
379
- end
380
- end
381
-
382
312
  def initialize_copy(orig)
383
- @bcf_hdr = LibHTS.bcf_hdr_dup(orig.struct)
313
+ @native = orig.__send__(:native_handle).duplicate
384
314
  @sync_depth = 0
385
315
  @sync_needed = false
316
+ @schema_version = orig.schema_version
386
317
  set_subset_state(orig.subset_samples, orig.send(:subset_imap))
387
318
  end
388
319
 
@@ -393,7 +324,7 @@ module HTS
393
324
  def set_subset_state(samples, imap)
394
325
  @subset_samples = samples&.dup
395
326
  @subset_imap = imap&.dup
396
- @subset_imap_pointer = build_subset_imap_pointer(@subset_imap)
327
+ @subset_imap_pointer = nil
397
328
  end
398
329
 
399
330
  private
@@ -417,25 +348,12 @@ module HTS
417
348
  raise UnknownSampleError, "Unknown sample names: #{missing.join(', ')}" unless missing.empty?
418
349
  end
419
350
 
420
- def read_subset_imap(pointer, length)
421
- return [] if length.zero?
422
-
423
- pointer.read_array_of_int(length)
424
- end
425
-
426
351
  def compose_subset_imap(imap)
427
352
  base_imap = @subset_imap || Array.new(samples.length, &:itself)
428
353
  imap.map { |index| base_imap.fetch(index) }
429
354
  end
430
355
 
431
- def build_subset_imap_pointer(imap)
432
- return nil unless imap
433
- return nil if imap.empty?
434
-
435
- FFI::MemoryPointer.new(:int, imap.length).tap do |pointer|
436
- pointer.write_array_of_int(imap)
437
- end
438
- end
356
+ def native_handle = @native
439
357
  end
440
358
  end
441
359
  end
@@ -3,47 +3,21 @@
3
3
  module HTS
4
4
  class Bcf < Hts
5
5
  class HeaderRecord
6
- def initialize(arg = nil)
7
- case arg
8
- when LibHTS::BcfHrec
9
- @bcf_hrec = arg
10
- else
11
- raise TypeError, "Invalid argument"
12
- end
13
- end
6
+ def initialize(native)
7
+ raise TypeError, "Invalid argument" unless native.is_a?(Native::BcfHeaderRecordHandle)
14
8
 
15
- def struct
16
- @bcf_hrec
9
+ @native = native
17
10
  end
18
11
 
19
- def add_key(key)
20
- LibHTS.bcf_hrec_add_key(@bcf_hrec, key, key.length)
21
- end
22
-
23
- def set_value(i, val, quote: true)
24
- is_quoted = quote ? 1 : 0
25
- LibHTS.bcf_hrec_set_val(@bcf_hrec, i, val, val.length, is_quoted)
26
- end
27
-
28
- def find_key(key)
29
- LibHTS.bcf_hrec_find_key(@bcf_hrec, key)
30
- end
31
-
32
- def to_s
33
- kstr = LibHTS::KString.new
34
- begin
35
- LibHTS.bcf_hrec_format(@bcf_hrec, kstr)
36
- kstr.read_string_copy
37
- ensure
38
- kstr.free_buffer
39
- end
40
- end
12
+ def add_key(key) = @native.add_key(key)
13
+ def set_value(index, value, quote: true) = @native.set_value(index, value, quote)
14
+ def find_key(key) = @native.find_key(key)
15
+ def to_s = @native.to_s
41
16
 
42
17
  private
43
18
 
44
- def initialize_copy(orig)
45
- @bcf_hrec = LibHTS.bcf_hrec_dup(orig.struct)
46
- end
19
+ def native_handle = @native
20
+ def initialize_copy(original) = (@native = original.__send__(:native_handle).duplicate)
47
21
  end
48
22
  end
49
23
  end