Эх сурвалжийг харах

ncd: speed up module lookup when initializing statements. For non-method statements, cache the module pointer in NCDInterpBlock. For
method statements, keep a global structure of methods grouped by method name, and store indices into this structure into
NCDInterpBlock. This way, when a method statement is initialized, we only need to traverse a list of all methods with the right name
and find the one with the right object type. This is very fast because there are few collisions (different object types with the same
method names).

ambrop7 13 жил өмнө
parent
commit
0ef729228f

+ 1 - 0
ncd/CMakeLists.txt

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

+ 33 - 1
ncd/NCDInterpBlock.c

@@ -145,11 +145,13 @@ fail:
     return 0;
 }
 
-int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block, NCDProcess *process, NCDPlaceholderDb *pdb)
+int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block, NCDProcess *process, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
 {
     ASSERT(block)
     ASSERT(process)
     ASSERT(pdb)
+    ASSERT(module_index)
+    ASSERT(method_index)
     
     if (NCDBlock_NumStatements(block) > INT_MAX) {
         BLog(BLOG_ERROR, "too many statements");
@@ -211,6 +213,14 @@ int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block, NCDProcess *process
                 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) {
+                BLog(BLOG_ERROR, "NCDMethodIndex_GetMethodNameId failed");
+                goto loop_fail3;
+            }
+        } else {
+            e->binding.simple_module = NCDModuleIndex_FindModule(module_index, NCDStatement_RegCmdName(s));
         }
         
         if (e->name) {
@@ -321,6 +331,28 @@ void NCDInterpBlock_StatementObjNames (NCDInterpBlock *o, int i, const char **ou
     *out_num_objnames = o->stmts[i].num_objnames;
 }
 
+const struct NCDModule * NCDInterpBlock_StatementGetSimpleModule (NCDInterpBlock *o, int i)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(i >= 0)
+    ASSERT(i < o->num_stmts)
+    ASSERT(!o->stmts[i].objnames)
+    
+    return o->stmts[i].binding.simple_module;
+}
+
+const struct NCDModule * NCDInterpBlock_StatementGetMethodModule (NCDInterpBlock *o, int i, const char *obj_type, NCDMethodIndex *method_index)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(i >= 0)
+    ASSERT(i < o->num_stmts)
+    ASSERT(o->stmts[i].objnames)
+    ASSERT(obj_type)
+    ASSERT(method_index)
+    
+    return NCDMethodIndex_GetMethodModule(method_index, obj_type, o->stmts[i].binding.method_name_id);
+}
+
 int NCDInterpBlock_CopyStatementArgs (NCDInterpBlock *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog)
 {
     DebugObject_Access(&o->d_obj);

+ 10 - 1
ncd/NCDInterpBlock.h

@@ -38,6 +38,9 @@
 #include <ncd/NCDAst.h>
 #include <ncd/NCDVal.h>
 #include <ncd/NCDPlaceholderDb.h>
+#include <ncd/NCDModule.h>
+#include <ncd/NCDModuleIndex.h>
+#include <ncd/NCDMethodIndex.h>
 
 #include "NCDInterpBlock_trie.h"
 #include <structure/CStringTrie_decl.h>
@@ -49,6 +52,10 @@ struct NCDInterpBlock__stmt {
     size_t num_objnames;
     char *arg_data;
     size_t arg_len;
+    union {
+        const struct NCDModule *simple_module;
+        int method_name_id;
+    } binding;
     NCDValSafeRef arg_ref;
     NCDValReplaceProg arg_prog;
     int alloc_size;
@@ -65,11 +72,13 @@ typedef struct {
     DebugObject d_obj;
 } NCDInterpBlock;
 
-int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block, NCDProcess *process, NCDPlaceholderDb *pdb) WARN_UNUSED;
+int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block, NCDProcess *process, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
 void NCDInterpBlock_Free (NCDInterpBlock *o);
 int NCDInterpBlock_FindStatement (NCDInterpBlock *o, int from_index, const char *name);
 const char * NCDInterpBlock_StatementCmdName (NCDInterpBlock *o, int i);
 void NCDInterpBlock_StatementObjNames (NCDInterpBlock *o, int i, const char **out_objnames, size_t *out_num_objnames);
+const struct NCDModule * NCDInterpBlock_StatementGetSimpleModule (NCDInterpBlock *o, int i);
+const struct NCDModule * NCDInterpBlock_StatementGetMethodModule (NCDInterpBlock *o, int i, const char *obj_type, NCDMethodIndex *method_index);
 int NCDInterpBlock_CopyStatementArgs (NCDInterpBlock *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) WARN_UNUSED;
 void NCDInterpBlock_StatementBumpAllocSize (NCDInterpBlock *o, int i, int alloc_size);
 int NCDInterpBlock_StatementPreallocSize (NCDInterpBlock *o, int i);

+ 4 - 2
ncd/NCDInterpProg.c

@@ -42,10 +42,12 @@
 #include "NCDInterpProg_hash.h"
 #include <structure/CHash_impl.h>
 
-int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb)
+int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index)
 {
     ASSERT(prog)
     ASSERT(pdb)
+    ASSERT(module_index)
+    ASSERT(method_index)
     
     if (NCDProgram_NumProcesses(prog) > INT_MAX) {
         BLog(BLOG_ERROR, "too many processes");
@@ -71,7 +73,7 @@ int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pd
         e->name = NCDProcess_Name(p);
         e->proc = p;
         
-        if (!NCDInterpBlock_Init(&e->iblock, NCDProcess_Block(p), p, pdb)) {
+        if (!NCDInterpBlock_Init(&e->iblock, NCDProcess_Block(p), p, pdb, module_index, method_index)) {
             BLog(BLOG_ERROR, "NCDInterpBlock_Init failed");
             goto fail2;
         }

+ 1 - 1
ncd/NCDInterpProg.h

@@ -57,7 +57,7 @@ typedef struct {
     DebugObject d_obj;
 } NCDInterpProg;
 
-int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb) WARN_UNUSED;
+int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index, NCDMethodIndex *method_index) WARN_UNUSED;
 void NCDInterpProg_Free (NCDInterpProg *o);
 int NCDInterpProg_FindProcess (NCDInterpProg *o, const char *name, NCDProcess **out_proc, NCDInterpBlock **out_iblock) WARN_UNUSED;
 

+ 256 - 0
ncd/NCDMethodIndex.c

@@ -0,0 +1,256 @@
+/**
+ * @file NCDMethodIndex.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 <limits.h>
+
+#include <misc/hashfun.h>
+#include <misc/balloc.h>
+#include <misc/strdup.h>
+
+#include "NCDMethodIndex.h"
+
+#include "NCDMethodIndex_hash.h"
+#include <structure/CHash_impl.h>
+
+#define GROWARRAY_NAME NamesArray
+#define GROWARRAY_OBJECT_TYPE NCDMethodIndex
+#define GROWARRAY_ARRAY_MEMBER names
+#define GROWARRAY_CAPACITY_MEMBER names_capacity
+#define GROWARRAY_MAX_CAPACITY INT_MAX
+#include <misc/grow_array.h>
+
+#define GROWARRAY_NAME EntriesArray
+#define GROWARRAY_OBJECT_TYPE NCDMethodIndex
+#define GROWARRAY_ARRAY_MEMBER entries
+#define GROWARRAY_CAPACITY_MEMBER entries_capacity
+#define GROWARRAY_MAX_CAPACITY INT_MAX
+#include <misc/grow_array.h>
+
+#include <generated/blog_channel_ncd.h>
+
+static struct NCDMethodIndex__entry * find_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx)
+{
+    ASSERT(method_name)
+    
+    NCDMethodIndex__HashRef ref = NCDMethodIndex__Hash_Lookup(&o->hash, o->names, method_name);
+    if (ref.link == -1) {
+        return NULL;
+    }
+    
+    ASSERT(ref.link >= 0)
+    ASSERT(ref.link < o->num_names)
+    
+    struct NCDMethodIndex__method_name *name_entry = ref.ptr;
+    ASSERT(!strcmp(name_entry->method_name, method_name))
+    ASSERT(name_entry->first_entry >= 0)
+    ASSERT(name_entry->first_entry < o->num_entries)
+    
+    if (out_entry_idx) {
+        *out_entry_idx = name_entry->first_entry;
+    }
+    return &o->entries[name_entry->first_entry];
+}
+
+static struct NCDMethodIndex__entry * add_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx)
+{
+    ASSERT(method_name)
+    ASSERT(!find_method_name(o, method_name, NULL))
+    
+    if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) {
+        BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed");
+        goto fail0;
+    }
+    
+    if (o->num_names == o->names_capacity && !NamesArray_DoubleUp(o)) {
+        BLog(BLOG_ERROR, "NamesArray_DoubleUp failed");
+        goto fail0;
+    }
+    
+    struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries];
+    entry->obj_type = NULL;
+    entry->next = -1;
+    
+    struct NCDMethodIndex__method_name *name_entry = &o->names[o->num_names];
+    
+    if (!(name_entry->method_name = b_strdup(method_name))) {
+        BLog(BLOG_ERROR, "b_strdup failed");
+        goto fail0;
+    }
+    
+    name_entry->first_entry = o->num_entries;
+    
+    NCDMethodIndex__HashRef ref = {name_entry, o->num_names};
+    int res = NCDMethodIndex__Hash_Insert(&o->hash, o->names, ref, NULL);
+    ASSERT(res)
+    
+    o->num_entries++;
+    o->num_names++;
+    
+    if (out_entry_idx) {
+        *out_entry_idx = name_entry->first_entry;
+    }
+    return entry;
+    
+fail0:
+    return NULL;
+}
+
+int NCDMethodIndex_Init (NCDMethodIndex *o)
+{
+    if (!NamesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) {
+        BLog(BLOG_ERROR, "NamesArray_Init failed");
+        goto fail0;
+    }
+    
+    if (!EntriesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_ENTRIES)) {
+        BLog(BLOG_ERROR, "EntriesArray_Init failed");
+        goto fail1;
+    }
+    
+    o->num_names = 0;
+    o->num_entries = 0;
+    
+    if (!NCDMethodIndex__Hash_Init(&o->hash, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) {
+        BLog(BLOG_ERROR, "NCDMethodIndex__Hash_Init failed");
+        goto fail2;
+    }
+    
+    return 1;
+    
+fail2:
+    EntriesArray_Free(o);
+fail1:
+    NamesArray_Free(o);
+fail0:
+    return 0;
+}
+
+void NCDMethodIndex_Free (NCDMethodIndex *o)
+{
+    for (int i = 0; i < o->num_names; i++) {
+        free(o->names[i].method_name);
+    }
+    
+    for (int i = 0; i < o->num_entries; i++) {
+        free(o->entries[i].obj_type);
+    }
+    
+    NCDMethodIndex__Hash_Free(&o->hash);
+    EntriesArray_Free(o);
+    NamesArray_Free(o);
+}
+
+int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, const char *method_name, const struct NCDModule *module)
+{
+    ASSERT(obj_type)
+    ASSERT(method_name)
+    ASSERT(module)
+    
+    struct NCDMethodIndex__entry *first_entry = find_method_name(o, method_name, NULL);
+    
+    if (!first_entry) {
+        struct NCDMethodIndex__entry *entry = add_method_name(o, method_name, NULL);
+        if (!entry) {
+            goto fail0;
+        }
+        
+        if (!(entry->obj_type = b_strdup(obj_type))) {
+            BLog(BLOG_ERROR, "b_strdup failed");
+            goto fail0;
+        }
+        
+        entry->module = module;
+    } else {
+        if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) {
+            BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed");
+            goto fail0;
+        }
+        
+        struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries];
+        
+        if (!(entry->obj_type = b_strdup(obj_type))) {
+            BLog(BLOG_ERROR, "b_strdup failed");
+            goto fail0;
+        }
+        
+        entry->module = module;
+        
+        entry->next = first_entry->next;
+        first_entry->next = o->num_entries;
+        
+        o->num_entries++;
+    }
+    
+    return 1;
+    
+fail0:
+    return 0;
+}
+
+int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name)
+{
+    ASSERT(method_name)
+    
+    int first_entry_idx;
+    
+    if (!find_method_name(o, method_name, &first_entry_idx)) {
+        if (!add_method_name(o, method_name, &first_entry_idx)) {
+            return -1;
+        }
+    }
+    
+    ASSERT(first_entry_idx >= 0)
+    ASSERT(first_entry_idx < o->num_entries)
+    
+    return first_entry_idx;
+}
+
+const struct NCDModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, const char *obj_type, int method_name_id)
+{
+    ASSERT(obj_type)
+    ASSERT(method_name_id >= 0)
+    ASSERT(method_name_id < o->num_entries)
+    
+    do {
+        struct NCDMethodIndex__entry *entry = &o->entries[method_name_id];
+        
+        if (entry->obj_type && !strcmp(entry->obj_type, obj_type)) {
+            ASSERT(entry->module)
+            return entry->module;
+        }
+        
+        method_name_id = entry->next;
+        ASSERT(method_name_id >= -1)
+        ASSERT(method_name_id < o->num_entries)
+    } while (method_name_id >= 0);
+    
+    return NULL;
+}

+ 125 - 0
ncd/NCDMethodIndex.h

@@ -0,0 +1,125 @@
+/**
+ * @file NCDMethodIndex.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_NCDMETHODINDEX_H
+#define BADVPN_NCDMETHODINDEX_H
+
+#include <misc/debug.h>
+#include <structure/CHash.h>
+#include <ncd/NCDModule.h>
+
+#define NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES 64
+#define NCDMETHODINDEX_NUM_EXPECTED_ENTRIES 64
+
+struct NCDMethodIndex__method_name {
+    char *method_name;
+    int first_entry;
+    int hash_next;
+};
+
+struct NCDMethodIndex__entry {
+    char *obj_type;
+    const struct NCDModule *module;
+    int next;
+};
+
+typedef struct NCDMethodIndex__method_name NCDMethodIndex__hashentry;
+typedef const char *NCDMethodIndex__hashkey;
+typedef struct NCDMethodIndex__method_name *NCDMethodIndex__hasharg;
+
+#include "NCDMethodIndex_hash.h"
+#include <structure/CHash_decl.h>
+
+/**
+ * The method index associates (object_type, method_name) pairs to pointers
+ * to corresponding \link NCDModule structures (whose type strings would
+ * be "object_type::method_name").
+ * More precisely, the method names are represented as indices into an
+ * internal array, which allows very efficient lookup when the method names
+ * are known in advance, but not the object types.
+ */
+typedef struct {
+    struct NCDMethodIndex__method_name *names;
+    struct NCDMethodIndex__entry *entries;
+    int names_capacity;
+    int entries_capacity;
+    int num_names;
+    int num_entries;
+    NCDMethodIndex__Hash hash;
+} NCDMethodIndex;
+
+/**
+ * Initializes the method index.
+ * 
+ * @return 1 on success, 0 on failure
+ */
+int NCDMethodIndex_Init (NCDMethodIndex *o) WARN_UNUSED;
+
+/**
+ * Frees the method index.
+ */
+void NCDMethodIndex_Free (NCDMethodIndex *o);
+
+/**
+ * Adds a method to the index.
+ * Duplicate methods will not be detected here.
+ * 
+ * @param obj_type object type of method, e.g. "cat" in "cat::meow".
+ *                 Must not be NULL.
+ * @param method_name name of method, e.g. "meow" in "cat::meow".
+ *                    Must not be NULL.
+ * @param module pointer to module structure. Must not be NULL.
+ * @return 1 on success, 0 on failure
+ */
+int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, const char *method_name, const struct NCDModule *module) WARN_UNUSED;
+
+/**
+ * Obtains an internal integer identifier for a method name. The intention is that
+ * this is stored and later passed to \link NCDMethodIndex_GetMethodModule for
+ * efficient lookup of modules corresponding to methods.
+ * 
+ * @param method_name name of method, e.g. "meow" in "cat::meow".
+ *                    Must not be NULL.
+ * @return non-negative integer on success, -1 on failure
+ */
+int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name);
+
+/**
+ * Looks up the module corresponding to a method. The method name is passed as an
+ * identifier obtained from \link NCDMethodIndex_GetMethodNameId.
+ * 
+ * @param obj_type object type of method, e.g. "cat" in "cat::meow".
+ *                 Must not be NULL.
+ * @param method_name_id method name identifier. Must have been previously returned
+ *                       by a successfull call of \link NCDMethodIndex_GetMethodNameId.
+ * @return module pointer, or NULL if no such method exists
+ */
+const struct NCDModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, const char *obj_type, int method_name_id);
+
+#endif

