Skip to content

Commit

Permalink
locale.c: Don't change locale if already there
Browse files Browse the repository at this point in the history
Changing the locale is cheap for some categories, but expensive for
others.  Changing LC_COLLATE is most expensive, requiring recalculation
of the collation transformation mapping.

This commit checks that we aren't already in the desired locale before
changing locales. and does nothing if no change is needed.
  • Loading branch information
khwilliamson committed Apr 29, 2021
1 parent 22a3c99 commit 23776c0
Showing 1 changed file with 37 additions and 0 deletions.
37 changes: 37 additions & 0 deletions locale.c
Expand Up @@ -2504,6 +2504,14 @@ time C<Perl_setlocale> is called from the same thread.
*/

#ifndef USE_LOCALE_NUMERIC
# define affects_LC_NUMERIC(cat) 0
#elif defined(LC_ALL)
# define affects_LC_NUMERIC(cat) (cat == LC_NUMERIC || cat == LC_ALL)
#else
# define affects_LC_NUMERIC(cat) (cat == LC_NUMERIC)
#endif

const char *
Perl_setlocale(const int category, const char * locale)
{
Expand Down Expand Up @@ -2580,7 +2588,25 @@ Perl_setlocale(const int category, const char * locale)
return retval;
} /* End of querying the current locale */


/* Here, the input has a locale to change to. First find the current
* locale */
cat_index = get_category_index(category, NULL);
retval = querylocale_i(cat_index);

/* If the new locale is the same as the current one, nothing is actually
* being changed, so do nothing. */
if ( strEQ(retval, locale)
&& (! affects_LC_NUMERIC(category) || strEQ(locale, PL_numeric_name)))
{
DEBUG_L(PerlIO_printf(Perl_debug_log,
"%s:%d: Already in requested locale: no action taken\n",
__FILE__, __LINE__));

return retval;
}

/* Here, an actual change is being requested. Do it */
retval = save_to_buffer(setlocale_i(cat_index, locale),
&PL_setlocale_buf, &PL_setlocale_bufsize);
if (! retval) {
Expand All @@ -2589,6 +2615,17 @@ Perl_setlocale(const int category, const char * locale)
return NULL;
}

/* We will need to save a copy of the return if the LC_NUMERIC category is
* involved. This is because we will toggle things back to the C locale,
* and the buffer 'retval' points to will be changed to point to that
* instead. Other categories may be temporarily switched by the
* update_functions calls below, but they should be switched back before
* returning, so the value will be restored to what it is now. */
if (affects_LC_NUMERIC(category)) {
retval = save_to_buffer(retval,
&PL_setlocale_buf, &PL_setlocale_bufsize);
}

/* Now that have changed locales, we have to update our records to
* correspond. Only certain categories have extra work to update. */
if (update_functions[cat_index]) {
Expand Down

0 comments on commit 23776c0

Please sign in to comment.