-
Notifications
You must be signed in to change notification settings - Fork 183
/
IOExtras.jl
127 lines (106 loc) · 2.96 KB
/
IOExtras.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
IOExtras
This module defines extensions to the `Base.IO` interface to support:
- `startwrite`, `closewrite`, `startread` and `closeread` for streams
with transactional semantics.
"""
module IOExtras
using Sockets
using MbedTLS: SSLContext, MbedException
using OpenSSL: SSLStream
export bytes, isbytes, nbytes, nobytes,
startwrite, closewrite, startread, closeread, readuntil,
tcpsocket, localport, safe_getpeername
"""
bytes(x)
If `x` is "castable" to an `AbstractVector{UInt8}`, then an
`AbstractVector{UInt8}` is returned; otherwise `x` is returned.
"""
function bytes end
bytes(s::AbstractVector{UInt8}) = s
bytes(s::AbstractString) = codeunits(s)
bytes(x) = x
"""whether `x` is "castable" to an `AbstractVector{UInt8}`; i.e. you can call `bytes(x)` if `isbytes(x)` === true"""
isbytes(x) = x isa AbstractVector{UInt8} || x isa AbstractString
"""
nbytes(x) -> Int
Length in bytes of `x` if `x` is `isbytes(x)`.
"""
function nbytes end
nbytes(x) = nothing
nbytes(x::AbstractVector{UInt8}) = length(x)
nbytes(x::AbstractString) = sizeof(x)
nbytes(x::Vector{T}) where T <: AbstractString = sum(sizeof, x)
nbytes(x::Vector{T}) where T <: AbstractVector{UInt8} = sum(length, x)
nbytes(x::IOBuffer) = bytesavailable(x)
nbytes(x::Vector{IOBuffer}) = sum(bytesavailable, x)
_doc = """
startwrite(::IO)
closewrite(::IO)
startread(::IO)
closeread(::IO)
Signal start/end of write or read operations.
"""
@static if isdefined(Base, :startwrite)
"$_doc"
Base.startwrite(io) = nothing
else
"$_doc"
startwrite(io) = nothing
end
@static if isdefined(Base, :closewrite)
"$_doc"
Base.closewrite(io) = nothing
else
"$_doc"
closewrite(io) = nothing
end
@static if isdefined(Base, :startread)
"$_doc"
Base.startread(io) = nothing
else
"$_doc"
startread(io) = nothing
end
@static if isdefined(Base, :closeread)
"$_doc"
Base.closeread(io) = nothing
else
"$_doc"
closeread(io) = nothing
end
tcpsocket(io::SSLContext)::TCPSocket = io.bio
tcpsocket(io::SSLStream)::TCPSocket = io.io
tcpsocket(io::TCPSocket)::TCPSocket = io
localport(io) = try !isopen(tcpsocket(io)) ? 0 :
Sockets.getsockname(tcpsocket(io))[2]
catch
0
end
function safe_getpeername(io)
try
if isopen(tcpsocket(io))
return Sockets.getpeername(tcpsocket(io))
end
catch
end
return IPv4(0), UInt16(0)
end
const nobytes = view(UInt8[], 1:0)
readuntil(args...) = Base.readuntil(args...)
"""
Read from an `IO` stream until `find_delimiter(bytes)` returns non-zero.
Return view of bytes up to the delimiter.
"""
function readuntil(buf::IOBuffer,
find_delimiter::F #= Vector{UInt8} -> Int =#
) where {F <: Function}
l = find_delimiter(view(buf.data, buf.ptr:buf.size))
if l == 0
return nobytes
end
bytes = buf.data[buf.ptr:buf.ptr + l - 1]
buf.ptr += l
return bytes
end
end