From 82864d1ab101d971e77b73cac9e994fd09cd796b Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 15 Nov 2009 16:18:58 +0000 Subject: Issue #7228: Add '%lld' and '%llu' support to PyFormat_FromString, PyFormat_FromStringV and PyErr_Format. --- Doc/c-api/exceptions.rst | 16 ++++++++ Doc/c-api/string.rst | 16 ++++++++ Include/pyport.h | 16 ++++++++ Misc/NEWS | 3 ++ Modules/_testcapimodule.c | 6 +++ Objects/stringobject.c | 71 ++++++++++++++++++++++++++------ configure | 100 +++++++++++++++++++++++++++++++++++++++++++++- configure.in | 48 ++++++++++++++++++++++ pyconfig.h.in | 3 ++ 9 files changed, 266 insertions(+), 13 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 4482cd0..ba18af1 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -161,6 +161,8 @@ is a separate error indicator for each thread. .. % The descriptions for %zd and %zu are wrong, but the truth is complicated .. % because not all compilers support the %z width modifier -- we fake it .. % when necessary via interpolating PY_FORMAT_SIZE_T. + .. % Similar comments apply to the %ll width modifier and + .. % PY_FORMAT_LONG_LONG. .. % %u, %lu, %zu should have "new in Python 2.5" blurbs. +-------------------+---------------+--------------------------------+ @@ -183,6 +185,12 @@ is a separate error indicator for each thread. | :attr:`%lu` | unsigned long | Exactly equivalent to | | | | ``printf("%lu")``. | +-------------------+---------------+--------------------------------+ + | :attr:`%lld` | long long | Exactly equivalent to | + | | | ``printf("%lld")``. | + +-------------------+---------------+--------------------------------+ + | :attr:`%llu` | unsigned | Exactly equivalent to | + | | long long | ``printf("%llu")``. | + +-------------------+---------------+--------------------------------+ | :attr:`%zd` | Py_ssize_t | Exactly equivalent to | | | | ``printf("%zd")``. | +-------------------+---------------+--------------------------------+ @@ -210,6 +218,14 @@ is a separate error indicator for each thread. An unrecognized format character causes all the rest of the format string to be copied as-is to the result string, and any extra arguments discarded. + .. note:: + + The `"%lld"` and `"%llu"` format specifiers are only available + when `HAVE_LONG_LONG` is defined. + + .. versionchanged:: 2.7 + Support for `"%lld"` and `"%llu"` added. + .. cfunction:: void PyErr_SetNone(PyObject *type) diff --git a/Doc/c-api/string.rst b/Doc/c-api/string.rst index c7d27a3..99fec5d 100644 --- a/Doc/c-api/string.rst +++ b/Doc/c-api/string.rst @@ -78,6 +78,8 @@ called with a non-string parameter. .. % The descriptions for %zd and %zu are wrong, but the truth is complicated .. % because not all compilers support the %z width modifier -- we fake it .. % when necessary via interpolating PY_FORMAT_SIZE_T. + .. % Similar comments apply to the %ll width modifier and + .. % PY_FORMAT_LONG_LONG. .. % %u, %lu, %zu should have "new in Python 2.5" blurbs. +-------------------+---------------+--------------------------------+ @@ -100,6 +102,12 @@ called with a non-string parameter. | :attr:`%lu` | unsigned long | Exactly equivalent to | | | | ``printf("%lu")``. | +-------------------+---------------+--------------------------------+ + | :attr:`%lld` | long long | Exactly equivalent to | + | | | ``printf("%lld")``. | + +-------------------+---------------+--------------------------------+ + | :attr:`%llu` | unsigned | Exactly equivalent to | + | | long long | ``printf("%llu")``. | + +-------------------+---------------+--------------------------------+ | :attr:`%zd` | Py_ssize_t | Exactly equivalent to | | | | ``printf("%zd")``. | +-------------------+---------------+--------------------------------+ @@ -127,6 +135,14 @@ called with a non-string parameter. An unrecognized format character causes all the rest of the format string to be copied as-is to the result string, and any extra arguments discarded. + .. note:: + + The `"%lld"` and `"%llu"` format specifiers are only available + when `HAVE_LONG_LONG` is defined. + + .. versionchanged:: 2.7 + Support for `"%lld"` and `"%llu"` added. + .. cfunction:: PyObject* PyString_FromFormatV(const char *format, va_list vargs) diff --git a/Include/pyport.h b/Include/pyport.h index 8310132..aab349e 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -229,6 +229,22 @@ typedef Py_intptr_t Py_ssize_t; # endif #endif +/* PY_FORMAT_LONG_LONG is analogous to PY_FORMAT_SIZE_T above, but for + * the long long type instead of the size_t type. It's only available + * when HAVE_LONG_LONG is defined. The "high level" Python format + * functions listed above will interpret "lld" or "llu" correctly on + * all platforms. + */ +#ifdef HAVE_LONG_LONG +# ifndef PY_FORMAT_LONG_LONG +# if defined(MS_WIN64) || defined(MS_WINDOWS) +# define PY_FORMAT_LONG_LONG "I64" +# else +# error "This platform's pyconfig.h needs to define PY_FORMAT_LONG_LONG" +# endif +# endif +#endif + /* Py_LOCAL can be used instead of static to get the fastest possible calling * convention for functions that are local to a given module. * diff --git a/Misc/NEWS b/Misc/NEWS index 051bff8..1e406f0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1462,6 +1462,9 @@ Documentation C-API ----- +- Issue #Add '%lld' and '%llu' support to PyString_FromFormat(V) + and PyErr_Format, on machines with HAVE_LONG_LONG defined. + - Add new C-API function PyOS_string_to_double, and deprecated PyOS_ascii_atof and PyOS_ascii_strtod. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index fb5cf73..f2181eb 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -954,6 +954,12 @@ test_string_from_format(PyObject *self, PyObject *args) CHECK_1_FORMAT("%lu", unsigned long); CHECK_1_FORMAT("%zu", size_t); + /* "%lld" and "%llu" support added in Python 2.7. */ +#ifdef HAVE_LONG_LONG + CHECK_1_FORMAT("%llu", unsigned PY_LONG_LONG); + CHECK_1_FORMAT("%lld", PY_LONG_LONG); +#endif + Py_RETURN_NONE; Fail: diff --git a/Objects/stringobject.c b/Objects/stringobject.c index b5faf13..4746b3c 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -189,6 +189,9 @@ PyString_FromFormatV(const char *format, va_list vargs) /* step 1: figure out how large a buffer we need */ for (f = format; *f; f++) { if (*f == '%') { +#ifdef HAVE_LONG_LONG + int longlongflag = 0; +#endif const char* p = f; while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f))) ; @@ -196,9 +199,21 @@ PyString_FromFormatV(const char *format, va_list vargs) /* skip the 'l' or 'z' in {%ld, %zd, %lu, %zu} since * they don't affect the amount of space we reserve. */ - if ((*f == 'l' || *f == 'z') && - (f[1] == 'd' || f[1] == 'u')) + if (*f == 'l') { + if (f[1] == 'd' || f[1] == 'u') { + ++f; + } +#ifdef HAVE_LONG_LONG + else if (f[1] == 'l' && + (f[2] == 'd' || f[2] == 'u')) { + longlongflag = 1; + f += 2; + } +#endif + } + else if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) { ++f; + } switch (*f) { case 'c': @@ -209,10 +224,21 @@ PyString_FromFormatV(const char *format, va_list vargs) break; case 'd': case 'u': case 'i': case 'x': (void) va_arg(count, int); - /* 20 bytes is enough to hold a 64-bit - integer. Decimal takes the most space. - This isn't enough for octal. */ - n += 20; +#ifdef HAVE_LONG_LONG + /* Need at most + ceil(log10(256)*SIZEOF_LONG_LONG) digits, + plus 1 for the sign. 53/22 is an upper + bound for log10(256). */ + if (longlongflag) + n += 2 + (SIZEOF_LONG_LONG*53-1) / 22; + else +#endif + /* 20 bytes is enough to hold a 64-bit + integer. Decimal takes the most + space. This isn't enough for + octal. */ + n += 20; + break; case 's': s = va_arg(count, char*); @@ -255,6 +281,9 @@ PyString_FromFormatV(const char *format, va_list vargs) const char* p = f++; Py_ssize_t i; int longflag = 0; +#ifdef HAVE_LONG_LONG + int longlongflag = 0; +#endif int size_tflag = 0; /* parse the width.precision part (we're only interested in the precision value, if any) */ @@ -269,14 +298,22 @@ PyString_FromFormatV(const char *format, va_list vargs) } while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f))) f++; - /* handle the long flag, but only for %ld and %lu. - others can be added when necessary. */ - if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) { - longflag = 1; - ++f; + /* Handle %ld, %lu, %lld and %llu. */ + if (*f == 'l') { + if (f[1] == 'd' || f[1] == 'u') { + longflag = 1; + ++f; + } +#ifdef HAVE_LONG_LONG + else if (f[1] == 'l' && + (f[2] == 'd' || f[2] == 'u')) { + longlongflag = 1; + f += 2; + } +#endif } /* handle the size_t flag. */ - if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) { + else if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) { size_tflag = 1; ++f; } @@ -288,6 +325,11 @@ PyString_FromFormatV(const char *format, va_list vargs) case 'd': if (longflag) sprintf(s, "%ld", va_arg(vargs, long)); +#ifdef HAVE_LONG_LONG + else if (longlongflag) + sprintf(s, "%" PY_FORMAT_LONG_LONG "d", + va_arg(vargs, PY_LONG_LONG)); +#endif else if (size_tflag) sprintf(s, "%" PY_FORMAT_SIZE_T "d", va_arg(vargs, Py_ssize_t)); @@ -299,6 +341,11 @@ PyString_FromFormatV(const char *format, va_list vargs) if (longflag) sprintf(s, "%lu", va_arg(vargs, unsigned long)); +#ifdef HAVE_LONG_LONG + else if (longlongflag) + sprintf(s, "%" PY_FORMAT_LONG_LONG "u", + va_arg(vargs, PY_LONG_LONG)); +#endif else if (size_tflag) sprintf(s, "%" PY_FORMAT_SIZE_T "u", va_arg(vargs, size_t)); diff --git a/configure b/configure index cd2d7c1..367c879 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 76052 . +# From configure.in Revision: 76300 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61 for python 2.7. # @@ -27014,6 +27014,104 @@ else echo "${ECHO_T}no" >&6; } fi +if test "$have_long_long" = yes +then + { echo "$as_me:$LINENO: checking for %lld and %llu printf() format support" >&5 +echo $ECHO_N "checking for %lld and %llu printf() format support... $ECHO_C" >&6; } + if test "${ac_cv_have_long_long_format+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_have_long_long_format=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #include + #include + #include + + #ifdef HAVE_SYS_TYPES_H + #include + #endif + + int main() + { + char buffer[256]; + + if (sprintf(buffer, "%lld", (long long)123) < 0) + return 1; + if (strcmp(buffer, "123")) + return 1; + + if (sprintf(buffer, "%lld", (long long)-123) < 0) + return 1; + if (strcmp(buffer, "-123")) + return 1; + + if (sprintf(buffer, "%llu", (unsigned long long)123) < 0) + return 1; + if (strcmp(buffer, "123")) + return 1; + + return 0; + } + +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_long_long_format=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_have_long_long_format=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + + +fi + + { echo "$as_me:$LINENO: result: $ac_cv_have_long_long_format" >&5 +echo "${ECHO_T}$ac_cv_have_long_long_format" >&6; } +fi + +if test $ac_cv_have_long_long_format = yes +then + +cat >>confdefs.h <<\_ACEOF +#define PY_FORMAT_LONG_LONG "ll" +_ACEOF + +fi + + { echo "$as_me:$LINENO: checking for %zd printf() format support" >&5 echo $ECHO_N "checking for %zd printf() format support... $ECHO_C" >&6; } if test "${ac_cv_have_size_t_format+set}" = set; then diff --git a/configure.in b/configure.in index 18555ee..fe8bb2b 100644 --- a/configure.in +++ b/configure.in @@ -3952,6 +3952,54 @@ else AC_MSG_RESULT(no) fi +if test "$have_long_long" = yes +then + AC_MSG_CHECKING(for %lld and %llu printf() format support) + AC_CACHE_VAL(ac_cv_have_long_long_format, + AC_TRY_RUN([[ + #include + #include + #include + + #ifdef HAVE_SYS_TYPES_H + #include + #endif + + int main() + { + char buffer[256]; + + if (sprintf(buffer, "%lld", (long long)123) < 0) + return 1; + if (strcmp(buffer, "123")) + return 1; + + if (sprintf(buffer, "%lld", (long long)-123) < 0) + return 1; + if (strcmp(buffer, "-123")) + return 1; + + if (sprintf(buffer, "%llu", (unsigned long long)123) < 0) + return 1; + if (strcmp(buffer, "123")) + return 1; + + return 0; + } + ]], ac_cv_have_long_long_format=yes, + ac_cv_have_long_long_format=no, + ac_cv_have_long_long_format=no) + ) + AC_MSG_RESULT($ac_cv_have_long_long_format) +fi + +if test $ac_cv_have_long_long_format = yes +then + AC_DEFINE(PY_FORMAT_LONG_LONG, "ll", + [Define to printf format modifier for long long type]) +fi + + AC_CACHE_CHECK([for %zd printf() format support], ac_cv_have_size_t_format, [dnl AC_TRY_RUN([ #include diff --git a/pyconfig.h.in b/pyconfig.h.in index 25187ce..c045593 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -903,6 +903,9 @@ /* Define as the preferred size in bits of long digits */ #undef PYLONG_BITS_IN_DIGIT +/* Define to printf format modifier for long long type */ +#undef PY_FORMAT_LONG_LONG + /* Define to printf format modifier for Py_ssize_t */ #undef PY_FORMAT_SIZE_T -- cgit v0.12