(root)/
glibc-2.38/
stdio-common/
grouping_iterator.c
       1  /* Iterator for inserting thousands separators into numbers.
       2     Copyright (C) 2022-2023 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.  */
      29  bool
      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  
      39  static bool
      40  grouping_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).  */
      90  static const char *
      91  get_grouping (int category, locale_t loc)
      92  {
      93    return _nl_lookup (loc, category,
      94                       category == LC_MONETARY ? MON_GROUPING : GROUPING);
      95  }
      96  
      97  
      98  bool
      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, get_grouping (category, loc));
     106  }
     107  
     108  bool
     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  }