Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
857
julia-0.6.2/share/julia/base/util.jl
Normal file
857
julia-0.6.2/share/julia/base/util.jl
Normal file
@@ -0,0 +1,857 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# timing
|
||||
|
||||
# time() in libc.jl
|
||||
|
||||
# high-resolution relative time, in nanoseconds
|
||||
|
||||
"""
|
||||
time_ns()
|
||||
|
||||
Get the time in nanoseconds. The time corresponding to 0 is undefined, and wraps every 5.8 years.
|
||||
"""
|
||||
time_ns() = ccall(:jl_hrtime, UInt64, ())
|
||||
|
||||
# This type must be kept in sync with the C struct in src/gc.h
|
||||
struct GC_Num
|
||||
allocd ::Int64 # GC internal
|
||||
deferred_alloc::Int64 # GC internal
|
||||
freed ::Int64 # GC internal
|
||||
malloc ::UInt64
|
||||
realloc ::UInt64
|
||||
poolalloc ::UInt64
|
||||
bigalloc ::UInt64
|
||||
freecall ::UInt64
|
||||
total_time ::UInt64
|
||||
total_allocd::UInt64 # GC internal
|
||||
since_sweep ::UInt64 # GC internal
|
||||
collect ::Csize_t # GC internal
|
||||
pause ::Cint
|
||||
full_sweep ::Cint
|
||||
end
|
||||
|
||||
gc_num() = ccall(:jl_gc_num, GC_Num, ())
|
||||
|
||||
# This type is to represent differences in the counters, so fields may be negative
|
||||
struct GC_Diff
|
||||
allocd ::Int64 # Bytes allocated
|
||||
malloc ::Int64 # Number of GC aware malloc()
|
||||
realloc ::Int64 # Number of GC aware realloc()
|
||||
poolalloc ::Int64 # Number of pool allocation
|
||||
bigalloc ::Int64 # Number of big (non-pool) allocation
|
||||
freecall ::Int64 # Number of GC aware free()
|
||||
total_time ::Int64 # Time spent in garbage collection
|
||||
pause ::Int64 # Number of GC pauses
|
||||
full_sweep ::Int64 # Number of GC full collection
|
||||
end
|
||||
|
||||
gc_total_bytes(gc_num::GC_Num) =
|
||||
(gc_num.allocd + gc_num.deferred_alloc +
|
||||
Int64(gc_num.collect) + Int64(gc_num.total_allocd))
|
||||
|
||||
function GC_Diff(new::GC_Num, old::GC_Num)
|
||||
# logic from `src/gc.c:jl_gc_total_bytes`
|
||||
old_allocd = gc_total_bytes(old)
|
||||
new_allocd = gc_total_bytes(new)
|
||||
return GC_Diff(new_allocd - old_allocd,
|
||||
Int64(new.malloc - old.malloc),
|
||||
Int64(new.realloc - old.realloc),
|
||||
Int64(new.poolalloc - old.poolalloc),
|
||||
Int64(new.bigalloc - old.bigalloc),
|
||||
Int64(new.freecall - old.freecall),
|
||||
Int64(new.total_time - old.total_time),
|
||||
new.pause - old.pause,
|
||||
new.full_sweep - old.full_sweep)
|
||||
end
|
||||
|
||||
function gc_alloc_count(diff::GC_Diff)
|
||||
diff.malloc + diff.realloc + diff.poolalloc + diff.bigalloc
|
||||
end
|
||||
|
||||
|
||||
# total time spend in garbage collection, in nanoseconds
|
||||
gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ())
|
||||
|
||||
# total number of bytes allocated so far
|
||||
gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ())
|
||||
|
||||
"""
|
||||
tic()
|
||||
|
||||
Set a timer to be read by the next call to [`toc`](@ref) or [`toq`](@ref). The
|
||||
macro call `@time expr` can also be used to time evaluation.
|
||||
|
||||
```julia-repl
|
||||
julia> tic()
|
||||
0x0000c45bc7abac95
|
||||
|
||||
julia> sleep(0.3)
|
||||
|
||||
julia> toc()
|
||||
elapsed time: 0.302745944 seconds
|
||||
0.302745944
|
||||
```
|
||||
"""
|
||||
function tic()
|
||||
t0 = time_ns()
|
||||
task_local_storage(:TIMERS, (t0, get(task_local_storage(), :TIMERS, ())))
|
||||
return t0
|
||||
end
|
||||
|
||||
"""
|
||||
toq()
|
||||
|
||||
Return, but do not print, the time elapsed since the last [`tic`](@ref). The
|
||||
macro calls `@timed expr` and `@elapsed expr` also return evaluation time.
|
||||
|
||||
```julia-repl
|
||||
julia> tic()
|
||||
0x0000c46477a9675d
|
||||
|
||||
julia> sleep(0.3)
|
||||
|
||||
julia> toq()
|
||||
0.302251004
|
||||
```
|
||||
"""
|
||||
function toq()
|
||||
t1 = time_ns()
|
||||
timers = get(task_local_storage(), :TIMERS, ())
|
||||
if timers === ()
|
||||
error("toc() without tic()")
|
||||
end
|
||||
t0 = timers[1]::UInt64
|
||||
task_local_storage(:TIMERS, timers[2])
|
||||
(t1-t0)/1e9
|
||||
end
|
||||
|
||||
"""
|
||||
toc()
|
||||
|
||||
Print and return the time elapsed since the last [`tic`](@ref). The macro call
|
||||
`@time expr` can also be used to time evaluation.
|
||||
|
||||
```julia-repl
|
||||
julia> tic()
|
||||
0x0000c45bc7abac95
|
||||
|
||||
julia> sleep(0.3)
|
||||
|
||||
julia> toc()
|
||||
elapsed time: 0.302745944 seconds
|
||||
0.302745944
|
||||
```
|
||||
"""
|
||||
function toc()
|
||||
t = toq()
|
||||
println("elapsed time: ", t, " seconds")
|
||||
return t
|
||||
end
|
||||
|
||||
# print elapsed time, return expression value
|
||||
const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"]
|
||||
const _cnt_units = ["", " k", " M", " G", " T", " P"]
|
||||
function prettyprint_getunits(value, numunits, factor)
|
||||
if value == 0 || value == 1
|
||||
return (value, 1)
|
||||
end
|
||||
unit = ceil(Int, log(value) / log(factor))
|
||||
unit = min(numunits, unit)
|
||||
number = value/factor^(unit-1)
|
||||
return number, unit
|
||||
end
|
||||
|
||||
function padded_nonzero_print(value,str)
|
||||
if value != 0
|
||||
blanks = " "[1:18-length(str)]
|
||||
println("$str:$blanks$value")
|
||||
end
|
||||
end
|
||||
|
||||
function time_print(elapsedtime, bytes, gctime, allocs)
|
||||
@printf("%10.6f seconds", elapsedtime/1e9)
|
||||
if bytes != 0 || allocs != 0
|
||||
bytes, mb = prettyprint_getunits(bytes, length(_mem_units), Int64(1024))
|
||||
allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000))
|
||||
if ma == 1
|
||||
@printf(" (%d%s allocation%s: ", allocs, _cnt_units[ma], allocs==1 ? "" : "s")
|
||||
else
|
||||
@printf(" (%.2f%s allocations: ", allocs, _cnt_units[ma])
|
||||
end
|
||||
if mb == 1
|
||||
@printf("%d %s%s", bytes, _mem_units[mb], bytes==1 ? "" : "s")
|
||||
else
|
||||
@printf("%.3f %s", bytes, _mem_units[mb])
|
||||
end
|
||||
if gctime > 0
|
||||
@printf(", %.2f%% gc time", 100*gctime/elapsedtime)
|
||||
end
|
||||
print(")")
|
||||
elseif gctime > 0
|
||||
@printf(", %.2f%% gc time", 100*gctime/elapsedtime)
|
||||
end
|
||||
println()
|
||||
end
|
||||
|
||||
function timev_print(elapsedtime, diff::GC_Diff)
|
||||
allocs = gc_alloc_count(diff)
|
||||
time_print(elapsedtime, diff.allocd, diff.total_time, allocs)
|
||||
print("elapsed time (ns): $elapsedtime\n")
|
||||
padded_nonzero_print(diff.total_time, "gc time (ns)")
|
||||
padded_nonzero_print(diff.allocd, "bytes allocated")
|
||||
padded_nonzero_print(diff.poolalloc, "pool allocs")
|
||||
padded_nonzero_print(diff.bigalloc, "non-pool GC allocs")
|
||||
padded_nonzero_print(diff.malloc, "malloc() calls")
|
||||
padded_nonzero_print(diff.realloc, "realloc() calls")
|
||||
padded_nonzero_print(diff.freecall, "free() calls")
|
||||
padded_nonzero_print(diff.pause, "GC pauses")
|
||||
padded_nonzero_print(diff.full_sweep, "full collections")
|
||||
end
|
||||
|
||||
"""
|
||||
@time
|
||||
|
||||
A macro to execute an expression, printing the time it took to execute, the number of
|
||||
allocations, and the total number of bytes its execution caused to be allocated, before
|
||||
returning the value of the expression.
|
||||
|
||||
See also [`@timev`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and
|
||||
[`@allocated`](@ref).
|
||||
|
||||
```julia-repl
|
||||
julia> @time rand(10^6);
|
||||
0.001525 seconds (7 allocations: 7.630 MiB)
|
||||
|
||||
julia> @time begin
|
||||
sleep(0.3)
|
||||
1+1
|
||||
end
|
||||
0.301395 seconds (8 allocations: 336 bytes)
|
||||
```
|
||||
"""
|
||||
macro time(ex)
|
||||
quote
|
||||
local stats = gc_num()
|
||||
local elapsedtime = time_ns()
|
||||
local val = $(esc(ex))
|
||||
elapsedtime = time_ns() - elapsedtime
|
||||
local diff = GC_Diff(gc_num(), stats)
|
||||
time_print(elapsedtime, diff.allocd, diff.total_time,
|
||||
gc_alloc_count(diff))
|
||||
val
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
@timev
|
||||
|
||||
This is a verbose version of the `@time` macro. It first prints the same information as
|
||||
`@time`, then any non-zero memory allocation counters, and then returns the value of the
|
||||
expression.
|
||||
|
||||
See also [`@time`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and
|
||||
[`@allocated`](@ref).
|
||||
|
||||
```julia-repl
|
||||
julia> @timev rand(10^6);
|
||||
0.001006 seconds (7 allocations: 7.630 MiB)
|
||||
elapsed time (ns): 1005567
|
||||
bytes allocated: 8000256
|
||||
pool allocs: 6
|
||||
malloc() calls: 1
|
||||
```
|
||||
"""
|
||||
macro timev(ex)
|
||||
quote
|
||||
local stats = gc_num()
|
||||
local elapsedtime = time_ns()
|
||||
local val = $(esc(ex))
|
||||
elapsedtime = time_ns() - elapsedtime
|
||||
timev_print(elapsedtime, GC_Diff(gc_num(), stats))
|
||||
val
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
@elapsed
|
||||
|
||||
A macro to evaluate an expression, discarding the resulting value, instead returning the
|
||||
number of seconds it took to execute as a floating-point number.
|
||||
|
||||
See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
|
||||
and [`@allocated`](@ref).
|
||||
|
||||
```julia-repl
|
||||
julia> @elapsed sleep(0.3)
|
||||
0.301391426
|
||||
```
|
||||
"""
|
||||
macro elapsed(ex)
|
||||
quote
|
||||
local t0 = time_ns()
|
||||
local val = $(esc(ex))
|
||||
(time_ns()-t0)/1e9
|
||||
end
|
||||
end
|
||||
|
||||
# measure bytes allocated without *most* contamination from compilation
|
||||
# Note: This reports a different value from the @time macros, because
|
||||
# it wraps the call in a function, however, this means that things
|
||||
# like: @allocated y = foo()
|
||||
# will not work correctly, because it will set y in the context of
|
||||
# the local function made by the macro, not the current function
|
||||
"""
|
||||
@allocated
|
||||
|
||||
A macro to evaluate an expression, discarding the resulting value, instead returning the
|
||||
total number of bytes allocated during evaluation of the expression. Note: the expression is
|
||||
evaluated inside a local function, instead of the current context, in order to eliminate the
|
||||
effects of compilation, however, there still may be some allocations due to JIT compilation.
|
||||
This also makes the results inconsistent with the `@time` macros, which do not try to adjust
|
||||
for the effects of compilation.
|
||||
|
||||
See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
|
||||
and [`@elapsed`](@ref).
|
||||
|
||||
```julia-repl
|
||||
julia> @allocated rand(10^6)
|
||||
8000080
|
||||
```
|
||||
"""
|
||||
macro allocated(ex)
|
||||
quote
|
||||
let
|
||||
local f
|
||||
function f()
|
||||
b0 = gc_bytes()
|
||||
$(esc(ex))
|
||||
gc_bytes() - b0
|
||||
end
|
||||
f()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
@timed
|
||||
|
||||
A macro to execute an expression, and return the value of the expression, elapsed time,
|
||||
total bytes allocated, garbage collection time, and an object with various memory allocation
|
||||
counters.
|
||||
|
||||
See also [`@time`](@ref), [`@timev`](@ref), [`@elapsed`](@ref), and
|
||||
[`@allocated`](@ref).
|
||||
|
||||
```julia-repl
|
||||
julia> val, t, bytes, gctime, memallocs = @timed rand(10^6);
|
||||
|
||||
julia> t
|
||||
0.006634834
|
||||
|
||||
julia> bytes
|
||||
8000256
|
||||
|
||||
julia> gctime
|
||||
0.0055765
|
||||
|
||||
julia> fieldnames(typeof(memallocs))
|
||||
9-element Array{Symbol,1}:
|
||||
:allocd
|
||||
:malloc
|
||||
:realloc
|
||||
:poolalloc
|
||||
:bigalloc
|
||||
:freecall
|
||||
:total_time
|
||||
:pause
|
||||
:full_sweep
|
||||
|
||||
julia> memallocs.total_time
|
||||
5576500
|
||||
```
|
||||
"""
|
||||
macro timed(ex)
|
||||
quote
|
||||
local stats = gc_num()
|
||||
local elapsedtime = time_ns()
|
||||
local val = $(esc(ex))
|
||||
elapsedtime = time_ns() - elapsedtime
|
||||
local diff = GC_Diff(gc_num(), stats)
|
||||
val, elapsedtime/1e9, diff.allocd, diff.total_time/1e9, diff
|
||||
end
|
||||
end
|
||||
|
||||
function fftw_vendor()
|
||||
if Base.libfftw_name in ("libmkl_rt", "mkl_rt")
|
||||
return :mkl
|
||||
else
|
||||
return :fftw
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## printing with color ##
|
||||
|
||||
function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false)
|
||||
buf = IOBuffer()
|
||||
have_color && bold && print(buf, text_colors[:bold])
|
||||
have_color && print(buf, get(text_colors, color, color_normal))
|
||||
try f(IOContext(buf, io), args...)
|
||||
finally
|
||||
have_color && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default]))
|
||||
have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold])
|
||||
print(io, String(take!(buf)))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
print_with_color(color::Union{Symbol, Int}, [io], xs...; bold::Bool = false)
|
||||
|
||||
Print `xs` in a color specified as a symbol.
|
||||
|
||||
`color` may take any of the values $(Base.available_text_colors_docstring)
|
||||
or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors.
|
||||
If the keyword `bold` is given as `true`, the result will be printed in bold.
|
||||
"""
|
||||
print_with_color(color::Union{Int, Symbol}, io::IO, msg...; bold::Bool = false) =
|
||||
with_output_color(print, color, io, msg...; bold = bold)
|
||||
print_with_color(color::Union{Int, Symbol}, msg...; bold::Bool = false) =
|
||||
print_with_color(color, STDOUT, msg...; bold = bold)
|
||||
println_with_color(color::Union{Int, Symbol}, io::IO, msg...; bold::Bool = false) =
|
||||
with_output_color(println, color, io, msg...; bold = bold)
|
||||
println_with_color(color::Union{Int, Symbol}, msg...; bold::Bool = false) =
|
||||
println_with_color(color, STDOUT, msg...; bold = bold)
|
||||
|
||||
## warnings and messages ##
|
||||
|
||||
const log_info_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}()
|
||||
const log_warn_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}()
|
||||
const log_error_to = Dict{Tuple{Union{Module,Void},Union{Symbol,Void}},IO}()
|
||||
|
||||
function _redirect(io::IO, log_to::Dict, sf::StackTraces.StackFrame)
|
||||
isnull(sf.linfo) && return io
|
||||
mod = get(sf.linfo).def.module
|
||||
fun = sf.func
|
||||
if haskey(log_to, (mod,fun))
|
||||
return log_to[(mod,fun)]
|
||||
elseif haskey(log_to, (mod,nothing))
|
||||
return log_to[(mod,nothing)]
|
||||
elseif haskey(log_to, (nothing,nothing))
|
||||
return log_to[(nothing,nothing)]
|
||||
else
|
||||
return io
|
||||
end
|
||||
end
|
||||
|
||||
function _redirect(io::IO, log_to::Dict, fun::Symbol)
|
||||
clos = string("#",fun,"#")
|
||||
kw = string("kw##",fun)
|
||||
local sf
|
||||
break_next_frame = false
|
||||
for trace in backtrace()
|
||||
stack::Vector{StackFrame} = StackTraces.lookup(trace)
|
||||
filter!(frame -> !frame.from_c, stack)
|
||||
for frame in stack
|
||||
isnull(frame.linfo) && continue
|
||||
sf = frame
|
||||
break_next_frame && (@goto skip)
|
||||
get(frame.linfo).def.module == Base || continue
|
||||
sff = string(frame.func)
|
||||
if frame.func == fun || startswith(sff, clos) || startswith(sff, kw)
|
||||
break_next_frame = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@label skip
|
||||
_redirect(io, log_to, sf)
|
||||
end
|
||||
|
||||
@inline function redirect(io::IO, log_to::Dict, arg::Union{Symbol,StackTraces.StackFrame})
|
||||
if isempty(log_to)
|
||||
return io
|
||||
else
|
||||
if length(log_to)==1 && haskey(log_to,(nothing,nothing))
|
||||
return log_to[(nothing,nothing)]
|
||||
else
|
||||
return _redirect(io, log_to, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
logging(io [, m [, f]][; kind=:all])
|
||||
logging([; kind=:all])
|
||||
|
||||
Stream output of informational, warning, and/or error messages to `io`,
|
||||
overriding what was otherwise specified. Optionally, divert stream only for
|
||||
module `m`, or specifically function `f` within `m`. `kind` can be `:all` (the
|
||||
default), `:info`, `:warn`, or `:error`. See `Base.log_{info,warn,error}_to`
|
||||
for the current set of redirections. Call `logging` with no arguments (or just
|
||||
the `kind`) to reset everything.
|
||||
"""
|
||||
function logging(io::IO, m::Union{Module,Void}=nothing, f::Union{Symbol,Void}=nothing;
|
||||
kind::Symbol=:all)
|
||||
(kind==:all || kind==:info) && (log_info_to[(m,f)] = io)
|
||||
(kind==:all || kind==:warn) && (log_warn_to[(m,f)] = io)
|
||||
(kind==:all || kind==:error) && (log_error_to[(m,f)] = io)
|
||||
nothing
|
||||
end
|
||||
|
||||
function logging(; kind::Symbol=:all)
|
||||
(kind==:all || kind==:info) && empty!(log_info_to)
|
||||
(kind==:all || kind==:warn) && empty!(log_warn_to)
|
||||
(kind==:all || kind==:error) && empty!(log_error_to)
|
||||
nothing
|
||||
end
|
||||
|
||||
"""
|
||||
info([io, ] msg..., [prefix="INFO: "])
|
||||
|
||||
Display an informational message.
|
||||
Argument `msg` is a string describing the information to be displayed.
|
||||
The `prefix` keyword argument can be used to override the default
|
||||
prepending of `msg`.
|
||||
|
||||
```jldoctest
|
||||
julia> info("hello world")
|
||||
INFO: hello world
|
||||
|
||||
julia> info("hello world"; prefix="MY INFO: ")
|
||||
MY INFO: hello world
|
||||
```
|
||||
|
||||
See also [`logging`](@ref).
|
||||
"""
|
||||
function info(io::IO, msg...; prefix="INFO: ")
|
||||
buf = IOBuffer()
|
||||
iob = redirect(IOContext(buf, io), log_info_to, :info)
|
||||
print_with_color(info_color(), iob, prefix; bold = true)
|
||||
println_with_color(info_color(), iob, chomp(string(msg...)))
|
||||
print(io, String(take!(buf)))
|
||||
return
|
||||
end
|
||||
info(msg...; prefix="INFO: ") = info(STDERR, msg..., prefix=prefix)
|
||||
|
||||
# print a warning only once
|
||||
|
||||
const have_warned = Set()
|
||||
|
||||
warn_once(io::IO, msg...) = warn(io, msg..., once=true)
|
||||
warn_once(msg...) = warn(STDERR, msg..., once=true)
|
||||
|
||||
"""
|
||||
warn([io, ] msg..., [prefix="WARNING: ", once=false, key=nothing, bt=nothing, filename=nothing, lineno::Int=0])
|
||||
|
||||
Display a warning. Argument `msg` is a string describing the warning to be
|
||||
displayed. Set `once` to true and specify a `key` to only display `msg` the
|
||||
first time `warn` is called. If `bt` is not `nothing` a backtrace is displayed.
|
||||
If `filename` is not `nothing` both it and `lineno` are displayed.
|
||||
|
||||
See also [`logging`](@ref).
|
||||
"""
|
||||
function warn(io::IO, msg...;
|
||||
prefix="WARNING: ", once=false, key=nothing, bt=nothing,
|
||||
filename=nothing, lineno::Int=0)
|
||||
str = chomp(string(msg...))
|
||||
if once
|
||||
if key === nothing
|
||||
key = str
|
||||
end
|
||||
(key in have_warned) && return
|
||||
push!(have_warned, key)
|
||||
end
|
||||
buf = IOBuffer()
|
||||
iob = redirect(IOContext(buf, io), log_warn_to, :warn)
|
||||
print_with_color(warn_color(), iob, prefix; bold = true)
|
||||
print_with_color(warn_color(), iob, str)
|
||||
if bt !== nothing
|
||||
show_backtrace(iob, bt)
|
||||
end
|
||||
if filename !== nothing
|
||||
print(iob, "\nwhile loading $filename, in expression starting on line $lineno")
|
||||
end
|
||||
println(iob)
|
||||
print(io, String(take!(buf)))
|
||||
return
|
||||
end
|
||||
|
||||
"""
|
||||
warn(msg)
|
||||
|
||||
Display a warning. Argument `msg` is a string describing the warning to be displayed.
|
||||
|
||||
```jldoctest
|
||||
julia> warn("Beep Beep")
|
||||
WARNING: Beep Beep
|
||||
```
|
||||
"""
|
||||
warn(msg...; kw...) = warn(STDERR, msg...; kw...)
|
||||
|
||||
warn(io::IO, err::Exception; prefix="ERROR: ", kw...) =
|
||||
warn(io, sprint(showerror, err), prefix=prefix; kw...)
|
||||
|
||||
warn(err::Exception; prefix="ERROR: ", kw...) =
|
||||
warn(STDERR, err, prefix=prefix; kw...)
|
||||
|
||||
info(io::IO, err::Exception; prefix="ERROR: ", kw...) =
|
||||
info(io, sprint(showerror, err), prefix=prefix; kw...)
|
||||
|
||||
info(err::Exception; prefix="ERROR: ", kw...) =
|
||||
info(STDERR, err, prefix=prefix; kw...)
|
||||
|
||||
function julia_cmd(julia=joinpath(JULIA_HOME, julia_exename()))
|
||||
opts = JLOptions()
|
||||
cpu_target = unsafe_string(opts.cpu_target)
|
||||
image_file = unsafe_string(opts.image_file)
|
||||
compile = if opts.compile_enabled == 0
|
||||
"no"
|
||||
elseif opts.compile_enabled == 2
|
||||
"all"
|
||||
elseif opts.compile_enabled == 3
|
||||
"min"
|
||||
else
|
||||
"yes"
|
||||
end
|
||||
depwarn = if opts.depwarn == 0
|
||||
"no"
|
||||
elseif opts.depwarn == 2
|
||||
"error"
|
||||
else
|
||||
"yes"
|
||||
end
|
||||
`$julia -C$cpu_target -J$image_file --compile=$compile --depwarn=$depwarn`
|
||||
end
|
||||
|
||||
function julia_exename()
|
||||
if ccall(:jl_is_debugbuild, Cint, ()) == 0
|
||||
return @static is_windows() ? "julia.exe" : "julia"
|
||||
else
|
||||
return @static is_windows() ? "julia-debug.exe" : "julia-debug"
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
securezero!(o)
|
||||
|
||||
`securezero!` fills the memory associated with an object `o` with zeros.
|
||||
Unlike `fill!(o,0)` and similar code, which might be optimized away by
|
||||
the compiler for objects about to be discarded, the `securezero!` function
|
||||
will always be called.
|
||||
"""
|
||||
function securezero! end
|
||||
@noinline securezero!(a::AbstractArray{<:Number}) = fill!(a, 0)
|
||||
securezero!(s::String) = unsafe_securezero!(pointer(s), sizeof(s))
|
||||
@noinline unsafe_securezero!{T}(p::Ptr{T}, len::Integer=1) =
|
||||
ccall(:memset, Ptr{T}, (Ptr{T}, Cint, Csize_t), p, 0, len*sizeof(T))
|
||||
unsafe_securezero!(p::Ptr{Void}, len::Integer=1) = Ptr{Void}(unsafe_securezero!(Ptr{UInt8}(p), len))
|
||||
|
||||
if is_windows()
|
||||
function getpass(prompt::AbstractString)
|
||||
print(prompt)
|
||||
flush(STDOUT)
|
||||
p = Vector{UInt8}(128) # mimic Unix getpass in ignoring more than 128-char passwords
|
||||
# (also avoids any potential memory copies arising from push!)
|
||||
try
|
||||
plen = 0
|
||||
while true
|
||||
c = ccall(:_getch, UInt8, ())
|
||||
if c == 0xff || c == UInt8('\n') || c == UInt8('\r')
|
||||
break # EOF or return
|
||||
elseif c == 0x00 || c == 0xe0
|
||||
ccall(:_getch, UInt8, ()) # ignore function/arrow keys
|
||||
elseif c == UInt8('\b') && plen > 0
|
||||
plen -= 1 # delete last character on backspace
|
||||
elseif !iscntrl(Char(c)) && plen < 128
|
||||
p[plen += 1] = c
|
||||
end
|
||||
end
|
||||
return unsafe_string(pointer(p), plen) # use unsafe_string rather than String(p[1:plen])
|
||||
# to be absolutely certain we never make an extra copy
|
||||
finally
|
||||
securezero!(p)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
else
|
||||
getpass(prompt::AbstractString) = unsafe_string(ccall(:getpass, Cstring, (Cstring,), prompt))
|
||||
end
|
||||
|
||||
# Windows authentication prompt
|
||||
if is_windows()
|
||||
struct CREDUI_INFO
|
||||
cbSize::UInt32
|
||||
parent::Ptr{Void}
|
||||
pszMessageText::Ptr{UInt16}
|
||||
pszCaptionText::Ptr{UInt16}
|
||||
banner::Ptr{Void}
|
||||
end
|
||||
|
||||
const CREDUIWIN_GENERIC = 0x0001
|
||||
const CREDUIWIN_IN_CRED_ONLY = 0x0020
|
||||
const CREDUIWIN_ENUMERATE_CURRENT_USER = 0x0200
|
||||
|
||||
const CRED_PACK_GENERIC_CREDENTIALS = 0x0004
|
||||
|
||||
const ERROR_SUCCESS = 0x0000
|
||||
const ERROR_CANCELLED = 0x04c7
|
||||
|
||||
function winprompt(message, caption, default_username; prompt_username = true)
|
||||
# Step 1: Create an encrypted username/password bundle that will be used to set
|
||||
# the default username (in theory could also provide a default password)
|
||||
credbuf = Array{UInt8,1}(1024)
|
||||
credbufsize = Ref{UInt32}(sizeof(credbuf))
|
||||
succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool,
|
||||
(UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}),
|
||||
CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize)
|
||||
@assert succeeded
|
||||
|
||||
# Step 2: Create the actual dialog
|
||||
# 2.1: Set up the window
|
||||
messageArr = Base.cwstring(message)
|
||||
captionArr = Base.cwstring(caption)
|
||||
pfSave = Ref{Bool}(false)
|
||||
cred = Ref{CREDUI_INFO}(CREDUI_INFO(sizeof(CREDUI_INFO), C_NULL, pointer(messageArr), pointer(captionArr), C_NULL))
|
||||
dwflags = CREDUIWIN_GENERIC | CREDUIWIN_ENUMERATE_CURRENT_USER
|
||||
if !prompt_username
|
||||
# Disable setting anything other than default_username
|
||||
dwflags |= CREDUIWIN_IN_CRED_ONLY
|
||||
end
|
||||
authPackage = Ref{Culong}(0)
|
||||
outbuf_data = Ref{Ptr{Void}}(C_NULL)
|
||||
outbuf_size = Ref{Culong}(0)
|
||||
|
||||
# 2.2: Do the actual request
|
||||
code = ccall((:CredUIPromptForWindowsCredentialsW, "credui.dll"), stdcall, UInt32, (Ptr{CREDUI_INFO}, UInt32, Ptr{Culong},
|
||||
Ptr{Void}, Culong, Ptr{Ptr{Void}}, Ptr{Culong}, Ptr{Bool}, UInt32), cred, 0, authPackage, credbuf, credbufsize[],
|
||||
outbuf_data, outbuf_size, pfSave, dwflags)
|
||||
|
||||
# 2.3: If that failed for any reason other than the user canceling, error out.
|
||||
# If the user canceled, just return a nullable
|
||||
if code == ERROR_CANCELLED
|
||||
return Nullable{Tuple{String,String}}()
|
||||
elseif code != ERROR_SUCCESS
|
||||
error(Base.Libc.FormatMessage(code))
|
||||
end
|
||||
|
||||
# Step 3: Convert encrypted credentials back to plain text
|
||||
passbuf = Array{UInt16,1}(1024)
|
||||
passlen = Ref{UInt32}(length(passbuf))
|
||||
usernamebuf = Array{UInt16,1}(1024)
|
||||
usernamelen = Ref{UInt32}(length(usernamebuf))
|
||||
# Need valid buffers for domain, even though we don't care
|
||||
dummybuf = Array{UInt16,1}(1024)
|
||||
succeeded = ccall((:CredUnPackAuthenticationBufferW, "credui.dll"), Bool,
|
||||
(UInt32, Ptr{Void}, UInt32, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}),
|
||||
0, outbuf_data[], outbuf_size[], usernamebuf, usernamelen, dummybuf, Ref{UInt32}(1024), passbuf, passlen)
|
||||
if !succeeded
|
||||
error(Base.Libc.FormatMessage())
|
||||
end
|
||||
|
||||
# Step 4: Free the encrypted buffer
|
||||
# ccall(:SecureZeroMemory, Ptr{Void}, (Ptr{Void}, Csize_t), outbuf_data[], outbuf_size[]) - not an actual function
|
||||
unsafe_securezero!(outbuf_data[], outbuf_size[])
|
||||
ccall((:CoTaskMemFree, "ole32.dll"), Void, (Ptr{Void},), outbuf_data[])
|
||||
|
||||
# Done.
|
||||
passbuf_ = passbuf[1:passlen[]-1]
|
||||
result = Nullable((String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])),
|
||||
String(transcode(UInt8, passbuf_))))
|
||||
securezero!(passbuf_)
|
||||
securezero!(passbuf)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
"""
|
||||
crc32c(data, crc::UInt32=0x00000000)
|
||||
Compute the CRC-32c checksum of the given `data`, which can be
|
||||
an `Array{UInt8}` or a `String`. Optionally, you can pass
|
||||
a starting `crc` integer to be mixed in with the checksum.
|
||||
(Technically, a little-endian checksum is computed.)
|
||||
"""
|
||||
function crc32c end
|
||||
crc32c(a::Union{Array{UInt8},String}, crc::UInt32=0x00000000) =
|
||||
ccall(:jl_crc32c, UInt32, (UInt32, Ptr{UInt8}, Csize_t), crc, a, sizeof(a))
|
||||
|
||||
"""
|
||||
@kwdef typedef
|
||||
|
||||
This is a helper macro that automatically defines a keyword-based constructor for the type
|
||||
declared in the expression `typedef`, which must be a `struct` or `mutable struct`
|
||||
expression. The default argument is supplied by declaring fields of the form `field::T =
|
||||
default`. If no default is provided then the default is provided by the `kwdef_val(T)`
|
||||
function.
|
||||
|
||||
```julia
|
||||
@kwdef struct Foo
|
||||
a::Cint # implied default Cint(0)
|
||||
b::Cint = 1 # specified default
|
||||
z::Cstring # implied default Cstring(C_NULL)
|
||||
y::Bar # implied default Bar()
|
||||
end
|
||||
```
|
||||
"""
|
||||
macro kwdef(expr)
|
||||
expr = macroexpand(expr) # to expand @static
|
||||
T = expr.args[2]
|
||||
params_ex = Expr(:parameters)
|
||||
call_ex = Expr(:call, T)
|
||||
_kwdef!(expr.args[3], params_ex, call_ex)
|
||||
quote
|
||||
Base.@__doc__($(esc(expr)))
|
||||
$(esc(Expr(:call,T,params_ex))) = $(esc(call_ex))
|
||||
end
|
||||
end
|
||||
|
||||
# @kwdef helper function
|
||||
# mutates arguments inplace
|
||||
function _kwdef!(blk, params_ex, call_ex)
|
||||
for i in eachindex(blk.args)
|
||||
ei = blk.args[i]
|
||||
isa(ei, Expr) || continue
|
||||
if ei.head == :(=)
|
||||
# var::Typ = defexpr
|
||||
dec = ei.args[1] # var::Typ
|
||||
var = dec.args[1] # var
|
||||
def = ei.args[2] # defexpr
|
||||
push!(params_ex.args, Expr(:kw, var, def))
|
||||
push!(call_ex.args, var)
|
||||
blk.args[i] = dec
|
||||
elseif ei.head == :(::)
|
||||
dec = ei # var::Typ
|
||||
var = dec.args[1] # var
|
||||
def = :(Base.kwdef_val($(ei.args[2])))
|
||||
push!(params_ex.args, Expr(:kw, var, def))
|
||||
push!(call_ex.args, dec.args[1])
|
||||
elseif ei.head == :block
|
||||
# can arise with use of @static inside type decl
|
||||
_kwdef!(ei, params_ex, call_ex)
|
||||
end
|
||||
end
|
||||
blk
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
kwdef_val(T)
|
||||
|
||||
The default value for a type for use with the `@kwdef` macro. Returns:
|
||||
|
||||
- null pointer for pointer types (`Ptr{T}`, `Cstring`, `Cwstring`)
|
||||
- zero for integer types
|
||||
- no-argument constructor calls (e.g. `T()`) for all other types
|
||||
"""
|
||||
function kwdef_val end
|
||||
|
||||
kwdef_val(::Type{Ptr{T}}) where {T} = Ptr{T}(C_NULL)
|
||||
kwdef_val(::Type{Cstring}) = Cstring(C_NULL)
|
||||
kwdef_val(::Type{Cwstring}) = Cwstring(C_NULL)
|
||||
|
||||
kwdef_val(::Type{T}) where {T<:Integer} = zero(T)
|
||||
|
||||
kwdef_val(::Type{T}) where {T} = T()
|
||||
Reference in New Issue
Block a user