Skip to content

Commit 8fdabed

Browse files
authored
Fix interaction between unread and mark (#208)
1 parent 8f7cbb6 commit 8fdabed

File tree

2 files changed

+83
-43
lines changed

2 files changed

+83
-43
lines changed

src/buffer.jl

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# position 1 markpos bufferpos marginpos lastindex(data)
1515
#
1616
# `markpos` is positive iff there are marked data; otherwise it is set to zero.
17-
# `markpos` ≤ `bufferpos` ≤ `marginpos` must hold whenever possible.
17+
# `markpos` ≤ `marginpos` and `bufferpos` ≤ `marginpos` must hold.
1818

1919
mutable struct Buffer
2020
# data and positions (see above)
@@ -87,6 +87,7 @@ end
8787

8888
function reset!(buf::Buffer)
8989
@assert buf.markpos > 0
90+
@assert buf.markpos buf.marginpos
9091
buf.bufferpos = buf.markpos
9192
buf.markpos = 0
9293
return buf.bufferpos
@@ -119,7 +120,8 @@ end
119120

120121
# Remove all buffered data.
121122
function emptybuffer!(buf::Buffer)
122-
buf.marginpos = buf.bufferpos
123+
buf.markpos = 0
124+
buf.bufferpos = buf.marginpos = 1
123125
return buf
124126
end
125127

@@ -133,16 +135,14 @@ function makemargin!(buf::Buffer, minsize::Integer; eager::Bool = false)
133135
if marginsize(buf) < minsize || eager
134136
# datapos refer to the leftmost position of data that must not be
135137
# discarded. We can left-shift to discard all data before this
136-
if buf.markpos == 0
138+
datapos = if iszero(buf.markpos)
137139
# If data is not marked we must not discard buffered (nonconsumed) data
138-
datapos = buf.bufferpos
139-
datasize = buffersize(buf)
140+
buf.bufferpos
140141
else
141-
# Else, we must not consume marked data
142-
# (Since markpos ≤ bufferpos, we do not consume buffered data either)
143-
datapos = buf.markpos
144-
datasize = buf.marginpos - buf.markpos
142+
# Else, we must not consume marked or buffered data
143+
min(buf.markpos, buf.bufferpos)
145144
end
145+
datasize = buf.marginpos - datapos
146146
# Shift data left in buffer to make space for new data
147147
copyto!(buf.data, 1, buf.data, datapos, datasize)
148148
shift = datapos - 1
@@ -204,9 +204,21 @@ end
204204
# Insert data to the current buffer.
205205
function insertdata!(buf::Buffer, data::Ptr{UInt8}, nbytes::Integer)
206206
makemargin!(buf, nbytes)
207-
copyto!(buf.data, buf.bufferpos + nbytes, buf.data, buf.bufferpos, buffersize(buf))
207+
datapos = if iszero(buf.markpos)
208+
# If data is not marked we must not discard buffered (nonconsumed) data
209+
buf.bufferpos
210+
else
211+
# Else, we must not consume marked or buffered data
212+
min(buf.markpos, buf.bufferpos)
213+
end
214+
datasize = buf.marginpos - datapos
215+
copyto!(buf.data, datapos + nbytes, buf.data, datapos, datasize)
208216
GC.@preserve buf unsafe_copyto!(bufferptr(buf), data, nbytes)
209217
supplied!(buf, nbytes)
218+
if !iszero(buf.markpos)
219+
buf.markpos += nbytes
220+
end
221+
@assert buf.markpos buf.marginpos
210222
return buf
211223
end
212224

test/codecnoop.jl

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -274,44 +274,72 @@
274274
@test !iswritable(stream)
275275
end
276276

277-
stream = NoopStream(IOBuffer(""))
278-
@test TranscodingStreams.unread(stream, b"foo") === nothing
279-
@test read(stream, 3) == b"foo"
280-
close(stream)
277+
@testset "unread" begin
278+
stream = NoopStream(IOBuffer(""))
279+
@test TranscodingStreams.unread(stream, b"foo") === nothing
280+
@test read(stream, 3) == b"foo"
281+
close(stream)
281282

282-
stream = NoopStream(IOBuffer("foo"))
283-
@test read(stream, 3) == b"foo"
284-
@test TranscodingStreams.unread(stream, b"bar") === nothing
285-
@test read(stream, 3) == b"bar"
286-
close(stream)
283+
stream = NoopStream(IOBuffer("foo"))
284+
@test read(stream, 3) == b"foo"
285+
@test TranscodingStreams.unread(stream, b"bar") === nothing
286+
@test read(stream, 3) == b"bar"
287+
close(stream)
287288

288-
stream = NoopStream(IOBuffer("foobar"))
289-
@test TranscodingStreams.unread(stream, b"baz") === nothing
290-
@test read(stream, 3) == b"baz"
291-
@test read(stream, 3) == b"foo"
292-
@test read(stream, 3) == b"bar"
293-
@test eof(stream)
294-
close(stream)
289+
stream = NoopStream(IOBuffer("foobar"))
290+
@test TranscodingStreams.unread(stream, b"baz") === nothing
291+
@test read(stream, 3) == b"baz"
292+
@test read(stream, 3) == b"foo"
293+
@test read(stream, 3) == b"bar"
294+
@test eof(stream)
295+
close(stream)
295296

296-
stream = NoopStream(IOBuffer("foobar"))
297-
@test read(stream, 3) == b"foo"
298-
@test TranscodingStreams.unread(stream, b"baz") === nothing
299-
@test read(stream, 3) == b"baz"
300-
@test read(stream, 3) == b"bar"
301-
@test eof(stream)
302-
close(stream)
297+
stream = NoopStream(IOBuffer("foobar"))
298+
@test read(stream, 3) == b"foo"
299+
@test TranscodingStreams.unread(stream, b"baz") === nothing
300+
@test read(stream, 3) == b"baz"
301+
@test read(stream, 3) == b"bar"
302+
@test eof(stream)
303+
close(stream)
303304

304-
stream = NoopStream(IOBuffer("foobar"))
305-
@test read(stream, 3) == b"foo"
306-
@test read(stream, 3) == b"bar"
307-
@test TranscodingStreams.unread(stream, b"baz") === nothing
308-
@test read(stream, 3) == b"baz"
309-
@test eof(stream)
310-
close(stream)
305+
stream = NoopStream(IOBuffer("foobar"))
306+
@test read(stream, 3) == b"foo"
307+
@test read(stream, 3) == b"bar"
308+
@test TranscodingStreams.unread(stream, b"baz") === nothing
309+
@test read(stream, 3) == b"baz"
310+
@test eof(stream)
311+
close(stream)
311312

312-
stream = NoopStream(IOBuffer("foobar"))
313-
@test_throws ArgumentError TranscodingStreams.unsafe_unread(stream, pointer(b"foo"), -1)
314-
close(stream)
313+
for bufsize in (1, 2, 3, 4, 100)
314+
for n in (1, 100)
315+
stream = NoopStream(IOBuffer("foo"^n*"bar"^n); bufsize)
316+
@test mark(stream) == 0
317+
@test read(stream, 3n) == codeunits("foo"^n)
318+
@test read(stream, 3n) == codeunits("bar"^n)
319+
TranscodingStreams.unread(stream, codeunits("baz"^n))
320+
@test reset(stream) == 0
321+
@test read(stream, 3n) == codeunits("foo"^n)
322+
@test read(stream, 3n) == codeunits("baz"^n)
323+
@test eof(stream)
324+
close(stream)
325+
end
326+
end
327+
328+
# unread before mark
329+
stream = NoopStream(IOBuffer("foobar"); bufsize=16)
330+
@test read(stream, String) == "foobar"
331+
mark(stream)
332+
for i in 1:100
333+
TranscodingStreams.unread(stream, b"foo")
334+
end
335+
@test read(stream, String) == "foo"^100
336+
@test reset(stream) == 6
337+
@test eof(stream)
338+
339+
stream = NoopStream(IOBuffer("foobar"))
340+
@test_throws ArgumentError TranscodingStreams.unsafe_unread(stream, pointer(b"foo"), -1)
341+
close(stream)
342+
end
315343

316344
stream = NoopStream(IOBuffer(""))
317345
unsafe_write(stream, C_NULL, 0)

0 commit comments

Comments
 (0)