Skip to content

Commit

Permalink
maybefloat & maybeint: parse string to Nullable
Browse files Browse the repository at this point in the history
Introduces following methods that parse a string as the indicated type and return a `Nullable` with the result instead of throwing exception:
- `maybeint{T<:Integer}(::Type{T<:Integer},s::AbstractString)`
- `maybefloat32(s::AbstractString)` and `maybefloat64(s::AbstractString)`

Ref: discussions at JuliaLang#9316, JuliaLang#3631, JuliaLang#5704
  • Loading branch information
tanmaykm committed Dec 29, 2014
1 parent 141bc79 commit baf7ec5
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 41 deletions.
3 changes: 3 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ export
float16,
float32,
float64,
maybefloat32,
maybefloat64,
maybeint,
floor,
frexp,
gamma,
Expand Down
17 changes: 14 additions & 3 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,28 @@ widen(::Type{BigInt}) = BigInt
BigInt(x::BigInt) = x
BigInt(s::AbstractString) = parseint(BigInt,s)

function Base.parseint_nocheck(::Type{BigInt}, s::AbstractString, base::Int)
function parse_bigint(s::AbstractString, base::Int, nothrow::Bool)
_n = Nullable{BigInt}()
s = bytestring(s)
sgn, base, i = Base.parseint_preamble(true,s,base)
if i == 0
nothrow && return _n
error("premature end of integer: $(repr(s))")
end
z = BigInt()
err = ccall((:__gmpz_set_str, :libgmp),
Int32, (Ptr{BigInt}, Ptr{UInt8}, Int32),
&z, convert(Ptr{UInt8},SubString(s,i)), base)
err == 0 || error("invalid big integer: $(repr(s))")
return sgn < 0 ? -z : z
if err != 0
nothrow && return _n
error("invalid big integer: $(repr(s))")
end
Nullable(sgn < 0 ? -z : z)
end

Base.maybeint_internal(::Type{BigInt}, s::AbstractString, base::Int) = parse_bigint(s, base, true)
Base.parseint_nocheck(::Type{BigInt}, s::AbstractString, base::Int) = get(parse_bigint(s, base, false))

function BigInt(x::Union(Clong,Int32))
z = BigInt()
ccall((:__gmpz_set_si, :libgmp), Void, (Ptr{BigInt}, Clong), &z, x)
Expand Down
76 changes: 75 additions & 1 deletion base/string.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1493,27 +1493,33 @@ parseint{T<:Integer}(::Type{T}, c::Char, base::Integer) = convert(T,parseint(c,b
parseint{T<:Integer}(::Type{T}, c::Char) = convert(T,parseint(c))

function parseint_next(s::AbstractString, i::Int=start(s))
done(s,i) && error("premature end of integer: $(repr(s))")
done(s,i) && (return Char(0), 0, 0)
j = i
c, i = next(s,i)
c, i, j
end

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

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

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

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

if base == 0
if c == '0' && !done(s,i)
c, i = next(s,i)
Expand All @@ -1528,9 +1534,71 @@ function parseint_preamble(signed::Bool, s::AbstractString, base::Int)
return sgn, base, j
end

safe_add{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 > (typemax(T) - n2)) : (n1 < (typemin(T) - n2))) ? Nullable{T}() : Nullable{T}(n1 + n2)
safe_mul{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? ((n1 > div(typemax(T),n2)) || (n1 < div(typemin(T),n2))) :
(n2 < -1) ? ((n1 > div(typemin(T),n2)) || (n1 < div(typemax(T),n2))) :
((n2 == -1) && n1 == typemin(T))) ? Nullable{T}() : Nullable{T}(n1 * n2)

#safe_sub{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 < (typemin(T) + n2)) : (n1 > (typemax(T) + n2))) ? Nullable{T}() : Nullable{T}(n1 - n2)
#safe_div{T<:Integer}(n1::T, n2::T) = ((n1 == typemin(T)) && (n2 == T(-1))) ? Nullable{T}() : Nullable{T}(div(n1, n2))
#safe_abs{T<:Integer}(n::T) = (n == typemin(T)) ? Nullable{T}() : abs(n)

function maybeint_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int)
_n = Nullable{T}()
sgn, base, i = parseint_preamble(T<:Signed,s,base)
(i == 0) && return _n
c, i = parseint_next(s,i)
(i == 0) && return _n

