Skip to content

Commit 7d32a4a

Browse files
authored
add unread and unsafe_unread (#13)
1 parent b34cf5a commit 7d32a4a

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

docs/src/examples.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,26 @@ using CodecZlib
143143
decompressed = transcode(ZlibDecompression(), b"x\x9cKL*JLNLI\x04R\x00\x19\xf2\x04U")
144144
String(decompressed)
145145
```
146+
147+
Unread data
148+
-----------
149+
150+
`TranscodingStream` supports *unread* operation, which inserts data into the
151+
current reading position. This is useful when you want to peek from the stream.
152+
`TranscodingStreams.unread` and `TranscodingStreams.unsafe_unread` functions are
153+
provided:
154+
```julia
155+
using TranscodingStreams
156+
stream = NoopStream(open("data.txt"))
157+
data1 = read(stream, 8)
158+
TranscodingStreams.unread(stream, data1)
159+
data2 = read(stream, 8)
160+
@assert data1 == data2
161+
```
162+
163+
The unread operaion is different from the write operation in that the unreaded
164+
data are not written to the wrapped stream. The unreaded data are stored in the
165+
internal buffer of a transcoding stream.
166+
167+
Unfortunately, *unwrite* operation is not provided because there is no way to
168+
cancel write operations that are already commited to the wrapped stream.

docs/src/references.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ TranscodingStream(codec::Codec, stream::IO)
1313
transcode(codec::Codec, data::Vector{UInt8})
1414
TranscodingStreams.TOKEN_END
1515
TranscodingStreams.unsafe_read
16+
TranscodingStreams.unread
17+
TranscodingStreams.unsafe_unread
1618
```
1719

1820
Codec

src/buffer.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ function takemarked!(buf::Buffer)
191191
return resize!(buf.data, sz)
192192
end
193193

194+
# Insert data to the current buffer.
195+
function insertdata!(buf::Buffer, data::Ptr{UInt8}, nbytes::Integer)
196+
makemargin!(buf, nbytes)
197+
copy!(buf.data, buf.bufferpos + nbytes, buf.data, buf.bufferpos, buffersize(buf))
198+
unsafe_copy!(bufferptr(buf), data, nbytes)
199+
buf.marginpos += nbytes
200+
return buf
201+
end
202+
194203
# Read as much data as possbile from `input` to the margin of `output`.
195204
# This function will not block if `input` has buffered data.
196205
function readdata!(input::IO, output::Buffer)

src/stream.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,35 @@ function Base.readavailable(stream::TranscodingStream)
244244
return data
245245
end
246246

247+
"""
248+
unread(stream::TranscodingStream, data::Vector{UInt8})
249+
250+
Insert `data` to the current reading position of `stream`.
251+
252+
The next `read(stream, sizeof(data))` call will read data that are just
253+
inserted.
254+
"""
255+
function unread(stream::TranscodingStream, data::Vector{UInt8})
256+
unsafe_unread(stream, pointer(data), sizeof(data))
257+
end
258+
259+
"""
260+
unsafe_unread(stream::TranscodingStream, data::Ptr, nbytes::Integer)
261+
262+
Insert `nbytes` pointed by `data` to the current reading position of `stream`.
263+
264+
The data are copied into the internal buffer and hence `data` can be safely used
265+
after the operation without interfering the stream.
266+
"""
267+
function unsafe_unread(stream::TranscodingStream, data::Ptr, nbytes::Integer)
268+
if nbytes < 0
269+
throw(ArgumentError("negative nbytes"))
270+
end
271+
changestate!(stream, :read)
272+
insertdata!(stream.state.buffer1, convert(Ptr{UInt8}, data), nbytes)
273+
return nothing
274+
end
275+
247276

248277
# Write Functions
249278
# ---------------

test/runtests.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,45 @@ end
198198
read(stream, 3)
199199
write(stream, b"xyz")
200200
end
201+
202+
stream = NoopStream(IOBuffer(""))
203+
@test TranscodingStreams.unread(stream, b"foo") === nothing
204+
@test read(stream, 3) == b"foo"
205+
close(stream)
206+
207+
stream = NoopStream(IOBuffer("foo"))
208+
@test read(stream, 3) == b"foo"
209+
@test TranscodingStreams.unread(stream, b"bar") === nothing
210+
@test read(stream, 3) == b"bar"
211+
close(stream)
212+
213+
stream = NoopStream(IOBuffer("foobar"))
214+
@test TranscodingStreams.unread(stream, b"baz") === nothing
215+
@test read(stream, 3) == b"baz"
216+
@test read(stream, 3) == b"foo"
217+
@test read(stream, 3) == b"bar"
218+
@test eof(stream)
219+
close(stream)
220+
221+
stream = NoopStream(IOBuffer("foobar"))
222+
@test read(stream, 3) == b"foo"
223+
@test TranscodingStreams.unread(stream, b"baz") === nothing
224+
@test read(stream, 3) == b"baz"
225+
@test read(stream, 3) == b"bar"
226+
@test eof(stream)
227+
close(stream)
228+
229+
stream = NoopStream(IOBuffer("foobar"))
230+
@test read(stream, 3) == b"foo"
231+
@test read(stream, 3) == b"bar"
232+
@test TranscodingStreams.unread(stream, b"baz") === nothing
233+
@test read(stream, 3) == b"baz"
234+
@test eof(stream)
235+
close(stream)
236+
237+
stream = NoopStream(IOBuffer("foobar"))
238+
@test_throws ArgumentError TranscodingStreams.unsafe_unread(stream, pointer(b"foo"), -1)
239+
close(stream)
201240
end
202241

203242
# This does not implement necessary interface methods.

0 commit comments

Comments
 (0)