diff options
Diffstat (limited to 'test/fuzz/text-fuzzer.c')
| -rw-r--r-- | test/fuzz/text-fuzzer.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/test/fuzz/text-fuzzer.c b/test/fuzz/text-fuzzer.c new file mode 100644 index 0000000..52dd31f --- /dev/null +++ b/test/fuzz/text-fuzzer.c @@ -0,0 +1,317 @@ +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include "fuzzer.h" +#include "text.h" +#include "text-util.h" +#include "util.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +typedef enum CmdStatus (*Cmd)(Text *txt, const char *cmd); + +static Mark mark = EMARK; + +static char data[BUFSIZ]; + +static uint64_t bench(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint64_t)(ts.tv_sec * 1000000 + ts.tv_nsec / 1000); + else + return 0; +} + +static size_t pos_start(Text *txt) { + return 0; +} + +static size_t pos_middle(Text *txt) { + return text_size(txt) / 2; +} + +static size_t pos_end(Text *txt) { + return text_size(txt); +} + +static size_t pos_random(Text *txt) { + return rand() % (text_size(txt) + 1); +} + +static size_t pos_prev(Text *txt) { + static size_t pos = EPOS; + size_t max = text_size(txt); + if (pos > max) + pos = max; + return pos-- % (max + 1); +} + +static size_t pos_next(Text *txt) { + static size_t pos = 0; + return pos++ % (text_size(txt) + 1); +} + +static size_t pos_stripe(Text *txt) { + static size_t pos = 0; + return pos+=1024 % (text_size(txt) + 1); +} + +static enum CmdStatus bench_insert(Text *txt, size_t pos, const char *cmd) { + return text_insert(txt, pos, data, sizeof data); +} + +static enum CmdStatus bench_delete(Text *txt, size_t pos, const char *cmd) { + return text_delete(txt, pos, 1); +} + +static enum CmdStatus bench_replace(Text *txt, size_t pos, const char *cmd) { + text_delete(txt, pos, 1); + text_insert(txt, pos, "-", 1); + return CMD_OK; +} + +static enum CmdStatus bench_mark(Text *txt, size_t pos, const char *cmd) { + Mark mark = text_mark_set(txt, pos); + if (mark == EMARK) + return CMD_FAIL; + if (text_mark_get(txt, mark) != pos) + return CMD_FAIL; + return CMD_OK; +} + +static enum CmdStatus cmd_bench(Text *txt, const char *cmd) { + + static enum CmdStatus (*bench_cmd[])(Text*, size_t, const char*) = { + ['i'] = bench_insert, + ['d'] = bench_delete, + ['r'] = bench_replace, + ['m'] = bench_mark, + }; + + static size_t (*bench_pos[])(Text*) = { + ['^'] = pos_start, + ['|'] = pos_middle, + ['$'] = pos_end, + ['%'] = pos_random, + ['-'] = pos_prev, + ['+'] = pos_next, + ['~'] = pos_stripe, + }; + + if (!data[0]) { + // make `p` command output more readable + int len = snprintf(data, sizeof data, "[ ... %zu bytes ... ]\n", sizeof data); + memset(data+len, '\r', sizeof(data) - len); + } + + const char *params = cmd; + while (*params == ' ') + params++; + + size_t idx_cmd = params[0]; + if (idx_cmd >= LENGTH(bench_cmd) || !bench_cmd[idx_cmd]) { + puts("Invalid bench command"); + return CMD_ERR; + } + + for (params++; *params == ' '; params++); + + size_t idx_pos = params[0]; + if (idx_pos >= LENGTH(bench_pos) || !bench_pos[idx_pos]) { + puts("Invalid bench position"); + return CMD_ERR; + } + + size_t iter = 1; + sscanf(params+1, "%zu\n", &iter); + + for (size_t i = 1; i <= iter; i++) { + size_t pos = bench_pos[idx_pos](txt); + uint64_t s = bench(); + enum CmdStatus ret = bench_cmd[idx_cmd](txt, pos, NULL); + uint64_t e = bench(); + if (ret != CMD_OK) + return ret; + printf("%zu: %" PRIu64 "us\n", i, e-s); + } + return CMD_OK; +} + +static enum CmdStatus cmd_insert(Text *txt, const char *cmd) { + char data[BUFSIZ]; + size_t pos; + if (sscanf(cmd, "%zu %s\n", &pos, data) != 2) + return CMD_ERR; + size_t len = strlen(data); + return text_insert(txt, pos, data, len); +} + +static enum CmdStatus cmd_delete(Text *txt, const char *cmd) { + size_t pos, len; + if (sscanf(cmd, "%zu %zu", &pos, &len) != 2) + return CMD_ERR; + return text_delete(txt, pos, len); +} + +static enum CmdStatus cmd_size(Text *txt, const char *cmd) { + printf("%zu bytes\n", text_size(txt)); + return CMD_OK; +} + +static enum CmdStatus cmd_snapshot(Text *txt, const char *cmd) { + text_snapshot(txt); + return CMD_OK; +} + +static enum CmdStatus cmd_undo(Text *txt, const char *cmd) { + return text_undo(txt) != EPOS; +} + +static enum CmdStatus cmd_redo(Text *txt, const char *cmd) { + return text_redo(txt) != EPOS; +} + +static enum CmdStatus cmd_earlier(Text *txt, const char *cmd) { + return text_earlier(txt) != EPOS; +} + +static enum CmdStatus cmd_later(Text *txt, const char *cmd) { + return text_later(txt) != EPOS; +} + +static enum CmdStatus cmd_mark_set(Text *txt, const char *cmd) { + size_t pos; + if (sscanf(cmd, "%zu\n", &pos) != 1) + return CMD_ERR; + Mark m = text_mark_set(txt, pos); + if (m != EMARK) + mark = m; + return m != EMARK; +} + +static enum CmdStatus cmd_mark_get(Text *txt, const char *cmd) { + size_t pos = text_mark_get(txt, mark); + if (pos != EPOS) + printf("%zu\n", pos); + return pos != EPOS; +} + +static enum CmdStatus cmd_print(Text *txt, const char *cmd) { + size_t start = 0, size = text_size(txt), rem = size; + for (Iterator it = text_iterator_get(txt, start); + rem > 0 && text_iterator_valid(&it); + text_iterator_next(&it)) { + size_t prem = it.end - it.text; + if (prem > rem) + prem = rem; + if (fwrite(it.text, prem, 1, stdout) != 1) + return CMD_ERR; + rem -= prem; + } + if (rem != size) + puts(""); + return rem == 0; +} + +static enum CmdStatus cmd_info(Text *txt, const char *cmd) { +#ifdef text_info + TextInfo info = text_info(txt); + printf("meta data: %zu\nblocks: %zu\ndata: %zu\nrevisions: %zu\n" + "changes: %zu\nchanges total: %zu\npieces: %zu\npieces total: %zu\n", + info.metadata, info.blocks, info.data, info.revisions, + info.changes, info.changes_total, info.pieces, info.pieces_total); +#endif + return CMD_OK; +} + +static enum CmdStatus cmd_dump(Text *txt, const char *cmd) { +#ifdef text_dump + text_dump(txt, stdout); +#endif + return CMD_OK; +} + +static enum CmdStatus cmd_quit(Text *txt, const char *cmd) { + return CMD_QUIT; +} + +static Cmd commands[] = { + ['%'] = cmd_info, + ['@'] = cmd_dump, + ['-'] = cmd_earlier, + ['+'] = cmd_later, + ['?'] = cmd_mark_get, + ['='] = cmd_mark_set, + ['#'] = cmd_size, + ['b'] = cmd_bench, + ['d'] = cmd_delete, + ['i'] = cmd_insert, + ['p'] = cmd_print, + ['q'] = cmd_quit, + ['r'] = cmd_redo, + ['s'] = cmd_snapshot, + ['u'] = cmd_undo, +}; + +static int repl(const char *name, FILE *input) { + Text *txt = text_load(name); + if (!name) + name = "-"; + if (!txt) { + fprintf(stderr, "Failed to load text from `%s'\n", name); + return 1; + } + + printf("Loaded %zu bytes from `%s'\n", text_size(txt), name); + + char line[BUFSIZ]; + for (;;) { + printf("> "); + if (!fgets(line, sizeof(line), input)) + break; + if (!isatty(0)) + printf("%s", line); + if (line[0] == '\n') + continue; + size_t idx = line[0]; + if (idx < LENGTH(commands) && commands[idx]) { + enum CmdStatus ret = commands[idx](txt, line+1); + printf("%s", cmd_status_msg[ret]); + if (ret == CMD_QUIT) + break; + } else { + puts("Invalid command"); + } + } + + text_free(txt); + + return 0; +} + +#ifdef LIBFUZZER + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) { + FILE *input = fmemopen((void*)data, len, "r"); + if (!input) + return 1; + int r = repl(NULL, input); + fclose(input); + return r; +} + +#else + +int main(int argc, char *argv[]) { + return repl(argc == 1 ? NULL : argv[1], stdin); +} + +#endif /* LIBFUZZER */ |
