@@ -26,9 +26,8 @@ def initialize(inputs, region: nil, beg: nil, end_: nil, maxcnt: nil, overlaps:
2626 end
2727
2828 n = @bams . length
29- @state_map = { }
30- @handles = [ ]
31- @iters = [ ]
29+ @iters = [ ]
30+ @data_blocks = [ ] # per-input packed pointers kept alive
3231
3332 # Prepare optional region iterators for each input
3433 @bams . each_with_index do |bam , i |
@@ -50,32 +49,38 @@ def initialize(inputs, region: nil, beg: nil, end_: nil, maxcnt: nil, overlaps:
5049 @iters << itr
5150 end
5251
53- # Build per-input handle pointers so C passes them back to the callback
52+ # Build per-input packed pointer blocks so C passes them back to the callback.
53+ # Layout per input: [0] hts_fp (htsFile*), [1] hdr_struct (bam_hdr_t*), [2] itr (hts_itr_t* or NULL)
54+ ptr_size = FFI . type_size ( :pointer )
5455 data_array = FFI ::MemoryPointer . new ( :pointer , n )
5556 @bams . each_with_index do |bam , i |
56- handle = FFI ::MemoryPointer . new ( :char , 1 ) # unique address token
57- @handles << handle
58- @state_map [ handle . address ] = { bam :, itr : @iters [ i ] }
59- data_array . put_pointer ( i * FFI . type_size ( :pointer ) , handle )
57+ hts_fp = bam . instance_variable_get ( :@hts_file )
58+ hdr_struct = bam . header . struct
59+ itr = @iters [ i ]
60+ block = FFI ::MemoryPointer . new ( :pointer , 3 )
61+ block . put_pointer ( 0 * ptr_size , hts_fp )
62+ block . put_pointer ( 1 * ptr_size , hdr_struct )
63+ block . put_pointer ( 2 * ptr_size , itr && !itr . null? ? itr : FFI ::Pointer ::NULL )
64+ @data_blocks << block
65+ data_array . put_pointer ( i * ptr_size , block )
6066 end
61- # Keep the array of per-input handles alive while the C side holds on to them
67+ # Keep the array of per-input blocks alive while the C side holds on to them
6268 @data_array = data_array
6369
6470 @cb = FFI ::Function . new ( :int , %i[ pointer pointer ] ) do |data , b |
65- st = @state_map [ data . address ]
66- bam = st [ :bam ]
67- itr = st [ :itr ]
71+ # Unpack pointers from the per-input block
72+ hts_fp = data . get_pointer ( 0 * ptr_size )
73+ hdr_struct = data . get_pointer ( 1 * ptr_size )
74+ itr = data . get_pointer ( 2 * ptr_size )
6875 if itr && !itr . null?
69- slen = HTS ::LibHTS . sam_itr_next ( bam . instance_variable_get ( :@hts_file ) , itr , b )
70- if slen > 0
76+ r = HTS ::LibHTS . sam_itr_next ( hts_fp , itr , b )
77+ if r >= 0
7178 0
72- elsif slen == -1
73- -1
7479 else
75- - 2
80+ ( r == - 1 ? - 1 : - 2 )
7681 end
7782 else
78- r = HTS ::LibHTS . sam_read1 ( bam . instance_variable_get ( :@hts_file ) , bam . header . struct , b )
83+ r = HTS ::LibHTS . sam_read1 ( hts_fp , hdr_struct , b )
7984 r == -1 ? -1 : 0
8085 end
8186 end
@@ -99,6 +104,8 @@ def each
99104 pos_ptr = FFI ::MemoryPointer . new ( :long_long )
100105 n_ptr = FFI ::MemoryPointer . new ( :int , n )
101106 plp_ptr = FFI ::MemoryPointer . new ( :pointer , n )
107+ plp1_size = HTS ::LibHTS ::BamPileup1 . size
108+ headers = @bams . map ( &:header )
102109
103110 begin
104111 while HTS ::LibHTS . bam_mplp64_auto ( @iter , tid_ptr , pos_ptr , n_ptr , plp_ptr ) > 0
@@ -108,20 +115,25 @@ def each
108115 counts = n_ptr . read_array_of_int ( n )
109116 plp_arr = plp_ptr . read_array_of_pointer ( n )
110117
111- cols = Array . new ( n ) do |i |
118+ cols = Array . new ( n )
119+ i = 0
120+ while i < n
112121 c = counts [ i ]
113122 if c <= 0 || plp_arr [ i ] . null?
114- HTS ::Bam ::Pileup ::PileupColumn . new ( tid : tid , pos : pos , alignments : [ ] )
123+ cols [ i ] = HTS ::Bam ::Pileup ::PileupColumn . new ( tid : tid , pos : pos , alignments : [ ] )
115124 else
116125 base_ptr = plp_arr [ i ]
117- size = HTS ::LibHTS ::BamPileup1 . size
118- aligns = c . times . map do |j |
119- e_ptr = base_ptr + ( j * size )
126+ aligns = Array . new ( c )
127+ j = 0
128+ while j < c
129+ e_ptr = base_ptr + ( j * plp1_size )
120130 entry = HTS ::LibHTS ::BamPileup1 . new ( e_ptr )
121- HTS ::Bam ::Pileup ::PileupRecord . new ( entry , @bams [ i ] . header )
131+ aligns [ j ] = HTS ::Bam ::Pileup ::PileupRecord . new ( entry , headers [ i ] )
132+ j += 1
122133 end
123- HTS ::Bam ::Pileup ::PileupColumn . new ( tid : tid , pos : pos , alignments : aligns )
134+ cols [ i ] = HTS ::Bam ::Pileup ::PileupColumn . new ( tid : tid , pos : pos , alignments : aligns )
124135 end
136+ i += 1
125137 end
126138
127139 yield cols
@@ -142,8 +154,8 @@ def close
142154 HTS ::LibHTS . hts_itr_destroy ( itr ) if itr && !itr . null?
143155 end
144156 @iters . clear
145- # Keep references to callback and handles to prevent GC
146- @_keepalive = [ @cb , * @handles ]
157+ # Keep references to callback and data blocks to prevent GC
158+ @_keepalive = [ @cb , @data_array , * @data_blocks ]
147159 # Close owned bams opened by this object
148160 @owned_bams . each do |b |
149161 b . close
0 commit comments