+ 12 - 0
ncd/NCDMethodIndex_hash.h

@@ -0,0 +1,12 @@
+#define CHASH_PARAM_NAME NCDMethodIndex__Hash
+#define CHASH_PARAM_ENTRY NCDMethodIndex__hashentry
+#define CHASH_PARAM_LINK int
+#define CHASH_PARAM_KEY NCDMethodIndex__hashkey
+#define CHASH_PARAM_ARG NCDMethodIndex__hasharg
+#define CHASH_PARAM_NULL ((int)-1)
+#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)])
+#define CHASH_PARAM_ENTRYHASH(arg, entry) (badvpn_djb2_hash((const uint8_t *)(entry).ptr->method_name))
+#define CHASH_PARAM_KEYHASH(arg, key) (badvpn_djb2_hash((const uint8_t *)(key)))
+#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->method_name, (entry2).ptr->method_name))
+#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->method_name))
+#define CHASH_PARAM_ENTRY_NEXT hash_next

+ 44 - 1
ncd/NCDModuleIndex.c

@@ -34,6 +34,7 @@
 #include <misc/balloc.h>
 #include <misc/hashfun.h>
 #include <misc/compare.h>
+#include <misc/substring.h>
 #include <base/BLog.h>
 
 #include "NCDModuleIndex.h"
