Skip to content

Commit 5b91c51

Browse files
committed
Refactor memory management in BAM and BCF classes to ensure proper buffer handling and prevent memory leaks
1 parent 16d794d commit 5b91c51

7 files changed

Lines changed: 71 additions & 16 deletions

File tree

lib/hts/bam/header.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,23 @@ def <<(obj)
8181
# experimental
8282
def find_line(type, key, value)
8383
ks = LibHTS::KString.new
84-
r = LibHTS.sam_hdr_find_line_id(@sam_hdr, type, key, value, ks)
85-
r == 0 ? ks[:s] : nil
84+
begin
85+
r = LibHTS.sam_hdr_find_line_id(@sam_hdr, type, key, value, ks)
86+
r == 0 ? ks.read_string_copy : nil
87+
ensure
88+
ks.free_buffer
89+
end
8690
end
8791

8892
# experimental
8993
def find_line_at(type, pos)
9094
ks = LibHTS::KString.new
91-
r = LibHTS.sam_hdr_find_line_pos(@sam_hdr, type, pos, ks)
92-
r == 0 ? ks[:s] : nil
95+
begin
96+
r = LibHTS.sam_hdr_find_line_pos(@sam_hdr, type, pos, ks)
97+
r == 0 ? ks.read_string_copy : nil
98+
ensure
99+
ks.free_buffer
100+
end
93101
end
94102

95103
# experimental

lib/hts/bam/record.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,13 @@ def base_mod(auto_parse: true)
354354
# @return [String] a string representation of the alignment.
355355
def to_s
356356
kstr = LibHTS::KString.new
357-
raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
357+
begin
358+
raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
358359

359-
kstr[:s]
360+
kstr.read_string_copy
361+
ensure
362+
kstr.free_buffer
363+
end
360364
end
361365

362366
private

lib/hts/bcf/header.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,23 @@ def get_hrec(bcf_hl_type, key, value, str_class = nil)
8585
def seqnames
8686
n = FFI::MemoryPointer.new(:int)
8787
names = LibHTS.bcf_hdr_seqnames(@bcf_hdr, n)
88-
names.read_array_of_pointer(n.read_int)
89-
.map(&:read_string)
88+
begin
89+
names.read_array_of_pointer(n.read_int)
90+
.map(&:read_string)
91+
ensure
92+
LibHTS.hts_free(names) unless names.null?
93+
end
9094
end
9195

9296
def to_s
9397
kstr = LibHTS::KString.new
94-
raise "Failed to get header string" unless LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr)
98+
begin
99+
raise "Failed to get header string" unless LibHTS.bcf_hdr_format(@bcf_hdr, 0, kstr)
95100

96-
kstr[:s]
101+
kstr.read_string_copy
102+
ensure
103+
kstr.free_buffer
104+
end
97105
end
98106

99107
def name2id(name)

lib/hts/bcf/header_record.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ def find_key(key)
3131

3232
def to_s
3333
kstr = LibHTS::KString.new
34-
LibHTS.bcf_hrec_format(@bcf_hrec, kstr)
35-
kstr[:s]
34+
begin
35+
LibHTS.bcf_hrec_format(@bcf_hrec, kstr)
36+
kstr.read_string_copy
37+
ensure
38+
kstr.free_buffer
39+
end
3640
end
3741

3842
private

lib/hts/bcf/record.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,13 @@ def format(key = nil)
130130

131131
def to_s
132132
ksr = LibHTS::KString.new
133-
raise "Failed to format record" if LibHTS.vcf_format(@header.struct, @bcf1, ksr) == -1
133+
begin
134+
raise "Failed to format record" if LibHTS.vcf_format(@header.struct, @bcf1, ksr) == -1
134135

135-
ksr[:s]
136+
ksr.read_string_copy
137+
ensure
138+
ksr.free_buffer
139+
end
136140
end
137141

138142
private

lib/hts/libhts/constants.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@ class KString < FFI::Struct
1212
:l, :size_t,
1313
:m, :size_t,
1414
:s, :string
15+
16+
def buffer_ptr
17+
to_ptr.get_pointer(self.class.offset_of(:s))
18+
end
19+
20+
def read_string_copy
21+
ptr = buffer_ptr
22+
return "" if ptr.null?
23+
24+
ptr.read_string(self[:l])
25+
end
26+
27+
def free_buffer
28+
ptr = buffer_ptr
29+
return if ptr.null?
30+
31+
LibHTS.hts_free(ptr)
32+
to_ptr.put_pointer(self.class.offset_of(:s), FFI::Pointer::NULL)
33+
self[:l] = 0
34+
self[:m] = 0
35+
end
1536
end
1637

1738
class KSeq < FFI::Struct

lib/hts/tabix.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ def name2id(name)
8989
def seqnames
9090
check_closed
9191
nseq = FFI::MemoryPointer.new(:int)
92-
LibHTS.tbx_seqnames(@idx, nseq).then do |pts|
92+
pts = LibHTS.tbx_seqnames(@idx, nseq)
93+
begin
9394
pts.read_array_of_pointer(nseq.read_int).map(&:read_string)
95+
ensure
96+
LibHTS.hts_free(pts) unless pts.null?
9497
end
9598
end
9699

@@ -144,8 +147,11 @@ def querys(region, &block)
144147
def query_yield(qiter)
145148
r = LibHTS::KString.new
146149
begin
147-
yield r[:s].split("\t") while LibHTS.tbx_itr_next(@hts_file, @idx, qiter, r) > 0
150+
while LibHTS.tbx_itr_next(@hts_file, @idx, qiter, r) > 0
151+
yield r.read_string_copy.split("\t")
152+
end
148153
ensure
154+
r.free_buffer
149155
LibHTS.hts_itr_destroy(qiter)
150156
end
151157
end

0 commit comments

Comments
 (0)