base = convert(T,base)
m::T = div(typemax(T)-base+1,base)
n::T = 0
while n <= m
d::T = '0' <= c <= '9' ? c-'0' :
'A' <= c <= 'Z' ? c-'A'+10 :
'a' <= c <= 'z' ? c-'a'+a : base
d < base || return _n
n *= base
n += d
if done(s,i)
n *= sgn
return Nullable{T}(n)
end
c, i = next(s,i)
isspace(c) && break
end
(T <: Signed) && (n *= sgn)
while !isspace(c)
d::T = '0' <= c <= '9' ? c-'0' :
'A' <= c <= 'Z' ? c-'A'+10 :
'a' <= c <= 'z' ? c-'a'+a : base
d < base || return _n
(T <: Signed) && (d *= sgn)

safe_n = safe_mul(n, base)
isnull(safe_n) || (safe_n = safe_add(get(safe_n), d))
isnull(safe_n) && return Nullable{T}()
n = get(safe_n)
done(s,i) && return Nullable{T}(n)
c, i = next(s,i)
end
while !done(s,i)
c, i = next(s,i)
isspace(c) || return _n
end
return Nullable{T}(n)
end
maybeint_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int) =
maybeint_internal(T, s, base, base <= 36 ? 10 : 36)
maybeint{T<:Integer}(::Type{T}, s::AbstractString) = maybeint_internal(T,s,0)

function parseint_nocheck{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int)
sgn, base, i = parseint_preamble(T<:Signed,s,base)
(i == 0) && error("premature end of integer: $(repr(s))")

c, i = parseint_next(s,i)
(i == 0) && error("premature end of integer: $(repr(s))")

base = convert(T,base)
## FIXME: remove 128-bit specific code once 128-bit div doesn't rely on BigInt
m::T = T===UInt128 || T===Int128 ? typemax(T) : div(typemax(T)-base+1,base)
Expand Down Expand Up @@ -1626,6 +1694,12 @@ begin
end
end

maybefloat64(s::AbstractString) = ccall(:jl_maybe_strtod, Nullable{Float64}, (Ptr{UInt8},), s)
maybefloat64(s::SubString) = ccall(:jl_maybe_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof)

maybefloat32(s::AbstractString) = ccall(:jl_maybe_strtof, Nullable{Float32}, (Ptr{UInt8},), s)
maybefloat32(s::SubString) = ccall(:jl_maybe_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof)

float(x::AbstractString) = float64(x)
parsefloat(x::AbstractString) = float64(x)
parsefloat(::Type{Float64}, x::AbstractString) = float64(x)
Expand Down
155 changes: 119 additions & 36 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,13 +644,14 @@ DLLEXPORT void jl_print_int64(JL_STREAM *s, int64_t i)
JL_PRINTF(s, "%lld", i);
}

DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
DLLEXPORT jl_nullable_float64_t jl_maybe_substrtod(char *str, size_t offset, int len)
{
char *p;
errno = 0;
char *bstr = str+offset;
char *pend = bstr+len;
int err = 0;

errno = 0;
if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) {
// confusing data outside substring. must copy.
char *newstr = (char*)malloc(len+1);
Expand All @@ -659,43 +660,86 @@ DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
bstr = newstr;
pend = bstr+len;
}
*out = strtod_c(bstr, &p);
if ((p == bstr) || (p != pend) ||
(errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL)))
err = 1;
double out = strtod_c(bstr, &p);
err = errno;

if (bstr != str+offset)
free(bstr);
return err;

if (err==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) {
errno = ERANGE;
err = 1;
}
else if ((p == bstr) || (p != pend)) {
err = 3;
}
else {
err = 0;
}

return (jl_nullable_float64_t){(uint8_t)err, out};
}

