Przeglądaj źródła

ncd: use unique string identifiers when resolveing variable/object names. The goal is to improve
performance by eliminating strcmp() and similar, but this isn't any faster at this point because stuff just
turns identifiers back into strings.

ambrop7 13 lat temu
rodzic
commit
a157cf9829

+ 1 - 0
ncd/CMakeLists.txt

@@ -80,6 +80,7 @@ add_executable(badvpn-ncd
     NCDInterpProg.c
     NCDPlaceholderDb.c
     NCDMethodIndex.c
+    NCDStringIndex.c
     BEventLock.c
     modules/command_template.c
     modules/event_template.c

+ 9 - 8
ncd/NCDInterpProcess.c

@@ -38,6 +38,7 @@
 #include <misc/maxalign.h>
 #include <misc/strdup.h>
 #include <base/BLog.h>
+#include <ncd/make_name_indices.h>
 
 #include "NCDInterpProcess.h"
 
@@ -147,9 +148,10 @@ fail:
     return 0;
 }
 
-int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
+int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
 {
     ASSERT(process)
+    ASSERT(string_index)
     ASSERT(pdb)
     ASSERT(module_index)
     ASSERT(method_index)
@@ -226,11 +228,10 @@ int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDPlacehol
         }
         
         if (NCDStatement_RegObjName(s)) {
-            if (!(e->objnames = b_strdup(NCDStatement_RegObjName(s)))) {
-                BLog(BLOG_ERROR, "b_strdup failed");
+            if (!ncd_make_name_indices(string_index, NCDStatement_RegObjName(s), &e->objnames, &e->num_objnames)) {
+                BLog(BLOG_ERROR, "ncd_make_name_indices failed");
                 goto loop_fail2;
             }
-            e->num_objnames = split_string_inplace2(e->objnames, '.') + 1;
             
             e->binding.method_name_id = NCDMethodIndex_GetMethodNameId(method_index, NCDStatement_RegCmdName(s));
             if (e->binding.method_name_id == -1) {
@@ -258,7 +259,7 @@ int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDPlacehol
         continue;
         
     loop_fail3:
-        free(e->objnames);
+        BFree(e->objnames);
     loop_fail2:
         BFree(e->arg_data);
     loop_fail1:
@@ -277,7 +278,7 @@ int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDPlacehol
 fail3:
     while (o->num_stmts-- > 0) {
         struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts];
-        free(e->objnames);
+        BFree(e->objnames);
         BFree(e->arg_data);
         NCDValReplaceProg_Free(&e->arg_prog);
         free(e->cmdname);
@@ -298,7 +299,7 @@ void NCDInterpProcess_Free (NCDInterpProcess *o)
     
     while (o->num_stmts-- > 0) {
         struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts];
-        free(e->objnames);
+        BFree(e->objnames);
         BFree(e->arg_data);
         NCDValReplaceProg_Free(&e->arg_prog);
         free(e->cmdname);
@@ -347,7 +348,7 @@ const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i)
     return o->stmts[i].cmdname;
 }
 
-void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const char **out_objnames, size_t *out_num_objnames)
+void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames)
 {
     DebugObject_Access(&o->d_obj);
     ASSERT(i >= 0)

+ 4 - 3
ncd/NCDInterpProcess.h

@@ -40,6 +40,7 @@
 #include <ncd/NCDPlaceholderDb.h>
 #include <ncd/NCDModule.h>
 #include <ncd/NCDModuleIndex.h>
+#include <ncd/NCDStringIndex.h>
 #include <ncd/NCDMethodIndex.h>
 
 #include "NCDInterpProcess_trie.h"
@@ -48,7 +49,7 @@
 struct NCDInterpProcess__stmt {
     char *name;
     char *cmdname;
-    char *objnames;
+    NCD_string_id_t *objnames;
     size_t num_objnames;
     union {
         const struct NCDModule *simple_module;
@@ -82,11 +83,11 @@ typedef struct {
     DebugObject d_obj;
 } NCDInterpProcess;
 
-int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
+int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
 void NCDInterpProcess_Free (NCDInterpProcess *o);
 int NCDInterpProcess_FindStatement (NCDInterpProcess *o, int from_index, const char *name);
 const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i);
-void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const char **out_objnames, size_t *out_num_objnames);
+void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames);
 const struct NCDModule * NCDInterpProcess_StatementGetSimpleModule (NCDInterpProcess *o, int i);
 const struct NCDModule * NCDInterpProcess_StatementGetMethodModule (NCDInterpProcess *o, int i, const char *obj_type, NCDMethodIndex *method_index);
 int NCDInterpProcess_CopyStatementArgs (NCDInterpProcess *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) WARN_UNUSED;

+ 3 - 2
ncd/NCDInterpProg.c

@@ -42,9 +42,10 @@
 #include "NCDInterpProg_hash.h"
 #include <structure/CHash_impl.h>
 
-int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
+int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
 {
     ASSERT(prog)
+    ASSERT(string_index)
     ASSERT(pdb)
     ASSERT(module_index)
     ASSERT(method_index)
@@ -72,7 +73,7 @@ int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pd
         
         e->name = NCDProcess_Name(p);
         
-        if (!NCDInterpProcess_Init(&e->iprocess, p, pdb, module_index, method_index)) {
+        if (!NCDInterpProcess_Init(&e->iprocess, p, string_index, pdb, module_index, method_index)) {
             BLog(BLOG_ERROR, "NCDInterpProcess_Init failed");
             goto fail2;
         }

+ 1 - 1
ncd/NCDInterpProg.h

@@ -56,7 +56,7 @@ typedef struct {
     DebugObject d_obj;
 } NCDInterpProg;
 
-int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
+int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
 void NCDInterpProg_Free (NCDInterpProg *o);
 NCDInterpProcess * NCDInterpProg_FindProcess (NCDInterpProg *o, const char *name);
 

+ 28 - 20
ncd/NCDModule.c

@@ -51,10 +51,10 @@
 #define PROCESS_STATE_TERMINATING 8
 #define PROCESS_STATE_TERMINATED 10
 
-static int object_func_getvar (NCDModuleInst *n, const char *name, NCDValMem *mem, NCDValRef *out_value);
-static int object_func_getobj (NCDModuleInst *n, const char *name, NCDObject *out_object);
-static int process_args_object_func_getvar (NCDModuleProcess *o, const char *name, NCDValMem *mem, NCDValRef *out_value);
-static int process_arg_object_func_getvar2 (NCDModuleProcess *o, void *n_ptr, const char *name, NCDValMem *mem, NCDValRef *out_value);
+static int object_func_getvar (NCDModuleInst *n, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
+static int object_func_getobj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object);
+static int process_args_object_func_getvar (NCDModuleProcess *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
+static int process_arg_object_func_getvar2 (NCDModuleProcess *o, void *n_ptr, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
 
 static void frontend_event (NCDModuleInst *n, int event)
 {
@@ -166,22 +166,28 @@ static int can_resolve (NCDModuleInst *n)
     }
 }
 
-static int object_func_getvar (NCDModuleInst *n, const char *name, NCDValMem *mem, NCDValRef *out_value)
+static int object_func_getvar (NCDModuleInst *n, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value)
 {
     DebugObject_Access(&n->d_obj);
     
-    if (!n->m->func_getvar || !can_resolve(n)) {
+    if ((!n->m->func_getvar && !n->m->func_getvar2) || !can_resolve(n)) {
         return 0;
     }
     
-    int res = n->m->func_getvar(n->inst_user, name, mem, out_value);
+    int res;
+    if (n->m->func_getvar2) {
+        res = n->m->func_getvar2(n->inst_user, name, mem, out_value);
+    } else {
+        const char *name_str = NCDStringIndex_Value(n->params->iparams->string_index, name);
+        res = n->m->func_getvar(n->inst_user, name_str, mem, out_value);
+    }
     ASSERT(res == 0 || res == 1)
     ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1))
     
     return res;
 }
 
-static int object_func_getobj (NCDModuleInst *n, const char *name, NCDObject *out_object)
+static int object_func_getobj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object)
 {
     DebugObject_Access(&n->d_obj);
     
@@ -253,13 +259,12 @@ void NCDModuleInst_Backend_Dead (NCDModuleInst *n)
     return;
 }
 
-int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name, NCDObject *out_object)
+int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object)
 {
     DebugObject_Access(&n->d_obj);
     ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN ||
            n->state == STATE_UP ||
            n->state == STATE_DYING)
-    ASSERT(name)
     ASSERT(out_object)
     
     int res = n->params->func_getobj(n, name, out_object);
