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(' {{ 0x{value:08x}, {offs} }}, /* {name} */'.format(offs=entry_offsets[name], value=value, name=name))
print('static const struct name_keysym name_to_keysym[] = {') 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_entries(sorted(entries, key=lambda e: e[0].lower()))
print('};\n') print('};\n')

View File

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

File diff suppressed because it is too large Load Diff