From d1d013c01c268d869597b35cbcd8b5d7c5baf2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 28 Sep 2011 07:41:54 +0200 Subject: Implement PEP 393. --- Python/formatter_unicode.c | 1445 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1439 insertions(+), 6 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index c350907da1..9fb840b160 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -3,12 +3,1445 @@ of int.__float__, etc., that take and return unicode objects */ #include "Python.h" -#include "../Objects/stringlib/unicodedefs.h" +#include +/* Raises an exception about an unknown presentation type for this + * type. */ -#define FORMAT_STRING _PyUnicode_FormatAdvanced -#define FORMAT_LONG _PyLong_FormatAdvanced -#define FORMAT_FLOAT _PyFloat_FormatAdvanced -#define FORMAT_COMPLEX _PyComplex_FormatAdvanced +static void +unknown_presentation_type(Py_UCS4 presentation_type, + const char* type_name) +{ + /* %c might be out-of-range, hence the two cases. */ + if (presentation_type > 32 && presentation_type < 128) + PyErr_Format(PyExc_ValueError, + "Unknown format code '%c' " + "for object of type '%.200s'", + (char)presentation_type, + type_name); + else + PyErr_Format(PyExc_ValueError, + "Unknown format code '\\x%x' " + "for object of type '%.200s'", + (unsigned int)presentation_type, + type_name); +} -#include "../Objects/stringlib/formatter.h" +static void +invalid_comma_type(Py_UCS4 presentation_type) +{ + if (presentation_type > 32 && presentation_type < 128) + PyErr_Format(PyExc_ValueError, + "Cannot specify ',' with '%c'.", + (char)presentation_type); + else + PyErr_Format(PyExc_ValueError, + "Cannot specify ',' with '\\x%x'.", + (unsigned int)presentation_type); +} + +/* + get_integer consumes 0 or more decimal digit characters from an + input string, updates *result with the corresponding positive + integer, and returns the number of digits consumed. + + returns -1 on error. +*/ +static int +get_integer(PyObject *str, Py_ssize_t *pos, Py_ssize_t end, + Py_ssize_t *result) +{ + Py_ssize_t accumulator, digitval, oldaccumulator; + int numdigits; + accumulator = numdigits = 0; + for (;;(*pos)++, numdigits++) { + if (*pos >= end) + break; + digitval = Py_UNICODE_TODECIMAL(PyUnicode_READ_CHAR(str, *pos)); + if (digitval < 0) + break; + /* + This trick was copied from old Unicode format code. It's cute, + but would really suck on an old machine with a slow divide + implementation. Fortunately, in the normal case we do not + expect too many digits. + */ + oldaccumulator = accumulator; + accumulator *= 10; + if ((accumulator+10)/10 != oldaccumulator+1) { + PyErr_Format(PyExc_ValueError, + "Too many decimal digits in format string"); + return -1; + } + accumulator += digitval; + } + *result = accumulator; + return numdigits; +} + +/************************************************************************/ +/*********** standard format specifier parsing **************************/ +/************************************************************************/ + +/* returns true if this character is a specifier alignment token */ +Py_LOCAL_INLINE(int) +is_alignment_token(Py_UCS4 c) +{ + switch (c) { + case '<': case '>': case '=': case '^': + return 1; + default: + return 0; + } +} + +/* returns true if this character is a sign element */ +Py_LOCAL_INLINE(int) +is_sign_element(Py_UCS4 c) +{ + switch (c) { + case ' ': case '+': case '-': + return 1; + default: + return 0; + } +} + + +typedef struct { + Py_UCS4 fill_char; + Py_UCS4 align; + int alternate; + Py_UCS4 sign; + Py_ssize_t width; + int thousands_separators; + Py_ssize_t precision; + Py_UCS4 type; +} InternalFormatSpec; + +#if 0 +/* Occassionally useful for debugging. Should normally be commented out. */ +static void +DEBUG_PRINT_FORMAT_SPEC(InternalFormatSpec *format) +{ + printf("internal format spec: fill_char %d\n", format->fill_char); + printf("internal format spec: align %d\n", format->align); + printf("internal format spec: alternate %d\n", format->alternate); + printf("internal format spec: sign %d\n", format->sign); + printf("internal format spec: width %zd\n", format->width); + printf("internal format spec: thousands_separators %d\n", + format->thousands_separators); + printf("internal format spec: precision %zd\n", format->precision); + printf("internal format spec: type %c\n", format->type); + printf("\n"); +} +#endif + + +/* + ptr points to the start of the format_spec, end points just past its end. + fills in format with the parsed information. + returns 1 on success, 0 on failure. + if failure, sets the exception +*/ +static int +parse_internal_render_format_spec(PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end, + InternalFormatSpec *format, + char default_type, + char default_align) +{ + Py_ssize_t pos = start; + /* end-pos is used throughout this code to specify the length of + the input string */ +#define READ_spec(index) PyUnicode_READ_CHAR(format_spec, index) + + Py_ssize_t consumed; + int align_specified = 0; + + format->fill_char = '\0'; + format->align = default_align; + format->alternate = 0; + format->sign = '\0'; + format->width = -1; + format->thousands_separators = 0; + format->precision = -1; + format->type = default_type; + + /* If the second char is an alignment token, + then parse the fill char */ + if (end-pos >= 2 && is_alignment_token(READ_spec(pos+1))) { + format->align = READ_spec(pos+1); + format->fill_char = READ_spec(pos); + align_specified = 1; + pos += 2; + } + else if (end-pos >= 1 && is_alignment_token(READ_spec(pos))) { + format->align = READ_spec(pos); + align_specified = 1; + ++pos; + } + + /* Parse the various sign options */ + if (end-pos >= 1 && is_sign_element(READ_spec(pos))) { + format->sign = READ_spec(pos); + ++pos; + } + + /* If the next character is #, we're in alternate mode. This only + applies to integers. */ + if (end-pos >= 1 && READ_spec(pos) == '#') { + format->alternate = 1; + ++pos; + } + + /* The special case for 0-padding (backwards compat) */ + if (format->fill_char == '\0' && end-pos >= 1 && READ_spec(pos) == '0') { + format->fill_char = '0'; + if (!align_specified) { + format->align = '='; + } + ++pos; + } + + consumed = get_integer(format_spec, &pos, end, &format->width); + if (consumed == -1) + /* Overflow error. Exception already set. */ + return 0; + + /* If consumed is 0, we didn't consume any characters for the + width. In that case, reset the width to -1, because + get_integer() will have set it to zero. -1 is how we record + that the width wasn't specified. */ + if (consumed == 0) + format->width = -1; + + /* Comma signifies add thousands separators */ + if (end-pos && READ_spec(pos) == ',') { + format->thousands_separators = 1; + ++pos; + } + + /* Parse field precision */ + if (end-pos && READ_spec(pos) == '.') { + ++pos; + + consumed = get_integer(format_spec, &pos, end, &format->precision); + if (consumed == -1) + /* Overflow error. Exception already set. */ + return 0; + + /* Not having a precision after a dot is an error. */ + if (consumed == 0) { + PyErr_Format(PyExc_ValueError, + "Format specifier missing precision"); + return 0; + } + + } + + /* Finally, parse the type field. */ + + if (end-pos > 1) { + /* More than one char remain, invalid conversion spec. */ + PyErr_Format(PyExc_ValueError, "Invalid conversion specification"); + return 0; + } + + if (end-pos == 1) { + format->type = READ_spec(pos); + ++pos; + } + + /* Do as much validating as we can, just by looking at the format + specifier. Do not take into account what type of formatting + we're doing (int, float, string). */ + + if (format->thousands_separators) { + switch (format->type) { + case 'd': + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + case '%': + case 'F': + case '\0': + /* These are allowed. See PEP 378.*/ + break; + default: + invalid_comma_type(format->type); + return 0; + } + } + + if (format->fill_char > 127 || format->align > 127 || + format->sign > 127) { + PyErr_SetString(PyExc_ValueError, "fill character too large"); + return 0; + } + + return 1; +} + +/* Calculate the padding needed. */ +static void +calc_padding(Py_ssize_t nchars, Py_ssize_t width, Py_UCS4 align, + Py_ssize_t *n_lpadding, Py_ssize_t *n_rpadding, + Py_ssize_t *n_total) +{ + if (width >= 0) { + if (nchars > width) + *n_total = nchars; + else + *n_total = width; + } + else { + /* not specified, use all of the chars and no more */ + *n_total = nchars; + } + + /* Figure out how much leading space we need, based on the + aligning */ + if (align == '>') + *n_lpadding = *n_total - nchars; + else if (align == '^') + *n_lpadding = (*n_total - nchars) / 2; + else if (align == '<' || align == '=') + *n_lpadding = 0; + else { + /* We should never have an unspecified alignment. */ + *n_lpadding = 0; + assert(0); + } + + *n_rpadding = *n_total - nchars - *n_lpadding; +} + +static void +unicode_fill(PyObject *str, Py_ssize_t start, Py_ssize_t end, Py_UCS4 ch) +{ + int kind = PyUnicode_KIND(str); + void *data = PyUnicode_DATA(str); + while (start < end) + PyUnicode_WRITE(kind, data, start++, ch); +} + +/* Do the padding, and return a pointer to where the caller-supplied + content goes. */ +static Py_ssize_t +fill_padding(PyObject *s, Py_ssize_t start, Py_ssize_t nchars, + Py_UCS4 fill_char, Py_ssize_t n_lpadding, + Py_ssize_t n_rpadding) +{ + /* Pad on left. */ + if (n_lpadding) + unicode_fill(s, start, start + n_lpadding, fill_char); + + /* Pad on right. */ + if (n_rpadding) + unicode_fill(s, start + nchars + n_lpadding, + start + nchars + n_lpadding + n_rpadding, fill_char); + + /* Pointer to the user content. */ + return start + n_lpadding; +} + +/************************************************************************/ +/*********** common routines for numeric formatting *********************/ +/************************************************************************/ + +/* Locale type codes. */ +#define LT_CURRENT_LOCALE 0 +#define LT_DEFAULT_LOCALE 1 +#define LT_NO_LOCALE 2 + +/* Locale info needed for formatting integers and the part of floats + before and including the decimal. Note that locales only support + 8-bit chars, not unicode. */ +typedef struct { + char *decimal_point; + char *thousands_sep; + char *grouping; +} LocaleInfo; + +/* describes the layout for an integer, see the comment in + calc_number_widths() for details */ +typedef struct { + Py_ssize_t n_lpadding; + Py_ssize_t n_prefix; + Py_ssize_t n_spadding; + Py_ssize_t n_rpadding; + char sign; + Py_ssize_t n_sign; /* number of digits needed for sign (0/1) */ + Py_ssize_t n_grouped_digits; /* Space taken up by the digits, including + any grouping chars. */ + Py_ssize_t n_decimal; /* 0 if only an integer */ + Py_ssize_t n_remainder; /* Digits in decimal and/or exponent part, + excluding the decimal itself, if + present. */ + + /* These 2 are not the widths of fields, but are needed by + STRINGLIB_GROUPING. */ + Py_ssize_t n_digits; /* The number of digits before a decimal + or exponent. */ + Py_ssize_t n_min_width; /* The min_width we used when we computed + the n_grouped_digits width. */ +} NumberFieldWidths; + + +/* Given a number of the form: + digits[remainder] + where ptr points to the start and end points to the end, find where + the integer part ends. This could be a decimal, an exponent, both, + or neither. + If a decimal point is present, set *has_decimal and increment + remainder beyond it. + Results are undefined (but shouldn't crash) for improperly + formatted strings. +*/ +static void +parse_number(PyObject *s, Py_ssize_t pos, Py_ssize_t end, + Py_ssize_t *n_remainder, int *has_decimal) +{ + Py_ssize_t remainder; + + while (posn_digits = n_end - n_start - n_remainder - (has_decimal?1:0); + spec->n_lpadding = 0; + spec->n_prefix = n_prefix; + spec->n_decimal = has_decimal ? strlen(locale->decimal_point) : 0; + spec->n_remainder = n_remainder; + spec->n_spadding = 0; + spec->n_rpadding = 0; + spec->sign = '\0'; + spec->n_sign = 0; + + /* the output will look like: + | | + | | + | | + + sign is computed from format->sign and the actual + sign of the number + + prefix is given (it's for the '0x' prefix) + + digits is already known + + the total width is either given, or computed from the + actual digits + + only one of lpadding, spadding, and rpadding can be non-zero, + and it's calculated from the width and other fields + */ + + /* compute the various parts we're going to write */ + switch (format->sign) { + case '+': + /* always put a + or - */ + spec->n_sign = 1; + spec->sign = (sign_char == '-' ? '-' : '+'); + break; + case ' ': + spec->n_sign = 1; + spec->sign = (sign_char == '-' ? '-' : ' '); + break; + default: + /* Not specified, or the default (-) */ + if (sign_char == '-') { + spec->n_sign = 1; + spec->sign = '-'; + } + } + + /* The number of chars used for non-digits and non-padding. */ + n_non_digit_non_padding = spec->n_sign + spec->n_prefix + spec->n_decimal + + spec->n_remainder; + + /* min_width can go negative, that's okay. format->width == -1 means + we don't care. */ + if (format->fill_char == '0' && format->align == '=') + spec->n_min_width = format->width - n_non_digit_non_padding; + else + spec->n_min_width = 0; + + if (spec->n_digits == 0) + /* This case only occurs when using 'c' formatting, we need + to special case it because the grouping code always wants + to have at least one character. */ + spec->n_grouped_digits = 0; + else + spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( + PyUnicode_1BYTE_KIND, NULL, 0, NULL, + spec->n_digits, spec->n_min_width, + locale->grouping, locale->thousands_sep); + + /* Given the desired width and the total of digit and non-digit + space we consume, see if we need any padding. format->width can + be negative (meaning no padding), but this code still works in + that case. */ + n_padding = format->width - + (n_non_digit_non_padding + spec->n_grouped_digits); + if (n_padding > 0) { + /* Some padding is needed. Determine if it's left, space, or right. */ + switch (format->align) { + case '<': + spec->n_rpadding = n_padding; + break; + case '^': + spec->n_lpadding = n_padding / 2; + spec->n_rpadding = n_padding - spec->n_lpadding; + break; + case '=': + spec->n_spadding = n_padding; + break; + case '>': + spec->n_lpadding = n_padding; + break; + default: + /* Shouldn't get here, but treat it as '>' */ + spec->n_lpadding = n_padding; + assert(0); + break; + } + } + return spec->n_lpadding + spec->n_sign + spec->n_prefix + + spec->n_spadding + spec->n_grouped_digits + spec->n_decimal + + spec->n_remainder + spec->n_rpadding; +} + +/* Fill in the digit parts of a numbers's string representation, + as determined in calc_number_widths(). + No error checking, since we know the buffer is the correct size. */ +static void +fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, + PyObject *digits, Py_ssize_t d_start, Py_ssize_t d_end, + PyObject *prefix, Py_ssize_t p_start, Py_UCS4 fill_char, + LocaleInfo *locale, int toupper) +{ + /* Used to keep track of digits, decimal, and remainder. */ + Py_ssize_t d_pos = d_start; + unsigned int kind = PyUnicode_KIND(out); + void *data = PyUnicode_DATA(out); + +#ifndef NDEBUG + Py_ssize_t r; +#endif + + if (spec->n_lpadding) { + unicode_fill(out, pos, pos + spec->n_lpadding, fill_char); + pos += spec->n_lpadding; + } + if (spec->n_sign == 1) { + PyUnicode_WRITE(kind, data, pos++, spec->sign); + } + if (spec->n_prefix) { + PyUnicode_CopyCharacters(out, pos, prefix, p_start, spec->n_prefix); + if (toupper) { + Py_ssize_t t; + /* XXX if the upper-case prefix is wider than the target + buffer, the caller should have allocated a wider string, + but currently doesn't. */ + for (t = 0; t < spec->n_prefix; ++t) + PyUnicode_WRITE(kind, data, pos + t, + Py_UNICODE_TOUPPER( + PyUnicode_READ(kind, data, pos + t))); + } + pos += spec->n_prefix; + } + if (spec->n_spadding) { + unicode_fill(out, pos, pos + spec->n_spadding, fill_char); + pos += spec->n_spadding; + } + + /* Only for type 'c' special case, it has no digits. */ + if (spec->n_digits != 0) { + /* Fill the digits with InsertThousandsGrouping. */ + char *pdigits = PyUnicode_DATA(digits); + if (PyUnicode_KIND(digits) < kind) { + pdigits = _PyUnicode_AsKind(digits, kind); + if (pdigits == NULL) { + /* XXX report exception */ + Py_FatalError("out of memory"); + return; + } + } +#ifndef NDEBUG + r = +#endif + _PyUnicode_InsertThousandsGrouping( + kind, + (char*)data + PyUnicode_KIND_SIZE(kind, pos), + spec->n_grouped_digits, + pdigits + PyUnicode_KIND_SIZE(kind, d_pos), + spec->n_digits, spec->n_min_width, + locale->grouping, locale->thousands_sep); +#ifndef NDEBUG + assert(r == spec->n_grouped_digits); +#endif + if (PyUnicode_KIND(digits) < kind) + PyMem_Free(pdigits); + d_pos += spec->n_digits; + } + if (toupper) { + Py_ssize_t t; + for (t = 0; t < spec->n_grouped_digits; ++t) + PyUnicode_WRITE(kind, data, pos + t, + Py_UNICODE_TOUPPER( + PyUnicode_READ(kind, data, pos + t))); + } + pos += spec->n_grouped_digits; + + if (spec->n_decimal) { + Py_ssize_t t; + for (t = 0; t < spec->n_decimal; ++t) + PyUnicode_WRITE(kind, data, pos + t, + locale->decimal_point[t]); + pos += spec->n_decimal; + d_pos += 1; + } + + if (spec->n_remainder) { + PyUnicode_CopyCharacters(out, pos, digits, d_pos, spec->n_remainder); + pos += spec->n_remainder; + d_pos += spec->n_remainder; + } + + if (spec->n_rpadding) { + unicode_fill(out, pos, pos + spec->n_rpadding, fill_char); + pos += spec->n_rpadding; + } +} + +static char no_grouping[1] = {CHAR_MAX}; + +/* Find the decimal point character(s?), thousands_separator(s?), and + grouping description, either for the current locale if type is + LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE, or + none if LT_NO_LOCALE. */ +static void +get_locale_info(int type, LocaleInfo *locale_info) +{ + switch (type) { + case LT_CURRENT_LOCALE: { + struct lconv *locale_data = localeconv(); + locale_info->decimal_point = locale_data->decimal_point; + locale_info->thousands_sep = locale_data->thousands_sep; + locale_info->grouping = locale_data->grouping; + break; + } + case LT_DEFAULT_LOCALE: + locale_info->decimal_point = "."; + locale_info->thousands_sep = ","; + locale_info->grouping = "\3"; /* Group every 3 characters. The + (implicit) trailing 0 means repeat + infinitely. */ + break; + case LT_NO_LOCALE: + locale_info->decimal_point = "."; + locale_info->thousands_sep = ""; + locale_info->grouping = no_grouping; + break; + default: + assert(0); + } +} + +/************************************************************************/ +/*********** string formatting ******************************************/ +/************************************************************************/ + +static PyObject * +format_string_internal(PyObject *value, const InternalFormatSpec *format) +{ + Py_ssize_t lpad; + Py_ssize_t rpad; + Py_ssize_t total; + Py_ssize_t pos; + Py_ssize_t len = PyUnicode_GET_SIZE(value); + PyObject *result = NULL; + int maxchar = 127; + + /* sign is not allowed on strings */ + if (format->sign != '\0') { + PyErr_SetString(PyExc_ValueError, + "Sign not allowed in string format specifier"); + goto done; + } + + /* alternate is not allowed on strings */ + if (format->alternate) { + PyErr_SetString(PyExc_ValueError, + "Alternate form (#) not allowed in string format " + "specifier"); + goto done; + } + + /* '=' alignment not allowed on strings */ + if (format->align == '=') { + PyErr_SetString(PyExc_ValueError, + "'=' alignment not allowed " + "in string format specifier"); + goto done; + } + + /* if precision is specified, output no more that format.precision + characters */ + if (format->precision >= 0 && len >= format->precision) { + len = format->precision; + } + + calc_padding(len, format->width, format->align, &lpad, &rpad, &total); + + /* allocate the resulting string */ + result = PyUnicode_New(total, maxchar); + if (result == NULL) + goto done; + + /* Write into that space. First the padding. */ + pos = fill_padding(result, 0, len, + format->fill_char=='\0'?' ':format->fill_char, + lpad, rpad); + + /* Then the source string. */ + PyUnicode_CopyCharacters(result, pos, value, 0, len); + +done: + return result; +} + + +/************************************************************************/ +/*********** long formatting ********************************************/ +/************************************************************************/ + +typedef PyObject* +(*IntOrLongToString)(PyObject *value, int base); + +static PyObject * +format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, + IntOrLongToString tostring) +{ + PyObject *result = NULL; + int maxchar = 127; + PyObject *tmp = NULL; + Py_ssize_t inumeric_chars; + Py_UCS4 sign_char = '\0'; + Py_ssize_t n_digits; /* count of digits need from the computed + string */ + Py_ssize_t n_remainder = 0; /* Used only for 'c' formatting, which + produces non-digits */ + Py_ssize_t n_prefix = 0; /* Count of prefix chars, (e.g., '0x') */ + Py_ssize_t n_total; + Py_ssize_t prefix; + NumberFieldWidths spec; + long x; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale; + + /* no precision allowed on integers */ + if (format->precision != -1) { + PyErr_SetString(PyExc_ValueError, + "Precision not allowed in integer format specifier"); + goto done; + } + + /* special case for character formatting */ + if (format->type == 'c') { + /* error to specify a sign */ + if (format->sign != '\0') { + PyErr_SetString(PyExc_ValueError, + "Sign not allowed with integer" + " format specifier 'c'"); + goto done; + } + + /* taken from unicodeobject.c formatchar() */ + /* Integer input truncated to a character */ +/* XXX: won't work for int */ + x = PyLong_AsLong(value); + if (x == -1 && PyErr_Occurred()) + goto done; + if (x < 0 || x > 0x10ffff) { + PyErr_SetString(PyExc_OverflowError, + "%c arg not in range(0x110000) " + "(wide Python build)"); + goto done; + } + tmp = PyUnicode_FromOrdinal(x); + inumeric_chars = 0; + n_digits = 1; + if (x > maxchar) + maxchar = x; + + /* As a sort-of hack, we tell calc_number_widths that we only + have "remainder" characters. calc_number_widths thinks + these are characters that don't get formatted, only copied + into the output string. We do this for 'c' formatting, + because the characters are likely to be non-digits. */ + n_remainder = 1; + } + else { + int base; + int leading_chars_to_skip = 0; /* Number of characters added by + PyNumber_ToBase that we want to + skip over. */ + + /* Compute the base and how many characters will be added by + PyNumber_ToBase */ + switch (format->type) { + case 'b': + base = 2; + leading_chars_to_skip = 2; /* 0b */ + break; + case 'o': + base = 8; + leading_chars_to_skip = 2; /* 0o */ + break; + case 'x': + case 'X': + base = 16; + leading_chars_to_skip = 2; /* 0x */ + break; + default: /* shouldn't be needed, but stops a compiler warning */ + case 'd': + case 'n': + base = 10; + break; + } + + /* The number of prefix chars is the same as the leading + chars to skip */ + if (format->alternate) + n_prefix = leading_chars_to_skip; + + /* Do the hard part, converting to a string in a given base */ + tmp = tostring(value, base); + if (tmp == NULL || PyUnicode_READY(tmp) == -1) + goto done; + + inumeric_chars = 0; + n_digits = PyUnicode_GET_LENGTH(tmp); + + prefix = inumeric_chars; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + if (PyUnicode_READ_CHAR(tmp, inumeric_chars) == '-') { + sign_char = '-'; + ++prefix; + ++leading_chars_to_skip; + } + + /* Skip over the leading chars (0x, 0b, etc.) */ + n_digits -= leading_chars_to_skip; + inumeric_chars += leading_chars_to_skip; + } + + /* Determine the grouping, separator, and decimal point, if any. */ + get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale); + + /* Calculate how much memory we'll need. */ + n_total = calc_number_widths(&spec, n_prefix, sign_char, tmp, inumeric_chars, + inumeric_chars + n_digits, n_remainder, 0, &locale, format); + + /* Allocate the memory. */ + result = PyUnicode_New(n_total, maxchar); + if (!result) + goto done; + + /* Populate the memory. */ + fill_number(result, 0, &spec, tmp, inumeric_chars, inumeric_chars + n_digits, + tmp, prefix, + format->fill_char == '\0' ? ' ' : format->fill_char, + &locale, format->type == 'X'); + +done: + Py_XDECREF(tmp); + return result; +} + +/************************************************************************/ +/*********** float formatting *******************************************/ +/************************************************************************/ + +static PyObject* +strtounicode(char *charbuffer, Py_ssize_t len) +{ + return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, charbuffer, len); +} + +/* much of this is taken from unicodeobject.c */ +static PyObject * +format_float_internal(PyObject *value, + const InternalFormatSpec *format) +{ + char *buf = NULL; /* buffer returned from PyOS_double_to_string */ + Py_ssize_t n_digits; + Py_ssize_t n_remainder; + Py_ssize_t n_total; + int has_decimal; + double val; + Py_ssize_t precision = format->precision; + Py_ssize_t default_precision = 6; + Py_UCS4 type = format->type; + int add_pct = 0; + Py_ssize_t index; + NumberFieldWidths spec; + int flags = 0; + PyObject *result = NULL; + int maxchar = 127; + Py_UCS4 sign_char = '\0'; + int float_type; /* Used to see if we have a nan, inf, or regular float. */ + PyObject *unicode_tmp = NULL; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale; + + if (format->alternate) + flags |= Py_DTSF_ALT; + + if (type == '\0') { + /* Omitted type specifier. Behaves in the same way as repr(x) + and str(x) if no precision is given, else like 'g', but with + at least one digit after the decimal point. */ + flags |= Py_DTSF_ADD_DOT_0; + type = 'r'; + default_precision = 0; + } + + if (type == 'n') + /* 'n' is the same as 'g', except for the locale used to + format the result. We take care of that later. */ + type = 'g'; + + val = PyFloat_AsDouble(value); + if (val == -1.0 && PyErr_Occurred()) + goto done; + + if (type == '%') { + type = 'f'; + val *= 100; + add_pct = 1; + } + + if (precision < 0) + precision = default_precision; + else if (type == 'r') + type = 'g'; + + /* Cast "type", because if we're in unicode we need to pass a + 8-bit char. This is safe, because we've restricted what "type" + can be. */ + buf = PyOS_double_to_string(val, (char)type, precision, flags, + &float_type); + if (buf == NULL) + goto done; + n_digits = strlen(buf); + + if (add_pct) { + /* We know that buf has a trailing zero (since we just called + strlen() on it), and we don't use that fact any more. So we + can just write over the trailing zero. */ + buf[n_digits] = '%'; + n_digits += 1; + } + + /* Since there is no unicode version of PyOS_double_to_string, + just use the 8 bit version and then convert to unicode. */ + unicode_tmp = strtounicode(buf, n_digits); + if (unicode_tmp == NULL) + goto done; + index = 0; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + if (PyUnicode_READ_CHAR(unicode_tmp, index) == '-') { + sign_char = '-'; + ++index; + --n_digits; + } + + /* Determine if we have any "remainder" (after the digits, might include + decimal or exponent or both (or neither)) */ + parse_number(unicode_tmp, index, index + n_digits, &n_remainder, &has_decimal); + + /* Determine the grouping, separator, and decimal point, if any. */ + get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale); + + /* Calculate how much memory we'll need. */ + n_total = calc_number_widths(&spec, 0, sign_char, unicode_tmp, index, + index + n_digits, n_remainder, has_decimal, + &locale, format); + + /* Allocate the memory. */ + result = PyUnicode_New(n_total, maxchar); + if (result == NULL) + goto done; + + /* Populate the memory. */ + fill_number(result, 0, &spec, unicode_tmp, index, index + n_digits, + NULL, 0, + format->fill_char == '\0' ? ' ' : format->fill_char, &locale, + 0); + +done: + PyMem_Free(buf); + Py_DECREF(unicode_tmp); + return result; +} + +/************************************************************************/ +/*********** complex formatting *****************************************/ +/************************************************************************/ + +static PyObject * +format_complex_internal(PyObject *value, + const InternalFormatSpec *format) +{ + double re; + double im; + char *re_buf = NULL; /* buffer returned from PyOS_double_to_string */ + char *im_buf = NULL; /* buffer returned from PyOS_double_to_string */ + + InternalFormatSpec tmp_format = *format; + Py_ssize_t n_re_digits; + Py_ssize_t n_im_digits; + Py_ssize_t n_re_remainder; + Py_ssize_t n_im_remainder; + Py_ssize_t n_re_total; + Py_ssize_t n_im_total; + int re_has_decimal; + int im_has_decimal; + Py_ssize_t precision = format->precision; + Py_ssize_t default_precision = 6; + Py_UCS4 type = format->type; + Py_ssize_t i_re; + Py_ssize_t i_im; + NumberFieldWidths re_spec; + NumberFieldWidths im_spec; + int flags = 0; + PyObject *result = NULL; + int maxchar = 127; + int rkind; + void *rdata; + Py_ssize_t index; + Py_UCS4 re_sign_char = '\0'; + Py_UCS4 im_sign_char = '\0'; + int re_float_type; /* Used to see if we have a nan, inf, or regular float. */ + int im_float_type; + int add_parens = 0; + int skip_re = 0; + Py_ssize_t lpad; + Py_ssize_t rpad; + Py_ssize_t total; + PyObject *re_unicode_tmp = NULL; + PyObject *im_unicode_tmp = NULL; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale; + + /* Zero padding is not allowed. */ + if (format->fill_char == '0') { + PyErr_SetString(PyExc_ValueError, + "Zero padding is not allowed in complex format " + "specifier"); + goto done; + } + + /* Neither is '=' alignment . */ + if (format->align == '=') { + PyErr_SetString(PyExc_ValueError, + "'=' alignment flag is not allowed in complex format " + "specifier"); + goto done; + } + + re = PyComplex_RealAsDouble(value); + if (re == -1.0 && PyErr_Occurred()) + goto done; + im = PyComplex_ImagAsDouble(value); + if (im == -1.0 && PyErr_Occurred()) + goto done; + + if (format->alternate) + flags |= Py_DTSF_ALT; + + if (type == '\0') { + /* Omitted type specifier. Should be like str(self). */ + type = 'r'; + default_precision = 0; + if (re == 0.0 && copysign(1.0, re) == 1.0) + skip_re = 1; + else + add_parens = 1; + } + + if (type == 'n') + /* 'n' is the same as 'g', except for the locale used to + format the result. We take care of that later. */ + type = 'g'; + + if (precision < 0) + precision = default_precision; + else if (type == 'r') + type = 'g'; + + /* Cast "type", because if we're in unicode we need to pass a + 8-bit char. This is safe, because we've restricted what "type" + can be. */ + re_buf = PyOS_double_to_string(re, (char)type, precision, flags, + &re_float_type); + if (re_buf == NULL) + goto done; + im_buf = PyOS_double_to_string(im, (char)type, precision, flags, + &im_float_type); + if (im_buf == NULL) + goto done; + + n_re_digits = strlen(re_buf); + n_im_digits = strlen(im_buf); + + /* Since there is no unicode version of PyOS_double_to_string, + just use the 8 bit version and then convert to unicode. */ + re_unicode_tmp = strtounicode(re_buf, n_re_digits); + if (re_unicode_tmp == NULL) + goto done; + i_re = 0; + + im_unicode_tmp = strtounicode(im_buf, n_im_digits); + if (im_unicode_tmp == NULL) + goto done; + i_im = 0; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + if (PyUnicode_READ_CHAR(re_unicode_tmp, i_re) == '-') { + re_sign_char = '-'; + ++i_re; + --n_re_digits; + } + if (PyUnicode_READ_CHAR(im_unicode_tmp, i_im) == '-') { + im_sign_char = '-'; + ++i_im; + --n_im_digits; + } + + /* Determine if we have any "remainder" (after the digits, might include + decimal or exponent or both (or neither)) */ + parse_number(re_unicode_tmp, i_re, i_re + n_re_digits, + &n_re_remainder, &re_has_decimal); + parse_number(im_unicode_tmp, i_im, i_im + n_im_digits, + &n_im_remainder, &im_has_decimal); + + /* Determine the grouping, separator, and decimal point, if any. */ + get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale); + + /* Turn off any padding. We'll do it later after we've composed + the numbers without padding. */ + tmp_format.fill_char = '\0'; + tmp_format.align = '<'; + tmp_format.width = -1; + + /* Calculate how much memory we'll need. */ + n_re_total = calc_number_widths(&re_spec, 0, re_sign_char, re_unicode_tmp, + i_re, i_re + n_re_digits, n_re_remainder, + re_has_decimal, &locale, &tmp_format); + + /* Same formatting, but always include a sign, unless the real part is + * going to be omitted, in which case we use whatever sign convention was + * requested by the original format. */ + if (!skip_re) + tmp_format.sign = '+'; + n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, im_unicode_tmp, + i_im, i_im + n_im_digits, n_im_remainder, + im_has_decimal, &locale, &tmp_format); + + if (skip_re) + n_re_total = 0; + + /* Add 1 for the 'j', and optionally 2 for parens. */ + calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, + format->width, format->align, &lpad, &rpad, &total); + + result = PyUnicode_New(total, maxchar); + if (result == NULL) + goto done; + rkind = PyUnicode_KIND(result); + rdata = PyUnicode_DATA(result); + + /* Populate the memory. First, the padding. */ + index = fill_padding(result, 0, + n_re_total + n_im_total + 1 + add_parens * 2, + format->fill_char=='\0' ? ' ' : format->fill_char, + lpad, rpad); + + if (add_parens) + PyUnicode_WRITE(rkind, rdata, index++, '('); + + if (!skip_re) { + fill_number(result, index, &re_spec, re_unicode_tmp, + i_re, i_re + n_re_digits, NULL, 0, 0, &locale, 0); + index += n_re_total; + } + fill_number(result, index, &im_spec, im_unicode_tmp, + i_im, i_im + n_im_digits, NULL, 0, 0, &locale, 0); + index += n_im_total; + PyUnicode_WRITE(rkind, rdata, index++, 'j'); + + if (add_parens) + PyUnicode_WRITE(rkind, rdata, index++, ')'); + +done: + PyMem_Free(re_buf); + PyMem_Free(im_buf); + Py_XDECREF(re_unicode_tmp); + Py_XDECREF(im_unicode_tmp); + return result; +} + +/************************************************************************/ +/*********** built in formatters ****************************************/ +/************************************************************************/ +PyObject * +_PyUnicode_FormatAdvanced(PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + InternalFormatSpec format; + PyObject *result = NULL; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + result = PyObject_Str(obj); + goto done; + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(format_spec, start, end, + &format, 's', '<')) + goto done; + + /* type conversion? */ + switch (format.type) { + case 's': + /* no type conversion needed, already a string. do the formatting */ + result = format_string_internal(obj, &format); + break; + default: + /* unknown */ + unknown_presentation_type(format.type, obj->ob_type->tp_name); + goto done; + } + +done: + return result; +} + +static PyObject* +format_int_or_long(PyObject* obj, PyObject* format_spec, + Py_ssize_t start, Py_ssize_t end, + IntOrLongToString tostring) +{ + PyObject *result = NULL; + PyObject *tmp = NULL; + InternalFormatSpec format; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + result = PyObject_Str(obj); + goto done; + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(format_spec, start, end, + &format, 'd', '>')) + goto done; + + /* type conversion? */ + switch (format.type) { + case 'b': + case 'c': + case 'd': + case 'o': + case 'x': + case 'X': + case 'n': + /* no type conversion needed, already an int (or long). do + the formatting */ + result = format_int_or_long_internal(obj, &format, tostring); + break; + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case '%': + /* convert to float */ + tmp = PyNumber_Float(obj); + if (tmp == NULL) + goto done; + result = format_float_internal(tmp, &format); + break; + + default: + /* unknown */ + unknown_presentation_type(format.type, obj->ob_type->tp_name); + goto done; + } + +done: + Py_XDECREF(tmp); + return result; +} + +/* Need to define long_format as a function that will convert a long + to a string. In 3.0, _PyLong_Format has the correct signature. */ +#define long_format _PyLong_Format + +PyObject * +_PyLong_FormatAdvanced(PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + return format_int_or_long(obj, format_spec, start, end, + long_format); +} + +PyObject * +_PyFloat_FormatAdvanced(PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + PyObject *result = NULL; + InternalFormatSpec format; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + result = PyObject_Str(obj); + goto done; + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(format_spec, start, end, + &format, '\0', '>')) + goto done; + + /* type conversion? */ + switch (format.type) { + case '\0': /* No format code: like 'g', but with at least one decimal. */ + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'n': + case '%': + /* no conversion, already a float. do the formatting */ + result = format_float_internal(obj, &format); + break; + + default: + /* unknown */ + unknown_presentation_type(format.type, obj->ob_type->tp_name); + goto done; + } + +done: + return result; +} + +PyObject * +_PyComplex_FormatAdvanced(PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + PyObject *result = NULL; + InternalFormatSpec format; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + result = PyObject_Str(obj); + goto done; + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(format_spec, start, end, + &format, '\0', '>')) + goto done; + + /* type conversion? */ + switch (format.type) { + case '\0': /* No format code: like 'g', but with at least one decimal. */ + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'n': + /* no conversion, already a complex. do the formatting */ + result = format_complex_internal(obj, &format); + break; + + default: + /* unknown */ + unknown_presentation_type(format.type, obj->ob_type->tp_name); + goto done; + } + +done: + return result; +} -- cgit v1.2.1 From 0511538a040f2b8c33fdafcfeabf4ad8bf8ff754 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Sep 2011 21:50:16 +0200 Subject: fill_char() can now propagate an error --- Python/formatter_unicode.c | 72 ++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 25 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 9fb840b160..86385f6f7f 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -341,7 +341,7 @@ fill_padding(PyObject *s, Py_ssize_t start, Py_ssize_t nchars, /* Pad on right. */ if (n_rpadding) - unicode_fill(s, start + nchars + n_lpadding, + unicode_fill(s, start + nchars + n_lpadding, start + nchars + n_lpadding + n_rpadding, fill_char); /* Pointer to the user content. */ @@ -501,7 +501,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, spec->n_grouped_digits = 0; else spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( - PyUnicode_1BYTE_KIND, NULL, 0, NULL, + PyUnicode_1BYTE_KIND, NULL, 0, NULL, spec->n_digits, spec->n_min_width, locale->grouping, locale->thousands_sep); @@ -541,11 +541,12 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, /* Fill in the digit parts of a numbers's string representation, as determined in calc_number_widths(). - No error checking, since we know the buffer is the correct size. */ -static void + Return -1 on error, or 0 on success. */ +static int fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, PyObject *digits, Py_ssize_t d_start, Py_ssize_t d_end, - PyObject *prefix, Py_ssize_t p_start, Py_UCS4 fill_char, + PyObject *prefix, Py_ssize_t p_start, + Py_UCS4 fill_char, LocaleInfo *locale, int toupper) { /* Used to keep track of digits, decimal, and remainder. */ @@ -589,11 +590,8 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, char *pdigits = PyUnicode_DATA(digits); if (PyUnicode_KIND(digits) < kind) { pdigits = _PyUnicode_AsKind(digits, kind); - if (pdigits == NULL) { - /* XXX report exception */ - Py_FatalError("out of memory"); - return; - } + if (pdigits == NULL) + return -1; } #ifndef NDEBUG r = @@ -640,6 +638,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, unicode_fill(out, pos, pos + spec->n_rpadding, fill_char); pos += spec->n_rpadding; } + return 0; } static char no_grouping[1] = {CHAR_MAX}; @@ -765,6 +764,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, Py_ssize_t prefix; NumberFieldWidths spec; long x; + int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -886,10 +886,13 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, goto done; /* Populate the memory. */ - fill_number(result, 0, &spec, tmp, inumeric_chars, inumeric_chars + n_digits, - tmp, prefix, - format->fill_char == '\0' ? ' ' : format->fill_char, - &locale, format->type == 'X'); + err = fill_number(result, 0, &spec, + tmp, inumeric_chars, inumeric_chars + n_digits, + tmp, prefix, + format->fill_char == '\0' ? ' ' : format->fill_char, + &locale, format->type == 'X'); + if (err) + Py_CLEAR(result); done: Py_XDECREF(tmp); @@ -929,6 +932,7 @@ format_float_internal(PyObject *value, Py_UCS4 sign_char = '\0'; int float_type; /* Used to see if we have a nan, inf, or regular float. */ PyObject *unicode_tmp = NULL; + int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -1010,7 +1014,7 @@ format_float_internal(PyObject *value, &locale); /* Calculate how much memory we'll need. */ - n_total = calc_number_widths(&spec, 0, sign_char, unicode_tmp, index, + n_total = calc_number_widths(&spec, 0, sign_char, unicode_tmp, index, index + n_digits, n_remainder, has_decimal, &locale, format); @@ -1020,10 +1024,13 @@ format_float_internal(PyObject *value, goto done; /* Populate the memory. */ - fill_number(result, 0, &spec, unicode_tmp, index, index + n_digits, - NULL, 0, - format->fill_char == '\0' ? ' ' : format->fill_char, &locale, - 0); + err = fill_number(result, 0, &spec, + unicode_tmp, index, index + n_digits, + NULL, 0, + format->fill_char == '\0' ? ' ' : format->fill_char, + &locale, 0); + if (err) + Py_CLEAR(result); done: PyMem_Free(buf); @@ -1077,6 +1084,7 @@ format_complex_internal(PyObject *value, Py_ssize_t total; PyObject *re_unicode_tmp = NULL; PyObject *im_unicode_tmp = NULL; + int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -1170,9 +1178,9 @@ format_complex_internal(PyObject *value, /* Determine if we have any "remainder" (after the digits, might include decimal or exponent or both (or neither)) */ - parse_number(re_unicode_tmp, i_re, i_re + n_re_digits, + parse_number(re_unicode_tmp, i_re, i_re + n_re_digits, &n_re_remainder, &re_has_decimal); - parse_number(im_unicode_tmp, i_im, i_im + n_im_digits, + parse_number(im_unicode_tmp, i_im, i_im + n_im_digits, &n_im_remainder, &im_has_decimal); /* Determine the grouping, separator, and decimal point, if any. */ @@ -1225,12 +1233,26 @@ format_complex_internal(PyObject *value, PyUnicode_WRITE(rkind, rdata, index++, '('); if (!skip_re) { - fill_number(result, index, &re_spec, re_unicode_tmp, - i_re, i_re + n_re_digits, NULL, 0, 0, &locale, 0); + err = fill_number(result, index, &re_spec, + re_unicode_tmp, i_re, i_re + n_re_digits, + NULL, 0, + 0, + &locale, 0); + if (err) { + Py_CLEAR(result); + goto done; + } index += n_re_total; } - fill_number(result, index, &im_spec, im_unicode_tmp, - i_im, i_im + n_im_digits, NULL, 0, 0, &locale, 0); + err = fill_number(result, index, &im_spec, + im_unicode_tmp, i_im, i_im + n_im_digits, + NULL, 0, + 0, + &locale, 0); + if (err) { + Py_CLEAR(result); + goto done; + } index += n_im_total; PyUnicode_WRITE(rkind, rdata, index++, 'j'); -- cgit v1.2.1 From 895a5d4f7037dfc0563aa482d07b5d32b5dca481 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Sep 2011 21:50:42 +0200 Subject: fill_number() ensures that the 'digits' string is ready --- Python/formatter_unicode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 86385f6f7f..52ccafa8d0 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -587,7 +587,10 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, /* Only for type 'c' special case, it has no digits. */ if (spec->n_digits != 0) { /* Fill the digits with InsertThousandsGrouping. */ - char *pdigits = PyUnicode_DATA(digits); + char *pdigits; + if (PyUnicode_READY(digits)) + return -1; + pdigits = PyUnicode_DATA(digits); if (PyUnicode_KIND(digits) < kind) { pdigits = _PyUnicode_AsKind(digits, kind); if (pdigits == NULL) -- cgit v1.2.1 From 5afe8babef534ce040e31ce8d000ce622a3366c7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Sep 2011 21:53:49 +0200 Subject: fill_number() and format_string_internal() check for PyUnicode_CopyCharacters() failure --- Python/formatter_unicode.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 52ccafa8d0..609df648b0 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -566,7 +566,10 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, PyUnicode_WRITE(kind, data, pos++, spec->sign); } if (spec->n_prefix) { - PyUnicode_CopyCharacters(out, pos, prefix, p_start, spec->n_prefix); + if (PyUnicode_CopyCharacters(out, pos, + prefix, p_start, + spec->n_prefix) < 0) + return -1; if (toupper) { Py_ssize_t t; /* XXX if the upper-case prefix is wider than the target @@ -632,7 +635,8 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, } if (spec->n_remainder) { - PyUnicode_CopyCharacters(out, pos, digits, d_pos, spec->n_remainder); + if (PyUnicode_CopyCharacters(out, pos, digits, d_pos, spec->n_remainder) < 0) + return -1; pos += spec->n_remainder; d_pos += spec->n_remainder; } @@ -735,7 +739,8 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) lpad, rpad); /* Then the source string. */ - PyUnicode_CopyCharacters(result, pos, value, 0, len); + if (PyUnicode_CopyCharacters(result, pos, value, 0, len) < 0) + Py_CLEAR(result); done: return result; -- cgit v1.2.1 From 4cea3a5c83fca73972f3cf75b9688542b85c4042 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 5 Oct 2011 21:24:08 +0200 Subject: Add asciilib: similar to ucs1, ucs2 and ucs4 library, but specialized to ASCII ucs1, ucs2 and ucs4 libraries have to scan created substring to find the maximum character, whereas it is not need to ASCII strings. Because ASCII strings are common, it is useful to optimize ASCII. --- Python/formatter_unicode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 609df648b0..c989d83654 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -501,7 +501,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, spec->n_grouped_digits = 0; else spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( - PyUnicode_1BYTE_KIND, NULL, 0, NULL, + NULL, PyUnicode_1BYTE_KIND, NULL, 0, NULL, spec->n_digits, spec->n_min_width, locale->grouping, locale->thousands_sep); @@ -603,7 +603,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, r = #endif _PyUnicode_InsertThousandsGrouping( - kind, + out, kind, (char*)data + PyUnicode_KIND_SIZE(kind, pos), spec->n_grouped_digits, pdigits + PyUnicode_KIND_SIZE(kind, d_pos), -- cgit v1.2.1 From 7cba64b1d6dd00da0c98fceaac11e3fc7086c852 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 6 Oct 2011 01:45:57 +0200 Subject: Don't check for the maximum character when copying from unicodeobject.c * Create copy_characters() function which doesn't check for the maximum character in release mode * _PyUnicode_CheckConsistency() is no more static to be able to use it in _PyUnicode_FormatAdvanced() (in formatter_unicode.c) * _PyUnicode_CheckConsistency() checks the string hash --- Python/formatter_unicode.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index c989d83654..a389734390 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -1284,33 +1284,31 @@ _PyUnicode_FormatAdvanced(PyObject *obj, Py_ssize_t start, Py_ssize_t end) { InternalFormatSpec format; - PyObject *result = NULL; + PyObject *result; /* check for the special case of zero length format spec, make it equivalent to str(obj) */ - if (start == end) { - result = PyObject_Str(obj); - goto done; - } + if (start == end) + return PyObject_Str(obj); /* parse the format_spec */ if (!parse_internal_render_format_spec(format_spec, start, end, &format, 's', '<')) - goto done; + return NULL; /* type conversion? */ switch (format.type) { case 's': /* no type conversion needed, already a string. do the formatting */ result = format_string_internal(obj, &format); + if (result != NULL) + assert(_PyUnicode_CheckConsistency(result, 1)); break; default: /* unknown */ unknown_presentation_type(format.type, obj->ob_type->tp_name); - goto done; + result = NULL; } - -done: return result; } -- cgit v1.2.1 From 631b94c03720b2c23e0098dfae52fed6526c6782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 7 Oct 2011 20:55:35 +0200 Subject: Change PyUnicode_KIND to 1,2,4. Drop _KIND_SIZE and _CHARACTER_SIZE. --- Python/formatter_unicode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index a389734390..037880068a 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -604,9 +604,9 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, #endif _PyUnicode_InsertThousandsGrouping( out, kind, - (char*)data + PyUnicode_KIND_SIZE(kind, pos), + (char*)data + kind * pos, spec->n_grouped_digits, - pdigits + PyUnicode_KIND_SIZE(kind, d_pos), + pdigits + kind * d_pos, spec->n_digits, spec->n_min_width, locale->grouping, locale->thousands_sep); #ifndef NDEBUG -- cgit v1.2.1 From a9a394e1adfc52461ed6e062282256b6f59e809b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 11 Oct 2011 22:11:42 +0200 Subject: Fix misuse of PyUnicode_GET_SIZE, use PyUnicode_GET_LENGTH instead --- Python/formatter_unicode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 037880068a..fda79fc657 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -693,7 +693,7 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) Py_ssize_t rpad; Py_ssize_t total; Py_ssize_t pos; - Py_ssize_t len = PyUnicode_GET_SIZE(value); + Py_ssize_t len = PyUnicode_GET_LENGTH(value); PyObject *result = NULL; int maxchar = 127; -- cgit v1.2.1 From c2a7e9ed61166d07969bf0553753b86430ef6943 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Thu, 1 Dec 2011 15:27:04 +0000 Subject: Issue #9530: Fix undefined behaviour due to signed overflow in Python/formatter_unicode.c. --- Python/formatter_unicode.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index fda79fc657..6deb3cd7fe 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -51,7 +51,7 @@ static int get_integer(PyObject *str, Py_ssize_t *pos, Py_ssize_t end, Py_ssize_t *result) { - Py_ssize_t accumulator, digitval, oldaccumulator; + Py_ssize_t accumulator, digitval; int numdigits; accumulator = numdigits = 0; for (;;(*pos)++, numdigits++) { @@ -61,19 +61,17 @@ get_integer(PyObject *str, Py_ssize_t *pos, Py_ssize_t end, if (digitval < 0) break; /* - This trick was copied from old Unicode format code. It's cute, - but would really suck on an old machine with a slow divide - implementation. Fortunately, in the normal case we do not - expect too many digits. + Detect possible overflow before it happens: + + accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if + accumulator > (PY_SSIZE_T_MAX - digitval) / 10. */ - oldaccumulator = accumulator; - accumulator *= 10; - if ((accumulator+10)/10 != oldaccumulator+1) { + if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) { PyErr_Format(PyExc_ValueError, "Too many decimal digits in format string"); return -1; } - accumulator += digitval; + accumulator = accumulator * 10 + digitval; } *result = accumulator; return numdigits; -- cgit v1.2.1 From 6ebaf24a65c493bffaccd39bacf38b12e4604fec Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Jan 2012 00:33:50 +0100 Subject: Add a new PyUnicode_Fill() function It is faster than the unicode_fill() function which was implemented in formatter_unicode.c. --- Python/formatter_unicode.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 6deb3cd7fe..ef0151192b 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -317,15 +317,6 @@ calc_padding(Py_ssize_t nchars, Py_ssize_t width, Py_UCS4 align, *n_rpadding = *n_total - nchars - *n_lpadding; } -static void -unicode_fill(PyObject *str, Py_ssize_t start, Py_ssize_t end, Py_UCS4 ch) -{ - int kind = PyUnicode_KIND(str); - void *data = PyUnicode_DATA(str); - while (start < end) - PyUnicode_WRITE(kind, data, start++, ch); -} - /* Do the padding, and return a pointer to where the caller-supplied content goes. */ static Py_ssize_t @@ -335,12 +326,12 @@ fill_padding(PyObject *s, Py_ssize_t start, Py_ssize_t nchars, { /* Pad on left. */ if (n_lpadding) - unicode_fill(s, start, start + n_lpadding, fill_char); + PyUnicode_Fill(s, start, start + n_lpadding, fill_char); /* Pad on right. */ if (n_rpadding) - unicode_fill(s, start + nchars + n_lpadding, - start + nchars + n_lpadding + n_rpadding, fill_char); + PyUnicode_Fill(s, start + nchars + n_lpadding, + start + nchars + n_lpadding + n_rpadding, fill_char); /* Pointer to the user content. */ return start + n_lpadding; @@ -557,7 +548,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, #endif if (spec->n_lpadding) { - unicode_fill(out, pos, pos + spec->n_lpadding, fill_char); + PyUnicode_Fill(out, pos, pos + spec->n_lpadding, fill_char); pos += spec->n_lpadding; } if (spec->n_sign == 1) { @@ -581,7 +572,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, pos += spec->n_prefix; } if (spec->n_spadding) { - unicode_fill(out, pos, pos + spec->n_spadding, fill_char); + PyUnicode_Fill(out, pos, pos + spec->n_spadding, fill_char); pos += spec->n_spadding; } @@ -640,7 +631,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, } if (spec->n_rpadding) { - unicode_fill(out, pos, pos + spec->n_rpadding, fill_char); + PyUnicode_Fill(out, pos, pos + spec->n_rpadding, fill_char); pos += spec->n_rpadding; } return 0; -- cgit v1.2.1 From 0d2f16a616919bf255b1b100e18347381b39a928 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 11 Jan 2012 21:00:42 -0500 Subject: remove some usage of Py_UNICODE_TOUPPER/LOWER --- Python/formatter_unicode.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index ef0151192b..12b880d7e7 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -561,13 +561,14 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, return -1; if (toupper) { Py_ssize_t t; - /* XXX if the upper-case prefix is wider than the target - buffer, the caller should have allocated a wider string, - but currently doesn't. */ - for (t = 0; t < spec->n_prefix; ++t) - PyUnicode_WRITE(kind, data, pos + t, - Py_UNICODE_TOUPPER( - PyUnicode_READ(kind, data, pos + t))); + for (t = 0; t < spec->n_prefix; t++) { + Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + if (c > 127) { + PyErr_SetString(PyExc_SystemError, "prefix not ASCII"); + return -1; + } + PyUnicode_WRITE(kind, data, pos + t, Py_TOUPPER(c)); + } } pos += spec->n_prefix; } @@ -607,10 +608,14 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, } if (toupper) { Py_ssize_t t; - for (t = 0; t < spec->n_grouped_digits; ++t) - PyUnicode_WRITE(kind, data, pos + t, - Py_UNICODE_TOUPPER( - PyUnicode_READ(kind, data, pos + t))); + for (t = 0; t < spec->n_grouped_digits; t++) { + Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + if (c > 127) { + PyErr_SetString(PyExc_SystemError, "non-ascii grouped digit"); + return -1; + } + PyUnicode_WRITE(kind, data, pos + t, Py_TOUPPER(c)); + } } pos += spec->n_grouped_digits; -- cgit v1.2.1 From a492ac5b62a9caa2282f4db44ebb767d1f1cfd0e Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Thu, 19 Jan 2012 20:04:28 -0500 Subject: Improve exception text. Closes issue 13811. --- Python/formatter_unicode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 12b880d7e7..db6364f513 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -241,8 +241,8 @@ parse_internal_render_format_spec(PyObject *format_spec, /* Finally, parse the type field. */ if (end-pos > 1) { - /* More than one char remain, invalid conversion spec. */ - PyErr_Format(PyExc_ValueError, "Invalid conversion specification"); + /* More than one char remain, invalid format specifier. */ + PyErr_Format(PyExc_ValueError, "Invalid format specifier"); return 0; } -- cgit v1.2.1 From 588f4583fb3bb584abb3f2277f3d84ef6dc8c53d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 21 Jan 2012 15:50:49 +0100 Subject: Issue #13706: Support non-ASCII fill characters --- Python/formatter_unicode.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index db6364f513..609ac16899 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -274,12 +274,8 @@ parse_internal_render_format_spec(PyObject *format_spec, } } - if (format->fill_char > 127 || format->align > 127 || - format->sign > 127) { - PyErr_SetString(PyExc_ValueError, "fill character too large"); - return 0; - } - + assert (format->align <= 127); + assert (format->sign <= 127); return 1; } @@ -563,10 +559,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, Py_ssize_t t; for (t = 0; t < spec->n_prefix; t++) { Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); - if (c > 127) { - PyErr_SetString(PyExc_SystemError, "prefix not ASCII"); - return -1; - } + assert (c <= 127); PyUnicode_WRITE(kind, data, pos + t, Py_TOUPPER(c)); } } @@ -722,6 +715,9 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) calc_padding(len, format->width, format->align, &lpad, &rpad, &total); + if (lpad != 0 || rpad != 0) + maxchar = Py_MAX(maxchar, format->fill_char); + /* allocate the resulting string */ result = PyUnicode_New(total, maxchar); if (result == NULL) @@ -791,21 +787,18 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, /* taken from unicodeobject.c formatchar() */ /* Integer input truncated to a character */ -/* XXX: won't work for int */ x = PyLong_AsLong(value); if (x == -1 && PyErr_Occurred()) goto done; if (x < 0 || x > 0x10ffff) { PyErr_SetString(PyExc_OverflowError, - "%c arg not in range(0x110000) " - "(wide Python build)"); + "%c arg not in range(0x110000)"); goto done; } tmp = PyUnicode_FromOrdinal(x); inumeric_chars = 0; n_digits = 1; - if (x > maxchar) - maxchar = x; + maxchar = Py_MAX(maxchar, x); /* As a sort-of hack, we tell calc_number_widths that we only have "remainder" characters. calc_number_widths thinks @@ -882,6 +875,9 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, n_total = calc_number_widths(&spec, n_prefix, sign_char, tmp, inumeric_chars, inumeric_chars + n_digits, n_remainder, 0, &locale, format); + if (spec.n_lpadding || spec.n_spadding || spec.n_rpadding) + maxchar = Py_MAX(maxchar, format->fill_char); + /* Allocate the memory. */ result = PyUnicode_New(n_total, maxchar); if (!result) @@ -1020,6 +1016,9 @@ format_float_internal(PyObject *value, index + n_digits, n_remainder, has_decimal, &locale, format); + if (spec.n_lpadding || spec.n_spadding || spec.n_rpadding) + maxchar = Py_MAX(maxchar, format->fill_char); + /* Allocate the memory. */ result = PyUnicode_New(n_total, maxchar); if (result == NULL) @@ -1219,6 +1218,11 @@ format_complex_internal(PyObject *value, calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, format->width, format->align, &lpad, &rpad, &total); + if (re_spec.n_lpadding || re_spec.n_spadding || re_spec.n_rpadding + || im_spec.n_lpadding || im_spec.n_spadding || im_spec.n_rpadding + || lpad || rpad) + maxchar = Py_MAX(maxchar, format->fill_char); + result = PyUnicode_New(total, maxchar); if (result == NULL) goto done; -- cgit v1.2.1 From 9d06170f8ed5c860eaf1ba30fb10e4e8a5e79b6b Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 23 Jan 2012 22:42:19 +0100 Subject: Fix compilation warnings (seen on win32 buildbot) --- Python/formatter_unicode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 609ac16899..db8c27c9e4 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -682,7 +682,7 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) Py_ssize_t pos; Py_ssize_t len = PyUnicode_GET_LENGTH(value); PyObject *result = NULL; - int maxchar = 127; + Py_UCS4 maxchar = 127; /* sign is not allowed on strings */ if (format->sign != '\0') { @@ -749,7 +749,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, IntOrLongToString tostring) { PyObject *result = NULL; - int maxchar = 127; + Py_UCS4 maxchar = 127; PyObject *tmp = NULL; Py_ssize_t inumeric_chars; Py_UCS4 sign_char = '\0'; @@ -926,7 +926,7 @@ format_float_internal(PyObject *value, NumberFieldWidths spec; int flags = 0; PyObject *result = NULL; - int maxchar = 127; + Py_UCS4 maxchar = 127; Py_UCS4 sign_char = '\0'; int float_type; /* Used to see if we have a nan, inf, or regular float. */ PyObject *unicode_tmp = NULL; @@ -1070,7 +1070,7 @@ format_complex_internal(PyObject *value, NumberFieldWidths im_spec; int flags = 0; PyObject *result = NULL; - int maxchar = 127; + Py_UCS4 maxchar = 127; int rkind; void *rdata; Py_ssize_t index; -- cgit v1.2.1 From 597ed82954be94ee0d4cf498586e4eb4e4c301aa Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 23 Jan 2012 23:20:43 +0100 Subject: Silence last compilation warning. --- Python/formatter_unicode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index db8c27c9e4..eee99134ad 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -798,7 +798,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, tmp = PyUnicode_FromOrdinal(x); inumeric_chars = 0; n_digits = 1; - maxchar = Py_MAX(maxchar, x); + maxchar = Py_MAX(maxchar, (Py_UCS4)x); /* As a sort-of hack, we tell calc_number_widths that we only have "remainder" characters. calc_number_widths thinks -- cgit v1.2.1 From 3e0ad1e1013b0ceefe9b761cbe21d5e9511dfeb5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 1 Feb 2012 00:22:23 +0100 Subject: Issue #13706: Add assertions to detect bugs earlier --- Python/formatter_unicode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index eee99134ad..ed716a5b97 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -559,8 +559,9 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, Py_ssize_t t; for (t = 0; t < spec->n_prefix; t++) { Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + c = Py_TOUPPER(c); assert (c <= 127); - PyUnicode_WRITE(kind, data, pos + t, Py_TOUPPER(c)); + PyUnicode_WRITE(kind, data, pos + t, c); } } pos += spec->n_prefix; @@ -603,11 +604,12 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, Py_ssize_t t; for (t = 0; t < spec->n_grouped_digits; t++) { Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + c = Py_TOUPPER(c); if (c > 127) { PyErr_SetString(PyExc_SystemError, "non-ascii grouped digit"); return -1; } - PyUnicode_WRITE(kind, data, pos + t, Py_TOUPPER(c)); + PyUnicode_WRITE(kind, data, pos + t, c); } } pos += spec->n_grouped_digits; @@ -733,6 +735,7 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) Py_CLEAR(result); done: + assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -759,7 +762,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, produces non-digits */ Py_ssize_t n_prefix = 0; /* Count of prefix chars, (e.g., '0x') */ Py_ssize_t n_total; - Py_ssize_t prefix; + Py_ssize_t prefix = 0; NumberFieldWidths spec; long x; int err; @@ -894,6 +897,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, done: Py_XDECREF(tmp); + assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -1036,6 +1040,7 @@ format_float_internal(PyObject *value, done: PyMem_Free(buf); Py_DECREF(unicode_tmp); + assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -1270,6 +1275,7 @@ done: PyMem_Free(im_buf); Py_XDECREF(re_unicode_tmp); Py_XDECREF(im_unicode_tmp); + assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } -- cgit v1.2.1 From 9e8e9c35e7a50d4fadb0898b3a09ae9029f04239 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 24 Feb 2012 00:37:51 +0100 Subject: Issue #13706: Fix format(int, "n") for locale with non-ASCII thousands separator * Decode thousands separator and decimal point using PyUnicode_DecodeLocale() (from the locale encoding), instead of decoding them implicitly from latin1 * Remove _PyUnicode_InsertThousandsGroupingLocale(), it was not used * Change _PyUnicode_InsertThousandsGrouping() API to return the maximum character if unicode is NULL * Replace MIN/MAX macros by Py_MIN/Py_MAX * stringlib/undef.h undefines STRINGLIB_IS_UNICODE * stringlib/localeutil.h only supports Unicode --- Python/formatter_unicode.c | 142 ++++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 53 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index ed716a5b97..94f8047e18 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -346,11 +346,13 @@ fill_padding(PyObject *s, Py_ssize_t start, Py_ssize_t nchars, before and including the decimal. Note that locales only support 8-bit chars, not unicode. */ typedef struct { - char *decimal_point; - char *thousands_sep; - char *grouping; + PyObject *decimal_point; + PyObject *thousands_sep; + const char *grouping; } LocaleInfo; +#define STATIC_LOCALE_INFO_INIT {0, 0, 0} + /* describes the layout for an integer, see the comment in calc_number_widths() for details */ typedef struct { @@ -415,7 +417,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, Py_UCS4 sign_char, PyObject *number, Py_ssize_t n_start, Py_ssize_t n_end, Py_ssize_t n_remainder, int has_decimal, const LocaleInfo *locale, - const InternalFormatSpec *format) + const InternalFormatSpec *format, Py_UCS4 *maxchar) { Py_ssize_t n_non_digit_non_padding; Py_ssize_t n_padding; @@ -423,7 +425,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, spec->n_digits = n_end - n_start - n_remainder - (has_decimal?1:0); spec->n_lpadding = 0; spec->n_prefix = n_prefix; - spec->n_decimal = has_decimal ? strlen(locale->decimal_point) : 0; + spec->n_decimal = has_decimal ? PyUnicode_GET_LENGTH(locale->decimal_point) : 0; spec->n_remainder = n_remainder; spec->n_spadding = 0; spec->n_rpadding = 0; @@ -484,11 +486,15 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, to special case it because the grouping code always wants to have at least one character. */ spec->n_grouped_digits = 0; - else + else { + Py_UCS4 grouping_maxchar; spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( - NULL, PyUnicode_1BYTE_KIND, NULL, 0, NULL, + NULL, 0, + 0, NULL, spec->n_digits, spec->n_min_width, - locale->grouping, locale->thousands_sep); + locale->grouping, locale->thousands_sep, &grouping_maxchar); + *maxchar = Py_MAX(*maxchar, grouping_maxchar); + } /* Given the desired width and the total of digit and non-digit space we consume, see if we need any padding. format->width can @@ -519,6 +525,10 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, break; } } + + if (spec->n_lpadding || spec->n_spadding || spec->n_rpadding) + *maxchar = Py_MAX(*maxchar, format->fill_char); + return spec->n_lpadding + spec->n_sign + spec->n_prefix + spec->n_spadding + spec->n_grouped_digits + spec->n_decimal + spec->n_remainder + spec->n_rpadding; @@ -587,12 +597,11 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, r = #endif _PyUnicode_InsertThousandsGrouping( - out, kind, - (char*)data + kind * pos, + out, pos, spec->n_grouped_digits, pdigits + kind * d_pos, spec->n_digits, spec->n_min_width, - locale->grouping, locale->thousands_sep); + locale->grouping, locale->thousands_sep, NULL); #ifndef NDEBUG assert(r == spec->n_grouped_digits); #endif @@ -615,10 +624,8 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, pos += spec->n_grouped_digits; if (spec->n_decimal) { - Py_ssize_t t; - for (t = 0; t < spec->n_decimal; ++t) - PyUnicode_WRITE(kind, data, pos + t, - locale->decimal_point[t]); + if (PyUnicode_CopyCharacters(out, pos, locale->decimal_point, 0, spec->n_decimal) < 0) + return -1; pos += spec->n_decimal; d_pos += 1; } @@ -643,32 +650,60 @@ static char no_grouping[1] = {CHAR_MAX}; grouping description, either for the current locale if type is LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE, or none if LT_NO_LOCALE. */ -static void +static int get_locale_info(int type, LocaleInfo *locale_info) { switch (type) { case LT_CURRENT_LOCALE: { struct lconv *locale_data = localeconv(); - locale_info->decimal_point = locale_data->decimal_point; - locale_info->thousands_sep = locale_data->thousands_sep; + locale_info->decimal_point = PyUnicode_DecodeLocale( + locale_data->decimal_point, + NULL); + if (locale_info->decimal_point == NULL) + return -1; + locale_info->thousands_sep = PyUnicode_DecodeLocale( + locale_data->thousands_sep, + NULL); + if (locale_info->thousands_sep == NULL) { + Py_DECREF(locale_info->decimal_point); + return -1; + } locale_info->grouping = locale_data->grouping; break; } case LT_DEFAULT_LOCALE: - locale_info->decimal_point = "."; - locale_info->thousands_sep = ","; + locale_info->decimal_point = PyUnicode_FromOrdinal('.'); + locale_info->thousands_sep = PyUnicode_FromOrdinal(','); + if (!locale_info->decimal_point || !locale_info->thousands_sep) { + Py_XDECREF(locale_info->decimal_point); + Py_XDECREF(locale_info->thousands_sep); + return -1; + } locale_info->grouping = "\3"; /* Group every 3 characters. The (implicit) trailing 0 means repeat infinitely. */ break; case LT_NO_LOCALE: - locale_info->decimal_point = "."; - locale_info->thousands_sep = ""; + locale_info->decimal_point = PyUnicode_FromOrdinal('.'); + locale_info->thousands_sep = PyUnicode_New(0, 0); + if (!locale_info->decimal_point || !locale_info->thousands_sep) { + Py_XDECREF(locale_info->decimal_point); + Py_XDECREF(locale_info->thousands_sep); + return -1; + } locale_info->grouping = no_grouping; break; default: assert(0); } + return 0; +} + +static void +free_locale_info(LocaleInfo *locale_info) +{ + Py_XDECREF(locale_info->decimal_point); + Py_XDECREF(locale_info->thousands_sep); } /************************************************************************/ @@ -769,7 +804,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ - LocaleInfo locale; + LocaleInfo locale = STATIC_LOCALE_INFO_INIT; /* no precision allowed on integers */ if (format->precision != -1) { @@ -868,18 +903,17 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, } /* Determine the grouping, separator, and decimal point, if any. */ - get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - (format->thousands_separators ? - LT_DEFAULT_LOCALE : - LT_NO_LOCALE), - &locale); + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale) == -1) + goto done; /* Calculate how much memory we'll need. */ n_total = calc_number_widths(&spec, n_prefix, sign_char, tmp, inumeric_chars, - inumeric_chars + n_digits, n_remainder, 0, &locale, format); - - if (spec.n_lpadding || spec.n_spadding || spec.n_rpadding) - maxchar = Py_MAX(maxchar, format->fill_char); + inumeric_chars + n_digits, n_remainder, 0, + &locale, format, &maxchar); /* Allocate the memory. */ result = PyUnicode_New(n_total, maxchar); @@ -897,6 +931,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, done: Py_XDECREF(tmp); + free_locale_info(&locale); assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -938,7 +973,7 @@ format_float_internal(PyObject *value, /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ - LocaleInfo locale; + LocaleInfo locale = STATIC_LOCALE_INFO_INIT; if (format->alternate) flags |= Py_DTSF_ALT; @@ -1009,19 +1044,17 @@ format_float_internal(PyObject *value, parse_number(unicode_tmp, index, index + n_digits, &n_remainder, &has_decimal); /* Determine the grouping, separator, and decimal point, if any. */ - get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - (format->thousands_separators ? - LT_DEFAULT_LOCALE : - LT_NO_LOCALE), - &locale); + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale) == -1) + goto done; /* Calculate how much memory we'll need. */ n_total = calc_number_widths(&spec, 0, sign_char, unicode_tmp, index, index + n_digits, n_remainder, has_decimal, - &locale, format); - - if (spec.n_lpadding || spec.n_spadding || spec.n_rpadding) - maxchar = Py_MAX(maxchar, format->fill_char); + &locale, format, &maxchar); /* Allocate the memory. */ result = PyUnicode_New(n_total, maxchar); @@ -1040,6 +1073,7 @@ format_float_internal(PyObject *value, done: PyMem_Free(buf); Py_DECREF(unicode_tmp); + free_locale_info(&locale); assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -1094,7 +1128,7 @@ format_complex_internal(PyObject *value, /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ - LocaleInfo locale; + LocaleInfo locale = STATIC_LOCALE_INFO_INIT; /* Zero padding is not allowed. */ if (format->fill_char == '0') { @@ -1190,11 +1224,12 @@ format_complex_internal(PyObject *value, &n_im_remainder, &im_has_decimal); /* Determine the grouping, separator, and decimal point, if any. */ - get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - (format->thousands_separators ? - LT_DEFAULT_LOCALE : - LT_NO_LOCALE), - &locale); + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + (format->thousands_separators ? + LT_DEFAULT_LOCALE : + LT_NO_LOCALE), + &locale) == -1) + goto done; /* Turn off any padding. We'll do it later after we've composed the numbers without padding. */ @@ -1205,7 +1240,8 @@ format_complex_internal(PyObject *value, /* Calculate how much memory we'll need. */ n_re_total = calc_number_widths(&re_spec, 0, re_sign_char, re_unicode_tmp, i_re, i_re + n_re_digits, n_re_remainder, - re_has_decimal, &locale, &tmp_format); + re_has_decimal, &locale, &tmp_format, + &maxchar); /* Same formatting, but always include a sign, unless the real part is * going to be omitted, in which case we use whatever sign convention was @@ -1214,7 +1250,8 @@ format_complex_internal(PyObject *value, tmp_format.sign = '+'; n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, im_unicode_tmp, i_im, i_im + n_im_digits, n_im_remainder, - im_has_decimal, &locale, &tmp_format); + im_has_decimal, &locale, &tmp_format, + &maxchar); if (skip_re) n_re_total = 0; @@ -1223,9 +1260,7 @@ format_complex_internal(PyObject *value, calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, format->width, format->align, &lpad, &rpad, &total); - if (re_spec.n_lpadding || re_spec.n_spadding || re_spec.n_rpadding - || im_spec.n_lpadding || im_spec.n_spadding || im_spec.n_rpadding - || lpad || rpad) + if (lpad || rpad) maxchar = Py_MAX(maxchar, format->fill_char); result = PyUnicode_New(total, maxchar); @@ -1275,6 +1310,7 @@ done: PyMem_Free(im_buf); Py_XDECREF(re_unicode_tmp); Py_XDECREF(im_unicode_tmp); + free_locale_info(&locale); assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } -- cgit v1.2.1 From c0cd820dd5983d13501dab01aab1fcf6380a8e0c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 24 Feb 2012 01:44:47 +0100 Subject: Issue #13706: Fix format(float, "n") for locale with non-ASCII decimal point (e.g. ps_aF) --- Python/formatter_unicode.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 94f8047e18..58e66e0d90 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -529,6 +529,9 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, if (spec->n_lpadding || spec->n_spadding || spec->n_rpadding) *maxchar = Py_MAX(*maxchar, format->fill_char); + if (spec->n_decimal) + *maxchar = Py_MAX(*maxchar, PyUnicode_MAX_CHAR_VALUE(locale->decimal_point)); + return spec->n_lpadding + spec->n_sign + spec->n_prefix + spec->n_spadding + spec->n_grouped_digits + spec->n_decimal + spec->n_remainder + spec->n_rpadding; @@ -548,10 +551,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, Py_ssize_t d_pos = d_start; unsigned int kind = PyUnicode_KIND(out); void *data = PyUnicode_DATA(out); - -#ifndef NDEBUG Py_ssize_t r; -#endif if (spec->n_lpadding) { PyUnicode_Fill(out, pos, pos + spec->n_lpadding, fill_char); @@ -593,18 +593,15 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, if (pdigits == NULL) return -1; } -#ifndef NDEBUG - r = -#endif - _PyUnicode_InsertThousandsGrouping( + r = _PyUnicode_InsertThousandsGrouping( out, pos, spec->n_grouped_digits, pdigits + kind * d_pos, spec->n_digits, spec->n_min_width, locale->grouping, locale->thousands_sep, NULL); -#ifndef NDEBUG + if (r == -1) + return -1; assert(r == spec->n_grouped_digits); -#endif if (PyUnicode_KIND(digits) < kind) PyMem_Free(pdigits); d_pos += spec->n_digits; -- cgit v1.2.1 From 675bdd2d38a866504d416c9d553513636d18f438 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 23 Apr 2012 10:55:29 -0400 Subject: inherit maxchar of field value where needed (closes #14648) --- Python/formatter_unicode.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 58e66e0d90..5e5b19f7fe 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -747,6 +747,9 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) len = format->precision; } + if (len) + maxchar = PyUnicode_MAX_CHAR_VALUE(value); + calc_padding(len, format->width, format->align, &lpad, &rpad, &total); if (lpad != 0 || rpad != 0) -- cgit v1.2.1 From 3241cd3276fdb9e4a1486e5c20a8f3876bca6c40 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 23 Apr 2012 23:36:38 +0200 Subject: Close #14648: Compute correctly maxchar in str.format() for substrin --- Python/formatter_unicode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 5e5b19f7fe..e1c00df9e6 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -716,7 +716,7 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) Py_ssize_t pos; Py_ssize_t len = PyUnicode_GET_LENGTH(value); PyObject *result = NULL; - Py_UCS4 maxchar = 127; + Py_UCS4 maxchar; /* sign is not allowed on strings */ if (format->sign != '\0') { @@ -747,11 +747,9 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) len = format->precision; } - if (len) - maxchar = PyUnicode_MAX_CHAR_VALUE(value); - calc_padding(len, format->width, format->align, &lpad, &rpad, &total); + maxchar = _PyUnicode_FindMaxChar(value, 0, len); if (lpad != 0 || rpad != 0) maxchar = Py_MAX(maxchar, format->fill_char); -- cgit v1.2.1 From 47ac69e2ef9682fb306f4e681c4cbe53a055224f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 29 May 2012 12:57:52 +0200 Subject: Issue #14744: Use the new _PyUnicodeWriter internal API to speed up str%args and str.format(args) * Formatting string, int, float and complex use the _PyUnicodeWriter API. It avoids a temporary buffer in most cases. * Add _PyUnicodeWriter_WriteStr() to restore the PyAccu optimization: just keep a reference to the string if the output is only composed of one string * Disable overallocation when formatting the last argument of str%args and str.format(args) * Overallocation allocates at least 100 characters: add min_length attribute to the _PyUnicodeWriter structure * Add new private functions: _PyUnicode_FastCopyCharacters(), _PyUnicode_FastFill() and _PyUnicode_FromASCII() The speed up is around 20% in average. --- Python/formatter_unicode.c | 412 ++++++++++++++++++++++++--------------------- 1 file changed, 218 insertions(+), 194 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index e1c00df9e6..fdb587d073 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -316,21 +316,28 @@ calc_padding(Py_ssize_t nchars, Py_ssize_t width, Py_UCS4 align, /* Do the padding, and return a pointer to where the caller-supplied content goes. */ static Py_ssize_t -fill_padding(PyObject *s, Py_ssize_t start, Py_ssize_t nchars, +fill_padding(_PyUnicodeWriter *writer, + Py_ssize_t nchars, Py_UCS4 fill_char, Py_ssize_t n_lpadding, Py_ssize_t n_rpadding) { + Py_ssize_t pos; + /* Pad on left. */ - if (n_lpadding) - PyUnicode_Fill(s, start, start + n_lpadding, fill_char); + if (n_lpadding) { + pos = writer->pos; + _PyUnicode_FastFill(writer->buffer, pos, n_lpadding, fill_char); + } /* Pad on right. */ - if (n_rpadding) - PyUnicode_Fill(s, start + nchars + n_lpadding, - start + nchars + n_lpadding + n_rpadding, fill_char); + if (n_rpadding) { + pos = writer->pos + nchars + n_lpadding; + _PyUnicode_FastFill(writer->buffer, pos, n_rpadding, fill_char); + } /* Pointer to the user content. */ - return start + n_lpadding; + writer->pos += n_lpadding; + return 0; } /************************************************************************/ @@ -541,7 +548,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, as determined in calc_number_widths(). Return -1 on error, or 0 on success. */ static int -fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, +fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec, PyObject *digits, Py_ssize_t d_start, Py_ssize_t d_end, PyObject *prefix, Py_ssize_t p_start, Py_UCS4 fill_char, @@ -549,36 +556,38 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, { /* Used to keep track of digits, decimal, and remainder. */ Py_ssize_t d_pos = d_start; - unsigned int kind = PyUnicode_KIND(out); - void *data = PyUnicode_DATA(out); + const enum PyUnicode_Kind kind = writer->kind; + const void *data = writer->data; Py_ssize_t r; if (spec->n_lpadding) { - PyUnicode_Fill(out, pos, pos + spec->n_lpadding, fill_char); - pos += spec->n_lpadding; + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_lpadding, fill_char); + writer->pos += spec->n_lpadding; } if (spec->n_sign == 1) { - PyUnicode_WRITE(kind, data, pos++, spec->sign); + PyUnicode_WRITE(kind, data, writer->pos, spec->sign); + writer->pos++; } if (spec->n_prefix) { - if (PyUnicode_CopyCharacters(out, pos, - prefix, p_start, - spec->n_prefix) < 0) - return -1; + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + prefix, p_start, + spec->n_prefix); if (toupper) { Py_ssize_t t; for (t = 0; t < spec->n_prefix; t++) { - Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); c = Py_TOUPPER(c); assert (c <= 127); - PyUnicode_WRITE(kind, data, pos + t, c); + PyUnicode_WRITE(kind, data, writer->pos + t, c); } } - pos += spec->n_prefix; + writer->pos += spec->n_prefix; } if (spec->n_spadding) { - PyUnicode_Fill(out, pos, pos + spec->n_spadding, fill_char); - pos += spec->n_spadding; + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_spadding, fill_char); + writer->pos += spec->n_spadding; } /* Only for type 'c' special case, it has no digits. */ @@ -594,7 +603,7 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, return -1; } r = _PyUnicode_InsertThousandsGrouping( - out, pos, + writer->buffer, writer->pos, spec->n_grouped_digits, pdigits + kind * d_pos, spec->n_digits, spec->n_min_width, @@ -609,34 +618,38 @@ fill_number(PyObject *out, Py_ssize_t pos, const NumberFieldWidths *spec, if (toupper) { Py_ssize_t t; for (t = 0; t < spec->n_grouped_digits; t++) { - Py_UCS4 c = PyUnicode_READ(kind, data, pos + t); + Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); c = Py_TOUPPER(c); if (c > 127) { PyErr_SetString(PyExc_SystemError, "non-ascii grouped digit"); return -1; } - PyUnicode_WRITE(kind, data, pos + t, c); + PyUnicode_WRITE(kind, data, writer->pos + t, c); } } - pos += spec->n_grouped_digits; + writer->pos += spec->n_grouped_digits; if (spec->n_decimal) { - if (PyUnicode_CopyCharacters(out, pos, locale->decimal_point, 0, spec->n_decimal) < 0) - return -1; - pos += spec->n_decimal; + _PyUnicode_FastCopyCharacters( + writer->buffer, writer->pos, + locale->decimal_point, 0, spec->n_decimal); + writer->pos += spec->n_decimal; d_pos += 1; } if (spec->n_remainder) { - if (PyUnicode_CopyCharacters(out, pos, digits, d_pos, spec->n_remainder) < 0) - return -1; - pos += spec->n_remainder; + _PyUnicode_FastCopyCharacters( + writer->buffer, writer->pos, + digits, d_pos, spec->n_remainder); + writer->pos += spec->n_remainder; d_pos += spec->n_remainder; } if (spec->n_rpadding) { - PyUnicode_Fill(out, pos, pos + spec->n_rpadding, fill_char); - pos += spec->n_rpadding; + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_rpadding, + fill_char); + writer->pos += spec->n_rpadding; } return 0; } @@ -707,17 +720,20 @@ free_locale_info(LocaleInfo *locale_info) /*********** string formatting ******************************************/ /************************************************************************/ -static PyObject * -format_string_internal(PyObject *value, const InternalFormatSpec *format) +static int +format_string_internal(PyObject *value, const InternalFormatSpec *format, + _PyUnicodeWriter *writer) { Py_ssize_t lpad; Py_ssize_t rpad; Py_ssize_t total; - Py_ssize_t pos; - Py_ssize_t len = PyUnicode_GET_LENGTH(value); - PyObject *result = NULL; + Py_ssize_t len; + int result = -1; Py_UCS4 maxchar; + assert(PyUnicode_IS_READY(value)); + len = PyUnicode_GET_LENGTH(value); + /* sign is not allowed on strings */ if (format->sign != '\0') { PyErr_SetString(PyExc_ValueError, @@ -741,6 +757,11 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) goto done; } + if (format->width == -1 && format->precision == -1) { + /* Fast path */ + return _PyUnicodeWriter_WriteStr(writer, value); + } + /* if precision is specified, output no more that format.precision characters */ if (format->precision >= 0 && len >= format->precision) { @@ -754,21 +775,23 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) maxchar = Py_MAX(maxchar, format->fill_char); /* allocate the resulting string */ - result = PyUnicode_New(total, maxchar); - if (result == NULL) + if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) goto done; /* Write into that space. First the padding. */ - pos = fill_padding(result, 0, len, - format->fill_char=='\0'?' ':format->fill_char, - lpad, rpad); + result = fill_padding(writer, len, + format->fill_char=='\0'?' ':format->fill_char, + lpad, rpad); + if (result == -1) + goto done; /* Then the source string. */ - if (PyUnicode_CopyCharacters(result, pos, value, 0, len) < 0) - Py_CLEAR(result); + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + value, 0, len); + writer->pos += (len + rpad); + result = 0; done: - assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -780,11 +803,11 @@ done: typedef PyObject* (*IntOrLongToString)(PyObject *value, int base); -static PyObject * -format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, - IntOrLongToString tostring) +static int +format_long_internal(PyObject *value, const InternalFormatSpec *format, + _PyUnicodeWriter *writer) { - PyObject *result = NULL; + int result = -1; Py_UCS4 maxchar = 127; PyObject *tmp = NULL; Py_ssize_t inumeric_chars; @@ -798,7 +821,6 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, Py_ssize_t prefix = 0; NumberFieldWidths spec; long x; - int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -872,13 +894,23 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, break; } + if (format->sign != '+' && format->sign != ' ' + && format->width == -1 + && format->type != 'X' && format->type != 'n' + && !format->thousands_separators + && PyLong_CheckExact(value)) + { + /* Fast path */ + return _PyLong_FormatWriter(writer, value, base, format->alternate); + } + /* The number of prefix chars is the same as the leading chars to skip */ if (format->alternate) n_prefix = leading_chars_to_skip; /* Do the hard part, converting to a string in a given base */ - tmp = tostring(value, base); + tmp = _PyLong_Format(value, base); if (tmp == NULL || PyUnicode_READY(tmp) == -1) goto done; @@ -914,23 +946,19 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format, &locale, format, &maxchar); /* Allocate the memory. */ - result = PyUnicode_New(n_total, maxchar); - if (!result) + if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) goto done; /* Populate the memory. */ - err = fill_number(result, 0, &spec, - tmp, inumeric_chars, inumeric_chars + n_digits, - tmp, prefix, - format->fill_char == '\0' ? ' ' : format->fill_char, - &locale, format->type == 'X'); - if (err) - Py_CLEAR(result); + result = fill_number(writer, &spec, + tmp, inumeric_chars, inumeric_chars + n_digits, + tmp, prefix, + format->fill_char == '\0' ? ' ' : format->fill_char, + &locale, format->type == 'X'); done: Py_XDECREF(tmp); free_locale_info(&locale); - assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -938,16 +966,11 @@ done: /*********** float formatting *******************************************/ /************************************************************************/ -static PyObject* -strtounicode(char *charbuffer, Py_ssize_t len) -{ - return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, charbuffer, len); -} - /* much of this is taken from unicodeobject.c */ -static PyObject * +static int format_float_internal(PyObject *value, - const InternalFormatSpec *format) + const InternalFormatSpec *format, + _PyUnicodeWriter *writer) { char *buf = NULL; /* buffer returned from PyOS_double_to_string */ Py_ssize_t n_digits; @@ -962,12 +985,11 @@ format_float_internal(PyObject *value, Py_ssize_t index; NumberFieldWidths spec; int flags = 0; - PyObject *result = NULL; + int result = -1; Py_UCS4 maxchar = 127; Py_UCS4 sign_char = '\0'; int float_type; /* Used to see if we have a nan, inf, or regular float. */ PyObject *unicode_tmp = NULL; - int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -1024,13 +1046,25 @@ format_float_internal(PyObject *value, /* Since there is no unicode version of PyOS_double_to_string, just use the 8 bit version and then convert to unicode. */ - unicode_tmp = strtounicode(buf, n_digits); + unicode_tmp = _PyUnicode_FromASCII(buf, n_digits); + PyMem_Free(buf); if (unicode_tmp == NULL) goto done; - index = 0; + + if (format->sign != '+' && format->sign != ' ' + && format->width == -1 + && format->type != 'n' + && !format->thousands_separators) + { + /* Fast path */ + result = _PyUnicodeWriter_WriteStr(writer, unicode_tmp); + Py_DECREF(unicode_tmp); + return result; + } /* Is a sign character present in the output? If so, remember it and skip it */ + index = 0; if (PyUnicode_READ_CHAR(unicode_tmp, index) == '-') { sign_char = '-'; ++index; @@ -1055,24 +1089,19 @@ format_float_internal(PyObject *value, &locale, format, &maxchar); /* Allocate the memory. */ - result = PyUnicode_New(n_total, maxchar); - if (result == NULL) + if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) goto done; /* Populate the memory. */ - err = fill_number(result, 0, &spec, - unicode_tmp, index, index + n_digits, - NULL, 0, - format->fill_char == '\0' ? ' ' : format->fill_char, - &locale, 0); - if (err) - Py_CLEAR(result); + result = fill_number(writer, &spec, + unicode_tmp, index, index + n_digits, + NULL, 0, + format->fill_char == '\0' ? ' ' : format->fill_char, + &locale, 0); done: - PyMem_Free(buf); Py_DECREF(unicode_tmp); free_locale_info(&locale); - assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } @@ -1080,9 +1109,10 @@ done: /*********** complex formatting *****************************************/ /************************************************************************/ -static PyObject * +static int format_complex_internal(PyObject *value, - const InternalFormatSpec *format) + const InternalFormatSpec *format, + _PyUnicodeWriter *writer) { double re; double im; @@ -1106,11 +1136,10 @@ format_complex_internal(PyObject *value, NumberFieldWidths re_spec; NumberFieldWidths im_spec; int flags = 0; - PyObject *result = NULL; + int result = -1; Py_UCS4 maxchar = 127; - int rkind; + enum PyUnicode_Kind rkind; void *rdata; - Py_ssize_t index; Py_UCS4 re_sign_char = '\0'; Py_UCS4 im_sign_char = '\0'; int re_float_type; /* Used to see if we have a nan, inf, or regular float. */ @@ -1122,7 +1151,6 @@ format_complex_internal(PyObject *value, Py_ssize_t total; PyObject *re_unicode_tmp = NULL; PyObject *im_unicode_tmp = NULL; - int err; /* Locale settings, either from the actual locale or from a hard-code pseudo-locale */ @@ -1191,12 +1219,12 @@ format_complex_internal(PyObject *value, /* Since there is no unicode version of PyOS_double_to_string, just use the 8 bit version and then convert to unicode. */ - re_unicode_tmp = strtounicode(re_buf, n_re_digits); + re_unicode_tmp = _PyUnicode_FromASCII(re_buf, n_re_digits); if (re_unicode_tmp == NULL) goto done; i_re = 0; - im_unicode_tmp = strtounicode(im_buf, n_im_digits); + im_unicode_tmp = _PyUnicode_FromASCII(im_buf, n_im_digits); if (im_unicode_tmp == NULL) goto done; i_im = 0; @@ -1261,47 +1289,49 @@ format_complex_internal(PyObject *value, if (lpad || rpad) maxchar = Py_MAX(maxchar, format->fill_char); - result = PyUnicode_New(total, maxchar); - if (result == NULL) + if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) goto done; - rkind = PyUnicode_KIND(result); - rdata = PyUnicode_DATA(result); + rkind = writer->kind; + rdata = writer->data; /* Populate the memory. First, the padding. */ - index = fill_padding(result, 0, - n_re_total + n_im_total + 1 + add_parens * 2, - format->fill_char=='\0' ? ' ' : format->fill_char, - lpad, rpad); + result = fill_padding(writer, + n_re_total + n_im_total + 1 + add_parens * 2, + format->fill_char=='\0' ? ' ' : format->fill_char, + lpad, rpad); + if (result == -1) + goto done; - if (add_parens) - PyUnicode_WRITE(rkind, rdata, index++, '('); + if (add_parens) { + PyUnicode_WRITE(rkind, rdata, writer->pos, '('); + writer->pos++; + } if (!skip_re) { - err = fill_number(result, index, &re_spec, - re_unicode_tmp, i_re, i_re + n_re_digits, - NULL, 0, - 0, - &locale, 0); - if (err) { - Py_CLEAR(result); + result = fill_number(writer, &re_spec, + re_unicode_tmp, i_re, i_re + n_re_digits, + NULL, 0, + 0, + &locale, 0); + if (result == -1) goto done; - } - index += n_re_total; } - err = fill_number(result, index, &im_spec, - im_unicode_tmp, i_im, i_im + n_im_digits, - NULL, 0, - 0, - &locale, 0); - if (err) { - Py_CLEAR(result); + result = fill_number(writer, &im_spec, + im_unicode_tmp, i_im, i_im + n_im_digits, + NULL, 0, + 0, + &locale, 0); + if (result == -1) goto done; + PyUnicode_WRITE(rkind, rdata, writer->pos, 'j'); + writer->pos++; + + if (add_parens) { + PyUnicode_WRITE(rkind, rdata, writer->pos, ')'); + writer->pos++; } - index += n_im_total; - PyUnicode_WRITE(rkind, rdata, index++, 'j'); - if (add_parens) - PyUnicode_WRITE(rkind, rdata, index++, ')'); + writer->pos += rpad; done: PyMem_Free(re_buf); @@ -1309,61 +1339,79 @@ done: Py_XDECREF(re_unicode_tmp); Py_XDECREF(im_unicode_tmp); free_locale_info(&locale); - assert(!result || _PyUnicode_CheckConsistency(result, 1)); return result; } /************************************************************************/ /*********** built in formatters ****************************************/ /************************************************************************/ -PyObject * -_PyUnicode_FormatAdvanced(PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) +int +format_obj(PyObject *obj, _PyUnicodeWriter *writer) +{ + PyObject *str; + int err; + + str = PyObject_Str(obj); + if (str == NULL) + return -1; + err = _PyUnicodeWriter_WriteStr(writer, str); + Py_DECREF(str); + return err; +} + +int +_PyUnicode_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) { InternalFormatSpec format; - PyObject *result; + + assert(PyUnicode_Check(obj)); /* check for the special case of zero length format spec, make it equivalent to str(obj) */ - if (start == end) - return PyObject_Str(obj); + if (start == end) { + if (PyUnicode_CheckExact(obj)) + return _PyUnicodeWriter_WriteStr(writer, obj); + else + return format_obj(obj, writer); + } /* parse the format_spec */ if (!parse_internal_render_format_spec(format_spec, start, end, &format, 's', '<')) - return NULL; + return -1; /* type conversion? */ switch (format.type) { case 's': /* no type conversion needed, already a string. do the formatting */ - result = format_string_internal(obj, &format); - if (result != NULL) - assert(_PyUnicode_CheckConsistency(result, 1)); - break; + return format_string_internal(obj, &format, writer); default: /* unknown */ unknown_presentation_type(format.type, obj->ob_type->tp_name); - result = NULL; + return -1; } - return result; } -static PyObject* -format_int_or_long(PyObject* obj, PyObject* format_spec, - Py_ssize_t start, Py_ssize_t end, - IntOrLongToString tostring) +int +_PyLong_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) { - PyObject *result = NULL; - PyObject *tmp = NULL; + PyObject *tmp = NULL, *str = NULL; InternalFormatSpec format; + int result = -1; /* check for the special case of zero length format spec, make it equivalent to str(obj) */ if (start == end) { - result = PyObject_Str(obj); - goto done; + if (PyLong_CheckExact(obj)) + return _PyLong_FormatWriter(writer, obj, 10, 0); + else + return format_obj(obj, writer); } /* parse the format_spec */ @@ -1382,7 +1430,7 @@ format_int_or_long(PyObject* obj, PyObject* format_spec, case 'n': /* no type conversion needed, already an int (or long). do the formatting */ - result = format_int_or_long_internal(obj, &format, tostring); + result = format_long_internal(obj, &format, writer); break; case 'e': @@ -1396,7 +1444,7 @@ format_int_or_long(PyObject* obj, PyObject* format_spec, tmp = PyNumber_Float(obj); if (tmp == NULL) goto done; - result = format_float_internal(tmp, &format); + result = format_float_internal(tmp, &format, writer); break; default: @@ -1407,41 +1455,27 @@ format_int_or_long(PyObject* obj, PyObject* format_spec, done: Py_XDECREF(tmp); + Py_XDECREF(str); return result; } -/* Need to define long_format as a function that will convert a long - to a string. In 3.0, _PyLong_Format has the correct signature. */ -#define long_format _PyLong_Format - -PyObject * -_PyLong_FormatAdvanced(PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) +int +_PyFloat_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) { - return format_int_or_long(obj, format_spec, start, end, - long_format); -} - -PyObject * -_PyFloat_FormatAdvanced(PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) -{ - PyObject *result = NULL; InternalFormatSpec format; /* check for the special case of zero length format spec, make it equivalent to str(obj) */ - if (start == end) { - result = PyObject_Str(obj); - goto done; - } + if (start == end) + return format_obj(obj, writer); /* parse the format_spec */ if (!parse_internal_render_format_spec(format_spec, start, end, &format, '\0', '>')) - goto done; + return -1; /* type conversion? */ switch (format.type) { @@ -1455,38 +1489,32 @@ _PyFloat_FormatAdvanced(PyObject *obj, case 'n': case '%': /* no conversion, already a float. do the formatting */ - result = format_float_internal(obj, &format); - break; + return format_float_internal(obj, &format, writer); default: /* unknown */ unknown_presentation_type(format.type, obj->ob_type->tp_name); - goto done; + return -1; } - -done: - return result; } -PyObject * -_PyComplex_FormatAdvanced(PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) +int +_PyComplex_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) { - PyObject *result = NULL; InternalFormatSpec format; /* check for the special case of zero length format spec, make it equivalent to str(obj) */ - if (start == end) { - result = PyObject_Str(obj); - goto done; - } + if (start == end) + return format_obj(obj, writer); /* parse the format_spec */ if (!parse_internal_render_format_spec(format_spec, start, end, &format, '\0', '>')) - goto done; + return -1; /* type conversion? */ switch (format.type) { @@ -1499,15 +1527,11 @@ _PyComplex_FormatAdvanced(PyObject *obj, case 'G': case 'n': /* no conversion, already a complex. do the formatting */ - result = format_complex_internal(obj, &format); - break; + return format_complex_internal(obj, &format, writer); default: /* unknown */ unknown_presentation_type(format.type, obj->ob_type->tp_name); - goto done; + return -1; } - -done: - return result; } -- cgit v1.2.1 From 8ebaf6f9098e414c044616640ce8200a6c094af5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 16 Jun 2012 02:22:37 +0200 Subject: Optimize _PyUnicode_FastCopyCharacters() when maxchar(from) > maxchar(to) --- Python/formatter_unicode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index fdb587d073..cd6667053e 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -786,8 +786,10 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format, goto done; /* Then the source string. */ - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - value, 0, len); + if (len) { + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + value, 0, len); + } writer->pos += (len + rpad); result = 0; -- cgit v1.2.1 From a3e7f9ea1c2ebd81f616d0969d12ad3b38fbe828 Mon Sep 17 00:00:00 2001 From: doko Date: Thu, 21 Jun 2012 12:12:20 +0200 Subject: format_obj: make it static --- Python/formatter_unicode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index cd6667053e..4b0fd91489 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -1347,7 +1347,7 @@ done: /************************************************************************/ /*********** built in formatters ****************************************/ /************************************************************************/ -int +static int format_obj(PyObject *obj, _PyUnicodeWriter *writer) { PyObject *str; -- cgit v1.2.1 From f00bb7f434a0e6b04549de35efe8329bf388d377 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 25 Jun 2012 16:13:44 -0400 Subject: Comment out a dead increment. Found by Clang's static analyzer. --- Python/formatter_unicode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 4b0fd91489..661bf192e9 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -642,7 +642,7 @@ fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec, writer->buffer, writer->pos, digits, d_pos, spec->n_remainder); writer->pos += spec->n_remainder; - d_pos += spec->n_remainder; + /* d_pos += spec->n_remainder; */ } if (spec->n_rpadding) { -- cgit v1.2.1 From 9d4261308fd0cc1ba49c7c938e9ae2bac14e33e1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 9 Aug 2012 21:38:23 +0200 Subject: Remove now unused IntOrLongToString type --- Python/formatter_unicode.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 661bf192e9..a263efc84f 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -802,9 +802,6 @@ done: /*********** long formatting ********************************************/ /************************************************************************/ -typedef PyObject* -(*IntOrLongToString)(PyObject *value, int base); - static int format_long_internal(PyObject *value, const InternalFormatSpec *format, _PyUnicodeWriter *writer) -- cgit v1.2.1 From fe140a41154b7e9801ddadef167374d4b8cdfdcf Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Thu, 6 Sep 2012 13:02:46 +0200 Subject: After the jump in line 1051 unicode_tmp is NULL. Found by Coverity. --- Python/formatter_unicode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/formatter_unicode.c') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index a263efc84f..aa62502dbe 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -1099,7 +1099,7 @@ format_float_internal(PyObject *value, &locale, 0); done: - Py_DECREF(unicode_tmp); + Py_XDECREF(unicode_tmp); free_locale_info(&locale); return result; } -- cgit v1.2.1