Skip to content

Commit baf7ec5

Browse files
committed
maybefloat & maybeint: parse string to Nullable
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
1 parent 141bc79 commit baf7ec5

File tree

7 files changed

+245
-41
lines changed

7 files changed

+245
-41
lines changed

base/exports.jl

+3
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ export
360360
float16,
361361
float32,
362362
float64,
363+
maybefloat32,
364+
maybefloat64,
365+
maybeint,
363366
floor,
364367
frexp,
365368
gamma,

base/gmp.jl

+14-3
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,28 @@ widen(::Type{BigInt}) = BigInt
7474
BigInt(x::BigInt) = x
7575
BigInt(s::AbstractString) = parseint(BigInt,s)
7676

77-
function Base.parseint_nocheck(::Type{BigInt}, s::AbstractString, base::Int)
77+
function parse_bigint(s::AbstractString, base::Int, nothrow::Bool)
78+
_n = Nullable{BigInt}()
7879
s = bytestring(s)
7980
sgn, base, i = Base.parseint_preamble(true,s,base)
81+
if i == 0
82+
nothrow && return _n
83+
error("premature end of integer: $(repr(s))")
84+
end
8085
z = BigInt()
8186
err = ccall((:__gmpz_set_str, :libgmp),
8287
Int32, (Ptr{BigInt}, Ptr{UInt8}, Int32),
8388
&z, convert(Ptr{UInt8},SubString(s,i)), base)
84-
err == 0 || error("invalid big integer: $(repr(s))")
85-
return sgn < 0 ? -z : z
89+
if err != 0
90+
nothrow && return _n
91+
error("invalid big integer: $(repr(s))")
92+
end
93+
Nullable(sgn < 0 ? -z : z)
8694
end
8795

96+
Base.maybeint_internal(::Type{BigInt}, s::AbstractString, base::Int) = parse_bigint(s, base, true)
97+
Base.parseint_nocheck(::Type{BigInt}, s::AbstractString, base::Int) = get(parse_bigint(s, base, false))
98+
8899
function BigInt(x::Union(Clong,Int32))
89100
z = BigInt()
90101
ccall((:__gmpz_set_si, :libgmp), Void, (Ptr{BigInt}, Clong), &z, x)

base/string.jl