@@ -334,6 +339,9 @@ int NCDModuleProcess_Init (NCDModuleProcess *o, NCDModuleInst *n, const char *te
     o->user = user;
     o->handler_event = handler_event;
     
+    // remember iparams
+    o->iparams = n->params->iparams;
+    
     // set no special functions
     o->func_getspecialobj = NULL;
     
@@ -402,11 +410,10 @@ void NCDModuleProcess_Terminate (NCDModuleProcess *o)
     o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_TERMINATE);
 }
 
-int NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name, NCDObject *out_object)
+int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object)
 {
     DebugObject_Access(&o->d_obj);
     ASSERT(o->state != PROCESS_STATE_INIT)
-    ASSERT(name)
     ASSERT(out_object)
     
     // interpreter gone?
@@ -476,22 +483,23 @@ void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o)
     return;
 }
 
-int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name, NCDObject *out_object)
+int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object)
 {
     DebugObject_Access(&o->d_obj);
     process_assert_interp(o);
-    ASSERT(name)
     ASSERT(out_object)
     
+    const char *name_str = NCDStringIndex_Value(o->iparams->string_index, name);
+    
     if (!NCDVal_IsInvalid(o->args)) {
-        if (!strcmp(name, "_args")) {
+        if (!strcmp(name_str, "_args")) {
             *out_object = NCDObject_Build(NULL, o, (NCDObject_func_getvar)process_args_object_func_getvar, NULL);
             return 1;
         }
         
         size_t len;
         uintmax_t n;
-        if ((len = string_begins_with(name, "_arg")) && parse_unsigned_integer(name + len, &n) && n < NCDVal_ListCount(o->args) && n < UINTPTR_MAX) {
+        if ((len = string_begins_with(name_str, "_arg")) && parse_unsigned_integer(name_str + len, &n) && n < NCDVal_ListCount(o->args) && n < UINTPTR_MAX) {
             *out_object = NCDObject_Build2(NULL, o, (void *)((uintptr_t)(n + 1)), (NCDObject_func_getvar2)process_arg_object_func_getvar2, NULL);
             return 1;
         }
@@ -507,13 +515,13 @@ int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name
     return res;
 }
 
-static int process_args_object_func_getvar (NCDModuleProcess *o, const char *name, NCDValMem *mem, NCDValRef *out_value)
+static int process_args_object_func_getvar (NCDModuleProcess *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value)
 {
     DebugObject_Access(&o->d_obj);
     process_assert_interp(o);
     ASSERT(!NCDVal_IsInvalid(o->args))
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     
@@ -524,13 +532,13 @@ static int process_args_object_func_getvar (NCDModuleProcess *o, const char *nam
     return 1;
 }
 
-static int process_arg_object_func_getvar2 (NCDModuleProcess *o, void *n_ptr, const char *name, NCDValMem *mem, NCDValRef *out_value)
+static int process_arg_object_func_getvar2 (NCDModuleProcess *o, void *n_ptr, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value)
 {
     DebugObject_Access(&o->d_obj);
     process_assert_interp(o);
     ASSERT(!NCDVal_IsInvalid(o->args))
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     

+ 51 - 14
ncd/NCDModule.h

@@ -37,6 +37,7 @@
 #include <udevmonitor/NCDUdevManager.h>
 #include <random/BRandom2.h>
 #include <ncd/NCDObject.h>
+#include <ncd/NCDStringIndex.h>
 
 #define NCDMODULE_EVENT_UP 1
 #define NCDMODULE_EVENT_DOWN 2
@@ -82,11 +83,11 @@ typedef void (*NCDModuleInst_func_event) (struct NCDModuleInst_s *inst, int even
  * This function must not have any side effects.
  * 
  * @param inst the module instance
- * @param name name of the object
+ * @param name name of the object as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-typedef int (*NCDModuleInst_func_getobj) (struct NCDModuleInst_s *inst, const char *name, NCDObject *out_object);
+typedef int (*NCDModuleInst_func_getobj) (struct NCDModuleInst_s *inst, NCD_string_id_t name, NCDObject *out_object);
 
 /**
  * Function called when the module instance wants the interpreter to
@@ -170,11 +171,11 @@ typedef void (*NCDModuleProcess_handler_event) (void *user, int event);
  * This function must have no side effects.
  * 
  * @param user as in {@link NCDModuleProcess_Init}
- * @param name name of the object
+ * @param name name of the object as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-typedef int (*NCDModuleProcess_func_getspecialobj) (void *user, const char *name, NCDObject *out_object);
+typedef int (*NCDModuleProcess_func_getspecialobj) (void *user, NCD_string_id_t name, NCDObject *out_object);
 
 #define NCDMODULEPROCESS_INTERP_EVENT_CONTINUE 1
 #define NCDMODULEPROCESS_INTERP_EVENT_TERMINATE 2
@@ -209,11 +210,11 @@ typedef void (*NCDModuleProcess_interp_func_event) (void *user, int event);
  * This function must not have any side effects.
  * 
  * @param user as in {@link NCDModuleProcess_Interp_SetHandlers}
- * @param name name of the object
+ * @param name name of the object as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 in failure
  */
-typedef int (*NCDModuleProcess_interp_func_getobj) (void *user, const char *name, NCDObject *out_object);
+typedef int (*NCDModuleProcess_interp_func_getobj) (void *user, NCD_string_id_t name, NCDObject *out_object);
 
 struct NCDModule;
 
@@ -296,6 +297,10 @@ struct NCDModuleInst_iparams {
      * Random number generator.
      */
     BRandom2 *random2;
+    /**
+     * String index which keeps a mapping between strings and string identifiers.
+     */
+    NCDStringIndex *string_index;
     /**
      * Callback to create a new template process.
      */
@@ -337,6 +342,7 @@ typedef struct NCDModuleProcess_s {
     NCDValRef args;
     void *user;
     NCDModuleProcess_handler_event handler_event;
+    const struct NCDModuleInst_iparams *iparams; // TODO remove
     NCDModuleProcess_func_getspecialobj func_getspecialobj;
     int state;
     void *interp_user;
@@ -467,11 +473,11 @@ void NCDModuleInst_Backend_Dead (NCDModuleInst *n);
  * statement in the containing process.
  * 
  * @param n backend instance handle
- * @param name name of the object to resolve
+ * @param name name of the object to resolve as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, const char *name, NCDObject *out_object) WARN_UNUSED;
+int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Logs a backend instance message.
@@ -586,11 +592,11 @@ void NCDModuleProcess_Terminate (NCDModuleProcess *o);
  * This function has no side effects.
  * 
  * @param o the process
- * @param name name of the object to resolve
+ * @param name name of the object to resolve as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-int NCDModuleProcess_GetObj (NCDModuleProcess *o, const char *name, NCDObject *out_object) WARN_UNUSED;
+int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Sets callback functions for the interpreter to implement the
@@ -647,11 +653,11 @@ void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o);
  * Resolves a special process object for the process backend.
  * 
  * @param o process backend handle
- * @param name name of the object
+ * @param name name of the object as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, const char *name, NCDObject *out_object) WARN_UNUSED;
+int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED;
 
 /**
  * Function called before any instance of any backend in a module
@@ -709,17 +715,31 @@ typedef void (*NCDModule_func_die) (void *o);
  */
 typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValMem *mem, NCDValRef *out);
 
+/**
+ * Function called to resolve a variable within a backend instance.
+ * The backend instance is in up state, or in up or down state if can_resolve_when_down=1.
+ * This function must not have any side effects.
+ * 
+ * @param o see {@link NCDModuleInst_Backend_SetUser}
+ * @param name name of the variable to resolve as an {@link NCDStringIndex} identifier
+ * @param mem value memory to use
+ * @param out on success, the backend should initialize the value here
+ * @return 1 if exists, 0 if not exists. If exists, but out of memory, return 1
+ *         and an invalid value.
+ */
+typedef int (*NCDModule_func_getvar2) (void *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
+
 /**
  * Function called to resolve an object within a backend instance.
  * The backend instance is in up state, or in up or down state if can_resolve_when_down=1.
  * This function must not have any side effects.
  * 
  * @param o see {@link NCDModuleInst_Backend_SetUser}
- * @param name name of the object to resolve
+ * @param name name of the object to resolve as an {@link NCDStringIndex} identifier
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
  */
-typedef int (*NCDModule_func_getobj) (void *o, const char *name, NCDObject *out_object);
+typedef int (*NCDModule_func_getobj) (void *o, NCD_string_id_t name, NCDObject *out_object);
 
 /**
  * Handler called when the module instance is in a clean state.
@@ -771,6 +791,12 @@ struct NCDModule {
      */
     NCDModule_func_getvar func_getvar;
     
+    /**
+     * Function called to resolve a variable within the backend instance.
+     * May be NULL.
+     */
+    NCDModule_func_getvar2 func_getvar2;
+    
     /**
      * Function called to resolve an object within the backend instance.
      * May be NULL.
@@ -826,6 +852,17 @@ struct NCDModuleGroup {
      * structure that has a NULL type member.
      */
     const struct NCDModule *modules;
+    
+    /**
+     * A pointer to an array of requests for string identifiers. The 'str'
+     * member of each element of this array should be set to a string which
+     * is to be mapped to an identifier using {@link NCDStringIndex}. The
+     * array must be terminated with an element with a NULL 'str' member.
+     * The interpreter will use {@link NCDStringIndex_GetRequests} to set
+     * the corresponds 'id' members.
+     * This can be NULL if the module does not require mapping any strings.
+     */
+    struct NCD_string_request *strings;
 };
 
 #endif

+ 11 - 13
ncd/NCDObject.c

@@ -62,9 +62,8 @@ const char * NCDObject_Type (NCDObject *o)
     return o->type;
 }
 
-int NCDObject_GetObj (NCDObject *o, const char *name, NCDObject *out_object)
+int NCDObject_GetObj (NCDObject *o, NCD_string_id_t name, NCDObject *out_object)
 {
-    ASSERT(name)
     ASSERT(out_object)
     
     int res;
@@ -79,9 +78,8 @@ int NCDObject_GetObj (NCDObject *o, const char *name, NCDObject *out_object)
     return res;
 }
 
-int NCDObject_GetVar (NCDObject *o, const char *name, NCDValMem *mem, NCDValRef *out_value)
+int NCDObject_GetVar (NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value)
 {
-    ASSERT(name)
     ASSERT(mem)
     ASSERT(out_value)
     
@@ -101,14 +99,14 @@ int NCDObject_GetVar (NCDObject *o, const char *name, NCDValMem *mem, NCDValRef
 static NCDObject dig_into_object (NCDObject object)
 {
     NCDObject obj2;
-    while (NCDObject_GetObj(&object, "", &obj2)) {
+    while (NCDObject_GetObj(&object, NCD_EMPTY_STRING_ID, &obj2)) {
         object = obj2;
     }
     
     return object;
 }
 
-int NCDObject_ResolveObjExprCompact (NCDObject *o, const char *names, size_t num_names, NCDObject *out_object)
+int NCDObject_ResolveObjExprCompact (NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object)
 {
     ASSERT(num_names == 0 || names)
     ASSERT(num_names >= 0)
@@ -118,13 +116,13 @@ int NCDObject_ResolveObjExprCompact (NCDObject *o, const char *names, size_t num
     
     while (num_names > 0) {
         NCDObject obj2;
-        if (!NCDObject_GetObj(&object, names, &obj2)) {
+        if (!NCDObject_GetObj(&object, *names, &obj2)) {
             return 0;
         }
         
         object = dig_into_object(obj2);
         
-        names += strlen(names) + 1;
+        names++;
         num_names--;
     }
     
@@ -132,7 +130,7 @@ int NCDObject_ResolveObjExprCompact (NCDObject *o, const char *names, size_t num
     return 1;
 }
 
-int NCDObject_ResolveVarExprCompact (NCDObject *o, const char *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value)
+int NCDObject_ResolveVarExprCompact (NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value)
 {
     ASSERT(num_names == 0 || names)
     ASSERT(num_names >= 0)
@@ -143,8 +141,8 @@ int NCDObject_ResolveVarExprCompact (NCDObject *o, const char *names, size_t num
     
     while (num_names > 0) {
         NCDObject obj2;
-        if (!NCDObject_GetObj(&object, names, &obj2)) {
-            if (num_names == 1 && NCDObject_GetVar(&object, names, mem, out_value)) {
+        if (!NCDObject_GetObj(&object, *names, &obj2)) {
+            if (num_names == 1 && NCDObject_GetVar(&object, *names, mem, out_value)) {
                 return 1;
             }
             
@@ -153,9 +151,9 @@ int NCDObject_ResolveVarExprCompact (NCDObject *o, const char *names, size_t num
         
         object = dig_into_object(obj2);
         
-        names += strlen(names) + 1;
+        names++;
         num_names--;
     }
     
-    return NCDObject_GetVar(&object, "", mem, out_value);
+    return NCDObject_GetVar(&object, NCD_EMPTY_STRING_ID, mem, out_value);
 }

+ 9 - 8
ncd/NCDObject.h

@@ -32,13 +32,14 @@
 
 #include <misc/debug.h>
 #include <ncd/NCDVal.h>
+#include <ncd/NCDStringIndex.h>
 
 typedef struct NCDObject_s NCDObject;
 
-typedef int (*NCDObject_func_getvar) (void *user, const char *name, NCDValMem *mem, NCDValRef *out_value);
-typedef int (*NCDObject_func_getobj) (void *user, const char *name, NCDObject *out_object);
-typedef int (*NCDObject_func_getvar2) (void *user, void *user2, const char *name, NCDValMem *mem, NCDValRef *out_value);
-typedef int (*NCDObject_func_getobj2) (void *user, void *user2, const char *name, NCDObject *out_object);
+typedef int (*NCDObject_func_getvar) (void *user, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
+typedef int (*NCDObject_func_getobj) (void *user, NCD_string_id_t name, NCDObject *out_object);
+typedef int (*NCDObject_func_getvar2) (void *user, void *user2, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
+typedef int (*NCDObject_func_getobj2) (void *user, void *user2, NCD_string_id_t name, NCDObject *out_object);
 
 struct NCDObject_s {
     const char *type;
@@ -57,9 +58,9 @@ struct NCDObject_s {
 NCDObject NCDObject_Build (const char *type, void *user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj);
 NCDObject NCDObject_Build2 (const char *type, void *user, void *user2, NCDObject_func_getvar2 func_getvar2, NCDObject_func_getobj2 func_getobj2);
 const char * NCDObject_Type (NCDObject *o);
-int NCDObject_GetObj (NCDObject *o, const char *name, NCDObject *out_object) WARN_UNUSED;
-int NCDObject_GetVar (NCDObject *o, const char *name, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED;
-int NCDObject_ResolveObjExprCompact (NCDObject *o, const char *names, size_t num_names, NCDObject *out_object) WARN_UNUSED;
-int NCDObject_ResolveVarExprCompact (NCDObject *o, const char *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED;
+int NCDObject_GetObj (NCDObject *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED;
+int NCDObject_GetVar (NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED;
+int NCDObject_ResolveObjExprCompact (NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) WARN_UNUSED;
+int NCDObject_ResolveVarExprCompact (NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED;
 
 #endif

+ 12 - 8
ncd/NCDPlaceholderDb.c

@@ -29,20 +29,25 @@
 
 #include <string.h>
 #include <limits.h>
+#include <stdlib.h>
 
 #include <misc/balloc.h>
 #include <misc/split_string.h>
 #include <misc/strdup.h>
 #include <base/BLog.h>
+#include <ncd/make_name_indices.h>
 
 #include "NCDPlaceholderDb.h"
 
 #include <generated/blog_channel_NCDPlaceholderDb.h>
 
-int NCDPlaceholderDb_Init (NCDPlaceholderDb *o)
+int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index)
 {
+    ASSERT(string_index)
+    
     o->count = 0;
     o->capacity = 1;
+    o->string_index = string_index;
     
     if (!(o->arr = BAllocArray(o->capacity, sizeof(o->arr[0])))) {
         BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed");
@@ -55,7 +60,7 @@ int NCDPlaceholderDb_Init (NCDPlaceholderDb *o)
 void NCDPlaceholderDb_Free (NCDPlaceholderDb *o)
 {
     for (size_t i = 0; i < o->count; i++) {
-        free(o->arr[i].varnames);
+        BFree(o->arr[i].varnames);
     }
     
     BFree(o->arr);
@@ -95,14 +100,13 @@ int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int
         return 0;
     }
     
-    char *varnames = b_strdup(varname);
-    if (!varnames) {
-        BLog(BLOG_ERROR, "b_strdup failed");
+    NCD_string_id_t *varnames;
+    size_t num_names;
+    if (!ncd_make_name_indices(o->string_index, varname, &varnames, &num_names)) {
+        BLog(BLOG_ERROR, "ncd_make_name_indices failed");
         return 0;
     }
     
-    size_t num_names = split_string_inplace2(varnames, '.') + 1;
-    
     *out_plid = o->count;
     
     o->arr[o->count].varnames = varnames;
@@ -112,7 +116,7 @@ int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int
     return 1;
 }
 
-void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const char **out_varnames, size_t *out_num_names)
+void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names)
 {
     ASSERT(plid >= 0)
     ASSERT(plid < o->count)

+ 5 - 3
ncd/NCDPlaceholderDb.h

@@ -33,9 +33,10 @@
 #include <stddef.h>
 
 #include <misc/debug.h>
+#include <ncd/NCDStringIndex.h>
 
 struct NCDPlaceholderDb__entry {
-    char *varnames;
+    NCD_string_id_t *varnames;
     size_t num_names;
 };
 
@@ -53,13 +54,14 @@ typedef struct {
     struct NCDPlaceholderDb__entry *arr;
     size_t count;
     size_t capacity;
+    NCDStringIndex *string_index;
 } NCDPlaceholderDb;
 
 /**
  * Initializes the placeholder database.
  * Returns 1 on success, and 0 on failure.
  */
-int NCDPlaceholderDb_Init (NCDPlaceholderDb *o) WARN_UNUSED;
+int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index) WARN_UNUSED;
 
 /**
  * Frees the placeholder database.
@@ -88,6 +90,6 @@ int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int
  *         database is freed.
  *         Note that there will always be at least one string in the result.
  */
-void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const char **out_varnames, size_t *out_num_names);
+void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names);
 
 #endif

+ 172 - 0
ncd/NCDStringIndex.c

@@ -0,0 +1,172 @@
+/**
+ * @file NCDStringIndex.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * 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.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 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 <string.h>
+#include <stdlib.h>
+
+#include <misc/hashfun.h>
+#include <misc/strdup.h>
+#include <base/BLog.h>
+
+#include "NCDStringIndex.h"
+
+#include "NCDStringIndex_hash.h"
+#include <structure/CHash_impl.h>
+
+#define GROWARRAY_NAME Array
+#define GROWARRAY_OBJECT_TYPE NCDStringIndex
+#define GROWARRAY_ARRAY_MEMBER entries
+#define GROWARRAY_CAPACITY_MEMBER entries_capacity
+#define GROWARRAY_MAX_CAPACITY NCD_STRING_ID_MAX
+#include <misc/grow_array.h>
+
+#include <generated/blog_channel_ncd.h>
+
+static NCD_string_id_t do_get (NCDStringIndex *o, const char *str)
+{
+    ASSERT(str)
+    
+    NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, str);
+    ASSERT(ref.link == -1 || ref.link >= 0)
+    ASSERT(ref.link == -1 || ref.link < o->entries_size)
+    ASSERT(ref.link == -1 || !strcmp(ref.ptr->str, str))
+    
+    if (ref.link != -1) {
+        return ref.link;
+    }
+    
+    if (o->entries_size == o->entries_capacity && !Array_DoubleUp(o)) {
+        BLog(BLOG_ERROR, "Array_DoubleUp failed");
+        return -1;
+    }
+    
+    ASSERT(o->entries_size < o->entries_capacity)
+    
+    struct NCDStringIndex__entry *entry = &o->entries[o->entries_size];
+    
+    if (!(entry->str = b_strdup(str))) {
+        BLog(BLOG_ERROR, "b_strdup failed");
+        return -1;
+    }
+    
+    NCDStringIndex__HashRef newref = {entry, o->entries_size};
+    int res = NCDStringIndex__Hash_Insert(&o->hash, o->entries, newref, NULL);
+    ASSERT_EXECUTE(res)
+    
+    return o->entries_size++;
+}
+
+int NCDStringIndex_Init (NCDStringIndex *o)
+{
+    o->entries_size = 0;
+    
+    if (!Array_Init(o, NCDSTRINGINDEX_INITIAL_CAPACITY)) {
+        BLog(BLOG_ERROR, "Array_Init failed");
+        goto fail0;
+    }
+    
+    if (!NCDStringIndex__Hash_Init(&o->hash, NCDSTRINGINDEX_HASH_BUCKETS)) {
+        BLog(BLOG_ERROR, "NCDStringIndex__Hash_Init failed");
+        goto fail1;
+    }
+    
+    if (do_get(o, "") < 0) {
+        goto fail2;
+    }
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail2:
+    NCDStringIndex__Hash_Free(&o->hash);
+fail1:
+    Array_Free(o);
+fail0:
+    return 0;
+}
+
+void NCDStringIndex_Free (NCDStringIndex *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    for (NCD_string_id_t i = 0; i < o->entries_size; i++) {
+        free(o->entries[i].str);
+    }
+    
+    NCDStringIndex__Hash_Free(&o->hash);
+    Array_Free(o);
+}
+
+NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(str)
+    
+    NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, str);
+    ASSERT(ref.link == -1 || ref.link >= 0)
+    ASSERT(ref.link == -1 || ref.link < o->entries_size)
+    ASSERT(ref.link == -1 || !strcmp(ref.ptr->str, str))
+    
+    return ref.link;
+}
+
+NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(str)
+    
+    return do_get(o, str);
+}
+
+const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(id >= 0)
+    ASSERT(id < o->entries_size)
+    ASSERT(o->entries[id].str)
+    
+    return o->entries[id].str;
+}
+
+int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(requests)
+    
+    while (requests->str) {
+        NCD_string_id_t id = NCDStringIndex_Get(o, requests->str);
+        if (id < 0) {
+            return 0;
+        }
+        requests->id = id;
+        requests++;
+    }
+    
+    return 1;
+}

+ 79 - 0
ncd/NCDStringIndex.h

@@ -0,0 +1,79 @@
+/**
+ * @file NCDStringIndex.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * 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.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 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.
+ */
+
+#ifndef BADVPN_NCD_STRING_INDEX_H
+#define BADVPN_NCD_STRING_INDEX_H
+
+#include <limits.h>
+
+#include <misc/debug.h>
+#include <structure/CHash.h>
+#include <base/DebugObject.h>
+
+#define NCDSTRINGINDEX_INITIAL_CAPACITY 1
+#define NCDSTRINGINDEX_HASH_BUCKETS 100
+
+typedef int NCD_string_id_t;
+
+#define NCD_STRING_ID_MAX INT_MAX
+
+struct NCDStringIndex__entry {
+    char *str;
+    NCD_string_id_t hash_next;
+};
+
+typedef const char *NCDStringIndex_hash_key;
+typedef struct NCDStringIndex__entry *NCDStringIndex_hash_arg;
+
+#include "NCDStringIndex_hash.h"
+#include <structure/CHash_decl.h>
+
+#define NCD_EMPTY_STRING_ID ((NCD_string_id_t)0)
+
+typedef struct {
+    struct NCDStringIndex__entry *entries;
+    NCD_string_id_t entries_capacity;
+    NCD_string_id_t entries_size;
+    NCDStringIndex__Hash hash;
+    DebugObject d_obj;
+} NCDStringIndex;
+
+struct NCD_string_request {
+    const char *str;
+    NCD_string_id_t id;
+};
+
+int NCDStringIndex_Init (NCDStringIndex *o) WARN_UNUSED;
+void NCDStringIndex_Free (NCDStringIndex *o);
+NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str);
+NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str);
+const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id);
+int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests) WARN_UNUSED;
+
+#endif

+ 13 - 0
ncd/NCDStringIndex_hash.h

@@ -0,0 +1,13 @@
+#define CHASH_PARAM_NAME NCDStringIndex__Hash
+#define CHASH_PARAM_ENTRY struct NCDStringIndex__entry
+#define CHASH_PARAM_LINK NCD_string_id_t
+#define CHASH_PARAM_KEY NCDStringIndex_hash_key
+#define CHASH_PARAM_ARG NCDStringIndex_hash_arg
+#define CHASH_PARAM_NULL ((NCD_string_id_t)-1)
+#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)])
+#define CHASH_PARAM_ENTRYHASH(arg, entry) badvpn_djb2_hash((const uint8_t *)(entry).ptr->str)
+#define CHASH_PARAM_KEYHASH(arg, key) badvpn_djb2_hash((const uint8_t *)(key))
+#define CHASH_PARAM_ENTRYHASH_IS_CHEAP 0
+#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->str, (entry2).ptr->str))
+#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->str))
+#define CHASH_PARAM_ENTRY_NEXT hash_next

+ 88 - 0
ncd/make_name_indices.h

@@ -0,0 +1,88 @@
+/**
+ * @file make_name_indices.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * 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.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 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.
+ */
+
+#ifndef BADVPN_MAKE_NAME_INDICES_H
+#define BADVPN_MAKE_NAME_INDICES_H
+
+#include <stdlib.h>
+#include <stddef.h>
+
+#include <misc/strdup.h>
+#include <misc/balloc.h>
+#include <misc/debug.h>
+#include <misc/split_string.h>
+#include <ncd/NCDStringIndex.h>
+
+static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names) WARN_UNUSED;
+
+static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names)
+{
+    ASSERT(string_index)
+    ASSERT(name)
+    ASSERT(out_varnames)
+    ASSERT(out_num_names)
+    
+    char *data = b_strdup(name);
+    if (!data) {
+        goto fail0;
+    }
+    
+    size_t num_names = split_string_inplace2(data, '.') + 1;
+    
+    NCD_string_id_t *varnames = BAllocArray(num_names, sizeof(varnames[0]));
+    if (!varnames) {
+        goto fail1;
+    }
+    
+    char *cur = data;
+    for (size_t i = 0; i < num_names; i++) {
+        NCD_string_id_t id = NCDStringIndex_Get(string_index, cur);
+        if (id < 0) {
+            goto fail2;
+        }
+        
+        varnames[i] = id;
+        cur += strlen(cur) + 1;
+    }
+    
+    free(data);
+    
+    *out_varnames = varnames;
+    *out_num_names = num_names;
+    return 1;
+    
+fail2:
+    BFree(varnames);
+fail1:
+    free(data);
+fail0:
+    return 0;
+}
+
+#endif

+ 18 - 52
ncd/modules/alias.c

@@ -44,6 +44,7 @@
 #include <misc/debug.h>
 #include <misc/balloc.h>
 #include <ncd/NCDModule.h>
+#include <ncd/make_name_indices.h>
 
 #include <generated/blog_channel_ncd_alias.h>
 
@@ -51,32 +52,15 @@
 
 struct instance {
     NCDModuleInst *i;
-    int num_extra_parts;
-    char strings[];
+    NCD_string_id_t *names;
+    size_t num_names;
 };
 
-static int split_string_inplace (char *str, char del)
+static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
 {
-    ASSERT(str)
-    
-    int num_extra_parts = 0;
-    
-    while (*str) {
-        if (*str == del) {
-            if (num_extra_parts == INT_MAX) {
-                return -1;
-            }
-            *str = '\0';
-            num_extra_parts++;
-        }
-        str++;
-    }
+    struct instance *o = vo;
+    o->i = i;
     
-    return num_extra_parts;
-}
-
-static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
-{
     // read arguments
     NCDValRef target_arg;
     if (!NCDVal_ListRead(params->args, 1, &target_arg)) {
@@ -88,35 +72,17 @@ static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst
         goto fail0;
     }
     const char *target = NCDVal_StringValue(target_arg);
-    size_t target_len = strlen(target);
-    
-    // calculate size
-    bsize_t size = bsize_add(bsize_fromsize(sizeof(struct instance)), bsize_fromsize(target_len + 1));
     
-    // allocate instance
-    struct instance *o = BAllocSize(size);
-    if (!o) {
-        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+    // make indices array
+    if (!ncd_make_name_indices(i->params->iparams->string_index, target, &o->names, &o->num_names)) {
+        ModuleLog(i, BLOG_ERROR, "ncd_make_name_indices failed");
         goto fail0;
     }
-    o->i = i;
-    NCDModuleInst_Backend_SetUser(i, o);
-    
-    // copy target
-    memcpy(o->strings, target, target_len + 1);
-    
-    // split target into components
-    if ((o->num_extra_parts = split_string_inplace(o->strings, '.')) < 0) {
-        ModuleLog(o->i, BLOG_ERROR, "split_string_inplace failed");
-        goto fail1;
-    }
     
     // signal up
     NCDModuleInst_Backend_Up(o->i);
     return;
     
-fail1:
-    BFree(o);
 fail0:
     NCDModuleInst_Backend_SetError(i);
     NCDModuleInst_Backend_Dead(i);
@@ -125,29 +91,28 @@ fail0:
 static void func_die (void *vo)
 {
     struct instance *o = vo;
-    NCDModuleInst *i = o->i;
     
-    // free instance
-    BFree(o);
+    // free indices array
+    BFree(o->names);
     
-    NCDModuleInst_Backend_Dead(i);
+    NCDModuleInst_Backend_Dead(o->i);
 }
 
-static int func_getobj (void *vo, const char *name, NCDObject *out_object)
+static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = vo;
     
     NCDObject object;
-    if (!NCDModuleInst_Backend_GetObj(o->i, o->strings, &object)) {
+    if (!NCDModuleInst_Backend_GetObj(o->i, o->names[0], &object)) {
         return 0;
     }
     
     NCDObject obj2;
-    if (!NCDObject_ResolveObjExprCompact(&object, o->strings + strlen(o->strings) + 1, o->num_extra_parts, &obj2)) {
+    if (!NCDObject_ResolveObjExprCompact(&object, o->names + 1, o->num_names - 1, &obj2)) {
         return 0;
     }
     
-    if (!strcmp(name, "")) {
+    if (name == NCD_EMPTY_STRING_ID) {
         *out_object = obj2;
         return 1;
     }
@@ -160,7 +125,8 @@ static const struct NCDModule modules[] = {
         .type = "alias",
         .func_new2 = func_new,
         .func_die = func_die,
-        .func_getobj = func_getobj
+        .func_getobj = func_getobj,
+        .alloc_size = sizeof(struct instance)
     }, {
         .type = NULL
     }

+ 10 - 8
ncd/modules/call.c

@@ -96,8 +96,8 @@ struct instance {
 };
 
 static void instance_free (struct instance *o);
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
-static int ref_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int ref_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
 
 static void process_handler_event (struct instance *o, int event)
 {
@@ -134,14 +134,16 @@ static void process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)caller_obj_func_getobj);
         return 1;
     }
     
-    if (!strcmp(name, "_ref")) {
+    if (!strcmp(name_str, "_ref")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)ref_obj_func_getobj);
         return 1;
     }
@@ -292,7 +294,7 @@ static void func_clean (void *vo)
     o->state = STATE_WORKING;
 }
 
-static int func_getobj (void *vo, const char *name, NCDObject *out_object)
+static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = vo;
     
@@ -303,12 +305,12 @@ static int func_getobj (void *vo, const char *name, NCDObject *out_object)
     return NCDModuleProcess_GetObj(&o->process, name, out_object);
 }
 
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
 
-static int ref_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int ref_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     if (!o->crh) {
         return 0;

+ 8 - 6
ncd/modules/call2.c

@@ -62,8 +62,8 @@ struct instance {
 };
 
 static void process_handler_event (struct instance *o, int event);
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object);
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
 static void func_new_templ (void *vo, NCDModuleInst *i, const char *template_name, NCDValRef args, int embed);
 static void instance_free (struct instance *o);
 
@@ -102,13 +102,15 @@ static void process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     if (o->embed) {
         return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
     }
     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)caller_obj_func_getobj);
         return 1;
     }
@@ -116,7 +118,7 @@ static int process_func_getspecialobj (struct instance *o, const char *name, NCD
     return 0;
 }
 
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
@@ -401,7 +403,7 @@ static void func_clean (void *vo)
     o->state = STATE_WORKING;
 }
 
-static int func_getobj (void *vo, const char *name, NCDObject *out_object)
+static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = vo;
     

+ 1 - 1
ncd/modules/depend.c

@@ -377,7 +377,7 @@ static void depend_func_clean (void *vo)
     }
 }
 
-static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     ASSERT(!o->p || !o->p->is_queued)

+ 1 - 1
ncd/modules/dynamic_depend.c

@@ -462,7 +462,7 @@ static void depend_func_clean (void *vo)
     name_continue_resetting(n);
 }
 
-static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     struct name *n = o->n;

+ 23 - 21
ncd/modules/foreach.c

@@ -127,12 +127,12 @@ static void work (struct instance *o);
 static void advance (struct instance *o);
 static void timer_handler (struct instance *o);
 static void element_process_handler_event (struct element *e, int event);
-static int element_process_func_getspecialobj (struct element *e, const char *name, NCDObject *out_object);
-static int element_caller_object_func_getobj (struct element *e, const char *name, NCDObject *out_object);
-static int element_list_index_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out);
-static int element_list_elem_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out);
-static int element_map_key_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out);
-static int element_map_val_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out);
+static int element_process_func_getspecialobj (struct element *e, NCD_string_id_t name, NCDObject *out_object);
+static int element_caller_object_func_getobj (struct element *e, NCD_string_id_t name, NCDObject *out_object);
+static int element_list_index_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
+static int element_list_elem_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
+static int element_map_key_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
+static int element_map_val_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
 static void instance_free (struct instance *o);
 
 static void assert_state (struct instance *o)
@@ -348,22 +348,24 @@ static void element_process_handler_event (struct element *e, int event)
     return;
 }
 
-static int element_process_func_getspecialobj (struct element *e, const char *name, NCDObject *out_object)
+static int element_process_func_getspecialobj (struct element *e, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
     switch (o->type) {
         case NCDVAL_LIST: {
             const char *index_name = (o->name2 ? o->name1 : NULL);
             const char *elem_name = (o->name2 ? o->name2 : o->name1);
             
-            if (index_name && !strcmp(name, index_name)) {
+            if (index_name && !strcmp(name_str, index_name)) {
                 *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_list_index_object_func_getvar, NULL);
                 return 1;
             }
             
-            if (!strcmp(name, elem_name)) {
+            if (!strcmp(name_str, elem_name)) {
                 *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_list_elem_object_func_getvar, NULL);
                 return 1;
             }
@@ -372,12 +374,12 @@ static int element_process_func_getspecialobj (struct element *e, const char *na
             const char *key_name = o->name1;
             const char *val_name = o->name2;
             
-            if (!strcmp(name, key_name)) {
+            if (!strcmp(name_str, key_name)) {
                 *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_map_key_object_func_getvar, NULL);
                 return 1;
             }
             
-            if (val_name && !strcmp(name, val_name)) {
+            if (val_name && !strcmp(name_str, val_name)) {
                 *out_object = NCDObject_Build(NULL, e, (NCDObject_func_getvar)element_map_val_object_func_getvar, NULL);
                 return 1;
             }
@@ -388,7 +390,7 @@ static int element_process_func_getspecialobj (struct element *e, const char *na
         return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
     }
     
-    if (!strcmp(name, "_caller")) {
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, e, NULL, (NCDObject_func_getobj)element_caller_object_func_getobj);
         return 1;
     }
@@ -396,7 +398,7 @@ static int element_process_func_getspecialobj (struct element *e, const char *na
     return 0;
 }
 
-static int element_caller_object_func_getobj (struct element *e, const char *name, NCDObject *out_object)
+static int element_caller_object_func_getobj (struct element *e, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
@@ -405,13 +407,13 @@ static int element_caller_object_func_getobj (struct element *e, const char *nam
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
 
-static int element_list_index_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out)
+static int element_list_index_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     ASSERT(o->type == NCDVAL_LIST)
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     
@@ -425,13 +427,13 @@ static int element_list_index_object_func_getvar (struct element *e, const char
     return 1;
 }
 
-static int element_list_elem_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out)
+static int element_list_elem_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     ASSERT(o->type == NCDVAL_LIST)
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     
@@ -442,13 +444,13 @@ static int element_list_elem_object_func_getvar (struct element *e, const char *
     return 1;
 }
 
-static int element_map_key_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out)
+static int element_map_key_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     ASSERT(o->type == NCDVAL_MAP)
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     
@@ -459,13 +461,13 @@ static int element_map_key_object_func_getvar (struct element *e, const char *na
     return 1;
 }
 
-static int element_map_val_object_func_getvar (struct element *e, const char *name, NCDValMem *mem, NCDValRef *out)
+static int element_map_val_object_func_getvar (struct element *e, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     struct instance *o = e->inst;
     ASSERT(e->state != ESTATE_FORGOTTEN)
     ASSERT(o->type == NCDVAL_MAP)
     
-    if (strcmp(name, "")) {
+    if (name != NCD_EMPTY_STRING_ID) {
         return 0;
     }
     

+ 7 - 5
ncd/modules/imperative.c

@@ -80,8 +80,8 @@ static int start_process (struct instance *o, const char *templ, NCDValRef args,
 static void go_deinit (struct instance *o);
 static void init_process_handler_event (struct instance *o, int event);
 static void deinit_process_handler_event (struct instance *o, int event);
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object);
-static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int process_caller_object_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
 static void deinit_timer_handler (struct instance *o);
 static void instance_free (struct instance *o);
 
@@ -192,11 +192,13 @@ static void deinit_process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->state != STATE_UP)
     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)process_caller_object_func_getobj);
         return 1;
     }
@@ -204,7 +206,7 @@ static int process_func_getspecialobj (struct instance *o, const char *name, NCD
     return 0;
 }
 
-static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_caller_object_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->state != STATE_UP)
     

+ 1 - 1
ncd/modules/multidepend.c

@@ -326,7 +326,7 @@ static void depend_func_clean (void *vo)
     depend_update(o);
 }
 
-static int depend_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct depend *o = vo;
     

+ 1 - 1
ncd/modules/ondemand.c

@@ -339,7 +339,7 @@ static void demand_func_die (void *vo)
     demand_free(o);
 }
 