@@ -74,6 +75,40 @@ static struct NCDModuleIndex_base_type * find_base_type (NCDModuleIndex *o, cons
     return bt;
 }
 
+static int add_method (char *type, const struct NCDModule *module, NCDMethodIndex *method_index)
+{
+    ASSERT(type)
+    ASSERT(module)
+    ASSERT(method_index)
+    
+    const char search[] = "::";
+    size_t search_len = sizeof(search) - 1;
+    
+    size_t table[sizeof(search) - 1];
+    build_substring_backtrack_table_reverse(search, search_len, table);
+    
+    size_t pos;
+    if (!find_substring_reverse(type, strlen(type), search, search_len, table, &pos)) {
+        return 1;
+    }
+    
+    ASSERT(pos >= 0)
+    ASSERT(pos <= strlen(type) - search_len)
+    ASSERT(!memcmp(type + pos, search, search_len))
+    
+    char save = type[pos];
+    type[pos] = '\0';
+    int res = NCDMethodIndex_AddMethod(method_index, type, type + pos + search_len, module);
+    type[pos] = save;
+    
+    if (!res) {
+        BLog(BLOG_ERROR, "NCDMethodIndex_AddMethod failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
 int NCDModuleIndex_Init (NCDModuleIndex *o)
 {
     // allocate modules array
@@ -121,9 +156,11 @@ void NCDModuleIndex_Free (NCDModuleIndex *o)
     BFree(o->modules);
 }
 
-int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group)
+int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, NCDMethodIndex *method_index)
 {
     DebugObject_Access(&o->d_obj);
+    ASSERT(group)
+    ASSERT(method_index)
     
     for (const struct NCDModule *nm = group->modules; nm->type; nm++) {
         if (find_module(o, nm->type)) {
@@ -170,6 +207,12 @@ int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *gro
         }
         
         o->num_modules++;
+        
+        if (!add_method(m->type, nm, method_index)) {
+            BLog(BLOG_ERROR, "failed to add method to method index");
+            return 0;
+        }
+        
         continue;
         
     loop_fail1:

+ 2 - 1
ncd/NCDModuleIndex.h

@@ -35,6 +35,7 @@
 #include <structure/CHash.h>
 #include <base/DebugObject.h>
 #include <ncd/NCDModule.h>
+#include <ncd/NCDMethodIndex.h>
 
 #define NCDMODULEINDEX_MAX_TYPE_LEN 35
 #define NCDMODULEINDEX_MAX_MODULES 256
@@ -69,7 +70,7 @@ typedef struct {
 
 int NCDModuleIndex_Init (NCDModuleIndex *o) WARN_UNUSED;
 void NCDModuleIndex_Free (NCDModuleIndex *o);
-int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group) WARN_UNUSED;
+int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, NCDMethodIndex *method_index) WARN_UNUSED;
 const struct NCDModule * NCDModuleIndex_FindModule (NCDModuleIndex *o, const char *type);
 
 #endif

+ 33 - 47
ncd/ncd.c

@@ -129,6 +129,9 @@ BProcessManager manager;
 // udev manager
 NCDUdevManager umanager;
 
+// method index
+NCDMethodIndex method_index;
+
 // module index
 NCDModuleIndex mindex;
 
@@ -148,15 +151,11 @@ struct NCDModuleInst_iparams module_iparams;
 // processes
 LinkedList1 processes;
 
-// buffer for concatenating base_type::method_name
-char method_concat_buf[200];
-
 static void print_help (const char *name);
 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 int build_method_type_string (const char *object_type, const char *method_name);
 static int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleProcess *module_process);
 static void process_free (struct process *p, NCDModuleProcess **out_mp);
 static int process_mem_is_preallocated (struct process *p, char *mem);
@@ -273,15 +272,21 @@ int main (int argc, char **argv)
     // init udev manager
     NCDUdevManager_Init(&umanager, options.no_udev, &reactor, &manager);
     
+    // init method index
+    if (!NCDMethodIndex_Init(&method_index)) {
+        BLog(BLOG_ERROR, "NCDMethodIndex_Init failed");
+        goto fail1b;
+    }
+    
     // init module index
     if (!NCDModuleIndex_Init(&mindex)) {
         BLog(BLOG_ERROR, "NCDModuleIndex_Init failed");
-        goto fail1b;
+        goto fail1c;
     }
     
     // add module groups to index
     for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) {
-        if (!NCDModuleIndex_AddGroup(&mindex, *g)) {
+        if (!NCDModuleIndex_AddGroup(&mindex, *g, &method_index)) {
             BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed");
             goto fail2;
         }
@@ -324,7 +329,7 @@ int main (int argc, char **argv)
     }
     
     // init interp program
-    if (!NCDInterpProg_Init(&iprogram, &program, &placeholder_db)) {
+    if (!NCDInterpProg_Init(&iprogram, &program, &placeholder_db, &mindex, &method_index)) {
         BLog(BLOG_ERROR, "NCDInterpProg_Init failed");
         goto fail4a;
     }
@@ -416,6 +421,9 @@ fail3:
 fail2:
     // free module index
     NCDModuleIndex_Free(&mindex);
+fail1c:
+    // free method index
+    NCDMethodIndex_Free(&method_index);
 fail1b:
     // free udev manager
     NCDUdevManager_Free(&umanager);
@@ -637,31 +645,6 @@ void start_terminate (int exit_code)
     }
 }
 
