Skip to content

Commit 9617908

Browse files
authored
Make mark consistent with skip and IOBuffer (#201)
1 parent e81cd34 commit 9617908

File tree

6 files changed

+94
-42
lines changed

6 files changed

+94
-42
lines changed

docs/src/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ TranscodingStreams.unsafe_read
1919
TranscodingStreams.unread
2020
TranscodingStreams.unsafe_unread
2121
Base.position(stream::TranscodingStream)
22+
Base.skip
2223
```
2324

2425
Statistics

ext/TestExt.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,15 @@ function TranscodingStreams.test_chunked_read(Encoder, Decoder)
8888
ok = true
8989
for chunk in chunks
9090
stream = TranscodingStream(Decoder(), buffer, stop_on_end=true)
91-
ok &= hash(read(stream)) == hash(chunk)
91+
ok &= read(stream) == chunk
9292
ok &= eof(stream)
9393
ok &= isreadable(stream)
9494
close(stream)
9595
end
96+
# read without stop_on_end should read the full data.
97+
stream = TranscodingStream(Decoder(), IOBuffer(data))
98+
ok &= read(stream) == reduce(vcat, chunks)
99+
close(stream)
96100
Test.@test ok
97101
end
98102
finalize(encoder)

src/stream.jl

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -229,50 +229,26 @@ end
229229
end
230230
end
231231

232-
function Base.ismarked(stream::TranscodingStream)
232+
function Base.ismarked(stream::TranscodingStream)::Bool
233233
checkmode(stream)
234-
return ismarked(stream.buffer1)
234+
isopen(stream) && ismarked(stream.buffer1)
235235
end
236236

237-
function Base.mark(stream::TranscodingStream)
238-
checkmode(stream)
239-
return mark!(stream.buffer1)
237+
function Base.mark(stream::TranscodingStream)::Int64
238+
ready_to_read!(stream)
239+
mark!(stream.buffer1)
240+
position(stream)
240241
end
241242

242-
function Base.unmark(stream::TranscodingStream)
243+
function Base.unmark(stream::TranscodingStream)::Bool
243244
checkmode(stream)
244-
return unmark!(stream.buffer1)
245+
isopen(stream) && unmark!(stream.buffer1)
245246
end
246247

247-
function Base.reset(stream::TranscodingStream)
248-
checkmode(stream)
249-
return reset!(stream.buffer1)
250-
end
251-
252-
function Base.skip(stream::TranscodingStream, offset::Integer)
253-
checkmode(stream)
254-
if offset < 0
255-
throw(ArgumentError("negative offset"))
256-
end
257-
mode = stream.state.mode
258-
buffer1 = stream.buffer1
259-
skipped = 0
260-
if mode === :read || mode === :stop
261-
while !eof(stream) && buffersize(buffer1) < offset - skipped
262-
n = buffersize(buffer1)
263-
emptybuffer!(buffer1)
264-
skipped += n
265-
end
266-
if eof(stream)
267-
emptybuffer!(buffer1)
268-
else
269-
skipbuffer!(buffer1, offset - skipped)
270-
end
271-
else
272-
# TODO: support skip in write mode
273-
throw(ArgumentError("not in read mode"))
274-
end
275-
return stream
248+
function Base.reset(stream::T) where T<:TranscodingStream
249+
Base.ismarked(stream) || throw(ArgumentError("$T not marked"))
250+
reset!(stream.buffer1)
251+
position(stream)
276252
end
277253

278254
"""
@@ -389,6 +365,32 @@ function Base.readuntil(stream::TranscodingStream, delim::UInt8; keep::Bool=fals
389365
return ret
390366
end
391367

368+
"""
369+
skip(stream::TranscodingStream, offset)
370+
371+
Read bytes from `stream` until `offset` bytes have been read or `eof(stream)` is reached.
372+
373+
Return `stream`, discarding read bytes.
374+
375+
This function will not throw an `EOFError` if `eof(stream)` is reached before
376+
`offset` bytes can be read.
377+
"""
378+
function Base.skip(stream::TranscodingStream, offset::Integer)
379+
if offset < 0
380+
# TODO support negative offset if stream is marked
381+
throw(ArgumentError("negative offset"))
382+
end
383+
ready_to_read!(stream)
384+
buffer1 = stream.buffer1
385+
skipped = 0
386+
while skipped < offset && !eof(stream)
387+
n = min(buffersize(buffer1), offset - skipped)
388+
skipbuffer!(buffer1, n)
389+
skipped += n
390+
end
391+
return stream
392+
end
393+
392394
function Base.unsafe_read(stream::TranscodingStream, output::Ptr{UInt8}, nbytes::UInt)
393395
ready_to_read!(stream)
394396
buffer = stream.buffer1

test/codecdoubleframe.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ DoubleFrameDecoderStream(stream::IO; kwargs...) = TranscodingStream(DoubleFrameD
287287
@testset "reading zero bytes from invalid stream" begin
288288
# This behavior is required to avoid breaking JLD2.jl
289289
# `s` must go into read mode, but not actually call `eof`
290-
for readnone in (io -> read!(io, UInt8[]), io -> read(io, 0))
290+
for readnone in (io -> read!(io, UInt8[]), io -> read(io, 0), io -> skip(io, 0))
291291
for invalid_data in (b"", b"asdf")
292292
s = DoubleFrameDecoderStream(IOBuffer(invalid_data;read=true,write=true))
293293
@test iswritable(s)

test/codecnoop.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,21 @@
5959
data = collect(0x00:0x0f)
6060
stream = TranscodingStream(Noop(), IOBuffer(data))
6161
@test !ismarked(stream)
62-
mark(stream)
62+
@test mark(stream) == 0
6363
@test ismarked(stream)
6464
@test [read(stream, UInt8) for _ in 1:3] == data[1:3]
65-
reset(stream)
65+
@test reset(stream) == 0
66+
@test_throws ArgumentError reset(stream)
6667
@test !ismarked(stream)
6768
@test [read(stream, UInt8) for _ in 1:3] == data[1:3]
68-
mark(stream)
69+
@test mark(stream) == 3
6970
@test ismarked(stream)
70-
unmark(stream)
71+
@test unmark(stream)
7172
@test !ismarked(stream)
73+
@test !unmark(stream)
74+
@test mark(stream) == 3
7275
close(stream)
76+
@test !ismarked(stream)
7377

7478
stream = TranscodingStream(Noop(), IOBuffer(b"foobarbaz"))
7579
@test stream == seek(stream, 2)

test/codecquadruple.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,47 @@ end
110110
end
111111
end
112112

113+
@testset "skip" begin
114+
@testset "position" begin
115+
# make sure position is updated correctly by skip
116+
iob = IOBuffer(repeat("x", 400))
117+
source = IOBuffer(repeat("x", 100))
118+
stream = TranscodingStream(QuadrupleCodec(), source, bufsize=16)
119+
@test position(stream) == position(iob)
120+
for len in 0:10:100
121+
skip(stream, len)
122+
skip(iob, len)
123+
@test position(stream) == position(iob)
124+
end
125+
close(stream)
126+
end
127+
@testset "mark" begin
128+
iob = IOBuffer(repeat("x", 400))
129+
source = IOBuffer(repeat("x", 100))
130+
stream = TranscodingStream(QuadrupleCodec(), source, bufsize=16)
131+
@test position(stream) == position(iob)
132+
@test mark(stream) == mark(iob)
133+
for len in 0:10:100
134+
skip(stream, len)
135+
skip(iob, len)
136+
@test position(stream) == position(iob)
137+
end
138+
@test Base.reset(stream) == Base.reset(iob)
139+
for len in 0:10:100
140+
skip(stream, len)
141+
skip(iob, len)
142+
@test position(stream) == position(iob)
143+
end
144+
@test mark(stream) == mark(iob)
145+
@test Base.reset(stream) == Base.reset(iob)
146+
@test mark(stream) == mark(iob)
147+
close(stream)
148+
close(iob)
149+
@test !ismarked(stream)
150+
@test !ismarked(iob)
151+
end
152+
end
153+
113154
@testset "seekstart" begin
114155
data = Vector(b"abracadabra")
115156
source = IOBuffer(data)

0 commit comments

Comments
 (0)