@@ -6,7 +6,6 @@ class Bcf < Hts
66 class Info
77 def initialize ( record )
88 @record = record
9- @p1 = FFI ::MemoryPointer . new ( :pointer ) # FIXME: naming
109 end
1110
1211 # @note Specify the type. If you don't specify a type, it will still work, but it will be slower.
@@ -16,37 +15,49 @@ def initialize(record)
1615 # I think they are better than `fetch_int`` and `fetch_float`.
1716 def get ( key , type = nil )
1817 n = FFI ::MemoryPointer . new ( :int )
19- p1 = @p1
18+ p1 = FFI ::MemoryPointer . new ( :pointer )
19+ p1 . write_pointer ( FFI ::Pointer ::NULL )
2020 h = @record . header . struct
2121 r = @record . struct
2222
23- info_values = proc do |typ |
23+ info_values = proc do |typ , reader |
2424 ret = LibHTS . bcf_get_info_values ( h , r , key , p1 , n , typ )
2525 return nil if ret < 0 # return from method.
2626
27- p1 . read_pointer
27+ dst = p1 . read_pointer
28+ begin
29+ reader . call ( dst , n . read_int )
30+ ensure
31+ LibHTS . hts_free ( dst ) unless dst . null?
32+ p1 . write_pointer ( FFI ::Pointer ::NULL )
33+ end
2834 end
2935
3036 type ||= ht_type_to_sym ( get_info_type ( key ) )
3137
3238 case type &.to_sym
3339 when :int , :int32
34- info_values . call ( LibHTS ::BCF_HT_INT )
35- . read_array_of_int32 ( n . read_int )
40+ info_values . call ( LibHTS ::BCF_HT_INT , -> ( dst , len ) { dst . read_array_of_int32 ( len ) } )
41+ when :int64 , :long
42+ info_values . call ( LibHTS ::BCF_HT_LONG , -> ( dst , len ) { dst . read_array_of_int64 ( len ) } )
3643 when :float , :real
37- info_values . call ( LibHTS ::BCF_HT_REAL )
38- . read_array_of_float ( n . read_int )
44+ info_values . call ( LibHTS ::BCF_HT_REAL , -> ( dst , len ) { dst . read_array_of_float ( len ) } )
3945 when :flag , :bool
40- case ret = LibHTS . bcf_get_info_flag ( h , r , key , p1 , n )
41- when 1 then true
42- when 0 then false
43- when -1 then nil
44- else
45- raise "Unknown return value from bcf_get_info_flag: #{ ret } "
46+ begin
47+ case ret = LibHTS . bcf_get_info_flag ( h , r , key , p1 , n )
48+ when 1 then true
49+ when 0 then false
50+ when -1 then nil
51+ else
52+ raise "Unknown return value from bcf_get_info_flag: #{ ret } "
53+ end
54+ ensure
55+ dst = p1 . read_pointer
56+ LibHTS . hts_free ( dst ) unless dst . null?
57+ p1 . write_pointer ( FFI ::Pointer ::NULL )
4658 end
4759 when :string , :str
48- info_values . call ( LibHTS ::BCF_HT_STR )
49- . read_string
60+ info_values . call ( LibHTS ::BCF_HT_STR , -> ( dst , _len ) { dst . read_string } )
5061 end
5162 end
5263
@@ -60,6 +71,11 @@ def get_float(key)
6071 get ( key , :float )
6172 end
6273
74+ # For compatibility with HTS.cr.
75+ def get_int64 ( key )
76+ get ( key , :int64 )
77+ end
78+
6379 # For compatibility with HTS.cr.
6480 def get_string ( key )
6581 get ( key , :string )
@@ -89,6 +105,9 @@ def []=(key, value)
89105 when true , false
90106 update_flag ( key , value )
91107 when Integer
108+ unless int32_range? ( value )
109+ raise RangeError , "Integer out of int32 range for []=. Current htslib backend does not support int64 INFO update."
110+ end
92111 update_int ( key , [ value ] )
93112 when Float
94113 update_float ( key , [ value ] )
@@ -98,6 +117,9 @@ def []=(key, value)
98117 if value . empty?
99118 raise ArgumentError , "Cannot set INFO field to empty array. Use nil to delete."
100119 elsif value . all? { |v | v . is_a? ( Integer ) }
120+ unless value . all? { |v | int32_range? ( v ) }
121+ raise RangeError , "Integer array contains out-of-int32 values for []=. Current htslib backend does not support int64 INFO update."
122+ end
101123 update_int ( key , value )
102124 elsif value . all? { |v | v . is_a? ( Numeric ) }
103125 update_float ( key , value )
@@ -130,6 +152,14 @@ def update_int(key, values)
130152 ret
131153 end
132154
155+ # Update INFO field with int64 value(s).
156+ # @note int64 INFO values are primarily relevant for VCF output.
157+ # @param key [String] INFO tag name
158+ # @param values [Array<Integer>] integer values (use single-element array for scalar)
159+ def update_int64 ( key , values )
160+ raise NotImplementedError , "htslib backend does not implement int64 INFO update (BCF_HT_LONG)"
161+ end
162+
133163 # Update INFO field with float value(s).
134164 # For compatibility with HTS.cr.
135165 # @param key [String] INFO tag name
@@ -295,9 +325,13 @@ def ht_type_to_sym(t)
295325 when LibHTS ::BCF_HT_INT then :int
296326 when LibHTS ::BCF_HT_REAL then :float
297327 when LibHTS ::BCF_HT_STR then :string
298- when LibHTS ::BCF_HT_LONG then :float
328+ when LibHTS ::BCF_HT_LONG then :int64
299329 end
300330 end
331+
332+ def int32_range? ( value )
333+ value >= -2_147_483_648 && value <= 2_147_483_647
334+ end
301335 end
302336 end
303337end
0 commit comments