-int build_method_type_string (const char *object_type, const char *method_name)
-{
-    ASSERT(object_type)
-    ASSERT(method_name)
-    
-    // This writes "<object_type>::<method_name>" into method_concat_buf.
-    // strlen+memcpy is used because it is much faster than snprintf.
-    
-    size_t len1 = strlen(object_type);
-    size_t len2 = strlen(method_name);
-    
-    if (len1 > sizeof(method_concat_buf) ||
-        len2 > sizeof(method_concat_buf) - len1 ||
-        3 > sizeof(method_concat_buf) - len1 - len2
-    ) {
-        return 0;
-    }
-    
-    memcpy(method_concat_buf, object_type, len1);
-    memcpy(method_concat_buf + len1, "::", 2);
-    memcpy(method_concat_buf + len1 + 2, method_name, len2 + 1);
-    
-    return 1;
-}
-
 int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleProcess *module_process)
 {
     // get num statements
@@ -1002,17 +985,26 @@ void process_advance (struct process *p)
     
     statement_log(ps, BLOG_INFO, "initializing");
     
+    // need to determine the module and object to use it on (if it's a method)
+    const struct NCDModule *module;
     NCDObject object;
     NCDObject *object_ptr = NULL;
     
+    // get object names, e.g. "my.cat" in "my.cat->meow();"
+    // (or NULL if this is not a method statement)
     const char *objnames;
     size_t num_objnames;
     NCDInterpBlock_StatementObjNames(p->iblock, p->ap, &objnames, &num_objnames);
     
-    const char *type = NCDInterpBlock_StatementCmdName(p->iblock, p->ap);
-    
-    // if this is a method-like statement, type is really "base_type(object)::method_name"
-    if (objnames) {
+    if (!objnames) {
+        // not a method; module is already known by NCDInterpBlock
+        module = NCDInterpBlock_StatementGetSimpleModule(p->iblock, p->ap);
+        
+        if (!module) {
+            statement_log(ps, BLOG_ERROR, "unknown simple statement: %s", NCDInterpBlock_StatementCmdName(p->iblock, p->ap));
+            goto fail0;
+        }
+    } else {
         // get object
         if (!process_resolve_object_expr(p, p->ap, objnames, num_objnames, &object)) {
             goto fail0;
@@ -1026,19 +1018,13 @@ void process_advance (struct process *p)
             goto fail0;
         }
         
-        // build type string
-        if (!build_method_type_string(object_type, type)) {
-            statement_log(ps, BLOG_ERROR, "type/method name too long");
+        // find module based on type of object
+        module = NCDInterpBlock_StatementGetMethodModule(p->iblock, p->ap, object_type, &method_index);
+        
+        if (!module) {
+            statement_log(ps, BLOG_ERROR, "unknown method statement: %s::%s", object_type, NCDInterpBlock_StatementCmdName(p->iblock, p->ap));
             goto fail0;
         }
-        type = method_concat_buf;
-    }
-    
-    // find module to instantiate
-    const struct NCDModule *module = NCDModuleIndex_FindModule(&mindex, type);
-    if (!module) {
-        statement_log(ps, BLOG_ERROR, "failed to find module: %s", type);
-        goto fail0;
     }
     
     // register alloc size for future preallocations