Instead of using a home-brew hashtable generator, we should instead use the gperf program which is known to work.
This removes the "makekeys" programs and instead replaces it by a file that can generate input files for gperf. Gperf then generates hashtables for all of these input files and writes them concatenated into ks_tables.h which then can be used from keysym.c Unfortunately, gperf does not support integer keys but only strings or binary data. Therefore, we have to make the keysym->name lookup little-endian to avoid errors during cross compilation. Signed-off-by: David Herrmann <[email protected]> --- .gitignore | 1 + Makefile.am | 24 +- configure.ac | 6 +- makekeys/Makefile.am | 10 - makekeys/makekeys.c | 616 ++++++++++++++++++++++++++++----------------------- src/keysym.c | 86 ++----- 6 files changed, 383 insertions(+), 360 deletions(-) delete mode 100644 makekeys/Makefile.am diff --git a/.gitignore b/.gitignore index 22b4465..5a309a5 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ core cscope.out test-suite.log test-driver +*.gperf diff --git a/Makefile.am b/Makefile.am index 26646fb..763e1bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = makekeys - pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = xkbcommon.pc @@ -92,11 +90,25 @@ src/xkbcomp/parser.c: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xk src/xkbcomp/parser.h: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xkbcomp/$(am__dirstamp) src/xkbcomp/scanner.c: $(top_builddir)/src/$(am__dirstamp) $(top_builddir)/src/xkbcomp/$(am__dirstamp) -src/ks_tables.h: $(top_builddir)/makekeys/makekeys$(EXEEXT) - $(AM_V_GEN)$(top_builddir)/makekeys/makekeys $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ +# makekeys program and src/ks_tables.h file + +noinst_PROGRAMS = makekeys/makekeys +makekeys_makekeys_SOURCES = makekeys/makekeys.c +CLEANFILES += \ + makekeys/name2key.gperf \ + makekeys/key2name.gperf + +src/ks_tables.h: \ + $(top_builddir)/makekeys/name2key.gperf \ + $(top_builddir)/makekeys/key2name.gperf + $(AM_V_GEN)$(GPERF) --language="ANSI-C" $(top_builddir)/makekeys/name2key.gperf > $@ + $(AM_V_GEN)$(GPERF) --language="ANSI-C" $(top_builddir)/makekeys/key2name.gperf >> $@ + +$(top_builddir)/makekeys/name2key.gperf: $(top_builddir)/makekeys/makekeys$(EXEEXT) + $(AM_V_GEN)$< --name2key $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ -$(top_builddir)/makekeys/makekeys$(EXEEXT): $(top_srcdir)/makekeys/makekeys.c - $(MAKE) -C makekeys +$(top_builddir)/makekeys/key2name.gperf: $(top_builddir)/makekeys/makekeys$(EXEEXT) + $(AM_V_GEN)$< --key2name $(top_srcdir)/xkbcommon/xkbcommon-keysyms.h > $@ # Documentation diff --git a/configure.ac b/configure.ac index df8a99e..4200118 100644 --- a/configure.ac +++ b/configure.ac @@ -62,6 +62,11 @@ if test ! -f "src/xkbcomp/parser.c"; then fi fi +AC_PATH_PROG([GPERF], "gperf", "") +if test -z "$GPERF"; then + AC_MSG_ERROR([gperf not found - enable to create src/ks_tables.h]) +fi + # Checks for library functions. AC_CHECK_FUNCS([strcasecmp strncasecmp]) if test "x$ac_cv_func_strcasecmp" = xno || \ @@ -121,7 +126,6 @@ AC_DEFINE_UNQUOTED([DEFAULT_XKB_LAYOUT], ["$DEFAULT_XKB_LAYOUT"], AC_CONFIG_FILES([ Makefile - makekeys/Makefile xkbcommon-uninstalled.pc xkbcommon.pc doc/Doxyfile diff --git a/makekeys/Makefile.am b/makekeys/Makefile.am deleted file mode 100644 index 5d9a441..0000000 --- a/makekeys/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -AM_CFLAGS = $(BASE_CFLAGS) -I$(top_srcdir) - -# need to use build-native compiler - -CC = $(CC_FOR_BUILD) -CPPFLAGS = $(CPPFLAGS_FOR_BUILD) -CFLAGS = $(CFLAGS_FOR_BUILD) -LDFLAGS = $(LDFLAGS_FOR_BUILD) -noinst_PROGRAMS = makekeys - diff --git a/makekeys/makekeys.c b/makekeys/makekeys.c index 62d7255..22ece2f 100644 --- a/makekeys/makekeys.c +++ b/makekeys/makekeys.c @@ -1,302 +1,362 @@ /* + * Copyright (c) 2012 David Herrmann <[email protected]> * - * Copyright 1990, 1998 The Open Group + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of The Open Group shall - * not be used in advertising or otherwise to promote the sale, use or - * other dealings in this Software without prior written authorization - * from The Open Group. + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ /* - * Constructs hash tables for xkb_keysym_to_string and - * xkb_string_from_keysym. + * Prepare source files for "gperf" which generates perfect hash-tables for + * xkb_keysym_get_name() and xkb_keysym_from_name(). */ -#include "xkbcommon/xkbcommon.h" - +#include <endian.h> #include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include "xkbcommon/xkbcommon.h" + +/* maximum length of a keysym name including XKB_KEY_* */ +#define MAX_KEYSYM_LEN 127 + +/* stringification of constants */ +#define xstr(x) #x +#define str(x) xstr(x) + +/* + * Parse one line that was read as input from a keysym-defintion. @buf contains + * the line and is zero-terminated. @key is a buffer that can hold any keysym of + * maximum length MAX_KEYSYM_LEN. + * Returns true if a keysym has been parsed and stores the symbol in @key and + * the value in @val. + */ +static bool parse_line(const char *buf, char *key, uint32_t *val) +{ + unsigned int i; + + i = sscanf(buf, "#define %" str(MAX_KEYSYM_LEN) "s 0x%" SCNx32, + key, val); + if (i == 2 && !strncmp(key, "XKB_KEY_", 8)) { + memmove(key, &key[8], strlen(&key[8]) + 1); + return true; + } + + return false; +} + +/* If we already have a keysym named @old in our list and a new keysym named + * @new is added, this function returns true if @new should overwrite @old. + * The caller must go sure that strcasecmp(@new, @old) returns "equal" before + * calling this. + * + * In general, we want lower-case letters to overwrite upper-case letters. As we + * generally not know which keysym-name is upper-case and which one is + * lower-case, we use a little hack: + * We simply sum up the ASCII values of both strings and use the greater + * value. This works because we know that the names of keysyms are written in + * upper-case letters if it is an upper case keysym. And upper-case letters + * have lower ASCII values than their respective lower-case letters. */ +static bool should_overwrite(const char *new, const char *old) +{ + unsigned int sum1, sum2, i; + const unsigned char *newu, *oldu; + + newu = (const void*)new; + oldu = (const void*)old; + sum1 = 0; + sum2 = 0; + for (i = 0; new[i] && old[i]; ++i) { + sum1 += newu[i]; + sum2 += oldu[i]; + } + + if (newu > oldu) + return true; + + return false; +} + +/* + * Flags for the gperf generator + * + * IGNORE_CASE causes the generator to create a case-insensitive hash-map and + * skips all duplicates. If a keysym is available as lower-case and upper-case + * keysym, then the lower-case keysym is taken. + * + * USE_NULL_STRINGS causes the hashtable to use NULL instead of "" for empty + * entries. This reduces memory space but causes a little performance-penalty. + * + * COMPARE_LENGTHS causes the hashtable to compare legths before doing a lookup. + * This causes a separate length-map to be added but can reduce performance for + * lookup-misses. + * + * REVERSE generates a reversed table, that is, the value is used as key and the + * name is returned. We simply convert the 4-byte keysym into a raw binary + * string and escape it correctly so gperf can read it. + */ +#define FLAG_IGNORE_CASE 0x01 +#define FLAG_USE_NULL_STRINGS 0x02 +#define FLAG_COMPARE_LENGTHS 0x04 +#define FLAG_REVERSE 0x08 + +struct entry { + char name[MAX_KEYSYM_LEN + 1]; + uint32_t value; +}; + +struct entry *entries; +unsigned int ent_used; +unsigned int ent_size; + +/* Pushes one keysym+value entry into the global list. Depending on @flags this + * might first check whether this entry wasn't added before. */ +static bool push_entry(const char *name, uint32_t value, unsigned int flags) +{ + struct entry *tmp; + unsigned int nsize, i; + + if (strlen(name) > MAX_KEYSYM_LEN) { + fprintf(stderr, "keysym name is too long: %s\n", name); + return false; + } + + if (ent_used == ent_size) { + nsize = ent_size * 2; + if (!nsize) + nsize = 1024; + + tmp = realloc(entries, sizeof(struct entry) * nsize); + if (!tmp) { + fprintf(stderr, "cannot allocate enough memory\n"); + return false; + } -typedef uint32_t Signature; + entries = tmp; + ent_size = nsize; + } -#define KTNUM 4000 + if (flags & FLAG_IGNORE_CASE) { + for (i = 0; i < ent_used; ++i) { + tmp = &entries[i]; + if (!strcasecmp(tmp->name, name)) { + if (should_overwrite(name, tmp->name)) + break; + else + return true; + } + } + } else if (flags & FLAG_REVERSE) { + /* if two symbols resolve to the same value, we prefer the first + * value for backwards compatibility. */ + for (i = 0; i < ent_used; ++i) { + tmp = &entries[i]; + if (tmp->value == value) + return true; + } + } else { + i = ent_used; + } -static struct info { - char *name; - xkb_keysym_t val; -} info[KTNUM]; + strcpy(entries[i].name, name); + entries[i].value = value; -#define MIN_REHASH 15 -#define MATCHES 10 + if (i == ent_used) + ++ent_used; -static char tab[KTNUM]; -static unsigned short offsets[KTNUM]; -static unsigned short indexes[KTNUM]; -static xkb_keysym_t values[KTNUM]; -static int ksnum = 0; + return true; +} + +/* + * This parses all files that are given on the command-line and pushes the + * symbols into the global symbol list. Command-line arguments starting with "-" + * are ignored. If your file starts with "-", then use "./-<file>". + */ +static bool parse_all(unsigned int flags, int argc, char **argv) +{ + unsigned int i; + FILE *f; + char buf[1024], key[128]; + uint32_t val; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '-') + continue; + + f = fopen(argv[i], "rb"); + if (!f) { + fprintf(stderr, "cannot open input file %s\n", argv[i]); + return false; + } + + while (fgets(buf, sizeof(buf), f)) { + if (!parse_line(buf, key, &val)) + continue; + + if (!push_entry(key, val, flags)) { + fprintf(stderr, "cannot save entry %s\n", key); + return false; + } + } + + if (!feof(f)) { + fprintf(stderr, "error while reading input file %s\n", + argv[i]); + return false; + } + + fclose(f); + } + + return true; +} + +static void write_header(FILE *out, const char *prefix, unsigned int flags) +{ + fprintf(out, "%%struct-type\n" + "%%readonly-tables\n" + "%%enum\n" + "%%pic\n"); + + if (flags & FLAG_USE_NULL_STRINGS) + fprintf(out, "%%null-strings\n"); + if (flags & (FLAG_COMPARE_LENGTHS | FLAG_REVERSE)) + fprintf(out, "%%compare-lengths\n"); + if (flags & FLAG_IGNORE_CASE) + fprintf(out, "%%ignore-case\n"); + + fprintf(out, "%%define hash-function-name %1$shash\n" + "%%define lookup-function-name %1$slookup\n" + "%%define string-pool-name %1$sstringpool\n" + "%%define word-array-name %1$swordarray\n" + "%%define length-table-name %1$slengthtable\n", + prefix); + + if (flags & FLAG_REVERSE) + fprintf(out, "struct %1$sentry {\n" + " int name;\n" + " const char *value;\n" + "};\n" + "%%%%\n", prefix); + else + fprintf(out, "struct %1$sentry {\n" + " int name;\n" + " uint32_t value;\n" + "};\n" + "%%%%\n", prefix); +} -static int -parse_line(const char *buf, char *key, xkb_keysym_t *val, char *prefix) +static void write_footer(FILE *out) { - int i; - char alias[128]; - - /* See if we can catch a straight XK_foo 0x1234-style definition first; - * the trickery around tmp is to account for prefices. */ - i = sscanf(buf, "#define %127s 0x%" SCNx32, key, val); - if (i == 2 && strncmp(key, "XKB_KEY_", 8) == 0) { - prefix[0] = '\0'; - memmove(key, key + 8, strlen(key + 8) + 1); - return 1; - } - - i = sscanf(buf, "#define %127s %127s", key, alias); - if (i == 2) - fprintf(stderr, "can't parse keysym definition: %s", buf); - - return 0; + fprintf(out, "%%%%\n"); +} + +/* + * Creates a single gperf configuration file with the given function prefix + * @prefix, the options @flags and the files given in @argv. + * Output is written to @out. + */ +static bool create_file(FILE *out, const char *prefix, unsigned int flags, + int argc, char **argv) +{ + unsigned int i; + uint32_t val; + const char *name; + uint8_t *ptr; + + if (!parse_all(flags, argc, argv)) + return false; + + write_header(out, prefix, flags); + + for (i = 0; i < ent_used; ++i) { + name = entries[i].name; + val = entries[i].value; + + if (flags & FLAG_REVERSE) { + /* For reversed lookup we make the keys little-endian + * to guarantee that the resulting gperf file can be + * cross-compiled. + * This adds a small performance-penalty to the runtime + * lookup function, but this is neglectable. */ + val = htole32(val); + ptr = (uint8_t*)&val; + fprintf(out, "\"\\x%" PRIx8 "\\x%" PRIx8, + ptr[0], ptr[1]); + fprintf(out, "\\x%" PRIx8 "\\x%" PRIx8 "\"", + ptr[2], ptr[3]); + fprintf(out, ", "); + fprintf(out, "\"%s\"", name); + } else { + fprintf(out, "\"%s\"", name); + fprintf(out, ", "); + fprintf(out, "%" PRIu32, val); + } + fprintf(out, "\n"); + } + + write_footer(out); + + return true; } -int -main(int argc, char *argv[]) +/* + * Main Entry + * This checks the argument-list for one of --name2key and --key2name. All other + * arguments are taken as file-list that should be parsed. The generated gperf + * input is written to STDOUT. + */ +int main(int argc, char **argv) { - FILE *fptr; - int max_rehash; - Signature sig; - int i, j, k, l, z; - char *name; - char c; - int first; - int best_max_rehash; - int best_z = 0; - int num_found; - xkb_keysym_t val; - char key[128], prefix[128]; - char buf[1024]; - - for (l = 1; l < argc; l++) { - fptr = fopen(argv[l], "r"); - if (!fptr) { - fprintf(stderr, "couldn't open %s\n", argv[l]); - continue; - } - - while (fgets(buf, sizeof(buf), fptr)) { - if (!parse_line(buf, key, &val, prefix)) - continue; - - if (val == XKB_KEY_VoidSymbol) - val = 0; - if (val > 0x1fffffff) { - fprintf(stderr, "ignoring illegal keysym (%s, %" PRIx32 ")\n", - key, - val); - continue; - } - - name = malloc(strlen(prefix) + strlen(key) + 1); - if (!name) { - fprintf(stderr, "makekeys: out of memory!\n"); - exit(1); - } - sprintf(name, "%s%s", prefix, key); - info[ksnum].name = name; - info[ksnum].val = val; - ksnum++; - if (ksnum == KTNUM) { - fprintf(stderr, "makekeys: too many keysyms!\n"); - exit(1); - } - } - - fclose(fptr); - } - - printf("/* This file is generated from keysymdef.h. */\n"); - printf("/* Do not edit. */\n"); - printf("\n"); - - best_max_rehash = ksnum; - num_found = 0; - for (z = ksnum; z < KTNUM; z++) { - max_rehash = 0; - for (name = tab, i = z; --i >= 0; ) - *name++ = 0; - for (i = 0; i < ksnum; i++) { - name = info[i].name; - sig = 0; - while ((c = *name++)) - sig = (sig << 1) + c; - first = j = sig % z; - for (k = 0; tab[j]; k++) { - j += first + 1; - if (j >= z) - j -= z; - if (j == first) - goto next1; - } - tab[j] = 1; - if (k > max_rehash) - max_rehash = k; - } - if (max_rehash < MIN_REHASH) { - if (max_rehash < best_max_rehash) { - best_max_rehash = max_rehash; - best_z = z; - } - num_found++; - if (num_found >= MATCHES) - break; - } -next1:; - } - - z = best_z; - printf("#ifndef KS_TABLES_H\n"); - printf("#define KS_TABLES_H\n\n"); - printf("static const unsigned char _XkeyTable[] = {\n"); - if (z == 0) { - fprintf(stderr, "makekeys: failed to find small enough hash!\n" - "Try increasing KTNUM in makekeys.c\n"); - exit(1); - } - printf("0,\n"); - k = 1; - for (i = 0; i < ksnum; i++) { - name = info[i].name; - sig = 0; - while ((c = *name++)) - sig = (sig << 1) + c; - first = j = sig % z; - while (offsets[j]) { - j += first + 1; - if (j >= z) - j -= z; - } - offsets[j] = k; - indexes[i] = k; - val = info[i].val; - printf("0x%.2" PRIx32 ", 0x%.2" PRIx32 ", 0x%.2" PRIx32 ", " - "0x%.2" PRIx32 ", 0x%.2" PRIx32 ", 0x%.2" PRIx32 ", ", - (sig >> 8) & 0xff, sig & 0xff, (val >> 24) & 0xff, - (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); - for (name = info[i].name, k += 7; (c = *name++); k++) - printf("'%c',", c); - printf((i == (ksnum - 1)) ? "0\n" : "0,\n"); - } - printf("};\n"); - printf("\n"); - printf("#define KTABLESIZE %d\n", z); - printf("#define KMAXHASH %d\n", best_max_rehash + 1); - printf("\n"); - printf("static const unsigned short hashString[KTABLESIZE] = {\n"); - for (i = 0; i < z; ) { - printf("0x%.4x", offsets[i]); - i++; - if (i == z) - break; - printf((i & 7) ? ", " : ",\n"); - } - printf("\n"); - printf("};\n"); - - best_max_rehash = ksnum; - num_found = 0; - for (z = ksnum; z < KTNUM; z++) { - max_rehash = 0; - for (name = tab, i = z; --i >= 0; ) - *name++ = 0; - for (i = 0; i < ksnum; i++) { - val = info[i].val; - first = j = val % z; - for (k = 0; tab[j]; k++) { - if (values[j] == val) - goto skip1; - j += first + 1; - if (j >= z) - j -= z; - if (j == first) - goto next2; - } - tab[j] = 1; - values[j] = val; - if (k > max_rehash) - max_rehash = k; -skip1:; - } - if (max_rehash < MIN_REHASH) { - if (max_rehash < best_max_rehash) { - best_max_rehash = max_rehash; - best_z = z; - } - num_found++; - if (num_found >= MATCHES) - break; - } -next2:; - } - - z = best_z; - if (z == 0) { - fprintf(stderr, "makekeys: failed to find small enough hash!\n" - "Try increasing KTNUM in makekeys.c\n"); - exit(1); - } - for (i = z; --i >= 0; ) - offsets[i] = 0; - for (i = 0; i < ksnum; i++) { - val = info[i].val; - first = j = val % z; - while (offsets[j]) { - if (values[j] == val) - goto skip2; - j += first + 1; - if (j >= z) - j -= z; - } - offsets[j] = indexes[i] + 2; - values[j] = val; -skip2:; - } - printf("\n"); - printf("#define VTABLESIZE %d\n", z); - printf("#define VMAXHASH %d\n", best_max_rehash + 1); - printf("\n"); - printf("static const unsigned short hashKeysym[VTABLESIZE] = {\n"); - for (i = 0; i < z; ) { - printf("0x%.4x", offsets[i]); - i++; - if (i == z) - break; - printf((i & 7) ? ", " : ",\n"); - } - printf("\n"); - printf("};\n"); - printf("\n#endif /* KS_TABLES_H */\n"); - - for (i = 0; i < ksnum; i++) - free(info[i].name); - - exit(0); + unsigned int i, flags; + const char *prefix; + bool res; + + prefix = NULL; + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--name2key")) { + prefix = "name2key_"; + flags = FLAG_USE_NULL_STRINGS; + flags |= FLAG_COMPARE_LENGTHS; + } else if (!strcmp(argv[i], "--key2name")) { + prefix = "key2name_"; + flags = FLAG_USE_NULL_STRINGS; + flags |= FLAG_COMPARE_LENGTHS; + flags |= FLAG_REVERSE; + } else if (argv[i][0] == '-') { + fprintf(stderr, "invalid option %s\n", argv[i]); + return EXIT_FAILURE; + } + } + + if (!prefix) { + fprintf(stderr, "none of --name2key and --key2name specified\n"); + return EXIT_FAILURE; + } + + res = create_file(stdout, prefix, flags, argc, argv); + if (!res) { + fprintf(stderr, "cannot create gperf file\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } diff --git a/src/keysym.c b/src/keysym.c index d659354..3ee0499 100644 --- a/src/keysym.c +++ b/src/keysym.c @@ -47,49 +47,35 @@ * DEALINGS IN THE SOFTWARE. */ +#include <endian.h> +#include <inttypes.h> +#include <string.h> #include "xkbcommon/xkbcommon.h" #include "utils.h" -#include "ks_tables.h" #include "keysym.h" +const struct name2key_entry *name2key_lookup(register const char *str, + register unsigned int len); +const struct key2name_entry *key2name_lookup(register const char *str, + register unsigned int len); + +#include "ks_tables.h" + XKB_EXPORT int xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) { - int i, n, h, idx; - const unsigned char *entry; - unsigned char val1, val2, val3, val4; + const struct key2name_entry *e; + uint32_t val; if ((ks & ((unsigned long) ~0x1fffffff)) != 0) { snprintf(buffer, size, "Invalid"); return -1; } - /* Try to find it in our hash table. */ - if (ks <= 0x1fffffff) { - val1 = ks >> 24; - val2 = (ks >> 16) & 0xff; - val3 = (ks >> 8) & 0xff; - val4 = ks & 0xff; - i = ks % VTABLESIZE; - h = i + 1; - n = VMAXHASH; - - while ((idx = hashKeysym[i])) { - entry = &_XkeyTable[idx]; - - if ((entry[0] == val1) && (entry[1] == val2) && - (entry[2] == val3) && (entry[3] == val4)) { - return snprintf(buffer, size, "%s", entry + 4); - } - - if (!--n) - break; - - i += h; - if (i >= VTABLESIZE) - i -= VTABLESIZE; - } - } + val = htole32(ks); + e = key2name_lookup((void*)&val, sizeof(val)); + if (e) + return snprintf(buffer, size, "%s", e->value); if (ks >= 0x01000100 && ks <= 0x0110ffff) /* Unnamed Unicode codepoint. */ @@ -102,42 +88,13 @@ xkb_keysym_get_name(xkb_keysym_t ks, char *buffer, size_t size) XKB_EXPORT xkb_keysym_t xkb_keysym_from_name(const char *s) { - int i, n, h, c, idx; - uint32_t sig = 0; - const char *p = s; + const struct name2key_entry *e; char *tmp; - const unsigned char *entry; - unsigned char sig1, sig2; - xkb_keysym_t val; - - while ((c = *p++)) - sig = (sig << 1) + c; - - i = sig % KTABLESIZE; - h = i + 1; - sig1 = (sig >> 8) & 0xff; - sig2 = sig & 0xff; - n = KMAXHASH; - - while ((idx = hashString[i])) { - entry = &_XkeyTable[idx]; + xkb_keysym_t val, ret; - if ((entry[0] == sig1) && (entry[1] == sig2) && - streq(s, (const char *) entry + 6)) { - val = (entry[2] << 24) | (entry[3] << 16) | - (entry[4] << 8) | entry[5]; - if (!val) - val = XKB_KEY_VoidSymbol; - return val; - } - - if (!--n) - break; - - i += h; - if (i >= KTABLESIZE) - i -= KTABLESIZE; - } + e = name2key_lookup(s, strlen(s)); + if (e) + return e->value; if (*s == 'U') { val = strtoul(&s[1], &tmp, 16); @@ -164,7 +121,6 @@ xkb_keysym_from_name(const char *s) * no separating underscore, while some XF86* syms in the latter did. * As a last ditch effort, try without. */ if (strncmp(s, "XF86_", 5) == 0) { - xkb_keysym_t ret; tmp = strdup(s); if (!tmp) return XKB_KEY_NoSymbol; -- 1.7.12.2 _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
