]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - string/strcoll_l.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / string / strcoll_l.c
index 5095e98fb7c9174c04a9acd9878d0a276243c503..1b5ec5d97917760003e397428215cfe6d51b3fce 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995-2013 Free Software Foundation, Inc.
+/* Copyright (C) 1995-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.org>, 1995.
 
 #include <locale.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <stdlib.h>
 #include <string.h>
 #include <sys/param.h>
+#include <libc-diag.h>
 
 #ifndef STRING_TYPE
 # define STRING_TYPE char
 # define USTRING_TYPE unsigned char
 # define STRCOLL __strcoll_l
 # define STRCMP strcmp
-# define STRLEN strlen
 # define WEIGHT_H "../locale/weight.h"
 # define SUFFIX        MB
 # define L(arg) arg
@@ -41,6 +40,7 @@
 #define CONCAT1(a,b) a##b
 
 #include "../locale/localeinfo.h"
+#include WEIGHT_H
 
 /* Track status while looking for sequences in a string.  */
 typedef struct
@@ -48,14 +48,11 @@ typedef struct
   int len;                     /* Length of the current sequence.  */
   size_t val;                  /* Position of the sequence relative to the
                                   previous non-ignored sequence.  */
-  size_t idxnow;               /* Current index in sequences.  */
   size_t idxmax;               /* Maximum index in sequences.  */
   size_t idxcnt;               /* Current count of indices.  */
   size_t backw;                        /* Current Backward sequence index.  */
   size_t backw_stop;           /* Index where the backward sequences stop.  */
   const USTRING_TYPE *us;      /* The string.  */
-  int32_t *idxarr;             /* Array to cache weight indices.  */
-  unsigned char *rulearr;      /* Array to cache rules.  */
   unsigned char rule;          /* Saved rule for the first sequence.  */
   int32_t idx;                 /* Index to weight of the current sequence.  */
   int32_t save_idx;            /* Save looked up index of a forward
@@ -64,185 +61,13 @@ typedef struct
   const USTRING_TYPE *back_us; /* Beginning of the backward sequence.  */
 } coll_seq;
 
-/* Get next sequence.  The weight indices are cached, so we don't need to
-   traverse the string.  */
-static void
-get_next_seq_cached (coll_seq *seq, int nrules, int pass,
-                    const unsigned char *rulesets,
-                    const USTRING_TYPE *weights)
-{
-  size_t val = seq->val = 0;
-  int len = seq->len;
-  size_t backw_stop = seq->backw_stop;
-  size_t backw = seq->backw;
-  size_t idxcnt = seq->idxcnt;
-  size_t idxmax = seq->idxmax;
-  size_t idxnow = seq->idxnow;
-  unsigned char *rulearr = seq->rulearr;
-  int32_t *idxarr = seq->idxarr;
-
-  while (len == 0)
-    {
-      ++val;
-      if (backw_stop != ~0ul)
-       {
-         /* There is something pushed.  */
-         if (backw == backw_stop)
-           {
-             /* The last pushed character was handled.  Continue
-                with forward characters.  */
-             if (idxcnt < idxmax)
-               {
-                 idxnow = idxcnt;
-                 backw_stop = ~0ul;
-               }
-             else
-               {
-                 /* Nothing any more.  The backward sequence
-                    ended with the last sequence in the string.  */
-                 idxnow = ~0ul;
-                 break;
-               }
-           }
-         else
-           idxnow = --backw;
-       }
-      else
-       {
-         backw_stop = idxcnt;
-
-         while (idxcnt < idxmax)
-           {
-             if ((rulesets[rulearr[idxcnt] * nrules + pass]
-                  & sort_backward) == 0)
-               /* No more backward characters to push.  */
-               break;
-             ++idxcnt;
-           }
-
-         if (backw_stop == idxcnt)
-           {
-             /* No sequence at all or just one.  */
-             if (idxcnt == idxmax)
-               /* Note that LEN is still zero.  */
-               break;
-
-             backw_stop = ~0ul;
-             idxnow = idxcnt++;
-           }
-         else
-           /* We pushed backward sequences.  */
-           idxnow = backw = idxcnt - 1;
-       }
-      len = weights[idxarr[idxnow]++];
-    }
-
-  /* Update the structure.  */
-  seq->val = val;
-  seq->len = len;
-  seq->backw_stop = backw_stop;
-  seq->backw = backw;
-  seq->idxcnt = idxcnt;
-  seq->idxnow = idxnow;
-}
-
 /* Get next sequence.  Traverse the string as required.  */
