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