+75-1
Original file line numberDiff line numberDiff line change
@@ -1493,27 +1493,33 @@ parseint{T<:Integer}(::Type{T}, c::Char, base::Integer) = convert(T,parseint(c,b
14931493
parseint{T<:Integer}(::Type{T}, c::Char) = convert(T,parseint(c))
14941494

14951495
function parseint_next(s::AbstractString, i::Int=start(s))
1496-
done(s,i) && error("premature end of integer: $(repr(s))")
1496+
done(s,i) && (return Char(0), 0, 0)
14971497
j = i
14981498
c, i = next(s,i)
14991499
c, i, j
15001500
end
15011501

15021502
function parseint_preamble(signed::Bool, s::AbstractString, base::Int)
15031503
c, i, j = parseint_next(s)
1504+
15041505
while isspace(c)
15051506
c, i, j = parseint_next(s,i)
15061507
end
1508+
(j == 0) && (return 0, 0, 0)
1509+
15071510
sgn = 1
15081511
if signed
15091512
if c == '-' || c == '+'
15101513
(c == '-') && (sgn = -1)
15111514
c, i, j = parseint_next(s,i)
15121515
end
15131516
end
1517+
15141518
while isspace(c)
15151519
c, i, j = parseint_next(s,i)
15161520
end
1521+
(j == 0) && (return 0, 0, 0)
1522+
15171523
if base == 0
15181524
if c == '0' && !done(s,i)
15191525
c, i = next(s,i)
@@ -1528,9 +1534,71 @@ function parseint_preamble(signed::Bool, s::AbstractString, base::Int)
15281534
return sgn, base, j
15291535
end
15301536

1537+
safe_add{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 > (typemax(T) - n2)) : (n1 < (typemin(T) - n2))) ? Nullable{T}() : Nullable{T}(n1 + n2)
1538+
safe_mul{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? ((n1 > div(typemax(T),n2)) || (n1 < div(typemin(T),n2))) :
1539+
(n2 < -1) ? ((n1 > div(typemin(T),n2)) || (n1 < div(typemax(T),n2))) :
1540+
((n2 == -1) && n1 == typemin(T))) ? Nullable{T}() : Nullable{T}(n1 * n2)
1541+
1542+
#safe_sub{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 < (typemin(T) + n2)) : (n1 > (typemax(T) + n2))) ? Nullable{T}() : Nullable{T}(n1 - n2)
1543+
#safe_div{T<:Integer}(n1::T, n2::T) = ((n1 == typemin(T)) && (n2 == T(-1))) ? Nullable{T}() : Nullable{T}(div(n1, n2))
1544+
#safe_abs{T<:Integer}(n::T) = (n == typemin(T)) ? Nullable{T}() : abs(n)
1545+
1546+
function maybeint_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int)
1547+
_n = Nullable{T}()
1548+
sgn, base, i = parseint_preamble(T<:Signed,s,base)
1549+
(i == 0) && return _n
1550+
c, i = parseint_next(s,i)
1551+
(i == 0) && return _n
1552+
1553+
base = convert(T,base)
1554+
m::T = div(typemax(T)-base+1,base)
1555+
n::T = 0
1556+
while n <= m
1557+
d::T = '0' <= c <= '9' ? c-'0' :
1558+
'A' <= c <= 'Z' ? c-'A'+10 :
1559+
'a' <= c <= 'z' ? c-'a'+a : base
1560+
d < base || return _n
1561+
n *= base
1562+
n += d
1563+
if done(s,i)
1564+
n *= sgn
1565+
return Nullable{T}(n)
1566+
end
1567+
c, i = next(s,i)
1568+
isspace(c) && break
1569+
end
1570+
(T <: Signed) && (n *= sgn)
1571+
while !isspace(c)
1572+
d::T = '0' <= c <= '9' ? c-'0' :
1573+
'A' <= c <= 'Z' ? c-'A'+10 :
1574+
'a' <= c <= 'z' ? c-'a'+a : base
1575+
d < base || return _n
1576+
(T <: Signed) && (d *= sgn)
1577+
1578+
safe_n = safe_mul(n, base)
1579+
isnull(safe_n) || (safe_n = safe_add(get(safe_n), d))
1580+
isnull(safe_n) && return Nullable{T}()
1581+
n = get(safe_n)
1582+
done(s,i) && return Nullable{T}(n)
1583+
c, i = next(s,i)
1584+
end
1585+
while !done(s,i)
1586+
c, i = next(s,i)
1587+
isspace(c) || return _n
1588+
end
1589+
return Nullable{T}(n)
1590+
end
1591+
maybeint_internal{T<:Integer}(::Type{T}, s::AbstractString, base::Int) =
1592+
maybeint_internal(T, s, base, base <= 36 ? 10 : 36)
1593+
maybeint{T<:Integer}(::Type{T}, s::AbstractString) = maybeint_internal(T,s,0)
1594+
15311595
function parseint_nocheck{T<:Integer}(::Type{T}, s::AbstractString, base::Int, a::Int)
15321596
sgn, base, i = parseint_preamble(T<:Signed,s,base)
1597+
(i == 0) && error("premature end of integer: $(repr(s))")
1598+
15331599
c, i = parseint_next(s,i)
1600+
(i == 0) && error("premature end of integer: $(repr(s))")
1601+
15341602
base = convert(T,base)
15351603
## FIXME: remove 128-bit specific code once 128-bit div doesn't rely on BigInt
15361604
m::T = T===UInt128 || T===Int128 ? typemax(T) : div(typemax(T)-base+1,base)
@@ -1626,6 +1694,12 @@ begin
16261694
end
16271695
end
16281696

