diff options
Diffstat (limited to 'ascii.c')
-rw-r--r-- | ascii.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/ascii.c b/ascii.c new file mode 100644 index 00000000..1873f3d6 --- /dev/null +++ b/ascii.c @@ -0,0 +1,169 @@ +/* $Id$ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons <kristaps@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include <assert.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "term.h" + +#define ASCII_PRINT_HI 126 +#define ASCII_PRINT_LO 32 + +/* + * Lookup and hashing routines for constructing the ASCII symbol table, + * which should contain a significant portion of mdoc(7)'s special + * symbols. + */ + +struct line { + const char *code; + const char *out; + /* 32- and 64-bit alignment safe. */ + size_t codesz; + size_t outsz; +}; + +struct linep { + const struct line *line; + struct linep *next; +}; + +#define LINE(w, x, y, z) \ + { (w), (y), (x), (z) }, +static const struct line lines[] = { +#include "ascii.in" +}; + + +static inline int match(const struct line *, + const char *, size_t); + + +void * +ascii2htab(void) +{ + void **htab; + struct linep *pp, *p; + int i, len, hash; + + /* + * Constructs a very basic chaining hashtable. The hash routine + * is simply the integral value of the first character. + * Subsequent entries are chained in the order they're processed + * (they're in-line re-ordered during lookup). + */ + + assert(0 == sizeof(lines) % sizeof(struct line)); + len = sizeof(lines) / sizeof(struct line); + + if (NULL == (p = calloc((size_t)len, sizeof(struct linep)))) + err(1, "malloc"); + + htab = calloc(ASCII_PRINT_HI - ASCII_PRINT_LO + 1, + sizeof(struct linep **)); + + if (NULL == htab) + err(1, "malloc"); + + for (i = 0; i < len; i++) { + assert(lines[i].codesz > 0); + assert(lines[i].code); + assert(lines[i].out); + + p[i].line = &lines[i]; + + hash = (int)lines[i].code[0] - ASCII_PRINT_LO; + + if (NULL == (pp = ((struct linep **)htab)[hash])) { + htab[hash] = &p[i]; + continue; + } + + for ( ; pp->next; pp = pp->next) + /* Scan ahead. */ ; + + pp->next = &p[i]; + } + + return((void *)htab); +} + + +const char * +a2ascii(void *htabp, const char *p, size_t sz, size_t *rsz) +{ + struct linep *pp, *prev; + void **htab; + int hash; + + htab = (void **)htabp; + + assert(p); + assert(sz > 0); + assert(p[0] >= ASCII_PRINT_LO && p[0] <= ASCII_PRINT_HI); + + /* + * Lookup the symbol in the symbol hash. See ascii2htab for the + * hashtable specs. This dynamically re-orders the hash chain + * to optimise for repeat hits. + */ + + hash = (int)p[0] - ASCII_PRINT_LO; + + if (NULL == (pp = ((struct linep **)htab)[hash])) + return(NULL); + + if (NULL == pp->next) { + if ( ! match(pp->line, p, sz)) + return(NULL); + *rsz = pp->line->outsz; + return(pp->line->out); + } + + for (prev = NULL; pp; pp = pp->next) { + if ( ! match(pp->line, p, sz)) { + prev = pp; + continue; + } + + /* Re-order the hash chain. */ + + if (prev) { + prev->next = pp->next; + pp->next = ((struct linep **)htab)[hash]; + htab[hash] = pp; + } + + *rsz = pp->line->outsz; + return(pp->line->out); + } + + return(NULL); +} + + +static inline int +match(const struct line *line, const char *p, size_t sz) +{ + + if (line->codesz != sz) + return(0); + return(0 == strncmp(line->code, p, sz)); +} |