-static void
+static __always_inline void
 get_next_seq (coll_seq *seq, int nrules, const unsigned char *rulesets,
              const USTRING_TYPE *weights, const int32_t *table,
-             const USTRING_TYPE *extra, const int32_t *indirect)
-{
-#include WEIGHT_H
-  size_t val = seq->val = 0;
-  int len = seq->len;
-  size_t backw_stop = seq->backw_stop;
-  size_t backw = seq->backw;
-  size_t idxcnt = seq->idxcnt;
-  size_t idxmax = seq->idxmax;
-  size_t idxnow = seq->idxnow;
-  unsigned char *rulearr = seq->rulearr;
-  int32_t *idxarr = seq->idxarr;
-  const USTRING_TYPE *us = seq->us;
-
-  while (len == 0)
-    {
-      ++val;
-      if (backw_stop != ~0ul)
-       {
-         /* There is something pushed.  */
-         if (backw == backw_stop)
-           {
-             /* The last pushed character was handled.  Continue
-                with forward characters.  */
-             if (idxcnt < idxmax)
-               {
-                 idxnow = idxcnt;
-                 backw_stop = ~0ul;
-               }
-             else
-               /* Nothing any more.  The backward sequence ended with
-                  the last sequence in the string.  Note that LEN
-                  is still zero.  */
-               break;
-           }
-         else
-           idxnow = --backw;
-       }
-      else
-       {
-         backw_stop = idxmax;
-
-         while (*us != L('\0'))
-           {
-             int32_t tmp = findidx (&us, -1);
-             rulearr[idxmax] = tmp >> 24;
-             idxarr[idxmax] = tmp & 0xffffff;
-             idxcnt = idxmax++;
-
-             if ((rulesets[rulearr[idxcnt] * nrules]
-                  & sort_backward) == 0)
-               /* No more backward characters to push.  */
-               break;
-             ++idxcnt;
-           }
-
-         if (backw_stop >= idxcnt)
-           {
-             /* No sequence at all or just one.  */
-             if (idxcnt == idxmax || backw_stop > idxcnt)
-               /* Note that LEN is still zero.  */
-               break;
-
-             backw_stop = ~0ul;
-             idxnow = idxcnt;
-           }
-         else
-           /* We pushed backward sequences.  */
-           idxnow = backw = idxcnt - 1;
-       }
-      len = weights[idxarr[idxnow]++];
-    }
-
-  /* Update the structure.  */
-  seq->val = val;
-  seq->len = len;
-  seq->backw_stop = backw_stop;
-  seq->backw = backw;
-  seq->idxcnt = idxcnt;
-  seq->idxmax = idxmax;
-  seq->idxnow = idxnow;
-  seq->us = us;
-}
-
-/* Get next sequence.  Traverse the string as required.  This function does not
-   set or use any index or rule cache.  */
-static void
-get_next_seq_nocache (coll_seq *seq, int nrules, const unsigned char *rulesets,
-                     const USTRING_TYPE *weights, const int32_t *table,
-                     const USTRING_TYPE *extra, const int32_t *indirect,
-                     int pass)
+             const USTRING_TYPE *extra, const int32_t *indirect,
+             int pass)
 {
-#include WEIGHT_H
   size_t val = seq->val = 0;
   int len = seq->len;
   size_t backw_stop = seq->backw_stop;
@@ -285,7 +110,7 @@ get_next_seq_nocache (coll_seq *seq, int nrules, const unsigned char *rulesets,
              us = seq->back_us;
              while (i < backw)
                {
-                 int32_t tmp = findidx (&us, -1);
+                 int32_t tmp = findidx (table, indirect, extra, &us, -1);
                  idx = tmp & 0xffffff;
                  i++;
                }
@@ -300,7 +125,7 @@ get_next_seq_nocache (coll_seq *seq, int nrules, const unsigned char *rulesets,
 
          while (*us != L('\0'))
            {
-             int32_t tmp = findidx (&us, -1);
+             int32_t tmp = findidx (table, indirect, extra, &us, -1);
              unsigned char rule = tmp >> 24;
              prev_idx = idx;
              idx = tmp & 0xffffff;
@@ -346,7 +171,19 @@ get_next_seq_nocache (coll_seq *seq, int nrules, const unsigned char *rulesets,
            }
        }
 
+      /* With GCC 5.3 when compiling with -Os the compiler complains
+        that idx, taken from seq->idx (seq1 or seq2 from STRCOLL) may
+        be used uninitialized.  In general this can't possibly be true
+        since seq1.idx and seq2.idx are initialized to zero in the
+        outer function.  Only one case where seq->idx is restored from
+        seq->save_idx might result in an uninitialized idx value, but
+        it is guarded by a sequence of checks against backw_stop which
+        ensures that seq->save_idx was saved to first and contains a
+        valid value.  */
+      DIAG_PUSH_NEEDS_COMMENT;
+      DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
       len = weights[idx++];
+      DIAG_POP_NEEDS_COMMENT;
       /* Skip over indices of previous levels.  */
       for (int i = 0; i < pass; i++)
        {
@@ -367,11 +204,10 @@ get_next_seq_nocache (coll_seq *seq, int nrules, const unsigned char *rulesets,
   seq->idx = idx;
 }
 
-/* Compare two sequences.  This version does not use the index and rules
-   cache.  */
-static int
-do_compare_nocache (coll_seq *seq1, coll_seq *seq2, int position,
-                   const USTRING_TYPE *weights)
+/* Compare two sequences.  */
+static __always_inline int
+do_compare (coll_seq *seq1, coll_seq *seq2, int position,
+           const USTRING_TYPE *weights)
 {
   int seq1len = seq1->len;
   int seq2len = seq2->len;
@@ -418,58 +254,8 @@ out:
   return result;
 }
 
-/* Compare two sequences using the index cache.  */
-static int
-do_compare (coll_seq *seq1, coll_seq *seq2, int position,
-           const USTRING_TYPE *weights)
-{
-  int seq1len = seq1->len;
-  int seq2len = seq2->len;
-  size_t val1 = seq1->val;
-  size_t val2 = seq2->val;
-  int32_t *idx1arr = seq1->idxarr;
-  int32_t *idx2arr = seq2->idxarr;
-  int idx1now = seq1->idxnow;
-  int idx2now = seq2->idxnow;
-  int result = 0;
-
-  /* Test for position if necessary.  */
-  if (position && val1 != val2)
-    {
-      result = val1 > val2 ? 1 : -1;
-      goto out;
-    }
-
-  /* Compare the two sequences.  */
-  do
-    {
-      if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]])
-       {
-         /* The sequences differ.  */
-         result = weights[idx1arr[idx1now]] - weights[idx2arr[idx2now]];
-         goto out;
-       }
-
-      /* Increment the offsets.  */
-      ++idx1arr[idx1now];
-      ++idx2arr[idx2now];
-
-      --seq1len;
-      --seq2len;
-    }
-  while (seq1len > 0 && seq2len > 0);
-
-  if (position && seq1len != seq2len)
-    result = seq1len - seq2len;
-
-out:
-  seq1->len = seq1len;
-  seq2->len = seq2len;
-  return result;
-}
-
 int
-STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, __locale_t l)
+STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, locale_t l)
 {
   struct __locale_data *current = l->__locales[LC_COLLATE];
   uint_fast32_t nrules = current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].word;