DLLEXPORT int jl_strtod(char *str, double *out)
DLLEXPORT jl_nullable_float64_t jl_maybe_strtod(char *str)
{
char *p;
int err = 0;

errno = 0;
*out = strtod_c(str, &p);
if (p == str ||
(errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL)))
return 1;
while (*p != '\0') {
if (!isspace((unsigned char)*p))
return 1;
p++;
double out = strtod_c(str, &p);

if (errno==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) {
err = 1;
}
else if (p == str) {
err = 3;
}
else {
while (*p != '\0') {
if (!isspace((unsigned char)*p)) {
err = 3;
break;
}
p++;
}
}
return 0;

return (jl_nullable_float64_t){(uint8_t)err, out};
}

DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
{
jl_nullable_float64_t nd = jl_maybe_substrtod(str, offset, len);
if(0 == nd.isnull) {
*out = nd.value;
return 0;
}
return 1;
}

DLLEXPORT int jl_strtod(char *str, double *out)
{
jl_nullable_float64_t nd = jl_maybe_strtod(str);
if(0 == nd.isnull) {
*out = nd.value;
return 0;
}
return 1;
}

// MSVC pre-2013 did not define HUGE_VALF
#ifndef HUGE_VALF
#define HUGE_VALF (1e25f * 1e25f)
#endif

DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
DLLEXPORT jl_nullable_float32_t jl_maybe_substrtof(char *str, size_t offset, int len)
{
char *p;
errno = 0;
char *bstr = str+offset;
char *pend = bstr+len;
int err = 0;

errno = 0;
if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) {
// confusing data outside substring. must copy.
char *newstr = (char*)malloc(len+1);
Expand All @@ -705,37 +749,76 @@ DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
pend = bstr+len;
}
#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_)
*out = (float)strtod_c(bstr, &p);
float out = (float)strtod_c(bstr, &p);
#else
*out = strtof_c(bstr, &p);
float out = strtof_c(bstr, &p);
#endif
err = errno;

if ((p == bstr) || (p != pend) ||
(errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF)))
err = 1;
if (bstr != str+offset)
free(bstr);
return err;

if (err==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) {
errno = ERANGE;
err = 1;
}
else if ((p == bstr) || (p != pend)) {
err = 3;
}
else {
err = 0;
}

return (jl_nullable_float32_t){(uint8_t)err, out};
}

DLLEXPORT int jl_strtof(char *str, float *out)
DLLEXPORT jl_nullable_float32_t jl_maybe_strtof(char *str)
{
char *p;
int err = 0;

errno = 0;
#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_)
*out = (float)strtod_c(str, &p);
float out = (float)strtod_c(str, &p);
#else
*out = strtof_c(str, &p);
float out = strtof_c(str, &p);
#endif
if (p == str ||
(errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF)))
return 1;
while (*p != '\0') {
if (!isspace((unsigned char)*p))
return 1;
p++;
if (errno==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) {
err = 1;
}
else if (p == str) {
err = 3;
}
else {
while (*p != '\0') {
if (!isspace((unsigned char)*p)) {
err = 3;
}
p++;
}
}
return 0;

return (jl_nullable_float32_t){(uint8_t)err, out};
}

DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
{
jl_nullable_float32_t nf = jl_maybe_substrtof(str, offset, len);
if(0 == nf.isnull) {
*out = nf.value;
return 0;
}
return 1;
}

DLLEXPORT int jl_strtof(char *str, float *out)
{
jl_nullable_float32_t nf = jl_maybe_strtof(str);
if(0 == nf.isnull) {
*out = nf.value;
return 0;
}
return 1;
}

// showing --------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/ccall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1257,7 +1257,7 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx)
JL_GC_POP();
if (!sret && lrt == T_void)
return literal_pointer_val((jl_value_t*)jl_nothing);
if (lrt->isStructTy()) {
if (lrt->isStructTy() && !jl_isbits(rt)) {
//fprintf(stderr, "ccall rt: %s -> %s\n", f_name, ((jl_tag_type_t*)rt)->name->name->name);
assert(jl_is_structtype(rt));
Value *strct =
Expand Down
Loading

0 comments on commit baf7ec5

Please sign in to comment.