-static int demand_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int demand_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct demand *o = vo;
     ASSERT(o->od->have_process)

+ 7 - 5
ncd/modules/process_manager.c

@@ -92,8 +92,8 @@ static int process_new (struct instance *o, const char *name, const char *templa
 static void process_free (struct process *p);
 static void process_retry_timer_handler (struct process *p);
 static void process_module_process_handler_event (struct process *p, int event);
-static int process_module_process_func_getspecialobj (struct process *p, const char *name, NCDObject *out_object);
-static int process_module_process_caller_obj_func_getobj (struct process *p, const char *name, NCDObject *out_object);
+static int process_module_process_func_getspecialobj (struct process *p, NCD_string_id_t name, NCDObject *out_object);
+static int process_module_process_caller_obj_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object);
 static void process_stop (struct process *p);
 static int process_restart (struct process *p, const char *template_name, NCDValRef args);
 static void process_try (struct process *p);
@@ -262,11 +262,13 @@ void process_module_process_handler_event (struct process *p, int event)
     }
 }
 
-int process_module_process_func_getspecialobj (struct process *p, const char *name, NCDObject *out_object)
+int process_module_process_func_getspecialobj (struct process *p, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(p->have_module_process)
     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(p->manager->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, p, NULL, (NCDObject_func_getobj)process_module_process_caller_obj_func_getobj);
         return 1;
     }
