Repository: JuliaLang/julia
File: base/parse.jl (Julia)

Code before:
"""
parse(T::Type, str; base = Int)
parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}")

function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer
    a::Int = (base <= 36 ? 10 : 36)
    2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base"))
    d = '0' <= c <= '9' ? c-'0'    :
        'A' <= c <= 'Z' ? c-'A'+10 :
        'a' <= c <= 'z' ? c-'a'+a  : throw(ArgumentError("invalid digit: $(repr(c))"))
    d < base || throw(ArgumentError("invalid base $base digit $(repr(c))"))
    convert(T, d)
end

function parseint_iterate(s::AbstractString, startpos::Int, endpos::Int)
    (0 < startpos <= endpos) || (return Char(0), 0, 0)
    j = startpos
    c, startpos = iterate(s,startpos)::Tuple{Char, Int}
    c, startpos, j
end

function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos::Int, endpos::Int)
    c, i, j = parseint_iterate(s, startpos, endpos)

    while isspace(c)
        c, i, j = parseint_iterate(s,i,endpos)
    end
    (j == 0) && (return 0, 0, 0)

    sgn = 1
    if signed
        if c == '-' || c == '+'
            (c == '-') && (sgn = -1)
            c, i, j = parseint_iterate(s,i,endpos)
        end
    end

    while isspace(c)
        c, i, j = parseint_iterate(s,i,endpos)
    end
    (j == 0) && (return 0, 0, 0)

    if base == 0
        if c == '0' && i <= endpos
            c, i = iterate(s,i)::Tuple{Char, Int}
            base = c=='b' ? 2 : c=='o' ? 8 : c=='x' ? 16 : 10
            if base != 10
                c, i, j = parseint_iterate(s,i,endpos)
            end
        else
            base = 10

Review comment (performance):
In general, I try to avoid second guessing the compiler on inlining heuristics unless I'm very familiar with the code and all its callsites and/or I've measured performance differences that confirm that I know better than the compiler.

This inlining choice seems accurate to me so I'm willing to proceed with the annotation if you think it helps performance.

Code after:
"""
parse(T::Type, str; base = Int)
parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}")

@noinline function _invalid_base(base)
    throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base"))
end

@noinline _invalid_digit(base, char) = throw(ArgumentError("invalid base $base digit $(repr(char))"))

function parse_char(::Type{T}, c::AbstractChar, base::Integer, throw::Bool) where T
    a::UInt8 = (base <= 36 ? 10 : 36)
    (2 <= base <= 62) || _invalid_base(base)
    base = base % UInt8
    cp = codepoint(c)
    cp = cp > 0x7a ? 0xff : cp % UInt8
    d = UInt8('0') ≤ cp ≤ UInt8('9') ? cp - UInt8('0') :
        UInt8('A') ≤ cp ≤ UInt8('Z') ? cp - UInt8('A') + UInt8(10) :
        UInt8('a') ≤ cp ≤ UInt8('z') ? cp - UInt8('a') + a :
        0xff
    d < base || (throw ? _invalid_digit(base, c) : return nothing)
    convert(T, d)::T
end

function parse(::Type{T}, c::AbstractChar; base::Integer=10) where {T <: Integer}
    @inline parse_char(T, c, base, true)
end

function tryparse(::Type{T}, c::AbstractChar; base::Integer=10) where {T <: Integer}
    @inline parse_char(T, c, base, false)
end

# For consistency with parse(t, AbstractString), support a `base` argument only when T<:Integer
parse(::Type{T}, c::AbstractChar) where T = @inline parse_char(T, c, 10, true)
tryparse(::Type{T}, c::AbstractChar) where T = @inline parse_char(T, c, 10, false)

function parseint_iterate(s::AbstractString, startpos::Int, endpos::Int)
    (0 < startpos <= endpos) || (return Char(0), 0, 0)
    j = startpos
    c, startpos = iterate(s,startpos)::Tuple{Char, Int}
    c, startpos, j
end

function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos::Int, endpos::Int)
    c, i, j = parseint_iterate(s, startpos, endpos)

    while isspace(c)
        c, i, j = parseint_iterate(s,i,endpos)
    end
    (j == 0) && (return 0, 0, 0)