Home | History | Annotate | Download | only in stringlib
      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 _GroupGenerator_init(GroupGenerator *self, const char *grouping)
     19 {
     20     self->grouping = grouping;
     21     self->i = 0;
     22     self->previous = 0;
     23 }
     24 
     25 /* Returns the next grouping, or 0 to signify end. */
     26 static Py_ssize_t
     27 _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     switch (self->grouping[self->i]) {
     34     case 0:
     35         return self->previous;
     36     case CHAR_MAX:
     37         /* Stop the generator. */
     38         return 0;
     39     default: {
     40         char ch = self->grouping[self->i];
     41         self->previous = ch;
     42         self->i++;
     43         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 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     if (thousands_sep) {
     60         *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         memcpy(*buffer_end, thousands_sep, thousands_sep_len);
     71 #endif
     72     }
     73 
     74     *buffer_end -= n_chars;
     75     *digits_end -= n_chars;
     76     memcpy(*buffer_end, *digits_end, n_chars * sizeof(STRINGLIB_CHAR));
     77 
     78     *buffer_end -= n_zeros;
     79     STRINGLIB_FILL(*buffer_end, '0', n_zeros);
     80 }
     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 _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     Py_ssize_t count = 0;
    122     Py_ssize_t n_zeros;
    123     int loop_broken = 0;
    124     int use_separator = 0; /* First time through, don't append the
    125                               separator. They only go between
    126                               groups. */
    127     STRINGLIB_CHAR *buffer_end = NULL;
    128     STRINGLIB_CHAR *digits_end = NULL;
    129     Py_ssize_t l;
    130     Py_ssize_t n_chars;
    131     Py_ssize_t thousands_sep_len = strlen(thousands_sep);
    132     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     _GroupGenerator_init(&groupgen, grouping);
    138 
    139     if (buffer) {
    140         buffer_end = buffer + n_buffer;
    141         digits_end = digits + n_digits;
    142     }
    143 
    144     while ((l = _GroupGenerator_next(&groupgen)) > 0) {
    145         l = MIN(l, MAX(MAX(remaining, min_width), 1));
    146         n_zeros = MAX(0, l - remaining);
    147         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         count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
    153 
    154         if (buffer) {
    155             /* Copy into the output buffer. */
    156             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         use_separator = 1;
    162 
    163         remaining -= n_chars;
    164         min_width -= l;
    165 
    166         if (remaining <= 0 && min_width <= 0) {
    167             loop_broken = 1;
    168             break;
    169         }
    170         min_width -= thousands_sep_len;
    171     }
    172     if (!loop_broken) {
    173         /* We left the loop without using a break statement. */
    174 
    175         l = MAX(MAX(remaining, min_width), 1);
    176         n_zeros = MAX(0, l - remaining);
    177         n_chars = MAX(0, MIN(remaining, l));
    178 
    179         /* Use n_zero zero's and n_chars chars */
    180         count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
    181         if (buffer) {
    182             /* Copy into the output buffer. */
    183             fill(&digits_end, &buffer_end, n_chars, n_zeros,
    184                  use_separator ? thousands_sep : NULL, thousands_sep_len);
    185         }
    186     }
    187     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 _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         struct lconv *locale_data = localeconv();
    206         const char *grouping = locale_data->grouping;
    207         const char *thousands_sep = locale_data->thousands_sep;
    208 
    209         return _Py_InsertThousandsGrouping(buffer, n_buffer, digits, n_digits,
    210                                            min_width, grouping, thousands_sep);
    211 }
    212 #endif /* STRINGLIB_LOCALEUTIL_H */
    213