1697+
maybefloat64(s::AbstractString) = ccall(:jl_maybe_strtod, Nullable{Float64}, (Ptr{UInt8},), s)
1698+
maybefloat64(s::SubString) = ccall(:jl_maybe_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof)
1699+
1700+
maybefloat32(s::AbstractString) = ccall(:jl_maybe_strtof, Nullable{Float32}, (Ptr{UInt8},), s)
1701+
maybefloat32(s::SubString) = ccall(:jl_maybe_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Cint), s.string, s.offset, s.endof)
1702+
16291703
float(x::AbstractString) = float64(x)
16301704
parsefloat(x::AbstractString) = float64(x)
16311705
parsefloat(::Type{Float64}, x::AbstractString) = float64(x)

src/builtins.c

+119-36
Original file line numberDiff line numberDiff line change
@@ -644,13 +644,14 @@ DLLEXPORT void jl_print_int64(JL_STREAM *s, int64_t i)
644644
JL_PRINTF(s, "%lld", i);
645645
}
646646

647-
DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
647+
DLLEXPORT jl_nullable_float64_t jl_maybe_substrtod(char *str, size_t offset, int len)
648648
{
649649
char *p;
650-
errno = 0;
651650
char *bstr = str+offset;
652651
char *pend = bstr+len;
653652
int err = 0;
653+
654+
errno = 0;
654655
if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) {
655656
// confusing data outside substring. must copy.
656657
char *newstr = (char*)malloc(len+1);
@@ -659,43 +660,86 @@ DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
659660
bstr = newstr;
660661
pend = bstr+len;
661662
}
662-
*out = strtod_c(bstr, &p);
663-
if ((p == bstr) || (p != pend) ||
664-
(errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL)))
665-
err = 1;
663+
double out = strtod_c(bstr, &p);
664+
err = errno;
665+
666666
if (bstr != str+offset)
667667
free(bstr);
668-
return err;
668+
669+
if (err==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) {
670+
errno = ERANGE;
671+
err = 1;
672+
}
673+
else if ((p == bstr) || (p != pend)) {
674+
err = 3;
675+
}
676+
else {
677+
err = 0;
678+
}
679+
680+
return (jl_nullable_float64_t){(uint8_t)err, out};
669681
}
670682

671-
DLLEXPORT int jl_strtod(char *str, double *out)
683+
DLLEXPORT jl_nullable_float64_t jl_maybe_strtod(char *str)
672684
{
673685
char *p;
686+
int err = 0;
687+
674688
errno = 0;
675-
*out = strtod_c(str, &p);
676-
if (p == str ||
677-
(errno==ERANGE && (*out==0 || *out==HUGE_VAL || *out==-HUGE_VAL)))
678-
return 1;
679-
while (*p != '\0') {
680-
if (!isspace((unsigned char)*p))
681-
return 1;
682-
p++;
689+
double out = strtod_c(str, &p);
690+
691+
if (errno==ERANGE && (out==0 || out==HUGE_VAL || out==-HUGE_VAL)) {
692+
err = 1;
693+
}
694+
else if (p == str) {
695+
err = 3;
696+
}
697+
else {
698+
while (*p != '\0') {
699+
if (!isspace((unsigned char)*p)) {
700+
err = 3;
701+
break;
702+
}
703+
p++;
704+
}
683705
}
684-
return 0;
706+
707+
return (jl_nullable_float64_t){(uint8_t)err, out};
708+
}
709+
710+
DLLEXPORT int jl_substrtod(char *str, size_t offset, int len, double *out)
711+
{
712+
jl_nullable_float64_t nd = jl_maybe_substrtod(str, offset, len);
713+
if(0 == nd.isnull) {
714+
*out = nd.value;
715+
return 0;
716+
}
717+
return 1;
718+
}
719+
720+
DLLEXPORT int jl_strtod(char *str, double *out)
721+
{
722+
jl_nullable_float64_t nd = jl_maybe_strtod(str);
723+
if(0 == nd.isnull) {
724+
*out = nd.value;
725+
return 0;
726+
}
727+
return 1;
685728
}
686729

687730
// MSVC pre-2013 did not define HUGE_VALF
688731
#ifndef HUGE_VALF
689732
#define HUGE_VALF (1e25f * 1e25f)
690733
#endif
691734

692-
DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
735+
DLLEXPORT jl_nullable_float32_t jl_maybe_substrtof(char *str, size_t offset, int len)
693736
{
694737
char *p;
695-
errno = 0;
696738
char *bstr = str+offset;
697739
char *pend = bstr+len;
698740
int err = 0;
741+
742+
errno = 0;
699743
if (!(*pend == '\0' || isspace((unsigned char)*pend) || *pend == ',')) {
700744
// confusing data outside substring. must copy.
701745
char *newstr = (char*)malloc(len+1);
@@ -705,37 +749,76 @@ DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
705749
pend = bstr+len;
706750
}
707751
#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_)
708-
*out = (float)strtod_c(bstr, &p);
752+
float out = (float)strtod_c(bstr, &p);
709753
#else
710-
*out = strtof_c(bstr, &p);
754+
float out = strtof_c(bstr, &p);
711755
#endif
756+
err = errno;
712757

713-
if ((p == bstr) || (p != pend) ||
714-
(errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF)))
715-
err = 1;
716758
if (bstr != str+offset)
717759
free(bstr);
718-
return err;
760+
761+
if (err==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) {
762+
errno = ERANGE;
763+
err = 1;
764+
}
765+
else if ((p == bstr) || (p != pend)) {
766+
err = 3;
767+
}
768+
else {
769+
err = 0;
770+
}
771+
772+
return (jl_nullable_float32_t){(uint8_t)err, out};
719773
}
720774

