htslib 0.4.1 → 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 -123
  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 -109
  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 -79
  47. data/lib/hts/libhts/tbx_funcs.rb +0 -32
  48. data/lib/hts/libhts/thread_pool.rb +0 -139
  49. data/lib/hts/libhts/vcf.rb +0 -557
  50. data/lib/hts/libhts/vcf_funcs.rb +0 -353
  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,28 +92,18 @@ module HTS
104
92
  subset_samples = normalize_subset_samples(samples)
105
93
  validate_subset_samples!(subset_samples)
106
94
 
107
- nil
108
- imap_pointer = nil
109
- if subset_samples.empty?
110
- subset_hdr = LibHTS.bcf_hdr_subset(@bcf_hdr, 0, ::FFI::Pointer::NULL, ::FFI::Pointer::NULL)
111
- else
112
- encoded_samples = subset_samples.map { |name| FFI::MemoryPointer.from_string(name) }
113
- sample_pointers = FFI::MemoryPointer.new(:pointer, subset_samples.length)
114
- sample_pointers.write_array_of_pointer(encoded_samples)
115
- imap_pointer = FFI::MemoryPointer.new(:int, subset_samples.length)
116
- subset_hdr = LibHTS.bcf_hdr_subset(@bcf_hdr, subset_samples.length, sample_pointers, imap_pointer)
117
- end
95
+ result = @native.subset(subset_samples)
96
+ raise SubsetError, "Failed to subset BCF header samples #{subset_samples.inspect}" unless result
118
97
 
119
- raise SubsetError, "Failed to subset BCF header samples #{subset_samples.inspect}" if subset_hdr.to_ptr.null?
120
-
121
- composed_imap = compose_subset_imap(read_subset_imap(imap_pointer, subset_samples.length))
122
- 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|
123
101
  header.send(:set_subset_state, subset_samples, composed_imap)
124
102
  end
125
103
  end
126
104
 
127
105
  def add_sample(sample, sync: true)
128
- rc = LibHTS.bcf_hdr_add_sample(@bcf_hdr, sample)
106
+ rc = @native.add_sample(sample)
129
107
  raise "Failed to add sample #{sample}" if rc.negative?
130
108
 
131
109
  mark_sync_needed!
@@ -134,17 +112,14 @@ module HTS
134
112
  end
135
113
 
136
114
  def merge(hdr)
137
- merged = LibHTS.bcf_hdr_merge(@bcf_hdr, hdr.struct)
138
- raise "Failed to merge BCF headers" if merged.to_ptr.null?
139
-
140
- @bcf_hdr = merged
115
+ @native.merge(hdr.__send__(:native_handle))
141
116
  mark_sync_needed!
142
117
  sync_if_needed!
143
118
  self
144
119
  end
145
120
 
146
121
  def sync
147
- rc = LibHTS.bcf_hdr_sync(@bcf_hdr)
122
+ rc = @native.sync
148
123
  raise "Failed to sync BCF header" if rc.negative?
149
124
 
150
125
  @sync_needed = false
@@ -152,11 +127,13 @@ module HTS
152
127
  end
153
128
 
154
129
  def read_bcf(fname)
155
- LibHTS.bcf_hdr_set(@bcf_hdr, fname)
130
+ result = @native.read_file(fname)
131
+ @schema_version += 1 unless result.negative?
132
+ result
156
133
  end
157
134
 
158
135
  def append(line)
159
- rc = LibHTS.bcf_hdr_append(@bcf_hdr, line)
136
+ rc = @native.append(line)
160
137
  raise "Failed to append VCF header line" if rc.negative?
161
138
 
162
139
  mark_sync_needed!
@@ -165,18 +142,14 @@ module HTS
165
142
 
166
143
  def delete(bcf_hl_type, key = nil) # FIXME
167
144
  existed = hrec_exists?(bcf_hl_type, key)
168
- type = bcf_hl_type_to_int(bcf_hl_type)
169
- LibHTS.bcf_hdr_remove(@bcf_hdr, type, key)
145
+ @native.remove(bcf_hl_type.to_s, key)
170
146
  mark_sync_needed! if existed
171
147
  existed
172
148
  end
173
149
 
174
150
  def get_hrec(bcf_hl_type, key, value, str_class = nil)
175
- type = bcf_hl_type_to_int(bcf_hl_type)
176
- hrec = borrowed_hrec(type, key, value, str_class)
177
- return nil if hrec.to_ptr.null?
178
-
179
- 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
180
153
  end
181
154
 
182
155
  def edit
@@ -252,33 +225,19 @@ module HTS
252
225
  end
253
226
 
254
227
  def seqnames
255
- n = FFI::MemoryPointer.new(:int)
256
- names = LibHTS.bcf_hdr_seqnames(@bcf_hdr, n)
257
- begin
258
- names.read_array_of_pointer(n.read_int)
259
- .map(&:read_string)
260
- ensure
261
- LibHTS.hts_free(names) unless names.null?
262
- end
228
+ @native.seqnames
263
229
  end
