## IOStream const sizeof_ios_t = int(ccall(:jl_sizeof_ios_t, Int32, ())) type IOStream <: IO handle::Ptr{Void} ios::Array{Uint8,1} name::String mark::Int64 IOStream(name::String, buf::Array{Uint8,1}) = new(pointer(buf), buf, name, -1) end # TODO: delay adding finalizer, e.g. for memio with a small buffer, or # in the case where we takebuf it. function IOStream(name::String, finalize::Bool) buf = zeros(Uint8,sizeof_ios_t) x = IOStream(name, buf) if finalize finalizer(x, close) end return x end IOStream(name::String) = IOStream(name, true) convert(T::Type{Ptr{Void}}, s::IOStream) = convert(T, s.ios) show(io::IO, s::IOStream) = print(io, "IOStream(", s.name, ")") fd(s::IOStream) = int(ccall(:jl_ios_fd, Clong, (Ptr{Void},), s.ios)) stat(s::IOStream) = stat(fd(s)) close(s::IOStream) = ccall(:ios_close, Void, (Ptr{Void},), s.ios) isopen(s::IOStream) = bool(ccall(:ios_isopen, Cint, (Ptr{Void},), s.ios)) function flush(s::IOStream) sigatomic_begin() systemerror("flush", ccall(:ios_flush, Cint, (Ptr{Void},), s.ios) != 0) sigatomic_end() s end iswritable(s::IOStream) = bool(ccall(:ios_get_writable, Cint, (Ptr{Void},), s.ios)) isreadable(s::IOStream) = bool(ccall(:ios_get_readable, Cint, (Ptr{Void},), s.ios)) modestr(s::IO) = modestr(isreadable(s), iswritable(s)) modestr(r::Bool, w::Bool) = r ? (w ? "r+" : "r") : (w ? "w" : error("Neither readable nor writable")) function truncate(s::IOStream, n::Integer) systemerror("truncate", ccall(:ios_trunc, Int32, (Ptr{Void}, Uint), s.ios, n) != 0) return s end function seek(s::IOStream, n::Integer) ret = ccall(:ios_seek, FileOffset, (Ptr{Void}, FileOffset), s.ios, n) systemerror("seek", ret == -1) ret < -1 && error("seek failed") return s end seekstart(s::IO) = seek(s,0) function seekend(s::IOStream) systemerror("seekend", ccall(:ios_seek_end, FileOffset, (Ptr{Void},), s.ios) != 0) return s end function skip(s::IOStream, delta::Integer) ret = ccall(:ios_skip, FileOffset, (Ptr{Void}, FileOffset), s.ios, delta) systemerror("skip", ret == -1) ret < -1 && error("skip failed") return s end function position(s::IOStream) pos = ccall(:ios_pos, FileOffset, (Ptr{Void},), s.ios) systemerror("position", pos == -1) return pos end eof(s::IOStream) = bool(ccall(:jl_ios_eof, Int32, (Ptr{Void},), s.ios)) # For interfacing with C FILE* functions immutable CFILE ptr::Ptr{Void} end function CFILE(s::IO) @unix_only FILEp = ccall(:fdopen, Ptr{Void}, (Cint, Ptr{Uint8}), convert(Cint, fd(s)), modestr(s)) @windows_only FILEp = ccall(:_fdopen, Ptr{Void}, (Cint, Ptr{Uint8}), convert(Cint, fd(s)), modestr(s)) systemerror("fdopen", FILEp == C_NULL) seek(CFILE(FILEp), position(s)) end convert(::Type{CFILE}, s::IO) = CFILE(s) function seek(h::CFILE, offset::Integer) systemerror("fseek", ccall(:fseek, Cint, (Ptr{Void}, Clong, Cint), h.ptr, convert(Clong, offset), int32(0)) != 0) h end position(h::CFILE) = ccall(:ftell, Clong, (Ptr{Void},), h.ptr) ## constructing and opening streams ## # "own" means the descriptor will be closed with the IOStream function fdio(name::String, fd::Integer, own::Bool=false) s = IOStream(name) ccall(:ios_fd, Ptr{Void}, (Ptr{Void}, Clong, Int32, Int32), s.ios, fd, 0, own); return s end fdio(fd::Integer, own::Bool=false) = fdio(string(""), fd, own) function open(fname::String, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool) s = IOStream(string("")) systemerror("opening file $fname", ccall(:ios_file, Ptr{Void}, (Ptr{Uint8}, Ptr{Uint8}, Int32, Int32, Int32, Int32), s.ios, fname, rd, wr, cr, tr) == C_NULL) if ff systemerror("seeking to end of file $fname", ccall(:ios_seek_end, FileOffset, (Ptr{Void},), s.ios) != 0) end return s end open(fname::String) = open(fname, true, false, false, false, false) function open(fname::String, mode::String) mode == "r" ? open(fname, true , false, false, false, false) : mode == "r+" ? open(fname, true , true , false, false, false) : mode == "w" ? open(fname, false, true , true , true , false) : mode == "w+" ? open(fname, true , true , true , true , false) : mode == "a" ? open(fname, false, true , true , false, true ) : mode == "a+" ? open(fname, true , true , true , false, true ) : error("invalid open mode: ", mode) end function open(f::Function, args...) io = open(args...) try f(io) finally close(io) end end ## low-level calls ## write(s::IOStream, b::Uint8) = int(ccall(:ios_putc, Int32, (Uint8, Ptr{Void}), b, s.ios)) function write{T}(s::IOStream, a::Array{T}) if isbits(T) if !iswritable(s) error("attempt to write to a read-only IOStream") end int(ccall(:ios_write, Uint, (Ptr{Void}, Ptr{Void}, Uint), s.ios, a, length(a)*sizeof(T))) else invoke(write, (IO, Array), s, a) end end function write(s::IOStream, p::Ptr, nb::Integer) if !iswritable(s) error("attempt to write to a read-only IOStream") end int(ccall(:ios_write, Uint, (Ptr{Void}, Ptr{Void}, Uint), s.ios, p, nb)) end function write{T,N,A<:Array}(s::IOStream, a::SubArray{T,N,A}) if !isbits(T) || stride(a,1)!=1 return invoke(write, (Any, AbstractArray), s, a) end colsz = size(a,1)*sizeof(T) if N<=1 return write(s, pointer(a, 1), colsz) else cartesianmap((idxs...)->write(s, pointer(a, idxs), colsz), tuple(1, size(a)[2:end]...)) return colsz*trailingsize(a,2) end end # num bytes available without blocking nb_available(s::IOStream) = ccall(:jl_nb_available, Int32, (Ptr{Void},), s.ios) function read(s::IOStream, ::Type{Uint8}) b = ccall(:ios_getc, Int32, (Ptr{Void},), s.ios) if b == -1 throw(EOFError()) end uint8(b) end function read!{T}(s::IOStream, a::Array{T}) if isbits(T) nb = length(a)*sizeof(T) if ccall(:ios_readall, Uint, (Ptr{Void}, Ptr{Void}, Uint), s.ios, a, nb) < nb throw(EOFError()) end else invoke(read!, (IO, Array), s, a) end a end ## text I/O ## function write(s::IOStream, c::Char) if !iswritable(s) error("attempt to write to a read-only IOStream") end int(ccall(:ios_pututf8, Int32, (Ptr{Void}, Char), s.ios, c)) end read(s::IOStream, ::Type{Char}) = ccall(:jl_getutf8, Char, (Ptr{Void},), s.ios) takebuf_string(s::IOStream) = ccall(:jl_takebuf_string, ByteString, (Ptr{Void},), s.ios) takebuf_array(s::IOStream) = ccall(:jl_takebuf_array, Vector{Uint8}, (Ptr{Void},), s.ios) function takebuf_raw(s::IOStream) sz = position(s) buf = ccall(:jl_takebuf_raw, Ptr{Uint8}, (Ptr{Void},), s.ios) return buf, sz end function sprint(size::Integer, f::Function, args...) s = IOBuffer(Array(Uint8,size), true, true) truncate(s,0) f(s, args...) takebuf_string(s) end sprint(f::Function, args...) = sprint(0, f, args...) write(x) = write(STDOUT::IO, x) function readuntil(s::IOStream, delim::Uint8) ccall(:jl_readuntil, Array{Uint8,1}, (Ptr{Void}, Uint8), s.ios, delim) end function readbytes!(s::IOStream, b::Array{Uint8}, nb=length(b)) olb = lb = length(b) nr = 0 while !eof(s) && nr < nb if lb < nr+1 lb = max(65536, (nr+1) * 2) resize!(b, lb) end nr += int(ccall(:ios_readall, Uint, (Ptr{Void}, Ptr{Void}, Uint), s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) end if lb > olb resize!(b, nr) # shrink to just contain input data if was resized end return nr end ## Character streams ## const _chtmp = Array(Char, 1) function peekchar(s::IOStream) if ccall(:ios_peekutf8, Int32, (Ptr{Void}, Ptr{Char}), s, _chtmp) < 0 return char(-1) end return _chtmp[1] end function peek(s::IOStream) ccall(:ios_peekc, Int32, (Ptr{Void},), s) end function skipchars(s::IOStream, pred; linecomment::Char=char(0xffffffff)) ch = peekchar(s); status = int(ch) while status >= 0 && (pred(ch) || ch == linecomment) if ch == linecomment readline(s) else read(s, Char) # advance one character end ch = peekchar(s); status = int(ch) end return s end