@@ -484,6 +270,10 @@ STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, __locale_t l)
   if (nrules == 0)
     return STRCMP (s1, s2);
 
+  /* Catch empty strings.  */
+  if (__glibc_unlikely (*s1 == '\0') || __glibc_unlikely (*s2 == '\0'))
+    return (*s1 != '\0') - (*s2 != '\0');
+
   rulesets = (const unsigned char *)
     current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string;
   table = (const int32_t *)
@@ -500,65 +290,25 @@ STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, __locale_t l)
   assert (((uintptr_t) extra) % __alignof__ (extra[0]) == 0);
   assert (((uintptr_t) indirect) % __alignof__ (indirect[0]) == 0);
 
-  /* We need this a few times.  */
-  size_t s1len = STRLEN (s1);
-  size_t s2len = STRLEN (s2);
-
-  /* Catch empty strings.  */
-  if (__glibc_unlikely (s1len == 0) || __glibc_unlikely (s2len == 0))
-    return (s1len != 0) - (s2len != 0);
-
-  /* Perform the first pass over the string and while doing this find
-     and store the weights for each character.  Since we want this to
-     be as fast as possible we are using `alloca' to store the temporary
-     values.  But since there is no limit on the length of the string
-     we have to use `malloc' if the string is too long.  We should be
-     very conservative here.
-
-     Please note that the localedef programs makes sure that `position'
-     is not used at the first level.  */
-
+  int result = 0, rule = 0;
+
+  /* With GCC 7 when compiling with -Os the compiler warns that
+     seq1.back_us and seq2.back_us might be used uninitialized.
+     Sometimes this warning appears at locations in locale/weightwc.h
+     where the actual use is, but on architectures other than x86_64,
+     x86 and s390x, a warning appears at the definitions of seq1 and
+     seq2.  This uninitialized use is impossible for the same reason
+     as described in comments in locale/weightwc.h.  */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_Os_NEEDS_COMMENT (7, "-Wmaybe-uninitialized");
   coll_seq seq1, seq2;
