diff --git a/docs/src/devnotes.md b/docs/src/devnotes.md index 5fd968c8..81029659 100644 --- a/docs/src/devnotes.md +++ b/docs/src/devnotes.md @@ -46,13 +46,10 @@ The `mode` field may be one of the following value: - `:idle` : initial and intermediate mode, no buffered data - `:read` : being ready to read data, data may be buffered - `:write`: being ready to write data, data may be buffered -- `:stop` : transcoding is stopped after read, data may be buffered - `:close`: closed, no buffered data - `:panic`: an exception has been thrown in codec, data may be buffered but we cannot do anything -Note that `mode=:stop` does not mean there is no data available in the stream. -This is because transcoded data may be left in the buffer. The initial mode is `:idle` and mode transition happens as shown in the following diagram: diff --git a/fuzz/fuzz.jl b/fuzz/fuzz.jl index 10b48060..ef36dcc5 100644 --- a/fuzz/fuzz.jl +++ b/fuzz/fuzz.jl @@ -120,8 +120,7 @@ end for r in rs d = r(stream) append!(x, d) - # TODO fix position - # length(x) == position(stream) || return false + length(x) == position(stream) || return false end x == data[eachindex(x)] end diff --git a/src/buffer.jl b/src/buffer.jl index 7247fac0..016504ff 100644 --- a/src/buffer.jl +++ b/src/buffer.jl @@ -14,7 +14,7 @@ # position 1 markpos bufferpos marginpos lastindex(data) # # `markpos` is positive iff there are marked data; otherwise it is set to zero. -# `markpos` ≤ `bufferpos` ≤ `marginpos` must hold whenever possible. +# `markpos` ≤ `marginpos` and `bufferpos` ≤ `marginpos` must hold. mutable struct Buffer # data and positions (see above) @@ -23,8 +23,9 @@ mutable struct Buffer bufferpos::Int marginpos::Int - # the total number of transcoded bytes - transcoded::Int64 + # total number of bytes shifted by makemargin! + # used to keep track of stream position + shifted::Int64 function Buffer(data::Vector{UInt8}, marginpos::Integer=length(data)+1) @assert 1 <= marginpos <= length(data)+1 @@ -93,41 +94,30 @@ function reset!(buf::Buffer) end # Notify that `n` bytes are consumed from `buf`. -function consumed!(buf::Buffer, n::Integer; transcode::Bool = false) +function consumed!(buf::Buffer, n::Integer) buf.bufferpos += n - if transcode - buf.transcoded += n - end return buf end # Notify that `n` bytes are supplied to `buf`. -function supplied!(buf::Buffer, n::Integer; transcode::Bool = false) +function supplied!(buf::Buffer, n::Integer) buf.marginpos += n - if transcode - buf.transcoded += n - end return buf end # Discard buffered data and initialize positions. function initbuffer!(buf::Buffer) - buf.markpos = buf.transcoded = 0 + buf.markpos = buf.shifted = 0 buf.bufferpos = buf.marginpos = 1 return buf end -# Remove all buffered data. -function emptybuffer!(buf::Buffer) - buf.marginpos = buf.bufferpos - return buf -end - # Make margin with ≥`minsize` and return the size of it. # If eager is true, it tries to move data even when the buffer has enough margin. function makemargin!(buf::Buffer, minsize::Integer; eager::Bool = false) @assert minsize ≥ 0 if buffersize(buf) == 0 && buf.markpos == 0 + buf.shifted += buf.bufferpos - 1 buf.bufferpos = buf.marginpos = 1 end if marginsize(buf) < minsize || eager @@ -138,10 +128,9 @@ function makemargin!(buf::Buffer, minsize::Integer; eager::Bool = false) datapos = buf.bufferpos datasize = buffersize(buf) else - # Else, we must not consume marked data - # (Since markpos ≤ bufferpos, we do not consume buffered data either) - datapos = buf.markpos - datasize = buf.marginpos - buf.markpos + # Else, we must not consume marked data or buffered data + datapos = min(buf.markpos, buf.bufferpos) + datasize = buf.marginpos - datapos end # Shift data left in buffer to make space for new data copyto!(buf.data, 1, buf.data, datapos, datasize) @@ -151,6 +140,7 @@ function makemargin!(buf::Buffer, minsize::Integer; eager::Bool = false) end buf.bufferpos -= shift buf.marginpos -= shift + buf.shifted += shift end # If there is still not enough margin, we expand buffer. # At least enough for minsize, but otherwise 1.5 times @@ -203,10 +193,29 @@ end # Insert data to the current buffer. function insertdata!(buf::Buffer, data::Ptr{UInt8}, nbytes::Integer) - makemargin!(buf, nbytes) - copyto!(buf.data, buf.bufferpos + nbytes, buf.data, buf.bufferpos, buffersize(buf)) + front_space_needed = nbytes - buf.bufferpos + 1 + if front_space_needed > 0 + if front_space_needed > marginsize(buf) + # make more space + resize!(buf.data, buf.marginpos + front_space_needed - 1) + end + @assert front_space_needed ≤ marginsize(buf) + # make space in front by shifting data to margin + for i in (buf.marginpos-1 + front_space_needed):-1:(1+nbytes) + buf.data[i] = buf.data[i-front_space_needed] + end + if buf.markpos > 0 + buf.markpos += front_space_needed + end + buf.bufferpos += front_space_needed + buf.marginpos += front_space_needed + buf.shifted -= front_space_needed + end + # here is the really spooky part where sometimes `markpos` > `bufferpos` + buf.bufferpos -= nbytes + @assert buf.bufferpos > 0 + @assert buffersize(buf) ≥ nbytes GC.@preserve buf unsafe_copyto!(bufferptr(buf), data, nbytes) - supplied!(buf, nbytes) return buf end diff --git a/src/noop.jl b/src/noop.jl index 9ece4e1f..5f047691 100644 --- a/src/noop.jl +++ b/src/noop.jl @@ -44,69 +44,91 @@ function TranscodingStream(codec::Noop, stream::IO; return TranscodingStream(codec, stream, state) end -""" - position(stream::NoopStream) - -Get the current poition of `stream`. - -Note that this method may return a wrong position when -- some data have been inserted by `TranscodingStreams.unread`, or -- the position of the wrapped stream has been changed outside of this package. -""" -function Base.position(stream::NoopStream) +function Base.position(stream::NoopStream)::Int64 mode = stream.state.mode - if mode === :idle - return Int64(0) - elseif mode === :write - return position(stream.stream) + buffersize(stream.buffer1) - elseif mode === :read - return position(stream.stream) - buffersize(stream.buffer1) + if has_sharedbuf(stream) + if mode === :idle || mode === :read || mode === :write + return position(stream.stream) - something(stream.state.offset) + else + throw_invalid_mode(mode) + end else - throw_invalid_mode(mode) + buffer1 = stream.buffer1 + if mode === :idle + return Int64(0) + elseif mode === :write + return buffer1.shifted + buffer1.marginpos - 1 + elseif mode === :read + return buffer1.shifted + buffer1.bufferpos - 1 + else + throw_invalid_mode(mode) + end end - @assert false "unreachable" end function Base.seek(stream::NoopStream, pos::Integer) - mode = stream.state.mode - if mode === :write - flushbuffer(stream) + if has_sharedbuf(stream) + seek(stream.stream, pos) + else + mode = stream.state.mode + if mode === :write + flushbuffer(stream) + end + seek(stream.stream, pos) + initbuffer!(stream.buffer1) + stream.buffer1.shifted = pos end - seek(stream.stream, pos) - initbuffer!(stream.buffer1) + stream.state.offset = 0 return stream end function Base.seekstart(stream::NoopStream) - mode = stream.state.mode - if mode === :write - flushbuffer(stream) + if has_sharedbuf(stream) + seekstart(stream.stream) + else + mode = stream.state.mode + if mode === :write + flushbuffer(stream) + end + seekstart(stream.stream) + initbuffer!(stream.buffer1) end - seekstart(stream.stream) - initbuffer!(stream.buffer1) + stream.state.offset = 0 return stream end function Base.seekend(stream::NoopStream) - mode = stream.state.mode - if mode === :write - flushbuffer(stream) + isnothing(stream.state.offset) && throw_invalid_offset(stream.stream) + if has_sharedbuf(stream) + seekend(stream.stream) + else + mode = stream.state.mode + if mode === :write + flushbuffer(stream) + end + seekend(stream.stream) + initbuffer!(stream.buffer1) + stream.buffer1.shifted = position(stream.stream) - something(stream.state.offset) end - seekend(stream.stream) - initbuffer!(stream.buffer1) return stream end function Base.unsafe_write(stream::NoopStream, input::Ptr{UInt8}, nbytes::UInt) changemode!(stream, :write) - buffer = stream.buffer1 - if marginsize(buffer) ≥ nbytes - copydata!(buffer, input, nbytes) - return Int(nbytes) - else - flushbuffer(stream) - # directly write data to the underlying stream + if has_sharedbuf(stream) return unsafe_write(stream.stream, input, nbytes) + else + buffer = stream.buffer1 + if marginsize(buffer) ≥ nbytes + copydata!(buffer, input, nbytes) + return Int(nbytes) + else + flushbuffer(stream) + # directly write data to the underlying stream + n = unsafe_write(stream.stream, input, nbytes) + buffer.shifted += n + return n + end end end @@ -125,20 +147,20 @@ end function stats(stream::NoopStream) state = stream.state mode = state.mode - buffer = stream.buffer1 - @assert buffer === stream.buffer2 if mode === :idle consumed = supplied = 0 elseif mode === :read - supplied = buffer.transcoded - consumed = supplied - buffersize(buffer) + isnothing(stream.state.offset) && throw_invalid_offset(stream.stream) + supplied = position(stream.stream) - something(stream.state.offset) + consumed = position(stream) elseif mode === :write - supplied = buffer.transcoded + buffersize(buffer) - consumed = buffer.transcoded + isnothing(stream.state.offset) && throw_invalid_offset(stream.stream) + supplied = position(stream) + consumed = position(stream.stream) - something(stream.state.offset) else throw_invalid_mode(mode) end - return Stats(consumed, supplied, supplied, supplied) + return Stats(supplied, consumed, supplied, supplied) end @@ -148,26 +170,26 @@ end # These methods are overloaded for the `Noop` codec because it has only one # buffer for efficiency. -function fillbuffer(stream::NoopStream; eager::Bool = false)::Int +@noinline function sloweof(stream::NoopStream)::Bool changemode!(stream, :read) buffer = stream.buffer1 - @assert buffer === stream.buffer2 - if stream.stream isa TranscodingStream && buffer === stream.stream.buffer1 - # Delegate the operation when buffers are shared. - underlying_mode::Symbol = stream.stream.state.mode - if underlying_mode === :idle || underlying_mode === :read - return fillbuffer(stream.stream, eager = eager) - else - return 0 - end - end - nfilled::Int = 0 - while ((!eager && buffersize(buffer) == 0) || (eager && makemargin!(buffer, 0, eager = true) > 0)) && !eof(stream.stream) + iszero(buffersize(buffer)) || return false + # fill buffer1 + eof(stream.stream) && return true + if !has_sharedbuf(stream) makemargin!(buffer, 1) - nfilled += readdata!(stream.stream, buffer) + navail = bytesavailable(stream.stream) + if navail == 0 + writebyte!(buffer, read(stream.stream, UInt8)) + navail = bytesavailable(stream.stream) + end + n = min(navail, marginsize(buffer)) + if !iszero(n) + GC.@preserve buffer Base.unsafe_read(stream.stream, marginptr(buffer), n) + supplied!(buffer, n) + end end - buffer.transcoded += nfilled - return nfilled + return false end function flushbuffer(stream::NoopStream, all::Bool=false) @@ -183,11 +205,11 @@ function flushbuffer(stream::NoopStream, all::Bool=false) nflushed += writedata!(stream.stream, buffer) makemargin!(buffer, 0) end - buffer.transcoded += nflushed + # buffer.transcoded += nflushed return nflushed end function flushuntilend(stream::NoopStream) - stream.buffer1.transcoded += writedata!(stream.stream, stream.buffer1) + writedata!(stream.stream, stream.buffer1) return end diff --git a/src/state.jl b/src/state.jl index 0cb54b6c..62beb866 100644 --- a/src/state.jl +++ b/src/state.jl @@ -9,12 +9,12 @@ See Developer's notes for details. """ mutable struct State # current stream mode - mode::Symbol # {:idle, :read, :write, :stop, :close, :panic} + mode::Symbol # {:idle, :read, :write, :close, :panic} # return code of the last method call code::Symbol # {:ok, :end, :error} - # flag to go :stop on :end while reading + # flag to go eof on :end while reading stop_on_end::Bool # exception thrown while data processing @@ -24,8 +24,11 @@ mutable struct State buffer1::Buffer buffer2::Buffer + # relative start position in underlying stream + offset::Union{Int64, Nothing} + function State(buffer1::Buffer, buffer2::Buffer) - return new(:idle, :ok, false, Error(), buffer1, buffer2) + return new(:idle, :ok, false, Error(), buffer1, buffer2, nothing) end end diff --git a/src/stream.jl b/src/stream.jl index 0eae4982..4aeaa932 100644 --- a/src/stream.jl +++ b/src/stream.jl @@ -31,6 +31,17 @@ struct TranscodingStream{C<:Codec,S<:IO} <: IO elseif state.mode != :idle throw(ArgumentError("invalid initial mode")) end + + # `stream` is not required to implement `position` + # In this case, offset is set to `nothing`, + # this may cause future `stat` and `seekend` calls to error. + state.offset = try + position(stream) + catch e + e isa InterruptException && rethrow() + nothing + end + if !initialized initialize(codec) end @@ -157,6 +168,16 @@ end # throw ArgumentError that mode is invalid. throw_invalid_mode(mode) = throw(ArgumentError(string("invalid mode :", mode))) +# throw ArgumentError stream.state.offset is invalid. +throw_invalid_offset(stream) = throw(ArgumentError( + "failed to get position of underlying stream: $(stream) during construction." +)) + +# Return true if the stream shares buffers with underlying stream +function has_sharedbuf(stream::TranscodingStream)::Bool + stream.stream isa TranscodingStream && stream.buffer2 === stream.stream.buffer1 +end + # Base IO Functions # ----------------- @@ -176,7 +197,7 @@ end function Base.isreadable(stream::TranscodingStream)::Bool mode = stream.state.mode - (mode === :idle || mode === :read || mode === :stop) && isreadable(stream.stream) + (mode === :idle || mode === :read) && isreadable(stream.stream) end function Base.iswritable(stream::TranscodingStream)::Bool @@ -202,31 +223,44 @@ function Base.eof(stream::TranscodingStream) eof = buffersize(stream.buffer1) == 0 state = stream.state mode = state.mode - if !(mode == :read || mode == :stop) || eof + if mode !== :read || eof eof = sloweof(stream) end return eof end -@noinline function sloweof(stream::TranscodingStream) - while true - state = stream.state - mode = state.mode - if mode == :read - return (buffersize(stream.buffer1) == 0 && fillbuffer(stream) == 0) - elseif mode == :idle - changemode!(stream, :read) - continue - elseif mode == :write - return eof(stream.stream) - elseif mode == :close - return true - elseif mode == :stop - return buffersize(stream.buffer1) == 0 - elseif mode == :panic - throw_panic_error() +@noinline function sloweof(stream::TranscodingStream)::Bool + changemode!(stream, :read) + buffer1 = stream.buffer1 + buffer2 = stream.buffer2 + state = stream.state + stop_on_end = state.stop_on_end + # fill buffer1 + while iszero(buffersize(buffer1)) + if state.code == :end + if stop_on_end || (iszero(buffersize(buffer2)) && eof(stream.stream)) + return true + end + callstartproc(stream, :read) + end + # fill buffer2 if underlying stream isn't eof. + # if stream is sharing a buffer with stream.stream + # eof(stream.stream) will fill buffer2 if there is any data left. + if !eof(stream.stream) && !has_sharedbuf(stream) + makemargin!(buffer2, 1) + navail = bytesavailable(stream.stream) + if navail == 0 + writebyte!(buffer2, read(stream.stream, UInt8)) + navail = bytesavailable(stream.stream) + end + n = min(navail, marginsize(buffer2)) + if !iszero(n) + GC.@preserve buffer2 Base.unsafe_read(stream.stream, marginptr(buffer2), n) + supplied!(buffer2, n) + end end - @assert false + _, Δout = callprocess(stream, buffer2, buffer1) end + return false end function Base.ismarked(stream::TranscodingStream)::Bool @@ -260,14 +294,15 @@ Note that the returned value will be different from that of the underlying stream wrapped by `stream`. This is because `stream` buffers some data and the codec may change the length of data. """ -function Base.position(stream::TranscodingStream) +function Base.position(stream::TranscodingStream)::Int64 mode = stream.state.mode + buffer1 = stream.buffer1 if mode === :idle return Int64(0) elseif mode === :read - return stats(stream).out + return buffer1.shifted + buffer1.bufferpos - 1 elseif mode === :write - return stats(stream).in + return buffer1.shifted + buffer1.marginpos - 1 else throw_invalid_mode(mode) end @@ -280,29 +315,19 @@ end function Base.seekstart(stream::TranscodingStream) mode = stream.state.mode - if mode === :read - callstartproc(stream, mode) - emptybuffer!(stream.buffer1) - emptybuffer!(stream.buffer2) - elseif mode === :idle + if mode === :read || mode === :idle + seekstart(stream.stream) else throw_invalid_mode(mode) end - seekstart(stream.stream) - return stream -end - -function Base.seekend(stream::TranscodingStream) - mode = stream.state.mode - if mode == :read + stream.state.offset = 0 + if mode === :read callstartproc(stream, mode) - emptybuffer!(stream.buffer1) - emptybuffer!(stream.buffer2) - elseif mode === :idle - else - throw_invalid_mode(mode) + initbuffer!(stream.buffer1) + if !has_sharedbuf(stream) + initbuffer!(stream.buffer2) + end end - seekend(stream.stream) return stream end @@ -311,14 +336,7 @@ end # -------------- function Base.read(stream::TranscodingStream, ::Type{UInt8}) - # eof and ready_to_read! are inlined here because ready_to_read! is very slow and eof is broken - eof = buffersize(stream.buffer1) == 0 - state = stream.state - mode = state.mode - if !(mode == :read || mode == :stop) - changemode!(stream, :read) - end - if eof && sloweof(stream) + if eof(stream) throw(EOFError()) end return readbyte!(stream.buffer1) @@ -469,7 +487,7 @@ end # Ready to read data from the stream. function ready_to_read!(stream::TranscodingStream) mode = stream.state.mode - if !(mode == :read || mode == :stop) + if mode !== :read changemode!(stream, :read) end return @@ -585,15 +603,25 @@ function stats(stream::TranscodingStream) if mode === :idle transcoded_in = transcoded_out = in = out = 0 elseif mode === :read - transcoded_in = buffer2.transcoded - transcoded_out = buffer1.transcoded - in = transcoded_in + buffersize(buffer2) - out = transcoded_out - buffersize(buffer1) + isnothing(stream.state.offset) && throw_invalid_offset(stream.stream) + in = position(stream.stream) - stream.state.offset + out = position(stream) + transcoded_in = if has_sharedbuf(stream) + in # don't double count buffer2 if it is shared + else + in - buffersize(buffer2) + end + transcoded_out = out + buffersize(buffer1) elseif mode === :write - transcoded_in = buffer1.transcoded - transcoded_out = buffer2.transcoded - in = transcoded_in + buffersize(buffer1) - out = transcoded_out - buffersize(buffer2) + isnothing(stream.state.offset) && throw_invalid_offset(stream.stream) + in = position(stream) + out = position(stream.stream) - stream.state.offset + transcoded_in = in - buffersize(buffer1) + transcoded_out = if has_sharedbuf(stream) + out # don't double count buffer2 if it is shared + else + out + buffersize(buffer2) + end else throw_invalid_mode(mode) end @@ -604,26 +632,6 @@ end # Buffering # --------- -function fillbuffer(stream::TranscodingStream; eager::Bool = false) - changemode!(stream, :read) - buffer1 = stream.buffer1 - buffer2 = stream.buffer2 - nfilled::Int = 0 - while ((!eager && buffersize(buffer1) == 0) || (eager && makemargin!(buffer1, 0, eager = true) > 0)) && stream.state.mode != :stop - if stream.state.code == :end - if buffersize(buffer2) == 0 && eof(stream.stream) - break - end - callstartproc(stream, :read) - end - makemargin!(buffer2, 1) - readdata!(stream.stream, buffer2) - _, Δout = callprocess(stream, buffer2, buffer1) - nfilled += Δout - end - return nfilled -end - function flushbuffer(stream::TranscodingStream, all::Bool=false) changemode!(stream, :write) state = stream.state @@ -687,17 +695,13 @@ function callprocess(stream::TranscodingStream, inbuf::Buffer, outbuf::Buffer) input_delta = Δin, output_delta = Δout, ) - consumed!(inbuf, Δin, transcode = true) - supplied!(outbuf, Δout, transcode = true) + consumed!(inbuf, Δin) + supplied!(outbuf, Δout) if state.code == :error changemode!(stream, :panic) elseif state.code == :ok && Δin == Δout == 0 # When no progress, expand the output buffer. makemargin!(outbuf, max(16, marginsize(outbuf) * 2)) - elseif state.code == :end && state.stop_on_end - if stream.state.mode == :read - changemode!(stream, :stop) - end end return Δin, Δout end @@ -706,30 +710,6 @@ end # I/O operations # -------------- -# Read as much data as possbile from `input` to the margin of `output`. -# This function will not block if `input` has buffered data. -function readdata!(input::IO, output::Buffer)::Int - if input isa TranscodingStream && input.buffer1 === output - # Delegate the operation to the underlying stream for shared buffers. - mode::Symbol = input.state.mode - if mode === :idle || mode === :read - return fillbuffer(input) - else - return 0 - end - end - nread::Int = 0 - navail = bytesavailable(input) - if navail == 0 && marginsize(output) > 0 && !eof(input) - nread += writebyte!(output, read(input, UInt8)) - navail = bytesavailable(input) - end - n = min(navail, marginsize(output)) - GC.@preserve output Base.unsafe_read(input, marginptr(output), n) - supplied!(output, n) - nread += n - return nread -end # Write all data to `output` from the buffer of `input`. function writedata!(output::IO, input::Buffer) @@ -780,7 +760,7 @@ function changemode!(stream::TranscodingStream, newmode::Symbol) return end elseif mode == :read - if newmode == :close || newmode == :stop + if newmode == :close state.mode = newmode finalize_codec(stream.codec, state.error) return @@ -793,11 +773,6 @@ function changemode!(stream::TranscodingStream, newmode::Symbol) finalize_codec(stream.codec, state.error) return end - elseif mode == :stop - if newmode == :close - state.mode = newmode - return - end elseif mode == :panic throw_panic_error() end diff --git a/test/codecnoop.jl b/test/codecnoop.jl index 3583cbdc..b81cc288 100644 --- a/test/codecnoop.jl +++ b/test/codecnoop.jl @@ -302,11 +302,17 @@ close(stream) stream = NoopStream(IOBuffer("foobar")) + @test position(stream) == 0 @test read(stream, 3) == b"foo" + @test position(stream) == 3 @test read(stream, 3) == b"bar" + @test position(stream) == 6 @test TranscodingStreams.unread(stream, b"baz") === nothing + @test position(stream) == 3 @test read(stream, 3) == b"baz" + @test position(stream) == 6 @test eof(stream) + @test position(stream) == 6 close(stream) stream = NoopStream(IOBuffer("foobar")) @@ -315,9 +321,9 @@ stream = NoopStream(IOBuffer("")) unsafe_write(stream, C_NULL, 0) - @test eof(stream) # write + @test_throws ArgumentError eof(stream) # write close(stream) - @test eof(stream) # close + @test_throws ArgumentError eof(stream) # close @testset "readuntil" begin stream = NoopStream(IOBuffer("")) diff --git a/test/codecquadruple.jl b/test/codecquadruple.jl index 1e17db94..0e6a4673 100644 --- a/test/codecquadruple.jl +++ b/test/codecquadruple.jl @@ -154,16 +154,16 @@ end @testset "seekstart" begin data = Vector(b"abracadabra") source = IOBuffer(data) - seekend(source) + seekstart(source) stream = TranscodingStream(QuadrupleCodec(), source, bufsize=16) @test seekstart(stream) == stream @test position(stream) == 0 @test read(stream, 5) == b"aaaab" @test position(stream) == 5 @test seekstart(stream) == stream - @test_broken position(stream) == 0 + @test position(stream) == 0 @test read(stream, 5) == b"aaaab" - @test_broken position(stream) == 5 + @test position(stream) == 5 end @testset "seekstart doesn't delete data" begin @@ -208,17 +208,17 @@ end sink = IOBuffer() stream = TranscodingStream(QuadrupleCodec(), sink, bufsize=16) write(stream, "x") - @test eof(stream) + @test_throws ArgumentError eof(stream) @test_throws ArgumentError read(stream, UInt8) - @test eof(stream) + @test_throws ArgumentError eof(stream) write(stream, "y") - @test eof(stream) + @test_throws ArgumentError eof(stream) write(stream, TranscodingStreams.TOKEN_END) - @test eof(stream) + @test_throws ArgumentError eof(stream) flush(stream) - @test eof(stream) + @test_throws ArgumentError eof(stream) @test take!(sink) == b"xxxxyyyy" close(stream) - @test eof(stream) + @test_throws ArgumentError eof(stream) end end diff --git a/test/runtests.jl b/test/runtests.jl index 310da504..e240005b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ using TranscodingStreams: marginptr, marginsize, marginmem, readbyte!, writebyte!, #=ismarked,=# mark!, unmark!, reset!, - makemargin!, emptybuffer! + makemargin! @testset "Buffer" begin buf = Buffer(1024) @@ -66,8 +66,6 @@ using TranscodingStreams: writebyte!(buf, 0x99) margin_size = makemargin!(buf, 20) @test margin_size >= 20 - emptybuffer!(buf) - @test makemargin!(buf, 0) === margin_size + 2 end @testset "Memory" begin