264
230
 
265
231
  def to_s
266
- kstr = LibHTS::KString.new
267
- begin
268
- raise "Failed to get header string" if LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr).negative?
269
-
270
- kstr.read_string_copy
271
- ensure
272
- kstr.free_buffer
273
- end
232
+ @native.to_s
274
233
  end
275
234
 
276
235
  def name2id(name)
277
- LibHTS.bcf_hdr_name2id(@bcf_hdr, name)
236
+ @native.name2id(name)
278
237
  end
279
238
 
280
239
  def id2name(id)
281
- LibHTS.bcf_hdr_id2name(@bcf_hdr, id)
240
+ @native.id2name(id)
282
241
  end
283
242
 
284
243
  private
@@ -325,6 +284,7 @@ module HTS
325
284
 
326
285
  def mark_sync_needed!
327
286
  @sync_needed = true
287
+ @schema_version += 1
328
288
  end
329
289
 
330
290
  def sync_if_needed!
@@ -332,58 +292,26 @@ module HTS
332
292
  end
333
293
 
334
294
  def hrec_exists?(bcf_hl_type, key)
335
- type = bcf_hl_type_to_int(bcf_hl_type)
336
- lookup_key, lookup_value, str_class = hrec_lookup_args(type, key)
337
- hrec = borrowed_hrec(type, lookup_key, lookup_value, str_class)
338
- !hrec.to_ptr.null?
339
- end
340
-
341
- def borrowed_hrec(type, key, value, str_class)
342
- LibHTS.bcf_hdr_get_hrec(@bcf_hdr, type, key, value, str_class)
343
- end
344
-
345
- def owned_hrec(hrec)
346
- LibHTS.bcf_hrec_dup(hrec).tap do |owned|
347
- raise "Failed to duplicate BCF header record" if owned.to_ptr.null?
348
- 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?
349
297
  end
350
298
 
351
299
  def hrec_lookup_args(type, key)
352
- case type
353
- 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"
354
302
  ["ID", key, nil]
355
- when LibHTS::BCF_HL_GEN
303
+ when "GENOTYPE", "GEN"
356
304
  [key, nil, nil]
357
305
  else
358
306
  ["ID", key, nil]
359
307
  end
360
308
  end
361
309
 
362
- def bcf_hl_type_to_int(bcf_hl_type)
363
- return bcf_hl_type if bcf_hl_type.is_a?(Integer)
364
-
365
- case bcf_hl_type.to_s.upcase
366
- when "FILTER", "FIL"
367
- LibHTS::BCF_HL_FLT
368
- when "INFO"
369
- LibHTS::BCF_HL_INFO
370
- when "FORMAT", "FMT"
371
- LibHTS::BCF_HL_FMT
372
- when "CONTIG", "CTG"
373
- LibHTS::BCF_HL_CTG
374
- when "STRUCTURED", "STR"
375
- LibHTS::BCF_HL_STR
376
- when "GENOTYPE", "GEN"
377
- LibHTS::BCF_HL_GEN
378
- else
379
- raise TypeError, "Invalid argument"
380
- end
381
- end
382
-
383
310
  def initialize_copy(orig)
384
- @bcf_hdr = LibHTS.bcf_hdr_dup(orig.struct)
311
+ @native = orig.__send__(:native_handle).duplicate
385
312
  @sync_depth = 0
386
313
  @sync_needed = false
314
+ @schema_version = orig.schema_version
387
315
  set_subset_state(orig.subset_samples, orig.send(:subset_imap))
388
316
  end
389
317
 
@@ -394,7 +322,7 @@ module HTS
394
322
  def set_subset_state(samples, imap)
395
323
  @subset_samples = samples&.dup
396
324
  @subset_imap = imap&.dup
397
- @subset_imap_pointer = build_subset_imap_pointer(@subset_imap)
325
+ @subset_imap_pointer = nil
398
326
  end
399
327
 
400
328
  private
@@ -418,25 +346,12 @@ module HTS
418
346
  raise UnknownSampleError, "Unknown sample names: #{missing.join(', ')}" unless missing.empty?
419
347
  end
420
348
 
421
- def read_subset_imap(pointer, length)
422
- return [] if length.zero?
423
-
424
- pointer.read_array_of_int(length)
425
- end
426
-
427
349
  def compose_subset_imap(imap)
428
350
  base_imap = @subset_imap || Array.new(samples.length, &:itself)
429
351
  imap.map { |index| base_imap.fetch(index) }
430
352
  end
431
353
 
432
- def build_subset_imap_pointer(imap)
433
- return nil unless imap
434
- return nil if imap.empty?
435
-
436
- FFI::MemoryPointer.new(:int, imap.length).tap do |pointer|
437
- pointer.write_array_of_int(imap)
438
- end
439
- end
354
+ def native_handle = @native
440
355
  end
441
356
  end
442
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