Skip to content

Commit

Permalink
Add readeach function for iterating streams (#36150)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Bauman <mbauman@juliacomputing.com>
  • Loading branch information
non-Jedi and mbauman authored Jul 23, 2020
1 parent 34b2ae5 commit 3a86b7d
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 18 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ New library functions

* New function `Base.kron!` and corresponding overloads for various matrix types for performing Kronecker product in-place. ([#31069]).
* New function `Base.Threads.foreach(f, channel::Channel)` for multithreaded `Channel` consumption. ([#34543]).
* New function `Base.readeach(io, T)` for iteratively performing `read(io, T)`. ([#36150])
* `Iterators.map` is added. It provides another syntax `Iterators.map(f, iterators...)`
for writing `(f(args...) for args in zip(iterators...))`, i.e. a lazy `map` ([#34352]).
* New function `sincospi` for simultaneously computing `sinpi(x)` and `cospi(x)` more
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ export
close,
countlines,
eachline,
readeach,
eof,
fd,
fdio,
Expand Down
46 changes: 38 additions & 8 deletions base/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,7 @@ function readuntil(s::IO, delim::AbstractChar; keep::Bool=false)
return readuntil_string(s, delim % UInt8, keep)
end
out = IOBuffer()
while !eof(s)
c = read(s, Char)
for c in readeach(s, Char)
if c == delim
keep && write(out, c)
break
Expand All @@ -786,8 +785,7 @@ end

function readuntil(s::IO, delim::T; keep::Bool=false) where T
out = (T === UInt8 ? StringVector(0) : Vector{T}())
while !eof(s)
c = read(s, T)
for c in readeach(s, T)
if c == delim
keep && push!(out, c)
break
Expand Down Expand Up @@ -823,8 +821,7 @@ function readuntil_vector!(io::IO, target::AbstractVector{T}, keep::Bool, out) w
max_pos = 1 # array-offset in cache
local cache # will be lazy initialized when needed
output! = (isa(out, IO) ? write : push!)
while !eof(io)
c = read(io, T)
for c in readeach(io, T)
# Backtrack until the next target character matches what was found
while true
c1 = target[pos + first]
Expand Down Expand Up @@ -1022,6 +1019,40 @@ eltype(::Type{<:EachLine}) = String

IteratorSize(::Type{<:EachLine}) = SizeUnknown()

struct ReadEachIterator{T, IOT <: IO}
stream::IOT
end

"""
readeach(io::IO, T)
Return an iterable object yielding [`read(io, T)`](@ref).
See also: [`skipchars`](@ref), [`eachline`](@ref), [`readuntil`](@ref)
!!! compat "Julia 1.6"
`readeach` requires Julia 1.6 or later.
# Examples
```jldoctest
julia> io = IOBuffer("JuliaLang is a GitHub organization.\\n It has many members.\\n");
julia> for c in readeach(io, Char)
c == '\\n' && break
print(c)
end
JuliaLang is a GitHub organization.
```
"""
readeach(stream::IOT, T::Type) where IOT<:IO = ReadEachIterator{T,IOT}(stream)

iterate(itr::ReadEachIterator{T}, state=nothing) where T =
eof(itr.stream) ? nothing : (read(itr.stream, T), nothing)

eltype(::Type{ReadEachIterator{T}}) where T = T

IteratorSize(::Type{<:ReadEachIterator}) = SizeUnknown()

# IOStream Marking
# Note that these functions expect that io.mark exists for
# the concrete IO type. This may not be true for IO types
Expand Down Expand Up @@ -1106,8 +1137,7 @@ julia> String(readavailable(buf))
```
"""
function skipchars(predicate, io::IO; linecomment=nothing)
while !eof(io)
c = read(io, Char)
for c in readeach(io, Char)
if c === linecomment
readline(io)
elseif !predicate(c)
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/io-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Base.read!
Base.readbytes!
Base.unsafe_read
Base.unsafe_write
Base.readeach
Base.peek
Base.position
Base.seek
Expand Down
6 changes: 2 additions & 4 deletions stdlib/Markdown/src/Common/block.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ function paragraph(stream::IO, md::MD)
push!(md, p)
skipwhitespace(stream)
prev_char = '\n'
while !eof(stream)
char = read(stream, Char)
for char in readeach(stream, Char)
if char == '\n' || char == '\r'
char == '\r' && !eof(stream) && peek(stream, Char) == '\n' && read(stream, Char)
if prev_char == '\\'
Expand Down Expand Up @@ -339,8 +338,7 @@ end
function horizontalrule(stream::IO, block::MD)
withstream(stream) do
n, rule = 0, ' '
while !eof(stream)
char = read(stream, Char)
for char in readeach(stream, Char)
char == '\n' && break
isspace(char) && continue
if n==0 || char==rule
Expand Down
3 changes: 1 addition & 2 deletions stdlib/Markdown/src/GitHub/GitHub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ function github_paragraph(stream::IO, md::MD)
buffer = IOBuffer()
p = Paragraph()
push!(md, p)
while !eof(stream)
char = read(stream, Char)
for char in readeach(stream, Char)
if char == '\n'
eof(stream) && break
if blankline(stream) || parse(stream, md, breaking = true)
Expand Down
6 changes: 2 additions & 4 deletions stdlib/Markdown/src/parse/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ Skip any leading blank lines. Returns the number skipped.
function skipblank(io::IO)
start = position(io)
i = 0
while !eof(io)
c = read(io, Char)
for c in readeach(io, Char)
c == '\n' && (start = position(io); i+=1; continue)
c == '\r' && (start = position(io); i+=1; continue)
c in whitespace || break
Expand Down Expand Up @@ -183,8 +182,7 @@ function parse_inline_wrapper(stream::IO, delimiter::AbstractString; rep = false
!eof(stream) && peek(stream, Char) in whitespace && return nothing

buffer = IOBuffer()
while !eof(stream)
char = read(stream, Char)
for char in readeach(stream, Char)
write(buffer, char)
if !(char in whitespace || char == '\n' || char in delimiter) && startswith(stream, delimiter^n)
trailing = 0
Expand Down
6 changes: 6 additions & 0 deletions test/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@ for (name, f) in l

cleanup()

verbose && println("$name readeach...")
@test collect(readeach(io(), Char)) == Vector{Char}(text)
@test collect(readeach(io(), UInt8)) == Vector{UInt8}(text)

cleanup()

verbose && println("$name countlines...")
@test countlines(io()) == countlines(IOBuffer(text))

Expand Down

0 comments on commit 3a86b7d

Please sign in to comment.