@@ -274,7 +276,7 @@ int process_module_process_func_getspecialobj (struct process *p, const char *na
     return 0;
 }
 
-int process_module_process_caller_obj_func_getobj (struct process *p, const char *name, NCDObject *out_object)
+int process_module_process_caller_obj_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object)
 {
     struct instance *o = p->manager;
     ASSERT(p->have_module_process)

+ 4 - 4
ncd/modules/ref.c

@@ -104,13 +104,13 @@ static void refhere_func_die (void *vo)
     NCDModuleInst_Backend_Dead(o->i);
 }
 
-static int refhere_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int refhere_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct refhere_instance *o = vo;
     
     // We don't redirect methods, and there will never be an object
     // with empty name. Fail here so we don't report non-errors.
-    if (!strcmp(objname, "")) {
+    if (objname == NCD_EMPTY_STRING_ID) {
         return 0;
     }
     
@@ -172,13 +172,13 @@ static void ref_func_die (void *vo)
     ref_instance_free(o);
 }
 
-static int ref_func_getobj (void *vo, const char *objname, NCDObject *out_object)
+static int ref_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct ref_instance *o = vo;
     
     // We don't redirect methods, and there will never be an object
     // with empty name. Fail here so we don't report non-errors.
-    if (!strcmp(objname, "")) {
+    if (objname == NCD_EMPTY_STRING_ID) {
         return 0;
     }
     

+ 8 - 6
ncd/modules/spawn.c

@@ -86,8 +86,8 @@ struct join_instance {
 
 static void assert_dirty_state (struct instance *o);
 static void process_handler_event (struct instance *o, int event);
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object);
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
 static void bring_joins_down (struct instance *o);
 static void continue_working (struct instance *o);
 static void continue_terminating (struct instance *o);
@@ -149,9 +149,11 @@ static void process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)caller_obj_func_getobj);
         return 1;
     }
