Skip to content

Commit 0a46abf

Browse files
authored
Fix stop_on_end = true closing underlying stream (#178)
1 parent f2916c9 commit 0a46abf

File tree

9 files changed

+101
-101
lines changed

9 files changed

+101
-101
lines changed

docs/src/assets/modes.dot

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,16 @@ digraph modes {
1212
"read" -> "panic";
1313

1414
"write" -> "write";
15-
"write" -> "done";
1615
"write" -> "close";
1716
"write" -> "panic";
1817

1918
"stop" -> "close";
20-
"done" -> "close";
2119

2220
"start" [ shape = point ];
2321
"idle" [ shape = circle ];
2422
"read" [ shape = circle ];
2523
"write" [ shape = circle ];
2624
"stop" [ shape = circle; style=bold; ];
27-
"done" [ shape = circle; style=bold; ];
2825
"close" [ shape = circle; style=bold; ];
2926
"panic" [ shape = circle; style=bold; ];
3027
}

docs/src/assets/modes.svg

Lines changed: 47 additions & 65 deletions
Loading

docs/src/devnotes.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ The `mode` field may be one of the following value:
4747
- `:read` : being ready to read data, data may be buffered
4848
- `:write`: being ready to write data, data may be buffered
4949
- `:stop` : transcoding is stopped after read, data may be buffered
50-
- `:done` : transcoding is stopped after write, data may be buffered
5150
- `:close`: closed, no buffered data
5251
- `:panic`: an exception has been thrown in codec, data may be buffered but we
5352
cannot do anything

ext/TestExt.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,11 @@ function TranscodingStreams.test_chunked_write(Encoder, Decoder)
108108
buffer = IOBuffer()
109109
stream = TranscodingStream(Decoder(), buffer, stop_on_end=true)
110110
write(stream, vcat(data...))
111-
flush(stream)
111+
close(stream)
112112
ok = true
113-
ok &= hash(take!(buffer)) == hash(chunks[1])
114-
ok &= buffersize(stream.state.buffer1) == length(data[2])
113+
ok &= hash(take!(buffer)) == hash(vcat(chunks...))
114+
ok &= buffersize(stream.state.buffer1) == 0
115115
Test.@test ok
116-
close(stream)
117116
end
118117
finalize(encoder)
119118
end

src/noop.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ end
3030

3131
function TranscodingStream(codec::Noop, stream::IO;
3232
bufsize::Integer=DEFAULT_BUFFER_SIZE,
33+
stop_on_end::Bool=false,
3334
sharedbuf::Bool=(stream isa TranscodingStream))
3435
checkbufsize(bufsize)
3536
checksharedbuf(sharedbuf, stream)
@@ -38,7 +39,9 @@ function TranscodingStream(codec::Noop, stream::IO;
3839
else
3940
buffer = Buffer(bufsize)
4041
end
41-
return TranscodingStream(codec, stream, State(buffer, buffer))
42+
state = State(buffer, buffer)
43+
state.stop_on_end = stop_on_end
44+
return TranscodingStream(codec, stream, state)
4245
end
4346

4447
"""

src/state.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ See Developer's notes for details.
99
"""
1010
mutable struct State
1111
# current stream mode
12-
mode::Symbol # {:idle, :read, :write, :stop, :done, :close, :panic}
12+
mode::Symbol # {:idle, :read, :write, :stop, :close, :panic}
1313

1414
# return code of the last method call
1515
code::Symbol # {:ok, :end, :error}
1616

17-
# flag to go :stop or :done on :end
17+
# flag to go :stop on :end while reading
1818
stop_on_end::Bool
1919

2020
# exception thrown while data processing

src/stream.jl

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ Arguments
8585
The initial buffer size (the default size is 16KiB). The buffer may be
8686
extended whenever `codec` requests so.
8787
- `stop_on_end`:
88-
The flag to stop transcoding on `:end` return code from `codec`. The
88+
The flag to stop reading on `:end` return code from `codec`. The
8989
transcoded data are readable even after stopping transcoding process. With
9090
this flag on, `stream` is not closed when the wrapper stream is closed with
91-
`close`. Note that some extra data may be read from `stream` into an
91+
`close`. Note that if reading some extra data may be read from `stream` into an
9292
internal buffer, and thus `stream` must be a `TranscodingStream` object and
9393
`sharedbuf` must be `true` to reuse `stream`.
9494
- `sharedbuf`:
@@ -184,11 +184,10 @@ end
184184

185185
function Base.close(stream::TranscodingStream)
186186
mode = stream.state.mode
187-
stopped = mode === :stop || mode === :done
188187
if mode != :panic
189188
changemode!(stream, :close)
190189
end
191-
if !stopped
190+
if !stream.state.stop_on_end
192191
close(stream.stream)
193192
end
194193
return nothing
@@ -214,8 +213,6 @@ end
214213
continue
215214
elseif mode == :write
216215
return eof(stream.stream)
217-
elseif mode == :done
218-
return eof(stream.stream)
219216
elseif mode == :close
220217
return true
221218
elseif mode == :stop
@@ -622,7 +619,7 @@ function flushbuffer(stream::TranscodingStream, all::Bool=false)
622619
buffer1 = stream.buffer1
623620
buffer2 = stream.buffer2
624621
nflushed::Int = 0
625-
while (all ? buffersize(buffer1) != 0 : makemargin!(buffer1, 0) == 0) && state.mode != :done
622+
while (all ? buffersize(buffer1) != 0 : makemargin!(buffer1, 0) == 0)
626623
if state.code == :end
627624
callstartproc(stream, :write)
628625
end
@@ -689,8 +686,6 @@ function callprocess(stream::TranscodingStream, inbuf::Buffer, outbuf::Buffer)
689686
elseif state.code == :end && state.stop_on_end
690687
if stream.state.mode == :read
691688
changemode!(stream, :stop)
692-
else
693-
changemode!(stream, :done)
694689
end
695690
end
696691
return Δin, Δout
@@ -775,11 +770,9 @@ function changemode!(stream::TranscodingStream, newmode::Symbol)
775770
return
776771
end
777772
elseif mode == :write
778-
if newmode == :close || newmode == :done
779-
if newmode == :close
780-
flushbufferall(stream)
781-
flushuntilend(stream)
782-
end
773+
if newmode == :close
774+
flushbufferall(stream)
775+
flushuntilend(stream)
783776
state.mode = newmode
784777
finalize_codec(stream.codec, state.error)
785778
return
@@ -789,11 +782,6 @@ function changemode!(stream::TranscodingStream, newmode::Symbol)
789782
state.mode = newmode
790783
return
791784
end
792-
elseif mode == :done
793-
if newmode == :close
794-
state.mode = newmode
795-
return
796-
end
797785
elseif mode == :panic
798786
throw_panic_error()
799787
end

test/codecdoubleframe.jl

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,37 @@ DoubleFrameDecoderStream(stream::IO; kwargs...) = TranscodingStream(DoubleFrameD
210210
@test transcode(DoubleFrameDecoder, b"[ ]] ]") == b" ]"
211211
@test transcode(DoubleFrameDecoder, b"[ aa ][ bb ]") == b"ab"
212212

213-
@testset "eof is true after write stops" begin
213+
@testset "stop_on_end=true prevents underlying stream closing" begin
214214
sink = IOBuffer()
215215
stream = TranscodingStream(DoubleFrameDecoder(), sink, stop_on_end=true)
216-
write(stream, "[ yy ]sdfsadfasdfdf")
217-
flush(stream)
218-
@test eof(stream)
219-
@test_throws ArgumentError read(stream, UInt8)
220-
@test take!(sink) == b"y"
216+
write(stream, "[ yy ]")
217+
write(stream, "[ xx ]")
221218
close(stream)
219+
@test isopen(sink)
220+
@test take!(sink) == b"yx"
221+
end
222+
223+
@testset "Issue #95" begin
224+
@test sprint() do outer
225+
inner = TranscodingStream(DoubleFrameEncoder(), outer, stop_on_end = true)
226+
println(inner, "Hello, world.")
227+
close(inner)
228+
end == "[ HHeelllloo,, wwoorrlldd..\n\n ]"
229+
end
230+
231+
@testset "TOKEN_END repeated doesn't create more empty frames" begin
232+
sink = IOBuffer()
233+
stream = TranscodingStream(DoubleFrameEncoder(), sink, stop_on_end=true)
234+
write(stream, TranscodingStreams.TOKEN_END)
235+
write(stream, TranscodingStreams.TOKEN_END)
236+
write(stream, "abc")
237+
write(stream, TranscodingStreams.TOKEN_END)
238+
write(stream, "de")
239+
write(stream, TranscodingStreams.TOKEN_END)
240+
write(stream, "") # This doesn't create an empty frame
241+
write(stream, TranscodingStreams.TOKEN_END)
242+
close(stream)
243+
@test String(take!(sink)) == "[ ][ aabbcc ][ ddee ]"
222244
end
223245

224246
test_roundtrip_read(DoubleFrameEncoderStream, DoubleFrameDecoderStream)

0 commit comments

Comments
 (0)