File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -18,24 +18,23 @@ class PileupRecord
1818 def initialize ( entry , header )
1919 @entry = entry
2020 @header = header
21+ @record = nil
2122 end
2223
23- # Lightweight read-only view over the underlying bam1_t without taking ownership.
24+ # Return Bam::Record. On the first call, duplicate the underlying bam1_t (bam_dup1)
25+ # so the record becomes safe to keep beyond the current pileup step. Subsequent calls
26+ # return the cached Bam::Record instance.
27+ # NOTE: Without duplication, bam1_t memory may be reused by HTSlib on the next step.
2428 def record
25- @record_view ||= ReadView . new ( @entry [ :b ] )
26- end
29+ return @record if @record
2730
28- class ReadView
29- def initialize ( bam1_ptr )
30- # Accept either FFI::Pointer or FFI::Struct (Managed/Unmanaged); store as raw pointer
31- @bam1_ptr = bam1_ptr . is_a? ( FFI ::Pointer ) ? bam1_ptr : bam1_ptr . to_ptr
32- end
31+ # Normalize to a raw pointer and duplicate to obtain owned memory.
32+ b_ptr = @entry [ :b ] . is_a? ( FFI ::Pointer ) ? @entry [ :b ] : @entry [ :b ] . to_ptr
33+ dup_ptr = HTS ::LibHTS . bam_dup1 ( b_ptr )
34+ raise "bam_dup1 failed" if dup_ptr . null?
3335
34- def base ( i )
35- view = HTS ::LibHTS ::Bam1View . new ( @bam1_ptr )
36- seq_ptr = HTS ::LibHTS . bam_get_seq ( view )
37- HTS ::Bam ::Record ::SEQ_NT16_STR [ HTS ::LibHTS . bam_seqi ( seq_ptr , i ) ]
38- end
36+ # Build a Bam::Record backed by the duplicated bam1_t.
37+ @record = HTS ::Bam ::Record . new ( @header , dup_ptr )
3938 end
4039
4140 def query_position
Original file line number Diff line number Diff line change @@ -43,4 +43,26 @@ def test_multipileup_accepts_bam_instances
4343 b2 . close
4444 end
4545 end
46+
47+ def test_multipileup_record_lazy_copy
48+ inputs = [ Fixtures [ "moo.bam" ] , Fixtures [ "moo.bam" ] ]
49+ mp = HTS ::Bam ::Mpileup . new ( inputs )
50+ begin
51+ kept = nil
52+ mp . each do |columns |
53+ # find first column with at least one alignment in any input
54+ col = columns . find { |c | !c . alignments . empty? }
55+ next unless col
56+
57+ kept = col . alignments . first . record
58+ break
59+ end
60+ refute_nil kept
61+ # Should be usable after iteration moved on
62+ assert_kind_of Integer , kept . len
63+ assert_includes %w[ = A C G T M R S V W Y H K D B N ] , kept . base ( 0 )
64+ ensure
65+ mp . close
66+ end
67+ end
4668end
Original file line number Diff line number Diff line change @@ -39,4 +39,22 @@ def test_pileup_basic_each
3939 assert_operator count , :> , 0
4040 end
4141 end
42+
43+ def test_pileup_record_persists_beyond_step
44+ HTS ::Bam . open ( Fixtures [ "moo.bam" ] ) do |bam |
45+ kept = nil
46+ # take a record from the first non-empty column and keep it
47+ bam . pileup do |col |
48+ next if col . alignments . empty?
49+
50+ kept = col . alignments . first . record
51+ break
52+ end
53+ refute_nil kept
54+ # Access after the pileup iterator has moved on; should be safe
55+ assert_kind_of String , kept . qname
56+ # Access a base in the read sequence
57+ assert_includes %w[ = A C G T M R S V W Y H K D B N ] , kept . base ( 0 )
58+ end
59+ end
4260end
You can’t perform that action at this time.
0 commit comments