@@ -159,7 +161,7 @@ static int process_func_getspecialobj (struct instance *o, const char *name, NCD
     return 0;
 }
 
-static int caller_obj_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int caller_obj_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
@@ -342,7 +344,7 @@ static void join_func_die (void *vo)
     NCDModuleInst_Backend_Dead(o->i);
 }
 
-static int join_func_getobj (void *vo, const char *name, NCDObject *out)
+static int join_func_getobj (void *vo, NCD_string_id_t name, NCDObject *out)
 {
     struct join_instance *o = vo;
     

+ 13 - 9
ncd/modules/sys_request_client.c

@@ -150,9 +150,9 @@ static void request_handler_sent (struct request_instance *o);
 static void request_handler_reply (struct request_instance *o, NCDValMem reply_mem, NCDValRef reply_value);
 static void request_handler_finished (struct request_instance *o, int is_error);
 static void request_process_handler_event (struct request_instance *o, int event);
-static int request_process_func_getspecialobj (struct request_instance *o, const char *name, NCDObject *out_object);
-static int request_process_caller_obj_func_getobj (struct request_instance *o, const char *name, NCDObject *out_object);
-static int request_process_reply_obj_func_getvar (struct request_instance *o, const char *name, NCDValMem *mem, NCDValRef *out);
+static int request_process_func_getspecialobj (struct request_instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int request_process_caller_obj_func_getobj (struct request_instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int request_process_reply_obj_func_getvar (struct request_instance *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out);
 static void request_gone (struct request_instance *o, int is_bad);
 static void request_terminate_process (struct request_instance *o);
 static void request_die (struct request_instance *o, int is_error);
@@ -312,16 +312,18 @@ static void request_process_handler_event (struct request_instance *o, int event
     }
 }
 
-static int request_process_func_getspecialobj (struct request_instance *o, const char *name, NCDObject *out_object)
+static int request_process_func_getspecialobj (struct request_instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->pstate != RPSTATE_NONE)
     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)request_process_caller_obj_func_getobj);
         return 1;
     }
     
