diff options
Diffstat (limited to 'string/strcoll.c')
-rw-r--r-- | string/strcoll.c | 602 |
1 files changed, 451 insertions, 151 deletions
diff --git a/string/strcoll.c b/string/strcoll.c index 96a8313..5af26c3 100644 --- a/string/strcoll.c +++ b/string/strcoll.c @@ -1,6 +1,6 @@ /* Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. - Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + Written by Ulrich Drepper <drepper@cygnus.com>, 1995. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -17,212 +17,512 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <endian.h> +#include <langinfo.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <string.h> -#ifndef STRING_TYPE -# define STRING_TYPE char -# define USTRING_TYPE unsigned char -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define STRCOLL __strcoll_l -# else -# define STRCOLL strcoll -# endif -# define STRCMP strcmp +#include "../locale/localeinfo.h" + +#ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define STRCOLL __strcoll_l +#else +# define STRCOLL strcoll #endif #ifndef USE_IN_EXTENDED_LOCALE_MODEL int STRCOLL (s1, s2) - const STRING_TYPE *s1; - const STRING_TYPE *s2; + const char *s1; + const char *s2; #else int STRCOLL (s1, s2, l) - const STRING_TYPE *s1; - const STRING_TYPE *s2; + const char *s1; + const char *s2; __locale_t l; #endif { - return STRCMP (s1, s2); -} +#ifdef USE_IN_EXTENDED_LOCALE_MODEL + struct locale_data *current = l->__locales[LC_COLLATE]; + uint_fast32_t nrules = *((uint32_t *) current->values[_NL_ITEM_INDEX (_NL_COLLATE_NRULES)].string); +#else + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); +#endif + /* We don't assign the following values right away since it might be + unnecessary in case there are no rules. */ + const unsigned char *rulesets; + const int32_t *table; + const unsigned char *weights; + const unsigned char *extra; + const int32_t *indirect; + uint_fast32_t pass; + int result = 0; + const unsigned char *us1; + const unsigned char *us2; + size_t s1len; + size_t s2len; + int32_t *idx1arr; + int32_t *idx2arr; + unsigned char *rule1arr; + unsigned char *rule2arr; + size_t idx1max; + size_t idx2max; + size_t idx1cnt; + size_t idx2cnt; + size_t backw1_stop; + size_t backw2_stop; + size_t backw1; + size_t backw2; + int val1; + int val2; + int position; + int use_malloc = 0; -#if 0 -/* Include the shared helper functions. `strxfrm'/`wcsxfrm' also use - these functions. */ #include "../locale/weight.h" + if (nrules == 0) + return strcmp (s1, s2); -/* Compare S1 and S2, returning less than, equal to or - greater than zero if the collated form of S1 is lexicographically - less than, equal to or greater than the collated form of S2. */ -#ifndef USE_IN_EXTENDED_LOCALE_MODEL -int -STRCOLL (s1, s2) - const STRING_TYPE *s1; - const STRING_TYPE *s2; -#else -int -STRCOLL (s1, s2, l) - const STRING_TYPE *s1; - const STRING_TYPE *s2; - __locale_t l; -#endif -{ #ifdef USE_IN_EXTENDED_LOCALE_MODEL - struct locale_data *current = l->__locales[LC_COLLATE]; -# if BYTE_ORDER == BIG_ENDIAN - const uint32_t *collate_table = (const uint32_t *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_TABLE_EB)].string; - const uint32_t *collate_extra = (const uint32_t *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EB)].string; -# elif BYTE_ORDER == LITTLE_ENDIAN - const uint32_t *collate_table = (const uint32_t *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_TABLE_EL)].string; - const uint32_t *collate_extra = (const uint32_t *) - current->values[_NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EL)].string; -# else -# error bizarre byte order -# endif + rulesets = (const unsigned char *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_RULESETS)].string; + table = (const int32_t *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_TABLEMB)].string; + weights = (const unsigned char *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_WEIGHTMB)].string; + extra = (const unsigned char *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_EXTRAMB)].string; + indirect = (const int32_t *) + current->values[_NL_ITEM_INDEX (_NL_COLLATE_INDIRECTMB)].string; +#else + rulesets = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_RULESETS); + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); #endif - weight_t *s1forw = NULL; - weight_t *s1backw = NULL; - weight_t *s2forw = NULL; - weight_t *s2backw = NULL; - size_t pass; - - /* If the current locale does not specify locale data we use normal - 8-bit string comparison. */ - if (collate_nrules == 0) - return STRCMP (s1, s2); - - /* Handle empty strings as a special case. */ - if (*s1 == '\0') - return *s2 == '\0' ? 0 : -1; - else if (*s2 == '\0') - return 1; - - /* Get full information about the strings. This means we get - information for all passes in a special data structure. */ - get_string (s1, s1forw, s1backw); - get_string (s2, s2forw, s2backw); - - /* Now we have all the information. In at most the given number of - passes we can finally decide about the order. */ - for (pass = 0; pass < collate_nrules; ++pass) + + /* We need this a few times. */ + s1len = strlen (s1); + s2len = strlen (s2); + + /* We need the elements of the strings as unsigned values since they + are used as indeces. */ + us1 = (const unsigned char *) s1; + us2 = (const unsigned char *) s2; + + /* 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. */ + if (s1len + s2len >= 16384) { - int forward = (collate_rules[pass] & sort_forward) != 0; - const weight_t *s1run = forward ? s1forw : s1backw; - const weight_t *s2run = forward ? s2forw : s2backw; - int s1idx = forward ? 0 : s1run->data[pass].number - 1; - int s2idx = forward ? 0 : s2run->data[pass].number - 1; + idx1arr = (int32_t *) malloc ((s1len + s2len) * (sizeof (int32_t) + 1)); + idx2arr = &idx1arr[s2len]; + rule1arr = (unsigned char *) &idx2arr[s2len]; + rule2arr = &rule1arr[s1len]; - while (1) + if (idx1arr == NULL) + /* No memory. Well, go with the stack then. + + XXX Once this implementation is stable we will handle this + differently. Instead of precomputing the indeces we will + do this in time. This means, though, that this happens for + every pass again. */ + goto try_stack; + use_malloc = 1; + } + else + { + try_stack: + idx1arr = (int32_t *) alloca (s1len * sizeof (int32_t)); + idx2arr = (int32_t *) alloca (s2len * sizeof (int32_t)); + rule1arr = (unsigned char *) alloca (s2len); + rule2arr = (unsigned char *) alloca (s2len); + } + + idx1cnt = 0; + idx2cnt = 0; + idx1max = 0; + idx2max = 0; + backw1_stop = ~0ul; + backw2_stop = ~0ul; + backw1 = ~0ul; + backw2 = ~0ul; + position = rulesets[rule1arr[0] * nrules] & sort_position; + while (1) + { + size_t idx1now; + size_t idx2now; + int seq1len; + int seq2len; + int i; + + val1 = 0; + val2 = 0; + + /* Get the next non-IGNOREd element for string `s1'. */ + do { - int s1ignore = 0; - int s2ignore = 0; - uint32_t w1 = 0; - uint32_t w2 = 0; - - /* Here we have to check for IGNORE entries. If these are - found we count them and go on with the next value. */ - while (s1run != NULL - && ((w1 = s1run->data[pass].value[s1idx]) - == (uint32_t) IGNORE_CHAR)) + ++val1; + + if (backw1_stop != ~0ul) { - ++s1ignore; - if (forward - ? ++s1idx >= s1run->data[pass].number - : --s1idx < 0) + /* The is something pushed. */ + if (backw1 == backw1_stop) { - weight_t *nextp = forward ? s1run->next : s1run->prev; - if (nextp == NULL) + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx1cnt < idx1max) + idx1now = idx1cnt; + else { - w1 = 0; - /* No more non-INGOREd elements means lowest - possible value. */ - s1ignore = -1; + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. */ + idx1now = ~0ul; + break; } - else - s1idx = forward ? 0 : nextp->data[pass].number - 1; - s1run = nextp; } + else + idx1now = --backw1; } - - while (s2run != NULL - && ((w2 = s2run->data[pass].value[s2idx]) - == (uint32_t) IGNORE_CHAR)) + else { - ++s2ignore; - if (forward - ? ++s2idx >= s2run->data[pass].number - : --s2idx < 0) + backw1_stop = idx1max; + + while (*us1 != '\0') { - weight_t *nextp = forward ? s2run->next : s2run->prev; - if (nextp == NULL) + int32_t tmp = findidx (&us1); + rule1arr[idx1max] = tmp >> 24; + idx1arr[idx1max] = tmp & 0xffffff; + idx1cnt = idx1max++; + + if ((rulesets[rule1arr[idx1cnt] * nrules] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx1cnt; + } + + if (backw1_stop >= idx1cnt) + { + /* No sequence at all or just one. */ + backw1_stop = ~0ul; + if (idx1cnt == idx1max || backw1_stop > idx1cnt) { - w2 = 0; - /* No more non-INGOREd elements means lowest - possible value. */ - s2ignore = -1; + idx1now = ~0ul; + break; } + idx1now = idx1cnt; + } + else + /* We pushed backward sequences. */ + idx1now = backw1 = idx1cnt - 1; + } + } + while (weights[idx1arr[idx1now]] == '\0'); + + /* And the same for string `s2'. */ + do + { + ++val2; + + if (backw2_stop != ~0ul) + { + /* The is something pushed. */ + if (backw2 == backw2_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx2cnt < idx2max) + idx2now = idx2cnt; else - s2idx = forward ? 0 : nextp->data[pass].number - 1; - s2run = nextp; + { + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. */ + idx2now = ~0ul; + break; + } + } + else + idx2now = --backw2; + } + else + { + backw2_stop = idx2max; + + while (*us2 != '\0') + { + int32_t tmp = findidx (&us2); + rule2arr[idx2max] = tmp >> 24; + idx2arr[idx2max] = tmp & 0xffffff; + idx2cnt = idx2max++; + + if ((rulesets[rule2arr[idx2cnt] * nrules] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx2cnt; + } + + if (backw2_stop >= idx2cnt) + { + /* No sequence at all or just one. */ + backw2_stop = ~0ul; + if (idx2cnt == idx2max || backw2_stop > idx2cnt) + { + idx2now = ~0ul; + break; + } + idx2now = idx2cnt; } + else + /* We pushed backward sequences. */ + idx2now = backw2 = idx2cnt - 1; } + } + while (weights[idx2arr[idx2now]] == '\0'); - /* If one string is completely processed stop. */ - if (s1run == NULL || s2run == NULL) + /* See whether any or both strings are empty. */ + if (idx1now == ~0ul || idx2now == ~0ul) + { + if (idx1now == idx2now) + /* Both ended. So far so good, both strings are equal at the + first level. */ break; - /* Now we have information of the number of ignored - weights and the value of the next weight. */ - if ((collate_rules[pass] & sort_position) != 0 - && s1ignore != s2ignore && (w1 != 0 || w2 != 0)) - return s1ignore < s2ignore ? -1 : 1; + /* This means one string is shorter than the other. Find out + which one and return an appropriate value. */ + result = idx1now == ~0ul ? -1 : 1; + goto free_and_return; + } - if (w1 != w2) - return w1 < w2 ? -1 : 1; + /* Test for position if necessary. */ + if (position && val1 != val2) + { + result = val1 - val2; + goto free_and_return; + } + + /* Compare the two sequences. */ + seq1len = weights[idx1arr[idx1now]++]; + seq2len = weights[idx2arr[idx2now]++]; + for (i = 0; i < seq1len && i < seq2len; ++i) + if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) + { + /* The sequences differ. */ + result = weights[idx1arr[idx1now]] - weights[idx2arr[idx2now]]; + goto free_and_return; + } + else + { + /* Increment the offsets. */ + ++idx1arr[idx1now]; + ++idx2arr[idx2now]; + } + + result = seq1len - seq2len; + if (result != 0) + goto free_and_return; + } - /* We have to increment the index counters. */ - if (forward) + /* Now the remaining passes over the weights. We now use the + indeces we found before. */ + for (pass = 1; pass < nrules; ++pass) + { + /* We assume that if a rule has defined `position' in one section + this is true for all of them. */ + idx1cnt = 0; + idx2cnt = 0; + backw1_stop = ~0ul; + backw2_stop = ~0ul; + backw1 = ~0ul; + backw2 = ~0ul; + position = rulesets[rule1arr[0] * nrules + pass] & sort_position; + + while (1) + { + size_t idx1now; + size_t idx2now; + int seq1len; + int seq2len; + int i; + + val1 = 0; + val2 = 0; + + /* Get the next non-IGNOREd element for string `s1'. */ + do { - if (++s1idx >= s1run->data[pass].number) + ++val1; + + if (backw1_stop != ~0ul) { - s1run = s1run->next; - s1idx = 0; + /* The is something pushed. */ + if (backw1 == backw1_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx1cnt < idx1max) + idx1now = idx1cnt; + else + { + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. */ + idx1now = ~0ul; + break; + } + } + else + idx1now = --backw1; } - if (++s2idx >= s2run->data[pass].number) + else { - s2run = s2run->next; - s2idx = 0; + backw1_stop = idx1cnt; + + while (idx1cnt < idx1max) + { + if ((rulesets[rule1arr[idx1cnt] * nrules + pass] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx1cnt; + } + + if (backw1_stop == idx1cnt) + { + /* No sequence at all or just one. */ + backw1_stop = ~0ul; + if (idx1cnt == idx1max) + { + idx1now = ~0ul; + break; + } + idx1now = idx1cnt++; + } + else + /* We pushed backward sequences. */ + idx1now = backw1 = idx1cnt - 1; } } - else + while (weights[idx1arr[idx1now]] == '\0'); + + /* And the same for string `s2'. */ + do { - if (--s1idx < 0) + ++val2; + + if (backw2_stop != ~0ul) { - s1run = s1run->prev; - if (s1run != NULL) - s1idx = s1run->data[pass].number - 1; + /* The is something pushed. */ + if (backw2 == backw2_stop) + { + /* The last pushed character was handled. Continue + with forward characters. */ + if (idx2cnt < idx2max) + idx2now = idx2cnt; + else + { + /* Nothing anymore. The backward sequence ended with + the last sequence in the string. */ + idx2now = ~0ul; + break; + } + } + else + idx2now = --backw2; } - if (--s2idx < 0) + else { - s2run = s2run->prev; - if (s2run != NULL) - s2idx = s2run->data[pass].number - 1; + backw2_stop = idx2cnt; + + while (idx2cnt < idx2max) + { + if ((rulesets[rule2arr[idx2cnt] * nrules + pass] + & sort_backward) == 0) + /* No more backward characters to push. */ + break; + ++idx2cnt; + } + + if (backw2_stop == idx2cnt) + { + /* No sequence at all or just one. */ + backw2_stop = ~0ul; + if (idx2cnt == idx2max) + { + idx2now = ~0ul; + break; + } + idx2now = idx2cnt++; + } + else + /* We pushed backward sequences. */ + idx2now = backw2 = idx2cnt - 1; } } - } + while (weights[idx2arr[idx2now]] == '\0'); + + /* See whether any or both strings are empty. */ + if (idx1now == ~0ul || idx2now == ~0ul) + { + if (idx1now == idx2now) + /* Both ended. So far so good, both strings are equal at the + first level. */ + break; + + /* This means one string is shorter than the other. Find out + which one and return an appropriate value. */ + result = idx1now == ~0ul ? -1 : 1; + goto free_and_return; + } - if (s1run != s2run) - return s1run != NULL ? 1 : -1; + /* Test for position if necessary. */ + if (position && val1 != val2) + { + result = val1 - val2; + goto free_and_return; + } + + /* Compare the two sequences. */ + seq1len = weights[idx1arr[idx1now]++]; + seq2len = weights[idx2arr[idx2now]++]; + for (i = 0; i < seq1len && i < seq2len; ++i) + if (weights[idx1arr[idx1now]] != weights[idx2arr[idx2now]]) + { + /* The sequences differ. */ + result = weights[idx1arr[idx1now]] - weights[idx2arr[idx2now]]; + goto free_and_return; + } + else + { + /* Increment the offsets. */ + ++idx1arr[idx1now]; + ++idx2arr[idx2now]; + } + + result = seq1len - seq2len; + if (result != 0) + goto free_and_return; + } } - return 0; + /* Free the memory if needed. */ + free_and_return: + if (use_malloc) + free (idx1arr); + + return result; } -#endif |