Skip to content

Commit

Permalink
Add GC.@preserve when using pointers (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
nhz2 authored Jun 20, 2024
1 parent bcad9e0 commit 74ac5c8
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 56 deletions.
2 changes: 1 addition & 1 deletion docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ using CodecZlib

function decompress(input, output)
buffer = Vector{UInt8}(undef, 16 * 1024)
while !eof(input)
GC.@preserve buffer while !eof(input)
n = min(bytesavailable(input), length(buffer))
unsafe_read(input, pointer(buffer), n)
unsafe_write(output, pointer(buffer), n)
Expand Down
1 change: 1 addition & 0 deletions src/memory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct Memory
size::UInt
end

# TODO remove this method
function Memory(data::ByteData)
return Memory(pointer(data), sizeof(data))
end
Expand Down
28 changes: 16 additions & 12 deletions src/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,18 @@ function Base.readuntil(stream::TranscodingStream, delim::UInt8; keep::Bool=fals
local ret::Vector{UInt8}
filled = 0
while !eof(stream)
p = findbyte(buffer1, delim)
found = false
if p < marginptr(buffer1)
found = true
sz = Int(p + 1 - bufferptr(buffer1))
if !keep
sz -= 1
GC.@preserve buffer1 begin
p = findbyte(buffer1, delim)
found = false
if p < marginptr(buffer1)
found = true
sz = Int(p + 1 - bufferptr(buffer1))
if !keep
sz -= 1
end
else
sz = buffersize(buffer1)
end
else
sz = buffersize(buffer1)
end
if @isdefined(ret)
resize!(ret, filled + sz)
Expand Down Expand Up @@ -703,9 +705,11 @@ end
# Call `process` with prologue and epilogue.
function callprocess(stream::TranscodingStream, inbuf::Buffer, outbuf::Buffer)
state = stream.state
input = buffermem(inbuf)
GC.@preserve inbuf makemargin!(outbuf, minoutsize(stream.codec, input))
Δin::Int, Δout::Int, state.code = GC.@preserve inbuf outbuf process(stream.codec, input, marginmem(outbuf), state.error)
makemargin!(
outbuf,
GC.@preserve(inbuf, minoutsize(stream.codec, buffermem(inbuf))),
)
Δin::Int, Δout::Int, state.code = GC.@preserve inbuf outbuf process(stream.codec, buffermem(inbuf), marginmem(outbuf), state.error)
@debug(
"called process()",
code = state.code,
Expand Down
15 changes: 7 additions & 8 deletions src/transcode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ function Base.transcode(codec::Type{C}, src::String) where {C<:Codec}
end

_default_output_buffer(codec, input) = Buffer(
initial_output_size(
GC.@preserve(input, initial_output_size(
codec,
buffermem(input)
)
))
)

"""
Expand Down Expand Up @@ -125,8 +125,7 @@ function transcode!(
Base.mightalias(input.data, output.data) && error(
"input and outbut buffers must be independent"
)
# GC.@preserve since unsafe_transcode! may convert to raw pointers
GC.@preserve input output codec unsafe_transcode!(output, codec, input)
unsafe_transcode!(output, codec, input)
end

"""
Expand All @@ -148,10 +147,10 @@ function unsafe_transcode!(
if code === :error
@goto error
end
n = minoutsize(codec, buffermem(input))
n = GC.@preserve input minoutsize(codec, buffermem(input))
@label process
makemargin!(output, n)
Δin, Δout, code = process(codec, buffermem(input), marginmem(output), error)
Δin, Δout, code = GC.@preserve input output process(codec, buffermem(input), marginmem(output), error)
@debug(
"called process()",
code = code,
Expand All @@ -169,13 +168,13 @@ function unsafe_transcode!(
if startproc(codec, :write, error) === :error
@goto error
end
n = minoutsize(codec, buffermem(input))
n = GC.@preserve input minoutsize(codec, buffermem(input))
@goto process
end
resize!(output.data, output.marginpos - 1)
return output.data
else
n = max(Δout, minoutsize(codec, buffermem(input)))
n = GC.@preserve input max(Δout, minoutsize(codec, buffermem(input)))
@goto process
end
@label error
Expand Down
10 changes: 6 additions & 4 deletions test/codecnoop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ using FillArrays: Zeros

stream = TranscodingStream(Noop(), IOBuffer())
@test_throws EOFError read(stream, UInt8)
@test_throws EOFError unsafe_read(stream, pointer(Vector{UInt8}(undef, 3)), 3)
data = Vector{UInt8}(undef, 3)
@test_throws EOFError GC.@preserve data unsafe_read(stream, pointer(data), 3)
close(stream)

stream = TranscodingStream(Noop(), IOBuffer("foobar"), bufsize=1)
@test read(stream, UInt8) === UInt8('f')
data = Vector{UInt8}(undef, 5)
unsafe_read(stream, pointer(data), 5) === nothing
GC.@preserve data unsafe_read(stream, pointer(data), 5) === nothing
@test data == b"oobar"
close(stream)

Expand Down Expand Up @@ -122,7 +123,7 @@ using FillArrays: Zeros
stream = TranscodingStream(Noop(), IOBuffer("foo"))
out = zeros(UInt8, 3)
@test bytesavailable(stream) == 0
@test TranscodingStreams.unsafe_read(stream, pointer(out), 10) == 3
@test GC.@preserve out TranscodingStreams.unsafe_read(stream, pointer(out), 10) == 3
@test out == b"foo"
close(stream)

Expand Down Expand Up @@ -384,7 +385,8 @@ using FillArrays: Zeros
@test eof(stream)

stream = NoopStream(IOBuffer("foobar"))
@test_throws ArgumentError TranscodingStreams.unsafe_unread(stream, pointer(b"foo"), -1)
data = b"foo"
@test_throws ArgumentError GC.@preserve data TranscodingStreams.unsafe_unread(stream, pointer(data), -1)
close(stream)

stream = NoopStream(IOBuffer("foo"))
Expand Down
3 changes: 2 additions & 1 deletion test/codecquadruple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ end
close(stream2)

stream = TranscodingStream(QuadrupleCodec(), IOBuffer("foo"))
@test_throws EOFError unsafe_read(stream, pointer(Vector{UInt8}(undef, 13)), 13)
data = Vector{UInt8}(undef, 13)
@test_throws EOFError GC.@preserve data unsafe_read(stream, pointer(data), 13)
close(stream)

@testset "position" begin
Expand Down
64 changes: 34 additions & 30 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ using TranscodingStreams:

data = Vector{UInt8}(b"foobar")
buf = Buffer(data)
@test buf isa Buffer
@test bufferptr(buf) === pointer(data)
@test buffersize(buf) === 6
@test buffermem(buf) === Memory(pointer(data), 6)
@test marginptr(buf) === pointer(data) + 6
@test marginsize(buf) === 0
@test marginmem(buf) === Memory(pointer(data)+6, 0)
GC.@preserve data buf begin
@test buf isa Buffer
@test bufferptr(buf) === pointer(data)
@test buffersize(buf) === 6
@test buffermem(buf) === Memory(pointer(data), 6)
@test marginptr(buf) === pointer(data) + 6
@test marginsize(buf) === 0
@test marginmem(buf) === Memory(pointer(data)+6, 0)
end

buf = Buffer(2)
writebyte!(buf, 0x34)
Expand Down Expand Up @@ -72,31 +74,33 @@ end

@testset "Memory" begin
data = Vector{UInt8}(b"foobar")
mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
@test mem isa TranscodingStreams.Memory
@test mem.ptr === pointer(data)
@test mem.size === length(mem) === UInt(sizeof(data))
@test lastindex(mem) === 6
@test mem[1] === UInt8('f')
@test mem[2] === UInt8('o')
@test mem[3] === UInt8('o')
@test mem[4] === UInt8('b')
@test mem[5] === UInt8('a')
@test mem[6] === UInt8('r')
@test_throws BoundsError mem[7]
@test_throws BoundsError mem[0]
mem[1] = UInt8('z')
@test mem[1] === UInt8('z')
mem[3] = UInt8('!')
@test mem[3] === UInt8('!')
@test_throws BoundsError mem[7] = 0x00
@test_throws BoundsError mem[0] = 0x00
GC.@preserve data let mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
@test mem isa TranscodingStreams.Memory
@test mem.ptr === pointer(data)
@test mem.size === length(mem) === UInt(sizeof(data))
@test lastindex(mem) === 6
@test mem[1] === UInt8('f')
@test mem[2] === UInt8('o')
@test mem[3] === UInt8('o')
@test mem[4] === UInt8('b')
@test mem[5] === UInt8('a')
@test mem[6] === UInt8('r')
@test_throws BoundsError mem[7]
@test_throws BoundsError mem[0]
mem[1] = UInt8('z')
@test mem[1] === UInt8('z')
mem[3] = UInt8('!')
@test mem[3] === UInt8('!')
@test_throws BoundsError mem[7] = 0x00
@test_throws BoundsError mem[0] = 0x00
end

data = Vector{UInt8}(b"foobar")
mem = TranscodingStreams.Memory(data)
@test mem isa TranscodingStreams.Memory
@test mem.ptr == pointer(data)
@test mem.size == sizeof(data)
GC.@preserve data let mem = TranscodingStreams.Memory(pointer(data), sizeof(data))
@test mem isa TranscodingStreams.Memory
@test mem.ptr == pointer(data)
@test mem.size == sizeof(data)
end
end

@testset "Stats" begin
Expand Down

0 comments on commit 74ac5c8

Please sign in to comment.