From ffde06c915d10c0717a0980508ccb28506c6ec63 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Mon, 19 Dec 2022 18:56:54 +0100 Subject: locale: Implement struct grouping_iterator The iterator allows grouping while scanning forward through the digits. This enables emitting digits as they are processed. Reviewed-by: Adhemerval Zanella --- stdio-common/grouping_iterator.c | 126 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 stdio-common/grouping_iterator.c (limited to 'stdio-common/grouping_iterator.c') diff --git a/stdio-common/grouping_iterator.c b/stdio-common/grouping_iterator.c new file mode 100644 index 0000000000..e91ec2891d --- /dev/null +++ b/stdio-common/grouping_iterator.c @@ -0,0 +1,126 @@ +/* Iterator for inserting thousands separators into numbers. + Copyright (C) 2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include +#include +#include +#include +#include + +/* Initializes *IT with no grouping information for a string of length + DIGITS, and return false to indicate no grouping. */ +bool +__grouping_iterator_init_none (struct grouping_iterator *it, + unsigned int digits) +{ + memset (it, 0, sizeof (*it)); + it->remaining_in_current_group = digits; + it->remaining = digits; + return false; +} + +static bool +grouping_iterator_setup (struct grouping_iterator *it, unsigned int digits, + const char *grouping) +{ + /* We treat all negative values like CHAR_MAX. */ + + if (*grouping == CHAR_MAX || *grouping <= 0) + /* No grouping should be done. */ + return __grouping_iterator_init_none (it, digits); + + unsigned int remaining_to_group = digits; + unsigned int non_repeating_groups = 0; + unsigned int groups = 0; + while (true) + { + non_repeating_groups += *grouping; + if (remaining_to_group <= (unsigned int) *grouping) + break; + + ++groups; + remaining_to_group -= *grouping++; + + if (*grouping == CHAR_MAX +#if CHAR_MIN < 0 + || *grouping < 0 +#endif + ) + /* No more grouping should be done. */ + break; + else if (*grouping == 0) + { + /* Same grouping repeats. */ + --grouping; + non_repeating_groups -= *grouping; /* Over-counted. */ + unsigned int repeats = (remaining_to_group - 1) / *grouping; + groups += repeats; + remaining_to_group -= repeats * *grouping; + break; + } + } + + it->remaining_in_current_group = remaining_to_group; + it->remaining = digits; + it->groupings = grouping; + it->non_repeating_groups = non_repeating_groups; + it->separators = groups; + return it->separators > 0; +} + +/* Returns the appropriate grouping item in LOC depending on CATEGORY + (which must be LC_MONETARY or LC_NUMERIC). */ +static const char * +get_grouping (int category, locale_t loc) +{ + return _nl_lookup (loc, category, + category == LC_MONETARY ? MON_GROUPING : GROUPING); +} + + +bool +__grouping_iterator_init (struct grouping_iterator *it, + int category, locale_t loc, unsigned int digits) +{ + if (digits <= 1) + return __grouping_iterator_init_none (it, digits); + else + return grouping_iterator_setup (it, digits, get_grouping (category, loc)); +} + +bool +__grouping_iterator_next (struct grouping_iterator *it) +{ + assert (it->remaining > 0); + --it->remaining; + + if (it->remaining_in_current_group > 0) + { + --it->remaining_in_current_group; + return false; + } + + /* If we are in the non-repeating part, switch group. */ + if (it->remaining < it->non_repeating_groups) + --it->groupings; + + it->remaining_in_current_group = *it->groupings - 1; + return true; +} -- cgit 1.4.1