Skip to content

Commit 9ddb506

Browse files
committed
Fix behavior when writing to/reading from closed streams
Reading from a closed StringDecoder returned the contents of the buffer undefinitely. Add more checks and tests for closed streams.
1 parent 27a5380 commit 9ddb506

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

src/StringEncodings.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ function finalize(s::Union{StringEncoder, StringDecoder})
111111
if s.cd != C_NULL
112112
iconv_close(s.cd)
113113
s.cd = C_NULL
114+
# To ensure that eof() returns true without an additional check
115+
if isa(s, StringDecoder)
116+
s.outbytesleft[] = BUFSIZE
117+
s.skip = 0
118+
end
114119
end
115120
nothing
116121
end
@@ -239,7 +244,10 @@ function close(s::StringEncoder)
239244
end
240245

241246
function write(s::StringEncoder, x::UInt8)
242-
s.inbytesleft[] >= length(s.inbuf) && flush(s)
247+
s.cd == C_NULL && throw(ArgumentError("cannot write to closed StringEncoder"))
248+
if s.inbytesleft[] >= length(s.inbuf)
249+
flush(s)
250+
end
243251
s.inbuf[s.inbytesleft[]+=1] = x
244252
1
245253
end
@@ -322,14 +330,15 @@ function close(s::StringDecoder)
322330
end
323331

324332
function read(s::StringDecoder, ::Type{UInt8})
333+
s.cd == C_NULL && throw(ArgumentError("cannot read from closed StringDecoder"))
325334
eof(s) ? throw(EOFError()) : s.outbuf[s.skip+=1]
326335
end
327336

328-
isreadable(s::StringDecoder) = isreadable(s.stream)
337+
isreadable(s::StringDecoder) = s.cd != C_NULL && isreadable(s.stream)
329338
iswritable(s::StringDecoder) = false
330339

331340
isreadable(s::StringEncoder) = false
332-
iswritable(s::StringEncoder) = iswritable(s.stream)
341+
iswritable(s::StringEncoder) = s.cd != C_NULL && iswritable(s.stream)
333342

334343

335344
## Convenience I/O functions

test/runtests.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@ let s = "a string チャネルパートナーの選択"
3333
p = StringEncoder(IOBuffer(), "UTF-16LE")
3434
write(p, s.data)
3535
close(p)
36+
# Test that closed pipe behaves correctly
37+
@test_throws ArgumentError write(p, 'a')
3638

3739
b = IOBuffer()
3840
p = StringEncoder(b, "UTF-16LE")
3941
@test string(p) == "StringEncoder{UTF-8, UTF-16LE}($(string(b)))"
4042
write(p, s.data[1:10])
4143
@test_throws IncompleteSequenceError close(p)
44+
# Test that closed pipe behaves correctly even after an error
45+
@test_throws ArgumentError write(p, 'a')
4246

4347
# This time, call show
4448
p = StringEncoder(IOBuffer(), "UTF-16LE")
@@ -57,6 +61,9 @@ let s = "a string チャネルパートナーの選択"
5761
@test string(p) == "StringDecoder{UTF-16LE, UTF-8}($(string(b)))"
5862
@test readstring(p) == s[1:9]
5963
@test_throws IncompleteSequenceError close(p)
64+
# Test that closed pipe behaves correctly even after an error
65+
@test eof(p)
66+
@test_throws ArgumentError read(p, UInt8)
6067

6168
# Test stateful encoding, which output some bytes on final reset
6269
# with strings containing different scripts
@@ -68,7 +75,7 @@ let s = "a string チャネルパートナーの選択"
6875
# Test that closed pipe behaves correctly
6976
close(p)
7077
@test eof(p)
71-
@test_throws EOFError read(p, UInt8)
78+
@test_throws ArgumentError read(p, UInt8)
7279
close(p)
7380
end
7481

0 commit comments

Comments
 (0)