1/* Iterator for inserting thousands separators into numbers.
2 Copyright (C) 2022-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <grouping_iterator.h>
20
21#include <assert.h>
22#include <limits.h>
23#include <locale/localeinfo.h>
24#include <stdint.h>
25#include <string.h>
26
27/* Initializes *IT with no grouping information for a string of length
28 DIGITS, and return false to indicate no grouping. */
29bool
30__grouping_iterator_init_none (struct grouping_iterator *it,
31 unsigned int digits)
32{
33 memset (it, 0, sizeof (*it));
34 it->remaining_in_current_group = digits;
35 it->remaining = digits;
36 return false;
37}
38
39static bool
40grouping_iterator_setup (struct grouping_iterator *it, unsigned int digits,
41 const char *grouping)
42{
43 /* We treat all negative values like CHAR_MAX. */
44
45 if (*grouping == CHAR_MAX || *grouping <= 0)
46 /* No grouping should be done. */
47 return __grouping_iterator_init_none (it, digits);
48
49 unsigned int remaining_to_group = digits;
50 unsigned int non_repeating_groups = 0;
51 unsigned int groups = 0;
52 while (true)
53 {
54 non_repeating_groups += *grouping;
55 if (remaining_to_group <= (unsigned int) *grouping)
56 break;
57
58 ++groups;
59 remaining_to_group -= *grouping++;
60
61 if (*grouping == CHAR_MAX
62#if CHAR_MIN < 0
63 || *grouping < 0
64#endif
65 )
66 /* No more grouping should be done. */
67 break;
68 else if (*grouping == 0)
69 {
70 /* Same grouping repeats. */
71 --grouping;
72 non_repeating_groups -= *grouping; /* Over-counted. */
73 unsigned int repeats = (remaining_to_group - 1) / *grouping;
74 groups += repeats;
75 remaining_to_group -= repeats * *grouping;
76 break;
77 }
78 }
79
80 it->remaining_in_current_group = remaining_to_group;
81 it->remaining = digits;
82 it->groupings = grouping;
83 it->non_repeating_groups = non_repeating_groups;
84 it->separators = groups;
85 return it->separators > 0;
86}
87
88/* Returns the appropriate grouping item in LOC depending on CATEGORY
89 (which must be LC_MONETARY or LC_NUMERIC). */
90static const char *
91get_grouping (int category, locale_t loc)
92{
93 return _nl_lookup (l: loc, category,
94 item: category == LC_MONETARY ? MON_GROUPING : GROUPING);
95}
96
97
98bool
99__grouping_iterator_init (struct grouping_iterator *it,
100 int category, locale_t loc, unsigned int digits)
101{
102 if (digits <= 1)
103 return __grouping_iterator_init_none (it, digits);
104 else
105 return grouping_iterator_setup (it, digits, grouping: get_grouping (category, loc));
106}
107
108bool
109__grouping_iterator_next (struct grouping_iterator *it)
110{
111 assert (it->remaining > 0);
112 --it->remaining;
113
114 if (it->remaining_in_current_group > 0)
115 {
116 --it->remaining_in_current_group;
117 return false;
118 }
119
120 /* If we are in the non-repeating part, switch group. */
121 if (it->remaining < it->non_repeating_groups)
122 --it->groupings;
123
124 it->remaining_in_current_group = *it->groupings - 1;
125 return true;
126}
127

source code of glibc/stdio-common/grouping_iterator.c