diff --git a/hotfuzz-module.c b/hotfuzz-module.c index 3636652..41b8dc0 100644 --- a/hotfuzz-module.c +++ b/hotfuzz-module.c @@ -82,6 +82,7 @@ static void match_row(struct Str a, struct Str b, int *bonuses, unsigned i, int static int calc_cost(struct Str needle, struct Str haystack, bool ignore_case) { unsigned n = haystack.len, m = needle.len; if (n > MAX_HAYSTACK_LEN || m > MAX_NEEDLE_LEN) return 100000; + if (!m) return 0; // Everything matches the empty needle, at no cost int c[MAX_NEEDLE_LEN], d[MAX_NEEDLE_LEN]; for (unsigned j = 0; j < m; ++j) c[j] = d[j] = 100000; @@ -131,32 +132,33 @@ static void bump_free(struct Bump *head) { /** Copies the Emacs string to make its contents accessible. */ static struct Str copy_emacs_string(emacs_env *env, struct Bump **bump, emacs_value value) { - char *buf = NULL; - ptrdiff_t origlen, len; - if (*bump) { - // Opportunistically try to copy into remaining space - buf = (*bump)->cursor; - len = origlen = (*bump)->limit - (*bump)->cursor; - } - // Determine the size of the string (including null-terminator) - if (env->copy_string_contents(env, value, buf, &len)) { - if (buf) goto success; - } else { - if (!buf || len == origlen) return (struct Str) { 0 }; - env->non_local_exit_clear(env); + // Ask for the size (including null-terminator) with a NULL buffer. Passing + // a buffer that turns out to be too small is not a usable size query: Emacs + // 31 signals `memory-buffer-too-small' in that case, and older versions + // leave the required size unreported when the shortfall is detected early. + ptrdiff_t len = 0; + if (!env->copy_string_contents(env, value, NULL, &len)) return (struct Str) { 0 }; + + // `strtolower' rewrites the string in uint64_t-sized chunks and so touches up + // to alignof(uint64_t)-1 bytes past the null-terminator. Reserve the rounded + // up size to keep those writes inside the block. + ptrdiff_t need = (len + alignof(uint64_t) - 1) & ~(ptrdiff_t) (alignof(uint64_t) - 1); + + char *buf; + if (*bump && (*bump)->limit - (*bump)->cursor >= need) + buf = (*bump)->cursor; // Fits in the remaining space + else { + size_t capacity = *bump ? 2 * ((*bump)->limit - (*bump)->b) : 2048; + if (capacity < (size_t) need) capacity = need; + struct Bump *new; + if (!(new = malloc(sizeof *new + capacity))) return (struct Str) { 0 }; + *new = (struct Bump) { .next = *bump, .cursor = new->b, .limit = new->b + capacity }; + *bump = new; + buf = new->cursor; } - size_t capacity = *bump ? 2 * ((*bump)->limit - (*bump)->b) : 2048; - if (capacity < (size_t) len) capacity = len + alignof(uint64_t) - 1; - struct Bump *new; - if (!(new = malloc(sizeof *new + capacity))) return (struct Str) { 0 }; - *new = (struct Bump) { .next = *bump, .cursor = new->b, .limit = new->b + capacity }; - *bump = new; - - env->copy_string_contents(env, value, buf = new->cursor, &len); -success: - (*bump)->cursor = (char *) (((uintptr_t) (*bump)->cursor + len - + alignof(uint64_t) - 1) & ~(alignof(uint64_t) - 1)); + if (!env->copy_string_contents(env, value, buf, &len)) return (struct Str) { 0 }; + (*bump)->cursor = buf + need; return (struct Str) { buf, len - 1 }; }