|
Packit |
7d6a7d |
################################################################################
|
|
Packit |
7d6a7d |
##
|
|
Packit |
7d6a7d |
## Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
|
|
Packit |
7d6a7d |
## Version 2.x, Copyright (C) 2001, Paul Marquess.
|
|
Packit |
7d6a7d |
## Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
|
|
Packit |
7d6a7d |
##
|
|
Packit |
7d6a7d |
## This program is free software; you can redistribute it and/or
|
|
Packit |
7d6a7d |
## modify it under the same terms as Perl itself.
|
|
Packit |
7d6a7d |
##
|
|
Packit |
7d6a7d |
################################################################################
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
=provides
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
grok_hex
|
|
Packit |
7d6a7d |
grok_oct
|
|
Packit |
7d6a7d |
grok_bin
|
|
Packit |
7d6a7d |
grok_numeric_radix
|
|
Packit |
7d6a7d |
grok_number
|
|
Packit |
7d6a7d |
__UNDEFINED__
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
=implementation
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
__UNDEFINED__ IN_PERL_COMPILETIME (PL_curcop == &PL_compiling)
|
|
Packit |
7d6a7d |
__UNDEFINED__ IN_LOCALE_RUNTIME (PL_curcop->op_private & HINT_LOCALE)
|
|
Packit |
7d6a7d |
__UNDEFINED__ IN_LOCALE_COMPILETIME (PL_hints & HINT_LOCALE)
|
|
Packit |
7d6a7d |
__UNDEFINED__ IN_LOCALE (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME)
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_IN_UV 0x01
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_GREATER_THAN_UV_MAX 0x02
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_NOT_INT 0x04
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_NEG 0x08
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_INFINITY 0x10
|
|
Packit |
7d6a7d |
__UNDEFINED__ IS_NUMBER_NAN 0x20
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
__UNDEFINED__ GROK_NUMERIC_RADIX(sp, send) grok_numeric_radix(sp, send)
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
__UNDEFINED__ PERL_SCAN_GREATER_THAN_UV_MAX 0x02
|
|
Packit |
7d6a7d |
__UNDEFINED__ PERL_SCAN_SILENT_ILLDIGIT 0x04
|
|
Packit |
7d6a7d |
__UNDEFINED__ PERL_SCAN_ALLOW_UNDERSCORES 0x01
|
|
Packit |
7d6a7d |
__UNDEFINED__ PERL_SCAN_DISALLOW_PREFIX 0x02
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#ifndef grok_numeric_radix
|
|
Packit |
7d6a7d |
#if { NEED grok_numeric_radix }
|
|
Packit |
7d6a7d |
bool
|
|
Packit |
7d6a7d |
grok_numeric_radix(pTHX_ const char **sp, const char *send)
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
#ifdef USE_LOCALE_NUMERIC
|
|
Packit |
7d6a7d |
#ifdef PL_numeric_radix_sv
|
|
Packit |
7d6a7d |
if (PL_numeric_radix_sv && IN_LOCALE) {
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
char* radix = SvPV(PL_numeric_radix_sv, len);
|
|
Packit |
7d6a7d |
if (*sp + len <= send && memEQ(*sp, radix, len)) {
|
|
Packit |
7d6a7d |
*sp += len;
|
|
Packit |
7d6a7d |
return TRUE;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#else
|
|
Packit |
7d6a7d |
/* older perls don't have PL_numeric_radix_sv so the radix
|
|
Packit |
7d6a7d |
* must manually be requested from locale.h
|
|
Packit |
7d6a7d |
*/
|
|
Packit |
7d6a7d |
#include <locale.h>
|
|
Packit |
7d6a7d |
dTHR; /* needed for older threaded perls */
|
|
Packit |
7d6a7d |
struct lconv *lc = localeconv();
|
|
Packit |
7d6a7d |
char *radix = lc->decimal_point;
|
|
Packit |
7d6a7d |
if (radix && IN_LOCALE) {
|
|
Packit |
7d6a7d |
STRLEN len = strlen(radix);
|
|
Packit |
7d6a7d |
if (*sp + len <= send && memEQ(*sp, radix, len)) {
|
|
Packit |
7d6a7d |
*sp += len;
|
|
Packit |
7d6a7d |
return TRUE;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif /* USE_LOCALE_NUMERIC */
|
|
Packit |
7d6a7d |
/* always try "." if numeric radix didn't match because
|
|
Packit |
7d6a7d |
* we may have data from different locales mixed */
|
|
Packit |
7d6a7d |
if (*sp < send && **sp == '.') {
|
|
Packit |
7d6a7d |
++*sp;
|
|
Packit |
7d6a7d |
return TRUE;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
return FALSE;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#ifndef grok_number
|
|
Packit |
7d6a7d |
#if { NEED grok_number }
|
|
Packit |
7d6a7d |
int
|
|
Packit |
7d6a7d |
grok_number(pTHX_ const char *pv, STRLEN len, UV *valuep)
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
const char *s = pv;
|
|
Packit |
7d6a7d |
const char *send = pv + len;
|
|
Packit |
7d6a7d |
const UV max_div_10 = UV_MAX / 10;
|
|
Packit |
7d6a7d |
const char max_mod_10 = UV_MAX % 10;
|
|
Packit |
7d6a7d |
int numtype = 0;
|
|
Packit |
7d6a7d |
int sawinf = 0;
|
|
Packit |
7d6a7d |
int sawnan = 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
while (s < send && isSPACE(*s))
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
if (s == send) {
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
} else if (*s == '-') {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
numtype = IS_NUMBER_NEG;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else if (*s == '+')
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if (s == send)
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
/* next must be digit or the radix separator or beginning of infinity */
|
|
Packit |
7d6a7d |
if (isDIGIT(*s)) {
|
|
Packit |
7d6a7d |
/* UVs are at least 32 bits, so the first 9 decimal digits cannot
|
|
Packit |
7d6a7d |
overflow. */
|
|
Packit |
7d6a7d |
UV value = *s - '0';
|
|
Packit |
7d6a7d |
/* This construction seems to be more optimiser friendly.
|
|
Packit |
7d6a7d |
(without it gcc does the isDIGIT test and the *s - '0' separately)
|
|
Packit |
7d6a7d |
With it gcc on arm is managing 6 instructions (6 cycles) per digit.
|
|
Packit |
7d6a7d |
In theory the optimiser could deduce how far to unroll the loop
|
|
Packit |
7d6a7d |
before checking for overflow. */
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
int digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send) {
|
|
Packit |
7d6a7d |
/* Now got 9 digits, so need to check
|
|
Packit |
7d6a7d |
each time for overflow. */
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
while (digit >= 0 && digit <= 9
|
|
Packit |
7d6a7d |
&& (value < max_div_10
|
|
Packit |
7d6a7d |
|| (value == max_div_10
|
|
Packit |
7d6a7d |
&& digit <= max_mod_10))) {
|
|
Packit |
7d6a7d |
value = value * 10 + digit;
|
|
Packit |
7d6a7d |
if (++s < send)
|
|
Packit |
7d6a7d |
digit = *s - '0';
|
|
Packit |
7d6a7d |
else
|
|
Packit |
7d6a7d |
break;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 9
|
|
Packit |
7d6a7d |
&& (s < send)) {
|
|
Packit |
7d6a7d |
/* value overflowed.
|
|
Packit |
7d6a7d |
skip the remaining digits, don't
|
|
Packit |
7d6a7d |
worry about setting *valuep. */
|
|
Packit |
7d6a7d |
do {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
} while (s < send && isDIGIT(*s));
|
|
Packit |
7d6a7d |
numtype |=
|
|
Packit |
7d6a7d |
IS_NUMBER_GREATER_THAN_UV_MAX;
|
|
Packit |
7d6a7d |
goto skip_value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_IN_UV;
|
|
Packit |
7d6a7d |
if (valuep)
|
|
Packit |
7d6a7d |
*valuep = value;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
skip_value:
|
|
Packit |
7d6a7d |
if (GROK_NUMERIC_RADIX(&s, send)) {
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_NOT_INT;
|
|
Packit |
7d6a7d |
while (s < send && isDIGIT(*s)) /* optional digits after the radix */
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else if (GROK_NUMERIC_RADIX(&s, send)) {
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */
|
|
Packit |
7d6a7d |
/* no digits before the radix means we need digits after it */
|
|
Packit |
7d6a7d |
if (s < send && isDIGIT(*s)) {
|
|
Packit |
7d6a7d |
do {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
} while (s < send && isDIGIT(*s));
|
|
Packit |
7d6a7d |
if (valuep) {
|
|
Packit |
7d6a7d |
/* integer approximation is valid - it's 0. */
|
|
Packit |
7d6a7d |
*valuep = 0;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
} else if (*s == 'I' || *s == 'i') {
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'F' && *s != 'f')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s < send && (*s == 'I' || *s == 'i')) {
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'I' && *s != 'i')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'T' && *s != 't')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0;
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
sawinf = 1;
|
|
Packit |
7d6a7d |
} else if (*s == 'N' || *s == 'n') {
|
|
Packit |
7d6a7d |
/* XXX TODO: There are signaling NaNs and quiet NaNs. */
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'A' && *s != 'a')) return 0;
|
|
Packit |
7d6a7d |
s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
sawnan = 1;
|
|
Packit |
7d6a7d |
} else
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if (sawinf) {
|
|
Packit |
7d6a7d |
numtype &= IS_NUMBER_NEG; /* Keep track of sign */
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT;
|
|
Packit |
7d6a7d |
} else if (sawnan) {
|
|
Packit |
7d6a7d |
numtype &= IS_NUMBER_NEG; /* Keep track of sign */
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT;
|
|
Packit |
7d6a7d |
} else if (s < send) {
|
|
Packit |
7d6a7d |
/* we can have an optional exponent part */
|
|
Packit |
7d6a7d |
if (*s == 'e' || *s == 'E') {
|
|
Packit |
7d6a7d |
/* The only flag we keep is sign. Blow away any "it's UV" */
|
|
Packit |
7d6a7d |
numtype &= IS_NUMBER_NEG;
|
|
Packit |
7d6a7d |
numtype |= IS_NUMBER_NOT_INT;
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
if (s < send && (*s == '-' || *s == '+'))
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
if (s < send && isDIGIT(*s)) {
|
|
Packit |
7d6a7d |
do {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
} while (s < send && isDIGIT(*s));
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
while (s < send && isSPACE(*s))
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
if (s >= send)
|
|
Packit |
7d6a7d |
return numtype;
|
|
Packit |
7d6a7d |
if (len == 10 && memEQ(pv, "0 but true", 10)) {
|
|
Packit |
7d6a7d |
if (valuep)
|
|
Packit |
7d6a7d |
*valuep = 0;
|
|
Packit |
7d6a7d |
return IS_NUMBER_IN_UV;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
return 0;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
/*
|
|
Packit |
7d6a7d |
* The grok_* routines have been modified to use warn() instead of
|
|
Packit |
7d6a7d |
* Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit,
|
|
Packit |
7d6a7d |
* which is why the stack variable has been renamed to 'xdigit'.
|
|
Packit |
7d6a7d |
*/
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#ifndef grok_bin
|
|
Packit |
7d6a7d |
#if { NEED grok_bin }
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_bin(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
const char *s = start;
|
|
Packit |
7d6a7d |
STRLEN len = *len_p;
|
|
Packit |
7d6a7d |
UV value = 0;
|
|
Packit |
7d6a7d |
NV value_nv = 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
const UV max_div_2 = UV_MAX / 2;
|
|
Packit |
7d6a7d |
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
|
|
Packit |
7d6a7d |
bool overflowed = FALSE;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
|
|
Packit |
7d6a7d |
/* strip off leading b or 0b.
|
|
Packit |
7d6a7d |
for compatibility silently suffer "b" and "0b" as valid binary
|
|
Packit |
7d6a7d |
numbers. */
|
|
Packit |
7d6a7d |
if (len >= 1) {
|
|
Packit |
7d6a7d |
if (s[0] == 'b') {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
len--;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else if (len >= 2 && s[0] == '0' && s[1] == 'b') {
|
|
Packit |
7d6a7d |
s+=2;
|
|
Packit |
7d6a7d |
len-=2;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
for (; len-- && *s; s++) {
|
|
Packit |
7d6a7d |
char bit = *s;
|
|
Packit |
7d6a7d |
if (bit == '0' || bit == '1') {
|
|
Packit |
7d6a7d |
/* Write it in this wonky order with a goto to attempt to get the
|
|
Packit |
7d6a7d |
compiler to make the common case integer-only loop pretty tight.
|
|
Packit |
7d6a7d |
With gcc seems to be much straighter code than old scan_bin. */
|
|
Packit |
7d6a7d |
redo:
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
if (value <= max_div_2) {
|
|
Packit |
7d6a7d |
value = (value << 1) | (bit - '0');
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
/* Bah. We're just overflowed. */
|
|
Packit |
7d6a7d |
warn("Integer overflow in binary number");
|
|
Packit |
7d6a7d |
overflowed = TRUE;
|
|
Packit |
7d6a7d |
value_nv = (NV) value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
value_nv *= 2.0;
|
|
Packit |
7d6a7d |
/* If an NV has not enough bits in its mantissa to
|
|
Packit |
7d6a7d |
* represent a UV this summing of small low-order numbers
|
|
Packit |
7d6a7d |
* is a waste of time (because the NV cannot preserve
|
|
Packit |
7d6a7d |
* the low-order bits anyway): we could just remember when
|
|
Packit |
7d6a7d |
* did we overflow and in the end just multiply value_nv by the
|
|
Packit |
7d6a7d |
* right amount. */
|
|
Packit |
7d6a7d |
value_nv += (NV)(bit - '0');
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (bit == '_' && len && allow_underscores && (bit = s[1])
|
|
Packit |
7d6a7d |
&& (bit == '0' || bit == '1'))
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
--len;
|
|
Packit |
7d6a7d |
++s;
|
|
Packit |
7d6a7d |
goto redo;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
|
|
Packit |
7d6a7d |
warn("Illegal binary digit '%c' ignored", *s);
|
|
Packit |
7d6a7d |
break;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if ( ( overflowed && value_nv > 4294967295.0)
|
|
Packit |
7d6a7d |
#if UVSIZE > 4
|
|
Packit |
7d6a7d |
|| (!overflowed && value > 0xffffffff )
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
) {
|
|
Packit |
7d6a7d |
warn("Binary number > 0b11111111111111111111111111111111 non-portable");
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*len_p = s - start;
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
*flags = 0;
|
|
Packit |
7d6a7d |
return value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
|
|
Packit |
7d6a7d |
if (result)
|
|
Packit |
7d6a7d |
*result = value_nv;
|
|
Packit |
7d6a7d |
return UV_MAX;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#ifndef grok_hex
|
|
Packit |
7d6a7d |
#if { NEED grok_hex }
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_hex(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
const char *s = start;
|
|
Packit |
7d6a7d |
STRLEN len = *len_p;
|
|
Packit |
7d6a7d |
UV value = 0;
|
|
Packit |
7d6a7d |
NV value_nv = 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
const UV max_div_16 = UV_MAX / 16;
|
|
Packit |
7d6a7d |
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
|
|
Packit |
7d6a7d |
bool overflowed = FALSE;
|
|
Packit |
7d6a7d |
const char *xdigit;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
|
|
Packit |
7d6a7d |
/* strip off leading x or 0x.
|
|
Packit |
7d6a7d |
for compatibility silently suffer "x" and "0x" as valid hex numbers.
|
|
Packit |
7d6a7d |
*/
|
|
Packit |
7d6a7d |
if (len >= 1) {
|
|
Packit |
7d6a7d |
if (s[0] == 'x') {
|
|
Packit |
7d6a7d |
s++;
|
|
Packit |
7d6a7d |
len--;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
else if (len >= 2 && s[0] == '0' && s[1] == 'x') {
|
|
Packit |
7d6a7d |
s+=2;
|
|
Packit |
7d6a7d |
len-=2;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
for (; len-- && *s; s++) {
|
|
Packit |
7d6a7d |
xdigit = strchr((char *) PL_hexdigit, *s);
|
|
Packit |
7d6a7d |
if (xdigit) {
|
|
Packit |
7d6a7d |
/* Write it in this wonky order with a goto to attempt to get the
|
|
Packit |
7d6a7d |
compiler to make the common case integer-only loop pretty tight.
|
|
Packit |
7d6a7d |
With gcc seems to be much straighter code than old scan_hex. */
|
|
Packit |
7d6a7d |
redo:
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
if (value <= max_div_16) {
|
|
Packit |
7d6a7d |
value = (value << 4) | ((xdigit - PL_hexdigit) & 15);
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
warn("Integer overflow in hexadecimal number");
|
|
Packit |
7d6a7d |
overflowed = TRUE;
|
|
Packit |
7d6a7d |
value_nv = (NV) value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
value_nv *= 16.0;
|
|
Packit |
7d6a7d |
/* If an NV has not enough bits in its mantissa to
|
|
Packit |
7d6a7d |
* represent a UV this summing of small low-order numbers
|
|
Packit |
7d6a7d |
* is a waste of time (because the NV cannot preserve
|
|
Packit |
7d6a7d |
* the low-order bits anyway): we could just remember when
|
|
Packit |
7d6a7d |
* did we overflow and in the end just multiply value_nv by the
|
|
Packit |
7d6a7d |
* right amount of 16-tuples. */
|
|
Packit |
7d6a7d |
value_nv += (NV)((xdigit - PL_hexdigit) & 15);
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (*s == '_' && len && allow_underscores && s[1]
|
|
Packit |
7d6a7d |
&& (xdigit = strchr((char *) PL_hexdigit, s[1])))
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
--len;
|
|
Packit |
7d6a7d |
++s;
|
|
Packit |
7d6a7d |
goto redo;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
|
|
Packit |
7d6a7d |
warn("Illegal hexadecimal digit '%c' ignored", *s);
|
|
Packit |
7d6a7d |
break;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if ( ( overflowed && value_nv > 4294967295.0)
|
|
Packit |
7d6a7d |
#if UVSIZE > 4
|
|
Packit |
7d6a7d |
|| (!overflowed && value > 0xffffffff )
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
) {
|
|
Packit |
7d6a7d |
warn("Hexadecimal number > 0xffffffff non-portable");
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*len_p = s - start;
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
*flags = 0;
|
|
Packit |
7d6a7d |
return value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
|
|
Packit |
7d6a7d |
if (result)
|
|
Packit |
7d6a7d |
*result = value_nv;
|
|
Packit |
7d6a7d |
return UV_MAX;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#ifndef grok_oct
|
|
Packit |
7d6a7d |
#if { NEED grok_oct }
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_oct(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *result)
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
const char *s = start;
|
|
Packit |
7d6a7d |
STRLEN len = *len_p;
|
|
Packit |
7d6a7d |
UV value = 0;
|
|
Packit |
7d6a7d |
NV value_nv = 0;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
const UV max_div_8 = UV_MAX / 8;
|
|
Packit |
7d6a7d |
bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
|
|
Packit |
7d6a7d |
bool overflowed = FALSE;
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
for (; len-- && *s; s++) {
|
|
Packit |
7d6a7d |
/* gcc 2.95 optimiser not smart enough to figure that this subtraction
|
|
Packit |
7d6a7d |
out front allows slicker code. */
|
|
Packit |
7d6a7d |
int digit = *s - '0';
|
|
Packit |
7d6a7d |
if (digit >= 0 && digit <= 7) {
|
|
Packit |
7d6a7d |
/* Write it in this wonky order with a goto to attempt to get the
|
|
Packit |
7d6a7d |
compiler to make the common case integer-only loop pretty tight.
|
|
Packit |
7d6a7d |
*/
|
|
Packit |
7d6a7d |
redo:
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
if (value <= max_div_8) {
|
|
Packit |
7d6a7d |
value = (value << 3) | digit;
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
/* Bah. We're just overflowed. */
|
|
Packit |
7d6a7d |
warn("Integer overflow in octal number");
|
|
Packit |
7d6a7d |
overflowed = TRUE;
|
|
Packit |
7d6a7d |
value_nv = (NV) value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
value_nv *= 8.0;
|
|
Packit |
7d6a7d |
/* If an NV has not enough bits in its mantissa to
|
|
Packit |
7d6a7d |
* represent a UV this summing of small low-order numbers
|
|
Packit |
7d6a7d |
* is a waste of time (because the NV cannot preserve
|
|
Packit |
7d6a7d |
* the low-order bits anyway): we could just remember when
|
|
Packit |
7d6a7d |
* did we overflow and in the end just multiply value_nv by the
|
|
Packit |
7d6a7d |
* right amount of 8-tuples. */
|
|
Packit |
7d6a7d |
value_nv += (NV)digit;
|
|
Packit |
7d6a7d |
continue;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
if (digit == ('_' - '0') && len && allow_underscores
|
|
Packit |
7d6a7d |
&& (digit = s[1] - '0') && (digit >= 0 && digit <= 7))
|
|
Packit |
7d6a7d |
{
|
|
Packit |
7d6a7d |
--len;
|
|
Packit |
7d6a7d |
++s;
|
|
Packit |
7d6a7d |
goto redo;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
/* Allow \octal to work the DWIM way (that is, stop scanning
|
|
Packit |
7d6a7d |
* as soon as non-octal characters are seen, complain only iff
|
|
Packit |
7d6a7d |
* someone seems to want to use the digits eight and nine). */
|
|
Packit |
7d6a7d |
if (digit == 8 || digit == 9) {
|
|
Packit |
7d6a7d |
if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
|
|
Packit |
7d6a7d |
warn("Illegal octal digit '%c' ignored", *s);
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
break;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
if ( ( overflowed && value_nv > 4294967295.0)
|
|
Packit |
7d6a7d |
#if UVSIZE > 4
|
|
Packit |
7d6a7d |
|| (!overflowed && value > 0xffffffff )
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
) {
|
|
Packit |
7d6a7d |
warn("Octal number > 037777777777 non-portable");
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*len_p = s - start;
|
|
Packit |
7d6a7d |
if (!overflowed) {
|
|
Packit |
7d6a7d |
*flags = 0;
|
|
Packit |
7d6a7d |
return value;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
*flags = PERL_SCAN_GREATER_THAN_UV_MAX;
|
|
Packit |
7d6a7d |
if (result)
|
|
Packit |
7d6a7d |
*result = value_nv;
|
|
Packit |
7d6a7d |
return UV_MAX;
|
|
Packit |
7d6a7d |
}
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
#endif
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
=xsinit
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
#define NEED_grok_number
|
|
Packit |
7d6a7d |
#define NEED_grok_numeric_radix
|
|
Packit |
7d6a7d |
#define NEED_grok_bin
|
|
Packit |
7d6a7d |
#define NEED_grok_hex
|
|
Packit |
7d6a7d |
#define NEED_grok_oct
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
=xsubs
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_number(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
const char *pv;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
if (!grok_number(pv, len, &RETVAL))
|
|
Packit |
7d6a7d |
XSRETURN_UNDEF;
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_bin(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = grok_bin(pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_hex(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = grok_hex(pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
grok_oct(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = grok_oct(pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
Perl_grok_number(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
const char *pv;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
if (!Perl_grok_number(aTHX_ pv, len, &RETVAL))
|
|
Packit |
7d6a7d |
XSRETURN_UNDEF;
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
Perl_grok_bin(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = Perl_grok_bin(aTHX_ pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
Perl_grok_hex(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = Perl_grok_hex(aTHX_ pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
UV
|
|
Packit |
7d6a7d |
Perl_grok_oct(string)
|
|
Packit |
7d6a7d |
SV *string
|
|
Packit |
7d6a7d |
PREINIT:
|
|
Packit |
7d6a7d |
char *pv;
|
|
Packit |
7d6a7d |
I32 flags = 0;
|
|
Packit |
7d6a7d |
STRLEN len;
|
|
Packit |
7d6a7d |
CODE:
|
|
Packit |
7d6a7d |
pv = SvPV(string, len);
|
|
Packit |
7d6a7d |
RETVAL = Perl_grok_oct(aTHX_ pv, &len, &flags, NULL);
|
|
Packit |
7d6a7d |
OUTPUT:
|
|
Packit |
7d6a7d |
RETVAL
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
=tests plan => 10
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::grok_number("42"), 42);
|
|
Packit |
7d6a7d |
ok(!defined(&Devel::PPPort::grok_number("A")));
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::grok_bin("10000001"), 129);
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::grok_hex("deadbeef"), 0xdeadbeef);
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::grok_oct("377"), 255);
|
|
Packit |
7d6a7d |
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::Perl_grok_number("42"), 42);
|
|
Packit |
7d6a7d |
ok(!defined(&Devel::PPPort::Perl_grok_number("A")));
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::Perl_grok_bin("10000001"), 129);
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::Perl_grok_hex("deadbeef"), 0xdeadbeef);
|
|
Packit |
7d6a7d |
ok(&Devel::PPPort::Perl_grok_oct("377"), 255);
|