LCOV - code coverage report
Current view: top level - Objects/stringlib - localeutil.h (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 37 62 59.7 %
Date: 2017-04-19 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* stringlib: locale related helpers implementation */
       2             : 
       3             : #ifndef STRINGLIB_LOCALEUTIL_H
       4             : #define STRINGLIB_LOCALEUTIL_H
       5             : 
       6             : #include <locale.h>
       7             : 
       8             : #define MAX(x, y) ((x) < (y) ? (y) : (x))
       9             : #define MIN(x, y) ((x) < (y) ? (x) : (y))
      10             : 
      11             : typedef struct {
      12             :     const char *grouping;
      13             :     char previous;
      14             :     Py_ssize_t i; /* Where we're currently pointing in grouping. */
      15             : } GroupGenerator;
      16             : 
      17             : static void
      18         756 : _GroupGenerator_init(GroupGenerator *self, const char *grouping)
      19             : {
      20         756 :     self->grouping = grouping;
      21         756 :     self->i = 0;
      22         756 :     self->previous = 0;
      23         756 : }
      24             : 
      25             : /* Returns the next grouping, or 0 to signify end. */
      26             : static Py_ssize_t
      27         756 : _GroupGenerator_next(GroupGenerator *self)
      28             : {
      29             :     /* Note that we don't really do much error checking here. If a
      30             :        grouping string contains just CHAR_MAX, for example, then just
      31             :        terminate the generator. That shouldn't happen, but at least we
      32             :        fail gracefully. */
      33         756 :     switch (self->grouping[self->i]) {
      34             :     case 0:
      35           0 :         return self->previous;
      36             :     case CHAR_MAX:
      37             :         /* Stop the generator. */
      38         756 :         return 0;
      39             :     default: {
      40           0 :         char ch = self->grouping[self->i];
      41           0 :         self->previous = ch;
      42           0 :         self->i++;
      43           0 :         return (Py_ssize_t)ch;
      44             :     }
      45             :     }
      46             : }
      47             : 
      48             : /* Fill in some digits, leading zeros, and thousands separator. All
      49             :    are optional, depending on when we're called. */
      50             : static void
      51         378 : fill(STRINGLIB_CHAR **digits_end, STRINGLIB_CHAR **buffer_end,
      52             :      Py_ssize_t n_chars, Py_ssize_t n_zeros, const char* thousands_sep,
      53             :      Py_ssize_t thousands_sep_len)
      54             : {
      55             : #if STRINGLIB_IS_UNICODE
      56             :     Py_ssize_t i;
      57             : #endif
      58             : 
      59         378 :     if (thousands_sep) {
      60           0 :         *buffer_end -= thousands_sep_len;
      61             : 
      62             :         /* Copy the thousands_sep chars into the buffer. */
      63             : #if STRINGLIB_IS_UNICODE
      64             :         /* Convert from the char's of the thousands_sep from
      65             :            the locale into unicode. */
      66             :         for (i = 0; i < thousands_sep_len; ++i)
      67             :             (*buffer_end)[i] = thousands_sep[i];
      68             : #else
      69             :         /* No conversion, just memcpy the thousands_sep. */
      70           0 :         memcpy(*buffer_end, thousands_sep, thousands_sep_len);
      71             : #endif
      72             :     }
      73             : 
      74         378 :     *buffer_end -= n_chars;
      75         378 :     *digits_end -= n_chars;
      76         378 :     memcpy(*buffer_end, *digits_end, n_chars * sizeof(STRINGLIB_CHAR));
      77             : 
      78         378 :     *buffer_end -= n_zeros;
      79         378 :     STRINGLIB_FILL(*buffer_end, '0', n_zeros);
      80         378 : }
      81             : 
      82             : /**
      83             :  * _Py_InsertThousandsGrouping:
      84             :  * @buffer: A pointer to the start of a string.
      85             :  * @n_buffer: Number of characters in @buffer.
      86             :  * @digits: A pointer to the digits we're reading from. If count
      87             :  *          is non-NULL, this is unused.
      88             :  * @n_digits: The number of digits in the string, in which we want
      89             :  *            to put the grouping chars.
      90             :  * @min_width: The minimum width of the digits in the output string.
      91             :  *             Output will be zero-padded on the left to fill.
      92             :  * @grouping: see definition in localeconv().
      93             :  * @thousands_sep: see definition in localeconv().
      94             :  *
      95             :  * There are 2 modes: counting and filling. If @buffer is NULL,
      96             :  *  we are in counting mode, else filling mode.
      97             :  * If counting, the required buffer size is returned.
      98             :  * If filling, we know the buffer will be large enough, so we don't
      99             :  *  need to pass in the buffer size.
     100             :  * Inserts thousand grouping characters (as defined by grouping and
     101             :  *  thousands_sep) into the string between buffer and buffer+n_digits.
     102             :  *
     103             :  * Return value: 0 on error, else 1.  Note that no error can occur if
     104             :  *  count is non-NULL.
     105             :  *
     106             :  * This name won't be used, the includer of this file should define
     107             :  *  it to be the actual function name, based on unicode or string.
     108             :  *
     109             :  * As closely as possible, this code mimics the logic in decimal.py's
     110             :     _insert_thousands_sep().
     111             :  **/
     112             : Py_ssize_t
     113         756 : _Py_InsertThousandsGrouping(STRINGLIB_CHAR *buffer,
     114             :                             Py_ssize_t n_buffer,
     115             :                             STRINGLIB_CHAR *digits,
     116             :                             Py_ssize_t n_digits,
     117             :                             Py_ssize_t min_width,
     118             :                             const char *grouping,
     119             :                             const char *thousands_sep)
     120             : {
     121         756 :     Py_ssize_t count = 0;
     122             :     Py_ssize_t n_zeros;
     123         756 :     int loop_broken = 0;
     124         756 :     int use_separator = 0; /* First time through, don't append the
     125             :                               separator. They only go between
     126             :                               groups. */
     127         756 :     STRINGLIB_CHAR *buffer_end = NULL;
     128         756 :     STRINGLIB_CHAR *digits_end = NULL;
     129             :     Py_ssize_t l;
     130             :     Py_ssize_t n_chars;
     131         756 :     Py_ssize_t thousands_sep_len = strlen(thousands_sep);
     132         756 :     Py_ssize_t remaining = n_digits; /* Number of chars remaining to
     133             :                                         be looked at */
     134             :     /* A generator that returns all of the grouping widths, until it
     135             :        returns 0. */
     136             :     GroupGenerator groupgen;
     137         756 :     _GroupGenerator_init(&groupgen, grouping);
     138             : 
     139         756 :     if (buffer) {
     140         378 :         buffer_end = buffer + n_buffer;
     141         378 :         digits_end = digits + n_digits;
     142             :     }
     143             : 
     144        1512 :     while ((l = _GroupGenerator_next(&groupgen)) > 0) {
     145           0 :         l = MIN(l, MAX(MAX(remaining, min_width), 1));
     146           0 :         n_zeros = MAX(0, l - remaining);
     147           0 :         n_chars = MAX(0, MIN(remaining, l));
     148             : 
     149             :         /* Use n_zero zero's and n_chars chars */
     150             : 
     151             :         /* Count only, don't do anything. */
     152           0 :         count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
     153             : 
     154           0 :         if (buffer) {
     155             :             /* Copy into the output buffer. */
     156           0 :             fill(&digits_end, &buffer_end, n_chars, n_zeros,
     157             :                  use_separator ? thousands_sep : NULL, thousands_sep_len);
     158             :         }
     159             : 
     160             :         /* Use a separator next time. */
     161           0 :         use_separator = 1;
     162             : 
     163           0 :         remaining -= n_chars;
     164           0 :         min_width -= l;
     165             : 
     166           0 :         if (remaining <= 0 && min_width <= 0) {
     167           0 :             loop_broken = 1;
     168           0 :             break;
     169             :         }
     170           0 :         min_width -= thousands_sep_len;
     171             :     }
     172         756 :     if (!loop_broken) {
     173             :         /* We left the loop without using a break statement. */
     174             : 
     175         756 :         l = MAX(MAX(remaining, min_width), 1);
     176         756 :         n_zeros = MAX(0, l - remaining);
     177         756 :         n_chars = MAX(0, MIN(remaining, l));
     178             : 
     179             :         /* Use n_zero zero's and n_chars chars */
     180         756 :         count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
     181         756 :         if (buffer) {
     182             :             /* Copy into the output buffer. */
     183         378 :             fill(&digits_end, &buffer_end, n_chars, n_zeros,
     184             :                  use_separator ? thousands_sep : NULL, thousands_sep_len);
     185             :         }
     186             :     }
     187         756 :     return count;
     188             : }
     189             : 
     190             : /**
     191             :  * _Py_InsertThousandsGroupingLocale:
     192             :  * @buffer: A pointer to the start of a string.
     193             :  * @n_digits: The number of digits in the string, in which we want
     194             :  *            to put the grouping chars.
     195             :  *
     196             :  * Reads thee current locale and calls _Py_InsertThousandsGrouping().
     197             :  **/
     198             : Py_ssize_t
     199           0 : _Py_InsertThousandsGroupingLocale(STRINGLIB_CHAR *buffer,
     200             :                                   Py_ssize_t n_buffer,
     201             :                                   STRINGLIB_CHAR *digits,
     202             :                                   Py_ssize_t n_digits,
     203             :                                   Py_ssize_t min_width)
     204             : {
     205           0 :         struct lconv *locale_data = localeconv();
     206           0 :         const char *grouping = locale_data->grouping;
     207           0 :         const char *thousands_sep = locale_data->thousands_sep;
     208             : 
     209           0 :         return _Py_InsertThousandsGrouping(buffer, n_buffer, digits, n_digits,
     210             :                                            min_width, grouping, thousands_sep);
     211             : }
     212             : #endif /* STRINGLIB_LOCALEUTIL_H */

Generated by: LCOV version 1.10