-  bool use_malloc = false;
-  int result = 0;
-
-  memset (&seq1, 0, sizeof (seq1));
-  seq2 = seq1;
-
-  size_t size_max = SIZE_MAX / (sizeof (int32_t) + 1);
-
-  if (MIN (s1len, s2len) > size_max
-      || MAX (s1len, s2len) > size_max - MIN (s1len, s2len))
-    {
-      /* If the strings are long enough to cause overflow in the size request,
-         then skip the allocation and proceed with the non-cached routines.  */
-    }
-  else if (! __libc_use_alloca ((s1len + s2len) * (sizeof (int32_t) + 1)))
-    {
-      seq1.idxarr = (int32_t *) malloc ((s1len + s2len) * (sizeof (int32_t) + 1));
+  DIAG_POP_NEEDS_COMMENT;
+  seq1.len = 0;
+  seq1.idxmax = 0;
+  seq1.rule = 0;
+  seq2.len = 0;
+  seq2.idxmax = 0;
 
-      /* If we failed to allocate memory, we leave everything as NULL so that
-        we use the nocache version of traversal and comparison functions.  */
-      if (seq1.idxarr != NULL)
-       {
-         seq2.idxarr = &seq1.idxarr[s1len];
-         seq1.rulearr = (unsigned char *) &seq2.idxarr[s2len];
-         seq2.rulearr = &seq1.rulearr[s1len];
-         use_malloc = true;
-       }
-    }
-  else
-    {
-      seq1.idxarr = (int32_t *) alloca (s1len * sizeof (int32_t));
-      seq2.idxarr = (int32_t *) alloca (s2len * sizeof (int32_t));
-      seq1.rulearr = (unsigned char *) alloca (s1len);
-      seq2.rulearr = (unsigned char *) alloca (s2len);
-    }
-
-  int rule = 0;
-
-  /* Cache values in the first pass and if needed, use them in subsequent
-     passes.  */
   for (int pass = 0; pass < nrules; ++pass)
     {
       seq1.idxcnt = 0;
@@ -576,64 +326,45 @@ STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, __locale_t l)
       seq2.us = (const USTRING_TYPE *) s2;
 
       /* We assume that if a rule has defined `position' in one section
-        this is true for all of them.  */
+        this is true for all of them.  Please note that the localedef programs
+        makes sure that `position' is not used at the first level.  */
+
       int position = rulesets[rule * nrules + pass] & sort_position;
 
       while (1)
        {
-         if (__glibc_unlikely (seq1.idxarr == NULL))
-           {
-             get_next_seq_nocache (&seq1, nrules, rulesets, weights, table,
+         get_next_seq (&seq1, nrules, rulesets, weights, table,
                                    extra, indirect, pass);
-             get_next_seq_nocache (&seq2, nrules, rulesets, weights, table,
+         get_next_seq (&seq2, nrules, rulesets, weights, table,
                                    extra, indirect, pass);
-           }
-         else if (pass == 0)
-           {
-             get_next_seq (&seq1, nrules, rulesets, weights, table, extra,
-                           indirect);
-             get_next_seq (&seq2, nrules, rulesets, weights, table, extra,
-                           indirect);
-           }
-         else
-           {
-             get_next_seq_cached (&seq1, nrules, pass, rulesets, weights);
-             get_next_seq_cached (&seq2, nrules, pass, rulesets, weights);
-           }
-
          /* See whether any or both strings are empty.  */
          if (seq1.len == 0 || seq2.len == 0)
            {
              if (seq1.len == seq2.len)
-               /* Both ended.  So far so good, both strings are equal
-                  at this level.  */
-               break;
+               {
+                 /* Both strings ended and are equal at this level.  Do a
+                    byte-level comparison to ensure that we don't waste time
+                    going through multiple passes for totally equal strings
+                    before proceeding to subsequent passes.  */
+                 if (pass == 0 && STRCMP (s1, s2) == 0)
+                   return result;
+                 else
+                   break;
+               }
 
              /* This means one string is shorter than the other.  Find out
                 which one and return an appropriate value.  */
-             result = seq1.len == 0 ? -1 : 1;
-             goto free_and_return;
+             return seq1.len == 0 ? -1 : 1;
            }
 
-         if (__glibc_unlikely (seq1.idxarr == NULL))
-           result = do_compare_nocache (&seq1, &seq2, position, weights);
-         else
-           result = do_compare (&seq1, &seq2, position, weights);
+         result = do_compare (&seq1, &seq2, position, weights);
          if (result != 0)
-           goto free_and_return;
+           return result;
        }
 
-      if (__glibc_likely (seq1.rulearr != NULL))
-       rule = seq1.rulearr[0];
-      else
-       rule = seq1.rule;
+      rule = seq1.rule;
     }
 
-  /* Free the memory if needed.  */
- free_and_return:
-  if (use_malloc)
-    free (seq1.idxarr);
-
   return result;
 }
 libc_hidden_def (STRCOLL)