diff --git a/src/net/listen.c b/src/net/listen.c index 1f6ec34d9f37eb308a1a5fc2d24d2ec47a14f739..dbbd58533de07c0465919e5cd46eccc87ec60013 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -18,6 +18,132 @@ #include <sys/un.h> #include <sys/socket.h> +/* Server commands, stored in a trie for a bit more efficient lookups. + * Commands have ascii names, but use the size of a char to not bother with + * bounds. If the `type` is not LKT_COMMAND_NULL, then the node is a terminal + * node, otherwise you need to reinterpret the `cmd_ptr` to the right function + * pointer type. */ + +#define CHARS_MAX (sizeof(char) * 256) + +typedef enum { + /* Not a terminal node, so no commands */ + LKT_COMMAND_NULL, + + /* Some common types of commands */ + LKT_COMMAND_SIMPLE, /* srv, c, args */ + LKT_COMMAND_INTEGER, /* srv, c, args, int */ + + /* Some specialized types... */ +} LKT_COMMAND_TYPE; + +struct cmd_trie_node { + LKT_COMMAND_TYPE type; /* The type of the command, to reinterpret the function pointer */ + void (*cmd_ptr)(void); /* The function pointer of the command */ + struct cmd_trie_node *children[CHARS_MAX]; /* Childrens, a byte is 256 possible values */ +}; + +PRIVATE_FUNCTION struct cmd_trie_node * +cmd_trie_new(void) +{ + struct cmd_trie_node *ret = safe_malloc(sizeof(struct cmd_trie_node)); + + for (size_t i = 0; i < CHARS_MAX; ++i) { + ret->children[i] = NULL; + } + + /* cmd_ptr == NULL && type == NULL => invalid node, for empty tries. */ + ret->cmd_ptr = NULL; + ret->type = LKT_COMMAND_NULL; + + return ret; +} + +PRIVATE_FUNCTION void +cmd_trie_insert(struct cmd_trie_node *root, const char *signed_cmd_name, + void (*cmd_ptr)(void), LKT_COMMAND_TYPE cmd_type) +{ + FAIL_UNLESS(root, "Passing an empty trie root"); + unsigned const char *cmd_name = (unsigned const char *)signed_cmd_name; + size_t cmd_name_index = 0; + size_t current_char = cmd_name[cmd_name_index]; + + while (current_char) { + /* Create the node if it doesn't exist */ + if (root->children[current_char] == NULL) { + root->children[current_char] = cmd_trie_new(); + } + + /* curr = curr->next */ + cmd_name_index += 1; + root = root->children[current_char]; + current_char = cmd_name[cmd_name_index]; + } + + /* Insert the new command */ + if (root->type == LKT_COMMAND_NULL) { + root->type = cmd_type; + root->cmd_ptr = cmd_ptr; + } + + /* The command was already inserted */ + else { + LOG_FATAL("The command '%s' was already present in the cmd_trie", signed_cmd_name); + } +} + +PRIVATE_FUNCTION void +___cmd_trie_print(struct cmd_trie_node *root, char *old_prefix, const size_t length) +{ + char prefix[length + 2]; /* Old + new_char + \0 */ + memcpy(prefix, old_prefix, sizeof(char) * length); + prefix[length + 1] = '\0'; + + switch (root->type) { + /* Not a terminal node */ + case LKT_COMMAND_NULL: + break; + + /* A terminal node */ + case LKT_COMMAND_SIMPLE: + case LKT_COMMAND_INTEGER: + LKT_OUTPUT("CMD_TRIE", "Got function in trie: %s (type: %d)", prefix, root->type); + break; + } + + for (size_t i = 0; i < CHARS_MAX; ++i) { + /* Rec-call with new children */ + if (root->children[i] != NULL) { + prefix[length] = i; + ___cmd_trie_print(root->children[i], prefix, length + 1); + } + } +} + +PRIVATE_FUNCTION void +cmd_trie_print(struct cmd_trie_node *root) +{ + FAIL_UNLESS(root, "Passing an empty trie root"); + ___cmd_trie_print(root, NULL, 0); +} + +struct cmd_trie_node *cmd_trie_root = NULL; + +CONSTRUCTOR_FUNCTION +___cmd_trie_init(void) +{ + cmd_trie_root = cmd_trie_new(); + cmd_trie_insert(cmd_trie_root, "next", FUNCTION_POINTER(command_next), LKT_COMMAND_SIMPLE); + cmd_trie_print(cmd_trie_root); +} + +DESTRUCTOR_FUNCTION +___cmd_trie_deinit(void) +{ +} + +/* Client structure */ + typedef enum { LKT_COMMAND_LIST_OFF, LKT_COMMAND_LIST_ON,