From 6ce4a9a9f2d690e9ce7121154fd9cc04082d7f59 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 16 Nov 2009 17:00:11 +0000 Subject: [PATCH] Merged revisions 76308 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76308 | mark.dickinson | 2009-11-15 16:18:58 +0000 (Sun, 15 Nov 2009) | 3 lines Issue #7228: Add '%lld' and '%llu' support to PyFormat_FromString, PyFormat_FromStringV and PyErr_Format. ........ --- Doc/c-api/exceptions.rst | 16 ++++++ Doc/c-api/unicode.rst | 18 ++++++ Include/pyport.h | 16 ++++++ Misc/NEWS | 3 + Modules/_testcapimodule.c | 6 ++ Objects/unicodeobject.c | 113 ++++++++++++++++++++++++++++++-------- configure | 100 ++++++++++++++++++++++++++++++++- configure.in | 48 ++++++++++++++++ pyconfig.h.in | 3 + 9 files changed, 299 insertions(+), 24 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 819e22e02c8..25f7c1152f2 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -155,6 +155,8 @@ in various ways. There 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. +-------------------+---------------+--------------------------------+ | Format Characters | Type | Comment | @@ -176,6 +178,12 @@ in various ways. There 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")``. | +-------------------+---------------+--------------------------------+ @@ -203,6 +211,14 @@ in various ways. There 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:: 3.2 + Support for `"%lld"` and `"%llu"` added. + .. cfunction:: void PyErr_SetNone(PyObject *type) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 4c0d6a462d4..0455ae58968 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -232,9 +232,12 @@ APIs: types and must correspond exactly to the format characters in the *format* string. The following format characters are allowed: + .. % This should be exactly the same as the table in PyErr_Format. .. % 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. +-------------------+---------------------+--------------------------------+ | Format Characters | Type | Comment | @@ -256,6 +259,12 @@ APIs: | :attr:`%lu` | unsigned long | Exactly equivalent to | | | | ``printf("%lu")``. | +-------------------+---------------------+--------------------------------+ + | :attr:`%lld` | long long | Exactly equivalent to | + | | | ``printf("%lld")``. | + +-------------------+---------------------+--------------------------------+ + | :attr:`%llu` | unsigned long long | Exactly equivalent to | + | | | ``printf("%llu")``. | + +-------------------+---------------------+--------------------------------+ | :attr:`%zd` | Py_ssize_t | Exactly equivalent to | | | | ``printf("%zd")``. | +-------------------+---------------------+--------------------------------+ @@ -301,6 +310,15 @@ APIs: 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:: 3.2 + Support for `"%lld"` and `"%llu"` added. + + .. cfunction:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs) diff --git a/Include/pyport.h b/Include/pyport.h index be15fe1b6c3..1adb5f0d020 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -219,6 +219,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 289a64c5a54..42d1d46e402 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -110,6 +110,9 @@ Core and Builtins C-API ----- +- Issue #Add '%lld' and '%llu' support to PyString_FromFormat(V) + and PyErr_Format, on machines with HAVE_LONG_LONG defined. + - Issue #6151: Made PyDescr_COMMON conform to standard C (like PyObject_HEAD in PEP 3123). The PyDescr_TYPE and PyDescr_NAME macros should be should used for accessing the d_type and d_name members of structures diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7e0e95c0b9f..0ed21115084 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1037,6 +1037,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/unicodeobject.c b/Objects/unicodeobject.c index cdb739a3d5d..9c0be9b23ca 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -667,7 +667,8 @@ PyObject *PyUnicode_FromWideChar(register const wchar_t *w, #undef CONVERT_WCHAR_TO_SURROGATES static void -makefmt(char *fmt, int longflag, int size_tflag, int zeropad, int width, int precision, char c) +makefmt(char *fmt, int longflag, int longlongflag, int size_tflag, + int zeropad, int width, int precision, char c) { *fmt++ = '%'; if (width) { @@ -679,6 +680,19 @@ makefmt(char *fmt, int longflag, int size_tflag, int zeropad, int width, int pre fmt += sprintf(fmt, ".%d", precision); if (longflag) *fmt++ = 'l'; + else if (longlongflag) { + /* longlongflag should only ever be nonzero on machines with + HAVE_LONG_LONG defined */ +#ifdef HAVE_LONG_LONG + char *f = PY_FORMAT_LONG_LONG; + while (*f) + *fmt++ = *f++; +#else + /* we shouldn't ever get here */ + assert(0); + *fmt++ = 'l'; +#endif + } else if (size_tflag) { char *f = PY_FORMAT_SIZE_T; while (*f) @@ -690,6 +704,16 @@ makefmt(char *fmt, int longflag, int size_tflag, int zeropad, int width, int pre #define appendstring(string) {for (copy = string;*copy;) *s++ = *copy++;} +/* size of fixed-size buffer for formatting single arguments */ +#define ITEM_BUFFER_LEN 21 +/* maximum number of characters required for output of %ld. 21 characters + allows for 64-bit integers (in decimal) and an optional sign. */ +#define MAX_LONG_CHARS 21 +/* maximum number of characters required for output of %lld. + We need at most ceil(log10(256)*SIZEOF_LONG_LONG) digits, + plus 1 for the sign. 53/22 is an upper bound for log10(256). */ +#define MAX_LONG_LONG_CHARS (2 + (SIZEOF_LONG_LONG*53-1) / 22) + PyObject * PyUnicode_FromFormatV(const char *format, va_list vargs) { @@ -705,13 +729,13 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) Py_UNICODE *s; PyObject *string; /* used by sprintf */ - char buffer[21]; + char buffer[ITEM_BUFFER_LEN+1]; /* use abuffer instead of buffer, if we need more space * (which can happen if there's a format specifier with width). */ char *abuffer = NULL; char *realbuffer; Py_ssize_t abuffersize = 0; - char fmt[60]; /* should be enough for %0width.precisionld */ + char fmt[61]; /* should be enough for %0width.precisionlld */ const char *copy; #ifdef VA_LIST_IS_ARRAY @@ -754,6 +778,9 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) /* step 3: 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; width = 0; while (ISDIGIT((unsigned)*f)) @@ -764,9 +791,21 @@ PyUnicode_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': @@ -777,14 +816,21 @@ PyUnicode_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. - If a width is specified we need more - (which we allocate later). */ - if (width < 20) - width = 20; +#ifdef HAVE_LONG_LONG + if (longlongflag) { + if (width < MAX_LONG_LONG_CHARS) + width = MAX_LONG_LONG_CHARS; + } + else +#endif + /* MAX_LONG_CHARS is enough to hold a 64-bit integer, + including sign. Decimal takes the most space. This + isn't enough for octal. If a width is specified we + need more (which we allocate later). */ + if (width < MAX_LONG_CHARS) + width = MAX_LONG_CHARS; n += width; + /* XXX should allow for large precision here too. */ if (abuffersize < width) abuffersize = width; break; @@ -881,8 +927,9 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) n++; } expand: - if (abuffersize > 20) { - abuffer = PyObject_Malloc(abuffersize); + if (abuffersize > ITEM_BUFFER_LEN) { + /* add 1 for sprintf's trailing null byte */ + abuffer = PyObject_Malloc(abuffersize + 1); if (!abuffer) { PyErr_NoMemory(); goto fail; @@ -906,6 +953,7 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) if (*f == '%') { const char* p = f++; int longflag = 0; + int longlongflag = 0; int size_tflag = 0; zeropad = (*f == '0'); /* parse the width.precision part */ @@ -918,11 +966,19 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) while (ISDIGIT((unsigned)*f)) precision = (precision*10) + *f++ - '0'; } - /* 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')) { @@ -935,9 +991,14 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) *s++ = va_arg(vargs, int); break; case 'd': - makefmt(fmt, longflag, size_tflag, zeropad, width, precision, 'd'); + makefmt(fmt, longflag, longlongflag, size_tflag, zeropad, + width, precision, 'd'); if (longflag) sprintf(realbuffer, fmt, va_arg(vargs, long)); +#ifdef HAVE_LONG_LONG + else if (longlongflag) + sprintf(realbuffer, fmt, va_arg(vargs, PY_LONG_LONG)); +#endif else if (size_tflag) sprintf(realbuffer, fmt, va_arg(vargs, Py_ssize_t)); else @@ -945,9 +1006,15 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) appendstring(realbuffer); break; case 'u': - makefmt(fmt, longflag, size_tflag, zeropad, width, precision, 'u'); + makefmt(fmt, longflag, longlongflag, size_tflag, zeropad, + width, precision, 'u'); if (longflag) sprintf(realbuffer, fmt, va_arg(vargs, unsigned long)); +#ifdef HAVE_LONG_LONG + else if (longlongflag) + sprintf(realbuffer, fmt, va_arg(vargs, + unsigned PY_LONG_LONG)); +#endif else if (size_tflag) sprintf(realbuffer, fmt, va_arg(vargs, size_t)); else @@ -955,12 +1022,12 @@ PyUnicode_FromFormatV(const char *format, va_list vargs) appendstring(realbuffer); break; case 'i': - makefmt(fmt, 0, 0, zeropad, width, precision, 'i'); + makefmt(fmt, 0, 0, 0, zeropad, width, precision, 'i'); sprintf(realbuffer, fmt, va_arg(vargs, int)); appendstring(realbuffer); break; case 'x': - makefmt(fmt, 0, 0, zeropad, width, precision, 'x'); + makefmt(fmt, 0, 0, 0, zeropad, width, precision, 'x'); sprintf(realbuffer, fmt, va_arg(vargs, int)); appendstring(realbuffer); break; diff --git a/configure b/configure index 188435b2feb..d0b086905d0 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 76030 . +# From configure.in Revision: 76301 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61 for python 3.2. # @@ -26314,6 +26314,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 4a0bf6833cc..2a0745f2323 100644 --- a/configure.in +++ b/configure.in @@ -3806,6 +3806,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_MSG_CHECKING(for %zd printf() format support) AC_CACHE_VAL(ac_cv_have_size_t_format, AC_TRY_RUN([[ diff --git a/pyconfig.h.in b/pyconfig.h.in index 9df729a0efc..223c9961bd0 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -910,6 +910,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