aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2016-01-01 14:37:20 +0300
committerBjörn Linse <bjorn.linse@gmail.com>2016-08-31 21:40:20 +0200
commit5e59916e84933b0b05fda8ed76cf5f24fa3f48b6 (patch)
treee82e7c531c79a2d71ec9889b3206cafe1d3121c8
parentabe00d583e06a67b9eddbe0aed255a8db06bc725 (diff)
downloadrneovim-5e59916e84933b0b05fda8ed76cf5f24fa3f48b6.tar.gz
rneovim-5e59916e84933b0b05fda8ed76cf5f24fa3f48b6.tar.bz2
rneovim-5e59916e84933b0b05fda8ed76cf5f24fa3f48b6.zip
eval: Use generated hash to look up function list
Problems: - Disables cross-compiling (alternative: keeps two hash implementations which need to be synchronized with each other). - Puts code-specific name literals into CMakeLists.txt. - Workaround for lua’s absence of bidirectional pipe communication is rather ugly.
-rw-r--r--scripts/geneval.lua30
-rw-r--r--src/genhash.c106
-rw-r--r--src/nvim/CMakeLists.txt10
-rw-r--r--src/nvim/eval.c105
-rw-r--r--src/nvim/lib/khash.h41
5 files changed, 204 insertions, 88 deletions
diff --git a/scripts/geneval.lua b/scripts/geneval.lua
index 1f8a14d27b..c9533d7908 100644
--- a/scripts/geneval.lua
+++ b/scripts/geneval.lua
@@ -15,28 +15,20 @@ end
package.path = nvimsrcdir .. '/?.lua;' .. package.path
local funcsfname = autodir .. '/funcs.generated.h'
-local funcsfile = io.open(funcsfname, 'w')
+
+local funcspipe = io.open(funcsfname .. '.hsh', 'w')
local funcs = require('eval')
-local sorted_funcs = {}
for name, def in pairs(funcs.funcs) do
- def.name = name
- def.args = def.args or 0
- if type(def.args) == 'number' then
- def.args = {def.args, def.args}
- elseif #def.args == 1 then
- def.args[2] = 'MAX_FUNC_ARGS'
+ args = def.args or 0
+ if type(args) == 'number' then
+ args = {args, args}
+ elseif #args == 1 then
+ args[2] = 'MAX_FUNC_ARGS'
end
- def.func = def.func or ('f_' .. def.name)
- sorted_funcs[#sorted_funcs + 1] = def
-end
-table.sort(sorted_funcs, function(a, b) return a.name < b.name end)
-
-funcsfile:write('static const VimLFuncDef functions[] = {\n')
-for _, def in ipairs(sorted_funcs) do
- funcsfile:write((' { "%s", %s, %s, &%s },\n'):format(
- def.name, def.args[1], def.args[2], def.func
- ))
+ func = def.func or ('f_' .. name)
+ local val = ('{ %s, %s, &%s }'):format(args[1], args[2], func)
+ funcspipe:write(name .. '\n' .. val .. '\n')
end
-funcsfile:write('};\n')
+funcspipe:close()
diff --git a/src/genhash.c b/src/genhash.c
new file mode 100644
index 0000000000..649b8deb2e
--- /dev/null
+++ b/src/genhash.c
@@ -0,0 +1,106 @@
+// Program used to generate static hashes
+//
+// Uses hashes from khash.h macros library.
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define USE_LIBC_ALLOCATOR
+#include "nvim/lib/khash.h"
+
+KHASH_MAP_INIT_STR(hash, char *)
+
+#define CHECK_FAIL(cond, ...) \
+ do { \
+ if (cond) { \
+ fprintf(stderr, __VA_ARGS__); \
+ putc('\n', stderr); \
+ return 1; \
+ } \
+ } while (0)
+
+int main(int argc, char **argv)
+{
+ if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+ puts("Usage:");
+ puts(" genhash SOURCE TARGET TYPE NAME VALTYPE NULLVAL");
+ puts("Transforms keys and values in a form \"key\\nval\\n\" into a hash");
+ puts("literal.");
+ puts("");
+ puts("SOURCE is the file name to read keys and values from.");
+ puts("TARGET is the file name to write to.");
+ puts("TYPE is the name of the hash type (khash_t argument).");
+ puts("NAME is the name of the generated hash.");
+ puts("VALTYPE is the name of the value type.");
+ puts("NULLVAL is the value used when no value is available.");
+ return 0;
+ }
+
+ CHECK_FAIL(argc != 7, "Expecting six arguments, got %i.", argc);
+
+ const char *const source = argv[1];
+ const char *const target = argv[2];
+ const char *const type = argv[3];
+ const char *const name = argv[4];
+ const char *const valtype = argv[5];
+ const char *const nullval = argv[6];
+
+ FILE *fin = fopen(source, "r");
+ CHECK_FAIL(!fin, "Failed to open source: %s.", strerror(errno));
+
+ char keybuf[80];
+ char valbuf[4096];
+ khash_t(hash) hash = KHASH_EMPTY_TABLE(hash);
+ while (fgets(keybuf, sizeof(keybuf), fin) != NULL) {
+ CHECK_FAIL(ferror(fin), "Failed to read key %i from source: %s",
+ (int) kh_size(&hash), strerror(ferror(fin)));
+ keybuf[strlen(keybuf) - 1] = 0;
+ CHECK_FAIL(!fgets(valbuf, sizeof(valbuf), fin),
+ "Failed to read value for key %i (%s): %s",
+ (int) kh_size(&hash), keybuf, (ferror(fin)
+ ? strerror(ferror(fin))
+ : "EOF found"));
+ valbuf[strlen(valbuf) - 1] = 0;
+ char *const key_copy = strdup(keybuf);
+ CHECK_FAIL(!key_copy, "Failed to allocate memory for a key");
+ int put_ret;
+ const khiter_t k = kh_put(hash, &hash, key_copy, &put_ret);
+ CHECK_FAIL(put_ret != 1, "Expecting unused non-empty bucket for key %s",
+ key_copy);
+ kh_value(&hash, k) = strdup(valbuf);
+ CHECK_FAIL(!kh_value(&hash, k), "Failed to allocate memory for a value");
+ }
+ CHECK_FAIL(fclose(fin), "Failed to close source: %s", strerror(errno));
+
+ FILE *f = fopen(target, "w");
+ CHECK_FAIL(!f, strerror(errno));
+ fprintf(f, "static const khash_t(%s) %s = {", type, name);
+ fprintf(f, " .n_buckets = %i,\n", (int) hash.n_buckets);
+ fprintf(f, " .size = %i,\n", (int) hash.size);
+ fprintf(f, " .n_occupied = %i,\n", (int) hash.n_occupied);
+ fprintf(f, " .upper_bound = %i,\n", (int) hash.upper_bound);
+ fprintf(f, " .flags = (khint32_t[]) {\n");
+ for (khint_t i = 0; i < kh_end(&hash); i++) {
+ fprintf(f, " %i,\n", (int) hash.flags[i]);
+ }
+ fprintf(f, " },\n");
+ fprintf(f, " .keys = (const char*[]) {\n");
+ for (khint_t i = 0; i < kh_end(&hash); i++) {
+ if (kh_exist(&hash, i)) {
+ fprintf(f, " \"%s\",\n", hash.keys[i]);
+ } else {
+ fprintf(f, " NULL,\n");
+ }
+ }
+ fprintf(f, " },\n");
+ fprintf(f, " .vals = (%s[]) {\n", valtype);
+ for (khint_t i = 0; i < kh_end(&hash); i++) {
+ fprintf(f, " %s,\n", (kh_exist(&hash, i) ? hash.vals[i] : nullval));
+ }
+ fprintf(f, " },\n");
+ fprintf(f, "};\n");
+ fclose(f);
+ return 0;
+}
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 3ff34122f0..3385c0e462 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -19,6 +19,7 @@ set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h)
+set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.hsh)
set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h)
set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h)
set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h)
@@ -37,6 +38,7 @@ set(UNICODEDATA_FILE ${UNICODE_DIR}/UnicodeData.txt)
set(CASEFOLDING_FILE ${UNICODE_DIR}/CaseFolding.txt)
set(EASTASIANWIDTH_FILE ${UNICODE_DIR}/EastAsianWidth.txt)
set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h)
+set(GENHASH_SOURCE ${PROJECT_SOURCE_DIR}/src/genhash.c)
include_directories(${GENERATED_DIR})
include_directories(${GENERATED_INCLUDES_DIR})
@@ -214,8 +216,10 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
add_custom_command(OUTPUT ${GENERATED_FUNCS}
COMMAND ${LUA_PRG} ${FUNCS_GENERATOR}
- ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR}
- DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE}
+ ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR}
+ COMMAND $<TARGET_FILE:genhash>
+ ${GENERATED_FUNCS_HASH_INPUT} ${GENERATED_FUNCS} functions functions VimLFuncDef "NOFUNC"
+ DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} genhash
)
add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
@@ -284,6 +288,8 @@ if(WIN32)
DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
+add_executable(genhash ${GENHASH_SOURCE} ${NEOVIM_HEADERS})
+
if(CLANG_ASAN_UBSAN)
message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.")
check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL)
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 64f85a2fe5..c5b5866f56 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -90,6 +90,8 @@
#include "nvim/os/dl.h"
#include "nvim/os/input.h"
#include "nvim/event/loop.h"
+#include "nvim/lib/kvec.h"
+#include "nvim/lib/khash.h"
#include "nvim/lib/queue.h"
#include "nvim/eval/typval_encode.h"
@@ -442,6 +444,18 @@ typedef struct {
ufunc_T *callback;
} timer_T;
+/// Prototype of C function that implements VimL function
+typedef void (*VimLFunc)(typval_T *args, typval_T *rvar);
+
+/// Structure holding VimL function definition
+typedef struct fst {
+ uint8_t min_argc; ///< Minimal number of arguments.
+ uint8_t max_argc; ///< Maximal number of arguments.
+ VimLFunc func; ///< Function implementation.
+} VimLFuncDef;
+
+KHASH_MAP_INIT_STR(functions, VimLFuncDef)
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -6681,17 +6695,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
-/// Prototype of C function that implements VimL function
-typedef void (*VimLFunc)(typval_T *args, typval_T *rvar);
-
-/// Structure holding VimL function definition
-typedef struct fst {
- char *f_name; ///< Function name.
- uint8_t f_min_argc; ///< Minimal number of arguments.
- uint8_t f_max_argc; ///< Maximal number of arguments.
- VimLFunc f_func; ///< Function implementation.
-} VimLFuncDef;
-
+#define NOFUNC { 0, 0, NULL }
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "funcs.generated.h"
#endif
@@ -6713,15 +6717,25 @@ char_u *get_function_name(expand_T *xp, int idx)
if (name != NULL)
return name;
}
- if (++intidx < (int)ARRAY_SIZE(functions)) {
- STRCPY(IObuff, functions[intidx].f_name);
- STRCAT(IObuff, "(");
- if (functions[intidx].f_max_argc == 0)
- STRCAT(IObuff, ")");
- return IObuff;
+ while ((khiter_t) ++intidx < kh_end(&functions)
+ && !kh_exist(&functions, (khiter_t) intidx)) {
}
- return NULL;
+ if ((khiter_t) intidx >= kh_end(&functions)) {
+ return NULL;
+ }
+
+ const char *const key = kh_key(&functions, (khiter_t) intidx);
+ const size_t key_len = strlen(key);
+ memcpy(IObuff, key, key_len);
+ IObuff[key_len] = '(';
+ if (kh_val(&functions, (khiter_t) intidx).max_argc == 0) {
+ IObuff[key_len + 1] = ')';
+ IObuff[key_len + 2] = NUL;
+ } else {
+ IObuff[key_len + 1] = NUL;
+ }
+ return IObuff;
}
/*
@@ -6746,32 +6760,16 @@ char_u *get_expr_name(expand_T *xp, int idx)
-/*
- * Find internal function in table above.
- * Return index, or -1 if not found
- */
-static int
-find_internal_func (
- char_u *name /* name of the function */
-)
+/// Find internal function in hash functions
+///
+/// @param[in] name Name of the function.
+///
+/// Returns pointer to the function definition or NULL if not found.
+static VimLFuncDef *find_internal_func(const char *const name)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL
{
- int first = 0;
- int last = (int)ARRAY_SIZE(functions) - 1;
-
- /*
- * Find the function name in the table. Binary search.
- */
- while (first <= last) {
- int x = first + ((unsigned)(last - first)) / 2;
- int cmp = STRCMP(name, functions[x].f_name);
- if (cmp < 0)
- last = x - 1;
- else if (cmp > 0)
- first = x + 1;
- else
- return x;
- }
- return -1;
+ const khiter_t k = kh_get(functions, &functions, name);
+ return (k == kh_end(&functions) ? NULL : &kh_val(&functions, k));
}
/*
@@ -6889,7 +6887,6 @@ call_func (
#define ERROR_NONE 5
#define ERROR_OTHER 6
int error = ERROR_NONE;
- int i;
int llen;
ufunc_T *fp;
#define FLEN_FIXED 40
@@ -6911,7 +6908,7 @@ call_func (
fname_buf[0] = K_SPECIAL;
fname_buf[1] = KS_EXTRA;
fname_buf[2] = (int)KE_SNR;
- i = 3;
+ int i = 3;
if (eval_fname_sid(name)) { /* "<SID>" or "s:" */
if (current_SID <= 0)
error = ERROR_SCRIPT;
@@ -6983,18 +6980,16 @@ call_func (
}
}
} else {
- /*
- * Find the function name in the table, call its implementation.
- */
- i = find_internal_func(fname);
- if (i >= 0) {
- if (argcount < functions[i].f_min_argc)
+ // Find the function name in the table, call its implementation.
+ VimLFuncDef *const fdef = find_internal_func((char *) fname);
+ if (fdef != NULL) {
+ if (argcount < fdef->min_argc) {
error = ERROR_TOOFEW;
- else if (argcount > functions[i].f_max_argc)
+ } else if (argcount > fdef->max_argc) {
error = ERROR_TOOMANY;
- else {
+ } else {
argvars[argcount].v_type = VAR_UNKNOWN;
- functions[i].f_func(argvars, rettv);
+ fdef->func(argvars, rettv);
error = ERROR_NONE;
}
}
@@ -20120,7 +20115,7 @@ void free_all_functions(void)
int translated_function_exists(char_u *name)
{
if (builtin_function(name, -1)) {
- return find_internal_func(name) >= 0;
+ return find_internal_func((char *) name) != NULL;
}
return find_func(name) != NULL;
}
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index 8287cb14da..f8530be79a 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -130,7 +130,9 @@ int main() {
#include <limits.h>
#include <stdint.h>
-#include "nvim/memory.h"
+#ifndef USE_LIBC_ALLOCATOR
+# include "nvim/memory.h"
+#endif
#include "nvim/func_attr.h"
@@ -171,17 +173,32 @@ typedef khint_t khiter_t;
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
-#ifndef kcalloc
-#define kcalloc(N,Z) xcalloc(N,Z)
-#endif
-#ifndef kmalloc
-#define kmalloc(Z) xmalloc(Z)
-#endif
-#ifndef krealloc
-#define krealloc(P,Z) xrealloc(P,Z)
-#endif
-#ifndef kfree
-#define kfree(P) xfree(P)
+#ifdef USE_LIBC_ALLOCATOR
+# ifndef kcalloc
+# define kcalloc(N,Z) calloc(N,Z)
+# endif
+# ifndef kmalloc
+# define kmalloc(Z) malloc(Z)
+# endif
+# ifndef krealloc
+# define krealloc(P,Z) realloc(P,Z)
+# endif
+# ifndef kfree
+# define kfree(P) free(P)
+# endif
+#else
+# ifndef kcalloc
+# define kcalloc(N,Z) xcalloc(N,Z)
+# endif
+# ifndef kmalloc
+# define kmalloc(Z) xmalloc(Z)
+# endif
+# ifndef krealloc
+# define krealloc(P,Z) xrealloc(P,Z)
+# endif
+# ifndef kfree
+# define kfree(P) xfree(P)
+# endif
#endif
#define __ac_HASH_UPPER 0.77