-    if (!o->process_is_finished && !strcmp(name, "_reply")) {
+    if (!o->process_is_finished && !strcmp(name_str, "_reply")) {
         *out_object = NCDObject_Build(NULL, o, (NCDObject_func_getvar)request_process_reply_obj_func_getvar, NULL);
         return 1;
     }
@@ -329,19 +331,21 @@ static int request_process_func_getspecialobj (struct request_instance *o, const
     return 0;
 }
 
-static int request_process_caller_obj_func_getobj (struct request_instance *o, const char *name, NCDObject *out_object)
+static int request_process_caller_obj_func_getobj (struct request_instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->pstate != RPSTATE_NONE)
     
     return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
 }
 
-static int request_process_reply_obj_func_getvar (struct request_instance *o, const char *name, NCDValMem *mem, NCDValRef *out)
+static int request_process_reply_obj_func_getvar (struct request_instance *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     ASSERT(o->pstate != RPSTATE_NONE)
     ASSERT(!o->process_is_finished)
     
-    if (!strcmp(name, "data")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "data")) {
         *out = NCDVal_NewCopy(mem, o->process_reply_data);
         if (NCDVal_IsInvalid(*out)) {
             ModuleLog(o->i, BLOG_ERROR, "NCDVal_NewCopy failed");

+ 12 - 8
ncd/modules/sys_request_server.c

@@ -161,8 +161,8 @@ static int request_init (struct connection *c, uint32_t request_id, const uint8_
 static void request_free (struct request *r);
 static struct request * find_request (struct connection *c, uint32_t request_id);
 static void request_process_handler_event (struct request *r, int event);
-static int request_process_func_getspecialobj (struct request *r, const char *name, NCDObject *out_object);
-static int request_process_request_obj_func_getvar (struct request *r, const char *name, NCDValMem *mem, NCDValRef *out_value);
+static int request_process_func_getspecialobj (struct request *r, NCD_string_id_t name, NCDObject *out_object);
+static int request_process_request_obj_func_getvar (struct request *r, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value);
 static void request_terminate (struct request *r);
 static struct reply * reply_init (struct connection *c, uint32_t request_id, NCDValRef reply_data);
 static void reply_start (struct reply *r, uint32_t type);
@@ -472,9 +472,11 @@ static void request_process_handler_event (struct request *r, int event)
     }
 }
 
-static int request_process_func_getspecialobj (struct request *r, const char *name, NCDObject *out_object)
+static int request_process_func_getspecialobj (struct request *r, NCD_string_id_t name, NCDObject *out_object)
 {
-    if (!strcmp(name, "_request")) {
+    const char *name_str = NCDStringIndex_Value(r->con->inst->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_request")) {
         *out_object = NCDObject_Build("sys.request_server.request", r, (NCDObject_func_getvar)request_process_request_obj_func_getvar, NULL);
         return 1;
     }
@@ -482,11 +484,13 @@ static int request_process_func_getspecialobj (struct request *r, const char *na
     return 0;
 }
 
-static int request_process_request_obj_func_getvar (struct request *r, const char *name, NCDValMem *mem, NCDValRef *out)
+static int request_process_request_obj_func_getvar (struct request *r, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
 {
     struct instance *o = r->con->inst;
     
-    if (!strcmp(name, "data")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "data")) {
         *out = NCDVal_NewCopy(mem, r->request_data);
         if (NCDVal_IsInvalid(*out)) {
             ModuleLog(o->i, BLOG_ERROR, "NCDVal_NewCopy failed");
@@ -494,7 +498,7 @@ static int request_process_request_obj_func_getvar (struct request *r, const cha
         return 1;
     }
     
-    if (!strcmp(name, "client_addr_type")) {
+    if (!strcmp(name_str, "client_addr_type")) {
         const char *str = "none";
         switch (r->con->addr.type) {
             case BADDR_TYPE_IPV4:
@@ -512,7 +516,7 @@ static int request_process_request_obj_func_getvar (struct request *r, const cha
         return 1;
     }
     
-    if (!strcmp(name, "client_addr")) {
+    if (!strcmp(name_str, "client_addr")) {
         char str[BIPADDR_MAX_PRINT_LEN] = "none";
         
         switch (r->con->addr.type) {

+ 8 - 6
ncd/modules/try.c

@@ -78,8 +78,8 @@ struct instance {
 #define STATE_FINISHED 3
 
 static void process_handler_event (struct instance *o, int event);
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object);
-static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object);
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
+static int process_caller_object_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object);
 static void start_terminating (struct instance *o);
 static void instance_free (struct instance *o);
 
@@ -121,16 +121,18 @@ static void process_handler_event (struct instance *o, int event)
     }
 }
 
-static int process_func_getspecialobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_func_getspecialobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
     
-    if (!strcmp(name, "_caller")) {
+    const char *name_str = NCDStringIndex_Value(o->i->params->iparams->string_index, name);
+    
+    if (!strcmp(name_str, "_caller")) {
         *out_object = NCDObject_Build(NULL, o, NULL, (NCDObject_func_getobj)process_caller_object_func_getobj);
         return 1;
     }
     
-    if (!strcmp(name, "_try")) {
+    if (!strcmp(name_str, "_try")) {
         *out_object = NCDObject_Build("try.try", o, NULL, NULL);
         return 1;
     }
@@ -138,7 +140,7 @@ static int process_func_getspecialobj (struct instance *o, const char *name, NCD
     return 0;
 }
 
-static int process_caller_object_func_getobj (struct instance *o, const char *name, NCDObject *out_object)
+static int process_caller_object_func_getobj (struct instance *o, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT)
     

+ 76 - 22
ncd/ncd.c

@@ -49,6 +49,7 @@
 #include <udevmonitor/NCDUdevManager.h>
 #include <random/BRandom2.h>
 #include <ncd/NCDConfigParser.h>
+#include <ncd/NCDStringIndex.h>
 #include <ncd/NCDModule.h>
 #include <ncd/NCDModuleIndex.h>
 #include <ncd/NCDSugar.h>
@@ -133,6 +134,9 @@ static NCDUdevManager umanager;
 // random number generator
 static BRandom2 random2;
 
+// string index
+static NCDStringIndex string_index;
+
 // method index
 static NCDMethodIndex method_index;
 
@@ -160,6 +164,7 @@ static void print_version (void);
 static int parse_arguments (int argc, char *argv[]);
 static void signal_handler (void *unused);
 static void start_terminate (int exit_code);
+static char * implode_id_strings (const NCD_string_id_t *names, size_t num_names, char del);
 static int process_new (NCDInterpProcess *iprocess, NCDModuleProcess *module_process);
 static void process_free (struct process *p, NCDModuleProcess **out_mp);
 static int process_state (struct process *p);
@@ -176,9 +181,9 @@ static void process_work_job_handler (struct process *p);
 static int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out);
 static void process_advance (struct process *p);
 static void process_wait_timer_handler (BSmallTimer *timer);
-static int process_find_object (struct process *p, int pos, const char *name, NCDObject *out_object);
-static int process_resolve_object_expr (struct process *p, int pos, const char *names, size_t num_names, NCDObject *out_object);
-static int process_resolve_variable_expr (struct process *p, int pos, const char *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value);
+static int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object);
+static int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object);
+static int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value);
 static void statement_logfunc (struct statement *ps);
 static void statement_log (struct statement *ps, int level, const char *fmt, ...);
 static struct process * statement_process (struct statement *ps);
@@ -186,14 +191,14 @@ static int statement_mem_is_allocated (struct statement *ps);
 static int statement_mem_size (struct statement *ps);
 static int statement_allocate_memory (struct statement *ps, int alloc_size);
 static void statement_instance_func_event (NCDModuleInst *inst, int event);
-static int statement_instance_func_getobj (NCDModuleInst *inst, const char *objname, NCDObject *out_object);
+static int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object);
 static int statement_instance_func_initprocess (NCDModuleProcess *mp, const char *template_name);
 static void statement_instance_logfunc (NCDModuleInst *inst);
 static void statement_instance_func_interp_exit (int exit_code);
 static int statement_instance_func_interp_getargs (NCDValMem *mem, NCDValRef *out_value);
 static btime_t statement_instance_func_interp_getretrytime (void);
 static void process_moduleprocess_func_event (struct process *p, int event);
-static int process_moduleprocess_func_getobj (struct process *p, const char *name, NCDObject *out_object);
+static int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object);
 
 int main (int argc, char **argv)
 {
@@ -288,6 +293,12 @@ int main (int argc, char **argv)
         goto fail1aa;
     }
     
+    // init string index
+    if (!NCDStringIndex_Init(&string_index)) {
+        BLog(BLOG_ERROR, "NCDStringIndex_Init failed");
+        goto fail1aaa;
+    }
+    
     // init method index
     if (!NCDMethodIndex_Init(&method_index)) {
         BLog(BLOG_ERROR, "NCDMethodIndex_Init failed");
@@ -339,13 +350,13 @@ int main (int argc, char **argv)
     }
     
     // init placeholder database
-    if (!NCDPlaceholderDb_Init(&placeholder_db)) {
+    if (!NCDPlaceholderDb_Init(&placeholder_db, &string_index)) {
         BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed");
         goto fail4;
     }
     
     // init interp program
-    if (!NCDInterpProg_Init(&iprogram, &program, &placeholder_db, &mindex, &method_index)) {
+    if (!NCDInterpProg_Init(&iprogram, &program, &string_index, &placeholder_db, &mindex, &method_index)) {
         BLog(BLOG_ERROR, "NCDInterpProg_Init failed");
         goto fail4a;
     }
@@ -360,10 +371,18 @@ int main (int argc, char **argv)
     // init modules
     size_t num_inited_modules = 0;
     for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) {
+        // map strings
+        if ((*g)->strings && !NCDStringIndex_GetRequests(&string_index, (*g)->strings)) {
+            BLog(BLOG_ERROR, "NCDStringIndex_GetRequests failed for some module");
+            goto fail5;
+        }
+        
+        // call func_globalinit
         if ((*g)->func_globalinit && !(*g)->func_globalinit(params)) {
             BLog(BLOG_ERROR, "globalinit failed for some module");
             goto fail5;
         }
+        
         num_inited_modules++;
     }
     
@@ -376,6 +395,7 @@ int main (int argc, char **argv)
     module_iparams.manager = &manager;
     module_iparams.umanager = &umanager;
     module_iparams.random2 = &random2;
+    module_iparams.string_index = &string_index;
     module_iparams.func_initprocess = statement_instance_func_initprocess;
     module_iparams.func_interp_exit = statement_instance_func_interp_exit;
     module_iparams.func_interp_getargs = statement_instance_func_interp_getargs;
@@ -441,6 +461,9 @@ fail1c:
     // free method index
     NCDMethodIndex_Free(&method_index);
 fail1b:
+    // free string index
+    NCDStringIndex_Free(&string_index);
+fail1aaa:
     // free random number generator
     BRandom2_Free(&random2);
 fail1aa:
@@ -664,6 +687,36 @@ void start_terminate (int exit_code)
     }
 }
 
+char * implode_id_strings (const NCD_string_id_t *names, size_t num_names, char del)
+{
+    ExpString str;
+    if (!ExpString_Init(&str)) {
+        goto fail0;
+    }
+    
+    int is_first = 1;
+    
+    while (num_names > 0) {
+        if (!is_first && !ExpString_AppendChar(&str, del)) {
+            goto fail1;
+        }
+        const char *name_str = NCDStringIndex_Value(&string_index, *names);
+        if (!ExpString_Append(&str, name_str)) {
+            goto fail1;
+        }
+        names++;
+        num_names--;
+        is_first = 0;
+    }
+    
+    return ExpString_Get(&str);
+    
+fail1:
+    ExpString_Free(&str);
+fail0:
+    return NULL;
+}
+
 int process_new (NCDInterpProcess *iprocess, NCDModuleProcess *module_process)
 {
     ASSERT(iprocess)
@@ -1009,7 +1062,7 @@ int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRe
     ASSERT(mem)
     ASSERT(out)
     
-    const char *varnames;
+    const NCD_string_id_t *varnames;
     size_t num_names;
     NCDPlaceholderDb_GetVariable(&placeholder_db, plid, &varnames, &num_names);
     
@@ -1039,7 +1092,7 @@ void process_advance (struct process *p)
     
     // get object names, e.g. "my.cat" in "my.cat->meow();"
     // (or NULL if this is not a method statement)
-    const char *objnames;
+    const NCD_string_id_t *objnames;
     size_t num_objnames;
     NCDInterpProcess_StatementObjNames(p->iprocess, p->ap, &objnames, &num_objnames);
     
@@ -1140,14 +1193,15 @@ void process_wait_timer_handler (BSmallTimer *timer)
     process_advance(p);
 }
 
-int process_find_object (struct process *p, int pos, const char *name, NCDObject *out_object)
+int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(pos >= 0)
     ASSERT(pos <= p->num_statements)
-    ASSERT(name)
     ASSERT(out_object)
     
-    int i = NCDInterpProcess_FindStatement(p->iprocess, pos, name);
+    const char *name_str = NCDStringIndex_Value(&string_index, name);
+    
+    int i = NCDInterpProcess_FindStatement(p->iprocess, pos, name_str);
     if (i >= 0) {
         struct statement *ps = &p->statements[i];
         ASSERT(i < p->num_statements)
@@ -1168,7 +1222,7 @@ int process_find_object (struct process *p, int pos, const char *name, NCDObject
     return 0;
 }
 
-int process_resolve_object_expr (struct process *p, int pos, const char *names, size_t num_names, NCDObject *out_object)
+int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object)
 {
     ASSERT(pos >= 0)
     ASSERT(pos <= p->num_statements)
@@ -1177,24 +1231,24 @@ int process_resolve_object_expr (struct process *p, int pos, const char *names,
     ASSERT(out_object)
     
     NCDObject object;
-    if (!process_find_object(p, pos, names, &object)) {
+    if (!process_find_object(p, pos, names[0], &object)) {
         goto fail;
     }
     
-    if (!NCDObject_ResolveObjExprCompact(&object, names + strlen(names) + 1, num_names - 1, out_object)) {
+    if (!NCDObject_ResolveObjExprCompact(&object, names + 1, num_names - 1, out_object)) {
         goto fail;
     }
     
     return 1;
     
 fail:;
-    char *name = implode_compact_strings(names, num_names, '.');
+    char *name = implode_id_strings(names, num_names, '.');
     process_log(p, BLOG_ERROR, "failed to resolve object (%s) from position %zu", (name ? name : ""), pos);
     free(name);
     return 0;
 }
 
-int process_resolve_variable_expr (struct process *p, int pos, const char *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value)
+int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value)
 {
     ASSERT(pos >= 0)
     ASSERT(pos <= p->num_statements)
@@ -1204,18 +1258,18 @@ int process_resolve_variable_expr (struct process *p, int pos, const char *names
     ASSERT(out_value)
     
     NCDObject object;
-    if (!process_find_object(p, pos, names, &object)) {
+    if (!process_find_object(p, pos, names[0], &object)) {
         goto fail;
     }
     
-    if (!NCDObject_ResolveVarExprCompact(&object, names + strlen(names) + 1, num_names - 1, mem, out_value)) {
+    if (!NCDObject_ResolveVarExprCompact(&object, names + 1, num_names - 1, mem, out_value)) {
         goto fail;
     }
     
     return 1;
     
 fail:;
-    char *name = implode_compact_strings(names, num_names, '.');
+    char *name = implode_id_strings(names, num_names, '.');
     process_log(p, BLOG_ERROR, "failed to resolve variable (%s) from position %zu", (name ? name : ""), pos);
     free(name);
     return 0;
@@ -1351,7 +1405,7 @@ void statement_instance_func_event (NCDModuleInst *inst, int event)
     }
 }
 
-int statement_instance_func_getobj (NCDModuleInst *inst, const char *objname, NCDObject *out_object)
+int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object)
 {
     struct statement *ps = UPPER_OBJECT(inst, struct statement, inst);
     ASSERT(ps->state != SSTATE_FORGOTTEN)
@@ -1457,7 +1511,7 @@ void process_moduleprocess_func_event (struct process *p, int event)
     }
 }
 
-int process_moduleprocess_func_getobj (struct process *p, const char *name, NCDObject *out_object)
+int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object)
 {
     ASSERT(p->module_process)