These three patches improve the Windows LCID lookup cache.

2025-12-10  Bruno Haible  <[email protected]>

        localename-unsafe: Improve Windows LCID lookup cache.
        * lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements
        instead of a cache with only one element.

        localename-unsafe: Improve Windows LCID lookup cache.
        Reported by Bryan Green <[email protected]> in
        <https://savannah.gnu.org/bugs/?67781>.
        * lib/localename-unsafe.c (get_lcid): Cache also the failed LCID
        lookups.

        localename-unsafe: Improve Windows LCID lookup cache.
        * lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale 
array.

>From 17d873bddaba8776fca8ceb2d5c180355deffd8e Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 10 Dec 2025 22:22:39 +0100
Subject: [PATCH 1/3] localename-unsafe: Improve Windows LCID lookup cache.

* lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array.
---
 ChangeLog               | 5 +++++
 lib/localename-unsafe.c | 4 ++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 52ac60d385..d9e20d56b2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2025-12-10  Bruno Haible  <[email protected]>
+
+	localename-unsafe: Improve Windows LCID lookup cache.
+	* lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array.
+
 2025-12-10  Bruno Haible  <[email protected]>
 
 	canonicalize-lgpl: Fix UMR introduced in recent change.
diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c
index 9177b6939d..f70cedafbc 100644
--- a/lib/localename-unsafe.c
+++ b/lib/localename-unsafe.c
@@ -2586,7 +2586,7 @@ get_lcid (const char *locale_name)
 {
   /* A simple cache.  */
   static LCID last_lcid;
-  static char last_locale[1000];
+  static char last_locale[sizeof (lname)];
 
   /* Lock while looking for an LCID, to protect access to static
      variables: last_lcid, last_locale, found_lcid, and lname.  */
@@ -2603,7 +2603,7 @@ get_lcid (const char *locale_name)
   if (found_lcid > 0)
     {
       last_lcid = found_lcid;
-      strcpy (last_locale, locale_name);
+      strcpy (last_locale, lname);
     }
   glwthread_mutex_unlock (&get_lcid_lock);
   return found_lcid;
-- 
2.52.0

>From fdbdc06db49643d838ba91c5084a4707fcdd87bf Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 10 Dec 2025 22:23:45 +0100
Subject: [PATCH 2/3] localename-unsafe: Improve Windows LCID lookup cache.

Reported by Bryan Green <[email protected]> in
<https://savannah.gnu.org/bugs/?67781>.

* lib/localename-unsafe.c (get_lcid): Cache also the failed LCID
lookups.
---
 ChangeLog               |  6 ++++++
 lib/localename-unsafe.c | 27 +++++++++++++++------------
 2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d9e20d56b2..6e07253e4b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2025-12-10  Bruno Haible  <[email protected]>
 
+	localename-unsafe: Improve Windows LCID lookup cache.
+	Reported by Bryan Green <[email protected]> in
+	<https://savannah.gnu.org/bugs/?67781>.
+	* lib/localename-unsafe.c (get_lcid): Cache also the failed LCID
+	lookups.
+
 	localename-unsafe: Improve Windows LCID lookup cache.
 	* lib/localename-unsafe.c (get_lcid): Don't overrun the last_locale array.
 
diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c
index f70cedafbc..009c044663 100644
--- a/lib/localename-unsafe.c
+++ b/lib/localename-unsafe.c
@@ -2580,33 +2580,36 @@ static glwthread_mutex_t get_lcid_lock = GLWTHREAD_MUTEX_INIT;
 /* Return the Locale ID (LCID) number given the locale's name, a
    string, in LOCALE_NAME.  This works by enumerating all the locales
    supported by the system, until we find one whose name matches
-   LOCALE_NAME.  */
+   LOCALE_NAME.  Return 0 if LOCALE_NAME does not correspond to a locale
+   supported by the system.  */
 static LCID
 get_lcid (const char *locale_name)
 {
   /* A simple cache.  */
+  static unsigned int last_cached /* = 0 */;
   static LCID last_lcid;
   static char last_locale[sizeof (lname)];
 
   /* Lock while looking for an LCID, to protect access to static
      variables: last_lcid, last_locale, found_lcid, and lname.  */
   glwthread_mutex_lock (&get_lcid_lock);
-  if (last_lcid > 0 && streq (locale_name, last_locale))
-    {
-      glwthread_mutex_unlock (&get_lcid_lock);
-      return last_lcid;
-    }
-  strncpy (lname, locale_name, sizeof (lname) - 1);
-  lname[sizeof (lname) - 1] = '\0';
-  found_lcid = 0;
-  EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED);
-  if (found_lcid > 0)
+
+  if (!(last_cached && streq (locale_name, last_locale)))
     {
+      strncpy (lname, locale_name, sizeof (lname) - 1);
+      lname[sizeof (lname) - 1] = '\0';
+
+      found_lcid = 0;
+      EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED);
+
       last_lcid = found_lcid;
       strcpy (last_locale, lname);
+      last_cached = 1;
     }
+
+  LCID result = last_lcid;
   glwthread_mutex_unlock (&get_lcid_lock);
-  return found_lcid;
+  return result;
 }
 
 # endif
-- 
2.52.0

>From e26598f163ed5e9ddfa41885fa479b5bc9f0a327 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Thu, 11 Dec 2025 00:55:13 +0100
Subject: [PATCH 3/3] localename-unsafe: Improve Windows LCID lookup cache.

* lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements
instead of a cache with only one element.
---
 ChangeLog               |   4 ++
 lib/localename-unsafe.c | 101 ++++++++++++++++++++++++++++++++++------
 2 files changed, 92 insertions(+), 13 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6e07253e4b..6cf490da82 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2025-12-10  Bruno Haible  <[email protected]>
 
+	localename-unsafe: Improve Windows LCID lookup cache.
+	* lib/localename-unsafe.c (get_lcid): Use an LRU cache with 6 elements
+	instead of a cache with only one element.
+
 	localename-unsafe: Improve Windows LCID lookup cache.
 	Reported by Bryan Green <[email protected]> in
 	<https://savannah.gnu.org/bugs/?67781>.
diff --git a/lib/localename-unsafe.c b/lib/localename-unsafe.c
index 009c044663..6af08beb19 100644
--- a/lib/localename-unsafe.c
+++ b/lib/localename-unsafe.c
@@ -2585,29 +2585,104 @@ static glwthread_mutex_t get_lcid_lock = GLWTHREAD_MUTEX_INIT;
 static LCID
 get_lcid (const char *locale_name)
 {
-  /* A simple cache.  */
-  static unsigned int last_cached /* = 0 */;
-  static LCID last_lcid;
-  static char last_locale[sizeof (lname)];
-
-  /* Lock while looking for an LCID, to protect access to static
-     variables: last_lcid, last_locale, found_lcid, and lname.  */
+  /* A least-recently-used cache with at most N = 6 entries.
+     (Because there are 6 locale categories.)  */
+
+  /* Number of bits for a index into the cache.  */
+  enum { nbits = 3 };
+  /* Maximum number of entries in the cache.  */
+  enum { N = 6 }; /* <= (1 << nbits) */
+  /* An entry in the cache.  */
+  typedef struct { LCID e_lcid; char e_locale[sizeof (lname)]; } entry_t;
+  /* An unsigned integer type with at least N * nbits bits.
+     Used as an array:
+       element [0] = bits nbits-1 .. 0,
+       element [1] = bits 2*nbits-1 .. nbits,
+       element [2] = bits 3*nbits-1 .. 2*nbits,
+       and so on.  */
+  typedef unsigned int indices_t;
+
+  /* Number of entries in the cache.  */
+  static size_t n; /* <= N */
+  /* The entire cache.  Only elements 0..n-1 are in use.  */
+  static entry_t lru[N];
+  /* Indices of used cache entries.  Only elements 0..n-1 are in use.  */
+  static indices_t indices;
+
+  /* Lock while looking for an LCID, to protect access to static variables:
+     found_lcid, lname, and the cache.  */
   glwthread_mutex_lock (&get_lcid_lock);
 
-  if (!(last_cached && streq (locale_name, last_locale)))
+  /* Look up locale_name in the cache.  */
+  size_t found = (size_t)(-1);
+  size_t i;
+  for (i = 0; i < n; i++)
+    {
+      size_t j = /* indices[i] */
+        (indices >> (nbits * i)) & ((1U << nbits) - 1U);
+      if (streq (locale_name, lru[j].e_locale))
+        {
+          found = j;
+          break;
+        }
+    }
+  LCID result;
+  if (i < n)
     {
+      /* We have a cache hit.  0 <= found < n.  */
+      result = lru[found].e_lcid;
+      if (i > 0)
+        {
+          /* Perform these assignments in parallel:
+             indices[0] := indices[i]
+             indices[1] := indices[0]
+             ...
+             indices[i] := indices[i-1]  */
+          indices = (indices & (-1U << (nbits * (i + 1))))
+                    | ((indices & ((1U << (nbits * i)) - 1U)) << nbits)
+                    | found;
+        }
+    }
+  else
+    {
+      /* We have a cache miss.  */
       strncpy (lname, locale_name, sizeof (lname) - 1);
       lname[sizeof (lname) - 1] = '\0';
 
       found_lcid = 0;
       EnumSystemLocales (enum_locales_fn, LCID_SUPPORTED);
 
-      last_lcid = found_lcid;
-      strcpy (last_locale, lname);
-      last_cached = 1;
-    }
+      size_t j;
+      if (n < N)
+        {
+          /* There is still room in the cache.  */
+          j = n;
+          /* Perform these assignments in parallel:
+             indices[0] := n
+             indices[1] := indices[0]
+             ...
+             indices[n] := indices[n-1]  */
+          indices = (indices << nbits) | n;
+          n++;
+        }
+      else /* n == N */
+        {
+          /* The cache is full.  Drop the least recently used entry and
+             reuse it.  */
+          j = /* indices[N-1] */
+            (indices >> (nbits * (N - 1))) & ((1U << nbits) - 1U);
+          /* Perform these assignments in parallel:
+             indices[0] := j
+             indices[1] := indices[0]
+             ...
+             indices[N-1] := indices[N-2]  */
+          indices = ((indices & ((1U << (nbits * (N - 1))) - 1U)) << nbits) | j;
+        }
+      strcpy (lru[j].e_locale, lname);
+      lru[j].e_lcid = found_lcid;
 
-  LCID result = last_lcid;
+      result = found_lcid;
+    }
   glwthread_mutex_unlock (&get_lcid_lock);
   return result;
 }
-- 
2.52.0

Reply via email to