Skip to content

Commit 74ac5c8

Browse files
authored
Add GC.@preserve when using pointers (#221)
1 parent bcad9e0 commit 74ac5c8

File tree

7 files changed

+67
-56
lines changed

7 files changed

+67
-56
lines changed

docs/src/examples.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ using CodecZlib
178178

179179
function decompress(input, output)
180180
buffer = Vector{UInt8}(undef, 16 * 1024)
181-
while !eof(input)
181+
GC.@preserve buffer while !eof(input)
182182
n = min(bytesavailable(input), length(buffer))
183183
unsafe_read(input, pointer(buffer), n)
184184
unsafe_write(output, pointer(buffer), n)

src/memory.jl

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct Memory
1111
size::UInt
1212
end
1313

14+
# TODO remove this method
1415
function Memory(data::ByteData)
1516
return Memory(pointer(data), sizeof(data))
1617
end

src/stream.jl

+16-12
Original file line numberDiff line numberDiff line change
@@ -343,16 +343,18 @@ function Base.readuntil(stream::TranscodingStream, delim::UInt8; keep::Bool=fals
343343
local ret::Vector{UInt8}
344344
filled = 0
345345
while !eof(stream)
346-
p = findbyte(buffer1, delim)
347-
found = false
348-
if p < marginptr(buffer1)
349-
found = true
350-
sz = Int(p + 1 - bufferptr(buffer1))
351-
if !keep
352-
sz -= 1
346+
GC.@preserve buffer1 begin
347+
p = findbyte(buffer1, delim)
348+
found = false
349+
if p < marginptr(buffer1)
350+
found = true
351+
sz = Int(p + 1 - bufferptr(buffer1))
352+
if !keep
353+
sz -= 1
354+
end
355+
else
356+
sz = buffersize(buffer1)
353357
end
354-
else
355-
sz = buffersize(buffer1)
356358
end
357359
if @isdefined(ret)
358360
resize!(ret, filled + sz)
@@ -703,9 +705,11 @@ end
703705
# Call `process` with prologue and epilogue.
704706
function callprocess(stream::TranscodingStream, inbuf::Buffer, outbuf::Buffer)
705707
state = stream.state
706-
input = buffermem(inbuf)
707-
GC.@preserve inbuf makemargin!(outbuf, minoutsize(stream.codec, input))
708-
Δin::Int, Δout::Int, state.code = GC.@preserve inbuf outbuf process(stream.codec, input, marginmem(outbuf), state.error)
708+
makemargin!(
709+
outbuf,
710+
GC.@preserve(inbuf, minoutsize(stream.codec, buffermem(inbuf))),
711+
)
712+
Δin::Int, Δout::Int, state.code = GC.@preserve inbuf outbuf process(stream.codec, buffermem(inbuf), marginmem(outbuf), state.error)
709713
@debug(
710714
"called process()",
711715
code = state.code,

src/transcode.jl

+7-8
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ function Base.transcode(codec::Type{C}, src::String) where {C<:Codec}
4646
end
4747

4848
_default_output_buffer(codec, input) = Buffer(
49-
initial_output_size(
49+
GC.@preserve(input, initial_output_size(
5050
codec,
5151
buffermem(input)
52-
)
52+
))
5353
)
5454

5555
"""
@@ -125,8 +125,7 @@ function transcode!(
125125
Base.mightalias(input.data, output.data) && error(
126126
"input and outbut buffers must be independent"
127127
)
128-
# GC.@preserve since unsafe_transcode! may convert to raw pointers
129-
GC.@preserve input output codec unsafe_transcode!(output, codec, input)
128+
unsafe_transcode!(output, codec, input)
130129
end
131130

132131
"""
@@ -148,10 +147,10 @@ function unsafe_transcode!(
148147
if code === :error
149148
@goto error
150149
end
151-
n = minoutsize(codec, buffermem(input))
150+
n = GC.@preserve input minoutsize(codec, buffermem(input))
152151
@label process
153152
makemargin!(output, n)
154-
Δin, Δout, code = process(codec, buffermem(input), marginmem(output), error)
153+
Δin, Δout, code = GC.@preserve input output process(codec, buffermem(input), marginmem(output), error)
155154
@debug(
156155
"called process()",
157156
code = code,
@@ -169,13 +168,13 @@ function unsafe_transcode!(
169168
if startproc(codec, :write, error) === :error
170169
@goto error
171170
end
172-
n = minoutsize(codec, buffermem(input))
171+
n = GC.@preserve input minoutsize(codec, buffermem(input))
173172
@goto process
174173
end
175174
resize!(output.data, output.marginpos - 1)
176175
return output.data
177176
else
178-
n = max(Δout, minoutsize(codec, buffermem(input)))
177+
n = GC.@preserve input max(Δout, minoutsize(codec, buffermem(input)))
179178
@goto process
180179
end
181180
@label error

test/codecnoop.jl

+6-4
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ using FillArrays: Zeros
2424

2525
stream = TranscodingStream(Noop(), IOBuffer())
2626
@test_throws EOFError read(stream, UInt8)
27-
@test_throws EOFError unsafe_read(stream, pointer(Vector{UInt8}(undef, 3)), 3)
27+
data = Vector{UInt8}(undef, 3)
28+
@test_throws EOFError GC.@preserve data unsafe_read(stream, pointer(data), 3)
2829
close(stream)
2930

3031
stream = TranscodingStream(Noop(), IOBuffer("foobar"), bufsize=1)
3132
@test read(stream, UInt8) === UInt8('f')
3233
data = Vector{UInt8}(undef, 5)
33-
unsafe_read(stream, pointer(data), 5) === nothing
34+
GC.@preserve data unsafe_read(stream, pointer(data), 5) === nothing
3435
@test data == b"oobar"
3536
close(stream)
3637

@@ -122,7 +123,7 @@ using FillArrays: Zeros
122123
stream = TranscodingStream(Noop(), IOBuffer("foo"))
123124
out = zeros(UInt8, 3)
124125
@test bytesavailable(stream) == 0
125-
@test TranscodingStreams.unsafe_read(stream, pointer(out), 10) == 3
126+
@test GC.@preserve out TranscodingStreams.unsafe_read(stream, pointer(out), 10) == 3
126127
@test out == b"foo"
127128
close(stream)
128129

@@ -384,7 +385,8 @@ using FillArrays: Zeros
384385
@test eof(stream)
385386

386387
stream = NoopStream(IOBuffer("foobar"))
387-
@test_throws ArgumentError TranscodingStreams.unsafe_unread(stream, pointer(b"foo"), -1)
388+
data = b"foo"
389+
@test_throws ArgumentError GC.@preserve data TranscodingStreams.unsafe_unread(stream, pointer(data), -1)
388390
close(stream)
389391

390392
stream = NoopStream(IOBuffer("foo"))

test/codecquadruple.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ end
8181
close(stream2)
8282

8383
stream = TranscodingStream(QuadrupleCodec(), IOBuffer("foo"))
84-
@test_throws EOFError unsafe_read(stream, pointer(Vector{UInt8}(undef, 13)), 13)
84+
data = Vector{UInt8}(undef, 13)
85+
@test_throws EOFError GC.@preserve data unsafe_read(stream, pointer(data), 13)
8586
close(stream)
8687

8788
@testset "position" begin

test/runtests.jl

+34-30
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ using TranscodingStreams:
2626

2727
data = Vector{UInt8}(b"foobar")
2828
buf = Buffer(data)
29-
@test buf isa Buffer
30-
@test bufferptr(buf) === pointer(data)
31-
@test buffersize(buf) === 6
32-
@test buffermem(buf) === Memory(pointer(data), 6)
33-
@test marginptr(buf) === pointer(data) + 6
34-
@test marginsize(buf) === 0
35-
@test marginmem(buf) === Memory(pointer(data)+6, 0)
29+
GC.@preserve data buf begin
30+
@test buf isa Buffer
31+
@test bufferptr(buf) === pointer(data)
32+
@test buffersize(buf) === 6
33+
@test buffermem(buf) === Memory(pointer(data), 6)
34+
@test marginptr(buf) === pointer(data) + 6
35+
@test marginsize(buf) === 0
36+
@test marginmem(buf) === Memory(pointer(data)+6, 0)
37+
end
3638

3739
buf = Buffer(2)
3840
writebyte!(buf, 0x34)
@@ -72,31 +74,33 @@ end
7274

7375
@testset "Memory" begin
7476
data = Vector{UInt8}(b"foobar")
75-
mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
76-
@test mem isa TranscodingStreams.Memory
77-
@test mem.ptr === pointer(data)
78-
@test mem.size === length(mem) === UInt(sizeof(data))
79-
@test lastindex(mem) === 6
80-
@test mem[1] === UInt8('f')
81-
@test mem[2] === UInt8('o')
82-
@test mem[3] === UInt8('o')
83-
@test mem[4] === UInt8('b')
84-
@test mem[5] === UInt8('a')
85-
@test mem[6] === UInt8('r')
86-
@test_throws BoundsError mem[7]
87-
@test_throws BoundsError mem[0]
88-
mem[1] = UInt8('z')
89-
@test mem[1] === UInt8('z')
90-
mem[3] = UInt8('!')
91-
@test mem[3] === UInt8('!')
92-
@test_throws BoundsError mem[7] = 0x00
93-
@test_throws BoundsError mem[0] = 0x00
77+
GC.@preserve data let mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
78+
@test mem isa TranscodingStreams.Memory
79+
@test mem.ptr === pointer(data)
80+
@test mem.size === length(mem) === UInt(sizeof(data))
81+
@test lastindex(mem) === 6
82+
@test mem[1] === UInt8('f')
83+
@test mem[2] === UInt8('o')
84+
@test mem[3] === UInt8('o')
85+
@test mem[4] === UInt8('b')
86+
@test mem[5] === UInt8('a')
87+
@test mem[6] === UInt8('r')
88+
@test_throws BoundsError mem[7]
89+
@test_throws BoundsError mem[0]
90+
mem[1] = UInt8('z')
91+
@test mem[1] === UInt8('z')
92+
mem[3] = UInt8('!')
93+
@test mem[3] === UInt8('!')
94+
@test_throws BoundsError mem[7] = 0x00
95+
@test_throws BoundsError mem[0] = 0x00
96+
end
9497

9598
data = Vector{UInt8}(b"foobar")
96-
mem = TranscodingStreams.Memory(data)
97-
@test mem isa TranscodingStreams.Memory
98-
@test mem.ptr == pointer(data)
99-
@test mem.size == sizeof(data)
99+
GC.@preserve data let mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
100+
@test mem isa TranscodingStreams.Memory
101+
@test mem.ptr == pointer(data)
102+
@test mem.size == sizeof(data)
103+
end
100104
end
101105

102106
@testset "Stats" begin

0 commit comments

Comments
 (0)