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
@@ -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,20 +41,12 @@ 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
56
47
 
57
48
  def set_version(version)
58
- rc = LibHTS.bcf_hdr_set_version(@bcf_hdr, version)
49
+ rc = @native.set_version(version)
59
50
  raise "Failed to set VCF header version" if rc.negative?
60
51
 
61
52
  mark_sync_needed!
@@ -64,7 +55,7 @@ module HTS
64
55
  end
65
56
 
66
57
  def nsamples
67
- LibHTS.bcf_hdr_nsamples(@bcf_hdr)
58
+ @native.nsamples
68
59
  end
69
60
 
70
61
  def target_count
@@ -84,13 +75,10 @@ module HTS
84
75
  end
85
76
 
86
77
  def samples
87
- # bcf_hdr_id2name is macro function
88
- @bcf_hdr[:samples]
89
- .read_array_of_pointer(nsamples)
90
- .map(&:read_string)
78
+ @native.samples
91
79
  end
92
80
 
93
- attr_reader :subset_samples, :subset_imap_pointer
81
+ attr_reader :subset_samples, :subset_imap_pointer, :schema_version
94
82
 
95
83
  def subset?
96
84
  !@subset_imap.nil?
@@ -104,27 +92,18 @@ module HTS
104
92
  subset_samples = normalize_subset_samples(samples)
105
93
  validate_subset_samples!(subset_samples)
106
94
 
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
95
+ result = @native.subset(subset_samples)
96
+ raise SubsetError, "Failed to subset BCF header samples #{subset_samples.inspect}" unless result
117
97
 
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|
98
+ subset_header, imap = result
99
+ composed_imap = compose_subset_imap(imap)
100
+ self.class.new(subset_header).tap do |header|
122
101
  header.send(:set_subset_state, subset_samples, composed_imap)
123
102
  end
124
103
  end
125
104
 
126
105
  def add_sample(sample, sync: true)
127
- rc = LibHTS.bcf_hdr_add_sample(@bcf_hdr, sample)
106
+ rc = @native.add_sample(sample)
128
107
  raise "Failed to add sample #{sample}" if rc.negative?
129
108
 
130
109
  mark_sync_needed!
@@ -133,17 +112,14 @@ module HTS
133
112
  end
134
113
 
135
114
  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
115
+ @native.merge(hdr.__send__(:native_handle))
140
116
  mark_sync_needed!
141
117
  sync_if_needed!
142
118
  self
143
119
  end
144
120
 
145
121
  def sync
146
- rc = LibHTS.bcf_hdr_sync(@bcf_hdr)
122
+ rc = @native.sync
147
123
  raise "Failed to sync BCF header" if rc.negative?
148
124
 
149
125
  @sync_needed = false
@@ -151,11 +127,13 @@ module HTS
151
127
  end
152
128
 
153
129
  def read_bcf(fname)
154
- LibHTS.bcf_hdr_set(@bcf_hdr, fname)
130
+ result = @native.read_file(fname)
131
+ @schema_version += 1 unless result.negative?
132
+ result
155
133
  end
156
134
 
157
135
  def append(line)
158
- rc = LibHTS.bcf_hdr_append(@bcf_hdr, line)
136
+ rc = @native.append(line)
159
137
  raise "Failed to append VCF header line" if rc.negative?
160
138
 
161
139
  mark_sync_needed!
@@ -164,18 +142,14 @@ module HTS
164
142
 
165
143
  def delete(bcf_hl_type, key = nil) # FIXME
166
144
  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)
145
+ @native.remove(bcf_hl_type.to_s, key)
169
146
  mark_sync_needed! if existed
170
147
  existed
171
148
  end
172
149
 
173
150
  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))
151
+ hrec = @native.get_hrec(bcf_hl_type.to_s, key, value, str_class)
152
+ hrec ? HeaderRecord.new(hrec) : nil
179
153
  end
180
154
 
181
155
  def edit
@@ -251,33 +225,19 @@ module HTS
251
225
  end
252
226
 
253
227
  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
228
+ @native.seqnames
262
229
  end
263
230
 
264
231
  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
232
+ @native.to_s
273
233
  end
274
234
 
275
235
  def name2id(name)
276
- LibHTS.bcf_hdr_name2id(@bcf_hdr, name)
236
+ @native.name2id(name)
277
237
  end
278
238
 
279
239
  def id2name(id)
280
- LibHTS.bcf_hdr_id2name(@bcf_hdr, id)
240
+ @native.id2name(id)
281
241
  end
282
242
 
283
243
  private
@@ -324,6 +284,7 @@ module HTS
324
284
 
325
285
  def mark_sync_needed!
326
286
  @sync_needed = true
287
+ @schema_version += 1
327
288
  end
328
289
 
329
290
  def sync_if_needed!
@@ -331,58 +292,26 @@ module HTS
331
292
  end
332
293
 
333
294
  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
295
+ lookup_key, lookup_value, str_class = hrec_lookup_args(bcf_hl_type, key)
296
+ !@native.get_hrec(bcf_hl_type.to_s, lookup_key, lookup_value, str_class).nil?
348
297
  end
349
298
 
350
299
  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
300
+ case type.to_s.upcase
301
+ when "FILTER", "FIL", "INFO", "FORMAT", "FMT", "CONTIG", "CTG"
353
302
  ["ID", key, nil]
354
- when LibHTS::BCF_HL_GEN
303
+ when "GENOTYPE", "GEN"
355
304
  [key, nil, nil]
356
305
  else
357
306
  ["ID", key, nil]
358
307
  end
359
308
  end
360
309
 
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
310
  def initialize_copy(orig)
383
- @bcf_hdr = LibHTS.bcf_hdr_dup(orig.struct)
311
+ @native = orig.__send__(:native_handle).duplicate
384
312
  @sync_depth = 0
385
313
  @sync_needed = false
314
+ @schema_version = orig.schema_version
386
315
  set_subset_state(orig.subset_samples, orig.send(:subset_imap))
387
316
  end
388
317
 
@@ -393,7 +322,7 @@ module HTS
393
322
  def set_subset_state(samples, imap)
394
323
  @subset_samples = samples&.dup
395
324
  @subset_imap = imap&.dup
396
- @subset_imap_pointer = build_subset_imap_pointer(@subset_imap)
325
+ @subset_imap_pointer = nil
397
326
  end
398
327
 
399
328
  private
@@ -417,25 +346,12 @@ module HTS
417
346
  raise UnknownSampleError, "Unknown sample names: #{missing.join(', ')}" unless missing.empty?
418
347
  end
419
348
 
420
- def read_subset_imap(pointer, length)
421
- return [] if length.zero?
422
-
423
- pointer.read_array_of_int(length)
424
- end
425
-
426
349
  def compose_subset_imap(imap)
427
350
  base_imap = @subset_imap || Array.new(samples.length, &:itself)
428
351
  imap.map { |index| base_imap.fetch(index) }
429
352
  end
430
353
 
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
354
+ def native_handle = @native
439
355
  end
440
356
  end
441
357
  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