721-
DLLEXPORT int jl_strtof(char *str, float *out)
775+
DLLEXPORT jl_nullable_float32_t jl_maybe_strtof(char *str)
722776
{
723777
char *p;
778+
int err = 0;
779+
724780
errno = 0;
725781
#if defined(_OS_WINDOWS_) && !defined(_COMPILER_MINGW_)
726-
*out = (float)strtod_c(str, &p);
782+
float out = (float)strtod_c(str, &p);
727783
#else
728-
*out = strtof_c(str, &p);
784+
float out = strtof_c(str, &p);
729785
#endif
730-
if (p == str ||
731-
(errno==ERANGE && (*out==0 || *out==HUGE_VALF || *out==-HUGE_VALF)))
732-
return 1;
733-
while (*p != '\0') {
734-
if (!isspace((unsigned char)*p))
735-
return 1;
736-
p++;
786+
if (errno==ERANGE && (out==0 || out==HUGE_VALF || out==-HUGE_VALF)) {
787+
err = 1;
788+
}
789+
else if (p == str) {
790+
err = 3;
791+
}
792+
else {
793+
while (*p != '\0') {
794+
if (!isspace((unsigned char)*p)) {
795+
err = 3;
796+
}
797+
p++;
798+
}
737799
}
738-
return 0;
800+
801+
return (jl_nullable_float32_t){(uint8_t)err, out};
802+
}
803+
804+
DLLEXPORT int jl_substrtof(char *str, int offset, int len, float *out)
805+
{
806+
jl_nullable_float32_t nf = jl_maybe_substrtof(str, offset, len);
807+
if(0 == nf.isnull) {
808+
*out = nf.value;
809+
return 0;
810+
}
811+
return 1;
812+
}
813+
814+
DLLEXPORT int jl_strtof(char *str, float *out)
815+
{
816+
jl_nullable_float32_t nf = jl_maybe_strtof(str);
817+
if(0 == nf.isnull) {
818+
*out = nf.value;
819+
return 0;
820+
}
821+
return 1;
739822
}
740823

741824
// showing --------------------------------------------------------------------

src/ccall.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx)
12571257
JL_GC_POP();
12581258
if (!sret && lrt == T_void)
12591259
return literal_pointer_val((jl_value_t*)jl_nothing);
1260-
if (lrt->isStructTy()) {
1260+
if (lrt->isStructTy() && !jl_isbits(rt)) {
12611261
//fprintf(stderr, "ccall rt: %s -> %s\n", f_name, ((jl_tag_type_t*)rt)->name->name->name);
12621262
assert(jl_is_structtype(rt));
12631263
Value *strct =

0 commit comments

Comments
 (0)