summaryrefslogtreecommitdiff
path: root/tools/genperf/genperf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/genperf/genperf.c')
-rw-r--r--tools/genperf/genperf.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/tools/genperf/genperf.c b/tools/genperf/genperf.c
new file mode 100644
index 00000000..1fcf2da1
--- /dev/null
+++ b/tools/genperf/genperf.c
@@ -0,0 +1,540 @@
+/* $Id$
+ *
+ * Generate Minimal Perfect Hash (genperf)
+ *
+ * Copyright (C) 2006-2007 Peter Johnson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+#include "tools/genperf/perfect.h"
+#include "libyasm/compat-queue.h"
+#include "libyasm/coretype.h"
+#include "libyasm/errwarn.h"
+
+typedef STAILQ_HEAD(slist, sval) slist;
+typedef struct sval {
+ STAILQ_ENTRY(sval) link;
+ char *str;
+} sval;
+
+typedef STAILQ_HEAD(keyword_list, keyword) keyword_list;
+typedef struct keyword {
+ STAILQ_ENTRY(keyword) link;
+ char *name;
+ char *args;
+ unsigned int line;
+} keyword;
+
+static unsigned int cur_line = 1;
+static int errors = 0;
+
+static void
+report_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%u: ", cur_line);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ errors++;
+}
+
+void
+yasm__fatal(const char *message, ...)
+{
+ abort();
+}
+
+/* make the c output for the perfect hash tab array */
+static void
+make_c_tab(
+ FILE *f,
+ bstuff *tab, /* table indexed by b */
+ ub4 smax, /* range of scramble[] */
+ ub4 blen, /* b in 0..blen-1, power of 2 */
+ ub4 *scramble) /* used in final hash */
+{
+ ub4 i;
+ /* table for the mapping for the perfect hash */
+ if (blen >= USE_SCRAMBLE) {
+ /* A way to make the 1-byte values in tab bigger */
+ if (smax > UB2MAXVAL+1) {
+ fprintf(f, " static const unsigned long scramble[] = {\n");
+ for (i=0; i<=UB1MAXVAL; i+=4)
+ fprintf(f, " 0x%.8lx, 0x%.8lx, 0x%.8lx, 0x%.8lx,\n",
+ scramble[i+0], scramble[i+1], scramble[i+2], scramble[i+3]);
+ } else {
+ fprintf(f, " static const unsigned short scramble[] = {\n");
+ for (i=0; i<=UB1MAXVAL; i+=8)
+ fprintf(f,
+" 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx, 0x%.4lx,\n",
+ scramble[i+0], scramble[i+1], scramble[i+2], scramble[i+3],
+ scramble[i+4], scramble[i+5], scramble[i+6], scramble[i+7]);
+ }
+ fprintf(f, " };\n");
+ fprintf(f, "\n");
+ }
+
+ if (blen > 0) {
+ /* small adjustments to _a_ to make values distinct */
+ if (smax <= UB1MAXVAL+1 || blen >= USE_SCRAMBLE)
+ fprintf(f, " static const unsigned char ");
+ else
+ fprintf(f, " static const unsigned short ");
+ fprintf(f, "tab[] = {\n");
+
+ if (blen < 16) {
+ for (i=0; i<blen; ++i)
+ fprintf(f, "%3ld,", scramble[tab[i].val_b]);
+ } else if (blen <= 1024) {
+ for (i=0; i<blen; i+=16)
+ fprintf(f, " %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n",
+ scramble[tab[i+0].val_b], scramble[tab[i+1].val_b],
+ scramble[tab[i+2].val_b], scramble[tab[i+3].val_b],
+ scramble[tab[i+4].val_b], scramble[tab[i+5].val_b],
+ scramble[tab[i+6].val_b], scramble[tab[i+7].val_b],
+ scramble[tab[i+8].val_b], scramble[tab[i+9].val_b],
+ scramble[tab[i+10].val_b], scramble[tab[i+11].val_b],
+ scramble[tab[i+12].val_b], scramble[tab[i+13].val_b],
+ scramble[tab[i+14].val_b], scramble[tab[i+15].val_b]);
+ } else if (blen < USE_SCRAMBLE) {
+ for (i=0; i<blen; i+=8)
+ fprintf(f, " %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n",
+ scramble[tab[i+0].val_b], scramble[tab[i+1].val_b],
+ scramble[tab[i+2].val_b], scramble[tab[i+3].val_b],
+ scramble[tab[i+4].val_b], scramble[tab[i+5].val_b],
+ scramble[tab[i+6].val_b], scramble[tab[i+7].val_b]);
+ } else {
+ for (i=0; i<blen; i+=16)
+ fprintf(f, " %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,\n",
+ tab[i+0].val_b, tab[i+1].val_b,
+ tab[i+2].val_b, tab[i+3].val_b,
+ tab[i+4].val_b, tab[i+5].val_b,
+ tab[i+6].val_b, tab[i+7].val_b,
+ tab[i+8].val_b, tab[i+9].val_b,
+ tab[i+10].val_b, tab[i+11].val_b,
+ tab[i+12].val_b, tab[i+13].val_b,
+ tab[i+14].val_b, tab[i+15].val_b);
+ }
+ fprintf(f, " };\n");
+ fprintf(f, "\n");
+ }
+}
+
+static void
+perfect_gen(FILE *out, const char *lookup_function_name,
+ const char *struct_name, keyword_list *kws,
+ const char *filename)
+{
+ ub4 nkeys;
+ key *keys;
+ hashform form;
+ bstuff *tab; /* table indexed by b */
+ hstuff *tabh; /* table indexed by hash value */
+ ub4 smax; /* scramble[] values in 0..smax-1, a power of 2 */
+ ub4 alen; /* a in 0..alen-1, a power of 2 */
+ ub4 blen; /* b in 0..blen-1, a power of 2 */
+ ub4 salt; /* a parameter to the hash function */
+ gencode final; /* code for final hash */
+ ub4 i;
+ ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */
+ char buf[10][80]; /* buffer for generated code */
+ char *buf2[10]; /* also for generated code */
+ keyword *kw;
+
+ /* perfect hash configuration */
+ form.mode = NORMAL_HM;
+ form.hashtype = STRING_HT;
+ form.perfect = MINIMAL_HP;
+ form.speed = SLOW_HS;
+
+ /* set up code for final hash */
+ final.line = buf2;
+ final.used = 0;
+ final.len = 10;
+ for (i=0; i<10; i++)
+ final.line[i] = buf[i];
+
+ /* build list of keys */
+ nkeys = 0;
+ keys = NULL;
+ STAILQ_FOREACH(kw, kws, link) {
+ key *k = yasm_xmalloc(sizeof(key));
+
+ k->name_k = yasm__xstrdup(kw->name);
+ k->len_k = (ub4)strlen(kw->name);
+ k->next_k = keys;
+ keys = k;
+ nkeys++;
+ }
+
+ /* find the hash */
+ findhash(&tab, &tabh, &alen, &blen, &salt, &final,
+ scramble, &smax, keys, nkeys, &form);
+
+ /* The hash function beginning */
+ fprintf(out, "static const struct %s *\n", struct_name);
+ fprintf(out, "%s(const char *key, size_t len)\n", lookup_function_name);
+ fprintf(out, "{\n");
+
+ /* output the dir table: this should loop up to smax for NORMAL_HP,
+ * or up to pakd.nkeys for MINIMAL_HP.
+ */
+ fprintf(out, " static const struct %s pd[%lu] = {\n", struct_name, nkeys);
+ for (i=0; i<nkeys; i++) {
+ if (tabh[i].key_h) {
+ STAILQ_FOREACH(kw, kws, link) {
+ if (strcmp(kw->name, tabh[i].key_h->name_k) == 0)
+ break;
+ }
+ if (!kw) {
+ report_error("internal error: could not find `%s'",
+ tabh[i].key_h->name_k);
+ break;
+ }
+ fprintf(out, "#line %u \"%s\"\n", kw->line, filename);
+ fprintf(out, " {\"%s\"%s}", kw->name, kw->args);
+ } else
+ fprintf(out, " { NULL }");
+
+ if (i < nkeys-1)
+ fprintf(out, ",");
+ fprintf(out, "\n");
+ }
+ fprintf(out, " };\n");
+
+ /* output the hash tab[] array */
+ make_c_tab(out, tab, smax, blen, scramble);
+
+ /* The hash function body */
+ fprintf(out, " const struct %s *ret;\n", struct_name);
+ for (i=0; i<final.used; ++i)
+ fprintf(out, final.line[i]);
+ fprintf(out, " if (rsl >= %lu) return NULL;\n", nkeys);
+ fprintf(out, " ret = &pd[rsl];\n");
+ fprintf(out, " if (strcmp(key, ret->name) != 0) return NULL;\n");
+ fprintf(out, " return ret;\n");
+ fprintf(out, "}\n");
+ fprintf(out, "\n");
+
+ free(tab);
+ free(tabh);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in, *out;
+ size_t i;
+ char *ch;
+ static char line[1024], tmp[1024];
+ static char struct_name[128] = "";
+ static char lookup_function_name[128] = "in_word_set";
+ static char language[16] = "";
+ static char delimiters[16] = ",\r\n";
+ static char name[128];
+ static char filename[768];
+ int need_struct = 0;
+ int have_struct = 0;
+ int go_keywords = 0;
+ int ignore_case = 0;
+ int compare_strncmp = 0;
+ int readonly_tables = 0;
+ slist usercode, usercode2;
+ keyword_list keywords;
+ sval *sv;
+ keyword *kw;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: genperf <in> <out>\n");
+ return EXIT_FAILURE;
+ }
+
+ in = fopen(argv[1], "rt");
+ if (!in) {
+ fprintf(stderr, "Could not open `%s' for reading\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ ch = argv[1];
+ i = 0;
+ while (*ch && i < 767) {
+ if (*ch == '\\') {
+ filename[i++] = '/';
+ ch++;
+ } else
+ filename[i++] = *ch++;
+ }
+ filename[i] = '\0';
+
+ STAILQ_INIT(&usercode);
+ STAILQ_INIT(&usercode2);
+ STAILQ_INIT(&keywords);
+
+ /* Parse declarations section */
+ while (fgets(line, 1024, in)) {
+ /* Comments start with # as the first thing on a line */
+ if (line[0] == '#') {
+ cur_line++;
+ continue;
+ }
+
+ /* Handle structure declaration */
+ if (strncmp(line, "struct", 6) == 0) {
+ int braces;
+
+ if (!need_struct) {
+ report_error("struct without %%struct-type declaration");
+ return EXIT_FAILURE;
+ }
+ if (have_struct) {
+ report_error("more than one struct declaration");
+ return EXIT_FAILURE;
+ }
+ have_struct = 1;
+
+ /* copy struct name */
+ ch = &line[6];
+ while (isspace(*ch))
+ ch++;
+ i = 0;
+ while ((isalnum(*ch) || *ch == '_') && i < 127)
+ struct_name[i++] = *ch++;
+ if (i == 127) {
+ report_error("struct name too long");
+ return EXIT_FAILURE;
+ }
+ struct_name[i] = '\0';
+
+ sv = yasm_xmalloc(sizeof(sval));
+ sprintf(tmp, "#line %u \"%s\"\n", cur_line, filename);
+ sv->str = yasm__xstrdup(tmp);
+ STAILQ_INSERT_TAIL(&usercode, sv, link);
+
+ braces = 0;
+ do {
+ /* count braces to determine when we're done */
+ ch = line;
+ while (*ch != '\0') {
+ if (*ch == '{')
+ braces++;
+ if (*ch == '}')
+ braces--;
+ ch++;
+ }
+ sv = yasm_xmalloc(sizeof(sval));
+ sv->str = yasm__xstrdup(line);
+ STAILQ_INSERT_TAIL(&usercode, sv, link);
+ cur_line++;
+ if (braces <= 0)
+ break;
+ } while (fgets(line, 1024, in));
+ cur_line++;
+ continue;
+ }
+
+ /* Ignore non-declaration lines */
+ if (line[0] != '%') {
+ cur_line++;
+ continue;
+ }
+
+ /* %% terminates declarations section */
+ if (line[1] == '%') {
+ if (need_struct && !have_struct) {
+ report_error("%%struct-type declaration, but no struct found");
+ return EXIT_FAILURE;
+ }
+ go_keywords = 1;
+ break; /* move on to keywords section */
+ }
+
+ /* %{ begins a verbatim code section that ends with %} */
+ if (line[1] == '{') {
+ sv = yasm_xmalloc(sizeof(sval));
+ sprintf(tmp, "#line %u \"%s\"\n\n", cur_line, filename);
+ sv->str = yasm__xstrdup(tmp);
+ STAILQ_INSERT_TAIL(&usercode, sv, link);
+
+ while (fgets(line, 1024, in)) {
+ cur_line++;
+ if (line[0] == '%' && line[1] == '}')
+ break;
+ sv = yasm_xmalloc(sizeof(sval));
+ sv->str = yasm__xstrdup(line);
+ STAILQ_INSERT_TAIL(&usercode, sv, link);
+ }
+ cur_line++;
+ continue;
+ }
+
+ if (strncmp(&line[1], "ignore-case", 11) == 0) {
+ ignore_case = 1;
+ } else if (strncmp(&line[1], "compare-strncmp", 15) == 0) {
+ compare_strncmp = 1;
+ } else if (strncmp(&line[1], "readonly-tables", 15) == 0) {
+ readonly_tables = 1;
+ } else if (strncmp(&line[1], "language=", 9) == 0) {
+ ch = &line[10];
+ i = 0;
+ while (*ch != '\n' && i<15)
+ language[i++] = *ch++;
+ language[i] = '\0';
+ } else if (strncmp(&line[1], "delimiters=", 11) == 0) {
+ ch = &line[12];
+ i = 0;
+ while (i<15)
+ delimiters[i++] = *ch++;
+ delimiters[i] = '\0';
+ } else if (strncmp(&line[1], "enum", 4) == 0) {
+ /* unused */
+ } else if (strncmp(&line[1], "struct-type", 11) == 0) {
+ need_struct = 1;
+ } else if (strncmp(&line[1], "define", 6) == 0) {
+ /* Several different defines we need to handle */
+ ch = &line[7];
+ while (isspace(*ch))
+ ch++;
+
+ if (strncmp(ch, "hash-function-name", 18) == 0) {
+ /* unused */
+ } else if (strncmp(ch, "lookup-function-name", 20) == 0) {
+ ch = &line[7+20+1];
+ while (isspace(*ch))
+ ch++;
+ i = 0;
+ while ((isalnum(*ch) || *ch == '_') && i < 127)
+ lookup_function_name[i++] = *ch++;
+ if (i == 127) {
+ report_error("struct name too long");
+ return EXIT_FAILURE;
+ }
+ lookup_function_name[i] = '\0';
+ } else {
+ fprintf(stderr, "%u: unrecognized define `%s'\n", cur_line,
+ line);
+ }
+ } else {
+ fprintf(stderr, "%u: unrecognized declaration `%s'\n", cur_line,
+ line);
+ }
+
+ cur_line++;
+ }
+
+ if (!go_keywords) {
+ report_error("no keywords section found");
+ return EXIT_FAILURE;
+ }
+
+ /* Parse keywords section */
+ while (fgets(line, 1024, in)) {
+ char *d;
+
+ /* Comments start with # as the first thing on a line */
+ if (line[0] == '#') {
+ cur_line++;
+ continue;
+ }
+
+ /* Keywords section terminated with %% */
+ if (line[0] == '%' && line[1] == '%')
+ break;
+
+ /* Look for name */
+ ch = &line[0];
+ i = 0;
+ while (strchr(delimiters, *ch) == NULL && i < 127)
+ name[i++] = *ch++;
+ if (i == 127) {
+ report_error("keyword name too long");
+ return EXIT_FAILURE;
+ }
+ name[i] = '\0';
+
+ /* Strip EOL */
+ d = strrchr(ch, '\n');
+ if (d)
+ *d = '\0';
+ d = strrchr(ch, '\r');
+ if (d)
+ *d = '\0';
+ kw = yasm_xmalloc(sizeof(keyword));
+ kw->name = yasm__xstrdup(name);
+ kw->args = yasm__xstrdup(ch);
+ kw->line = cur_line;
+ STAILQ_INSERT_TAIL(&keywords, kw, link);
+ cur_line++;
+ }
+
+ if (errors > 0)
+ return EXIT_FAILURE;
+
+ /* Pull in any end code */
+ if (!feof(in)) {
+ sv = yasm_xmalloc(sizeof(sval));
+ sprintf(tmp, "#line %u \"%s\"\n\n", cur_line, filename);
+ sv->str = yasm__xstrdup(tmp);
+ STAILQ_INSERT_TAIL(&usercode2, sv, link);
+
+ while (fgets(line, 1024, in)) {
+ sv = yasm_xmalloc(sizeof(sval));
+ sv->str = yasm__xstrdup(line);
+ STAILQ_INSERT_TAIL(&usercode2, sv, link);
+ }
+ }
+
+ /* output code */
+ out = fopen(argv[2], "wt");
+ if (!out) {
+ fprintf(stderr, "Could not open `%s' for writing\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ fprintf(out, "/* %s code produced by genperf */\n", language);
+ fprintf(out, "/* Command-line: genperf %s %s */\n", argv[1], argv[2]);
+
+ STAILQ_FOREACH(sv, &usercode, link)
+ fprintf(out, "%s", sv->str);
+
+ /* Get perfect hash */
+ perfect_gen(out, lookup_function_name, struct_name, &keywords, filename);
+
+ STAILQ_FOREACH(sv, &usercode2, link)
+ fprintf(out, "%s", sv->str);
+
+ fclose(out);
+
+ if (errors > 0) {
+ remove(argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+