keysym: fast path for case sensitive xkb_keysym_from_name

xkb_keysym_from_name() is called a lot in Compose file parsing. The
lower case handling slows things down a lot (particularly given we can't
use the optimized strcasecmp() due to locale issues). So add separate
handling for the non-case-sensitive case which is used by Compose.

To do this we need to add another version of the ks_tables table. This
adds ~20kb to the shared library binary. We can probably do something
better here but I think it's fine.

Signed-off-by: Ran Benita <ran@unusedvar.com>
master
Ran Benita 2021-03-28 15:51:01 +03:00
parent 3b506497bf
commit 7d84809fdc
3 changed files with 2620 additions and 51 deletions

View File

@ -46,6 +46,10 @@ def print_entries(x):
print(' {{ 0x{value:08x}, {offs} }}, /* {name} */'.format(offs=entry_offsets[name], value=value, name=name))
print('static const struct name_keysym name_to_keysym[] = {')
print_entries(sorted(entries, key=lambda e: e[0]))
print('};\n')
print('static const struct name_keysym name_to_keysym_icase[] = {')
print_entries(sorted(entries, key=lambda e: e[0].lower()))
print('};\n')

View File

@ -102,65 +102,76 @@ xkb_keysym_from_name(const char *name, enum xkb_keysym_flags flags)
if (flags & ~XKB_KEYSYM_CASE_INSENSITIVE)
return XKB_KEY_NoSymbol;
/*
* We need to !icase case to be fast, for e.g. Compose file parsing.
* So do it in a fast path.
*/
if (!icase) {
size_t lo = 0, hi = ARRAY_SIZE(name_to_keysym) - 1;
while (hi >= lo) {
size_t mid = (lo + hi) / 2;
int cmp = istrcmp(name, get_name(&name_to_keysym[mid]));
int cmp = strcmp(name, get_name(&name_to_keysym[mid]));
if (cmp > 0)
lo = mid + 1;
else if (cmp < 0)
hi = mid - 1;
else
return name_to_keysym[mid].keysym;
}
}
/*
* Find the correct keysym for case-insensitive match.
*
* The name_to_keysym_icase table is sorted by istrcmp(). So the binary
* search may return _any_ of all possible case-insensitive duplicates. This
* code searches the entry, all previous and all next entries that match by
* case-insensitive comparison and returns the "best" case-insensitive
* match.
*
* The "best" case-insensitive match is the lower-case keysym which we find
* with the help of xkb_keysym_is_lower(). The only keysyms that only differ
* by letter-case are keysyms that are available as lower-case and
* upper-case variant (like KEY_a and KEY_A). So returning the first
* lower-case match is enough in this case.
*/
else {
size_t lo = 0, hi = ARRAY_SIZE(name_to_keysym_icase) - 1;
while (hi >= lo) {
size_t mid = (lo + hi) / 2;
int cmp = istrcmp(name, get_name(&name_to_keysym_icase[mid]));
if (cmp > 0) {
lo = mid + 1;
} else if (cmp < 0) {
hi = mid - 1;
} else {
entry = &name_to_keysym[mid];
entry = &name_to_keysym_icase[mid];
break;
}
}
if (entry) {
/*
* Find the correct keysym if one case-insensitive match is given.
*
* The name_to_keysym table is sorted by istrcmp(). So the binary search
* may return _any_ of all possible case-insensitive duplicates. This
* code searches the entry, all previous and all next entries that match
* by case-insensitive comparison and returns the exact match to name.
* If icase is true, then this returns the best case-insensitive match
* instead of a correct match.
* The "best" case-insensitive match is the lower-case keysym which we
* find with the help of xkb_keysym_is_lower().
* The only keysyms that only differ by letter-case are keysyms that are
* available as lower-case and upper-case variant (like KEY_a and
* KEY_A). So returning the first lower-case match is enough in this
* case.
*/
const struct name_keysym *iter, *last;
if (!icase && strcmp(get_name(entry), name) == 0)
return entry->keysym;
if (icase && xkb_keysym_is_lower(entry->keysym))
return entry->keysym;
for (iter = entry - 1; iter >= name_to_keysym; --iter) {
if (!icase && strcmp(get_name(iter), name) == 0)
return iter->keysym;
for (iter = entry - 1; iter >= name_to_keysym_icase; --iter) {
if (istrcmp(get_name(iter), get_name(entry)) != 0)
break;
if (icase && xkb_keysym_is_lower(iter->keysym))
if (xkb_keysym_is_lower(iter->keysym))
return iter->keysym;
}
last = name_to_keysym + ARRAY_SIZE(name_to_keysym);
last = name_to_keysym_icase + ARRAY_SIZE(name_to_keysym_icase);
for (iter = entry + 1; iter < last; ++iter) {
if (!icase && strcmp(get_name(iter), name) == 0)
return iter->keysym;
if (istrcmp(get_name(iter), get_name(entry)) != 0)
break;
if (icase && xkb_keysym_is_lower(iter->keysym))
if (xkb_keysym_is_lower(iter->keysym))
return iter->keysym;
}
if (icase)
return entry->keysym;
}
}
if (*name == 'U' || (icase && *name == 'u')) {
val = strtoul(&name[1], &tmp, 16);

File diff suppressed because it is too large Load Diff