Skip to content

Commit 7516678

Browse files
authored
Add finalizer to ZStream and make TranscodingStreams.finalize a noop (#97)
1 parent f29c86f commit 7516678

File tree

4 files changed

+58
-39
lines changed

4 files changed

+58
-39
lines changed

Diff for: src/compression.jl

+9-14
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ function GzipCompressor(;level::Integer=Z_DEFAULT_COMPRESSION,
3737
elseif !(9 windowbits 15)
3838
throw(ArgumentError("windowbits must be within 9..15"))
3939
end
40-
return GzipCompressor(ZStream(), level, windowbits+16)
40+
zstream = ZStream()
41+
finalizer(compress_finalizer!, zstream)
42+
return GzipCompressor(zstream, level, windowbits+16)
4143
end
4244

4345
const GzipCompressorStream{S} = TranscodingStream{GzipCompressor,S} where S<:IO
@@ -85,7 +87,9 @@ function ZlibCompressor(;level::Integer=Z_DEFAULT_COMPRESSION,
8587
elseif !(9 windowbits 15)
8688
throw(ArgumentError("windowbits must be within 9..15"))
8789
end
88-
return ZlibCompressor(ZStream(), level, windowbits)
90+
zstream = ZStream()
91+
finalizer(compress_finalizer!, zstream)
92+
return ZlibCompressor(zstream, level, windowbits)
8993
end
9094

9195
const ZlibCompressorStream{S} = TranscodingStream{ZlibCompressor,S} where S<:IO
@@ -133,7 +137,9 @@ function DeflateCompressor(;level::Integer=Z_DEFAULT_COMPRESSION,
133137
elseif !(9 windowbits 15)
134138
throw(ArgumentError("windowbits must be within 9..15"))
135139
end
136-
return DeflateCompressor(ZStream(), level, -Int(windowbits))
140+
zstream = ZStream()
141+
finalizer(compress_finalizer!, zstream)
142+
return DeflateCompressor(zstream, level, -Int(windowbits))
137143
end
138144

139145
const DeflateCompressorStream{S} = TranscodingStream{DeflateCompressor,S} where S<:IO
@@ -155,17 +161,6 @@ end
155161
# Methods
156162
# -------
157163

158-
function TranscodingStreams.finalize(codec::CompressorCodec)
159-
zstream = codec.zstream
160-
if zstream.state != C_NULL
161-
code = deflate_end!(zstream)
162-
if code != Z_OK
163-
zerror(zstream, code)
164-
end
165-
end
166-
return
167-
end
168-
169164
function TranscodingStreams.startproc(codec::CompressorCodec, state::Symbol, error_ref::Error)
170165
if codec.zstream.state == C_NULL
171166
code = deflate_init!(codec.zstream, codec.level, codec.windowbits)

Diff for: src/decompression.jl

+9-14
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ function GzipDecompressor(;windowbits::Integer=Z_DEFAULT_WINDOWBITS, gziponly::B
3535
if !(8 windowbits 15)
3636
throw(ArgumentError("windowbits must be within 8..15"))
3737
end
38-
return GzipDecompressor(ZStream(), windowbits+(gziponly ? 16 : 32))
38+
zstream = ZStream()
39+
finalizer(decompress_finalizer!, zstream)
40+
return GzipDecompressor(zstream, windowbits+(gziponly ? 16 : 32))
3941
end
4042

4143
const GzipDecompressorStream{S} = TranscodingStream{GzipDecompressor,S} where S<:IO
@@ -78,7 +80,9 @@ function ZlibDecompressor(;windowbits::Integer=Z_DEFAULT_WINDOWBITS)
7880
if !(8 windowbits 15)
7981
throw(ArgumentError("windowbits must be within 8..15"))
8082
end
81-
return ZlibDecompressor(ZStream(), windowbits)
83+
zstream = ZStream()
84+
finalizer(decompress_finalizer!, zstream)
85+
return ZlibDecompressor(zstream, windowbits)
8286
end
8387

8488
const ZlibDecompressorStream{S} = TranscodingStream{ZlibDecompressor,S} where S<:IO
@@ -121,7 +125,9 @@ function DeflateDecompressor(;windowbits::Integer=Z_DEFAULT_WINDOWBITS)
121125
if !(8 windowbits 15)
122126
throw(ArgumentError("windowbits must be within 8..15"))
123127
end
124-
return DeflateDecompressor(ZStream(), -Int(windowbits))
128+
zstream = ZStream()
129+
finalizer(decompress_finalizer!, zstream)
130+
return DeflateDecompressor(zstream, -Int(windowbits))
125131
end
126132

127133
const DeflateDecompressorStream{S} = TranscodingStream{DeflateDecompressor,S} where S<:IO
@@ -143,17 +149,6 @@ end
143149
# Methods
144150
# -------
145151

146-
function TranscodingStreams.finalize(codec::DecompressorCodec)
147-
zstream = codec.zstream
148-
if zstream.state != C_NULL
149-
code = inflate_end!(zstream)
150-
if code != Z_OK
151-
zerror(zstream, code)
152-
end
153-
end
154-
return
155-
end
156-
157152
function TranscodingStreams.startproc(codec::DecompressorCodec, ::Symbol, error_ref::Error)
158153
# indicate that no input data is being provided for future zlib compat
159154
codec.zstream.next_in = C_NULL

Diff for: src/libz.jl

+25-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ mutable struct ZStream
2323
reserved::Culong
2424
end
2525

26+
@assert typemax(Csize_t) typemax(Cuint)
27+
28+
function zalloc(::Ptr{Cvoid}, items::Cuint, size::Cuint)::Ptr{Cvoid}
29+
s, f = Base.Checked.mul_with_overflow(items, size)
30+
if f
31+
C_NULL
32+
else
33+
ccall(:jl_malloc, Ptr{Cvoid}, (Csize_t,), s%Csize_t)
34+
end
35+
end
36+
zfree(::Ptr{Cvoid}, p::Ptr{Cvoid}) = ccall(:jl_free, Cvoid, (Ptr{Cvoid},), p)
37+
2638
function ZStream()
2739
ZStream(
2840
# input
@@ -32,7 +44,9 @@ function ZStream()
3244
# message and state
3345
C_NULL, C_NULL,
3446
# memory allocation
35-
C_NULL, C_NULL, C_NULL,
47+
@cfunction(zalloc, Ptr{Cvoid}, (Ptr{Cvoid}, Cuint, Cuint)),
48+
@cfunction(zfree, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid})),
49+
C_NULL,
3650
# data type, adler and reserved
3751
0, 0, 0)
3852
end
@@ -83,6 +97,11 @@ function deflate_end!(zstream::ZStream)
8397
return ccall((:deflateEnd, libz), Cint, (Ref{ZStream},), zstream)
8498
end
8599

100+
function compress_finalizer!(zstream::ZStream)
101+
deflate_end!(zstream)
102+
nothing
103+
end
104+
86105
function deflate!(zstream::ZStream, flush::Integer)
87106
return ccall((:deflate, libz), Cint, (Ref{ZStream}, Cint), zstream, flush)
88107
end
@@ -99,6 +118,11 @@ function inflate_end!(zstream::ZStream)
99118
return ccall((:inflateEnd, libz), Cint, (Ref{ZStream},), zstream)
100119
end
101120

121+
function decompress_finalizer!(zstream::ZStream)
122+
inflate_end!(zstream)
123+
nothing
124+
end
125+
102126
function inflate!(zstream::ZStream, flush::Integer)
103127
return ccall((:inflate, libz), Cint, (Ref{ZStream}, Cint), zstream, flush)
104128
end

Diff for: test/big-mem-tests.jl

+15-10
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66
using Test
77
using CodecZlib
88

9-
# Enable this when https://github.com/JuliaIO/CodecZlib.jl/issues/88 is fixed.
10-
# @testset "memory leak" begin
11-
# function foo()
12-
# for i in 1:1000000
13-
# c = transcode(GzipCompressor(), zeros(UInt8,16))
14-
# u = transcode(GzipDecompressor(), c)
15-
# end
16-
# end
17-
# foo()
18-
# end
9+
@testset "memory leak" begin
10+
function foo()
11+
for (encode, decode) in [
12+
(GzipCompressor, GzipDecompressor),
13+
(ZlibCompressor, ZlibDecompressor),
14+
(DeflateCompressor, DeflateDecompressor),
15+
]
16+
for i in 1:1000000
17+
c = transcode(encode(), zeros(UInt8,16))
18+
u = transcode(decode(), c)
19+
end
20+
end
21+
end
22+
foo()
23+
end
1924

2025
@testset "Big Memory Tests" begin
2126
Sys.WORD_SIZE == 64 || error("tests require 64 bit word size")

0 commit comments

Comments
 (0)