refactor: start using void* with trie

generic-data-trie
Jef Roosens 2022-12-07 23:20:39 +01:00
parent 01eb5ece55
commit c99bc83015
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
8 changed files with 83 additions and 57 deletions

View File

@ -125,25 +125,25 @@ int main() {
// Serve an entry // Serve an entry
CROW_ROUTE(app, "/<string>") CROW_ROUTE(app, "/<string>")
.methods(crow::HTTPMethod::Get)( .methods(
[trie](crow::response &res, std::string key) { crow::HTTPMethod::Get)([trie](crow::response &res, std::string key) {
trie_rlock(trie); trie_rlock(trie);
Entry *entry; Entry *entry;
TrieExitCode status = trie_search(trie, &entry, key.c_str()); TrieExitCode status = trie_search(trie, (void **)&entry, key.c_str());
if (status == Ok) { if (status == Ok) {
if (entry->type == Redirect) { if (entry->type == Redirect) {
res.redirect(entry->string); res.redirect(entry->string);
} else if (entry->type == Paste) { } else if (entry->type == Paste) {
res.set_static_file_info("pastes/" + key); res.set_static_file_info("pastes/" + key);
} }
} else { } else {
res.code = 404; res.code = 404;
} }
res.end(); res.end();
trie_unlock(trie); trie_unlock(trie);
}); });
// Add a new Redirect with a short randomly generated key // Add a new Redirect with a short randomly generated key
CROW_ROUTE(app, "/s/") CROW_ROUTE(app, "/s/")
@ -214,15 +214,14 @@ int main() {
TrieExitCode status = trie_add(trie, key.c_str(), new_entry); TrieExitCode status = trie_add(trie, key.c_str(), new_entry);
trie_unlock(trie); trie_unlock(trie);
if (status != Ok) { switch (status) {
case Ok:
return crow::response(base_url + key);
case AlreadyPresent:
return crow::response(crow::status::CONFLICT); return crow::response(crow::status::CONFLICT);
} default:
if (!store_paste(key.c_str(), req.body.c_str())) {
return crow::response(crow::status::INTERNAL_SERVER_ERROR); return crow::response(crow::status::INTERNAL_SERVER_ERROR);
} }
return crow::response(base_url + key);
}); });
app.port(18080).multithreaded().run(); app.port(18080).multithreaded().run();
} }

View File

@ -1,12 +1,15 @@
#ifndef AD3_TERNARYTRIE #ifndef AD3_TERNARYTRIE
#define AD3_TERNARYTRIE #define AD3_TERNARYTRIE
#define ALPHABET_SIZE 256
#define DELIMITER '\0' #define DELIMITER '\0'
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
// Should not be higher than 254 or stuff will break // The compiler aligns structs to be multiple of 8 bytes. Therefore, we have a
#define TRIE_MAX_SKIP_SIZE 8 // few "free" bytes that are allocated whether we use them or not, so we might
// as, hence the seemingly weird number. This is the largest number we can use
// where the size of TernaryTrieNode is still 32 bytes.
//
// NOTE: Should not be higher than 254 or stuff will break.
#define TRIE_MAX_SKIP_SIZE 13
/** /**
* The implementation of a Ternary Trie. * The implementation of a Ternary Trie.
@ -17,6 +20,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <string.h> #include <string.h>
static const char charset[] = static const char charset[] =
@ -50,6 +54,8 @@ typedef enum trie_exit_code {
Entry *entry_new(EntryType type, const char *string); Entry *entry_new(EntryType type, const char *string);
void entry_free(Entry *entry);
/** /**
* Allocate & initialize a new trie, and populate it with the data from the * Allocate & initialize a new trie, and populate it with the data from the
* given data file. * given data file.
@ -73,7 +79,7 @@ void trie_free(Trie *trie);
* @param key key representing the entry * @param key key representing the entry
* @return 0 if the search was successful, 1 if not found * @return 0 if the search was successful, 1 if not found
*/ */
TrieExitCode trie_search(Trie *trie, Entry **entry_ptr, const char *key); TrieExitCode trie_search(Trie *trie, void **data_ptr, const char *key);
/** /**
* Add a string to this trie. * Add a string to this trie.
@ -83,7 +89,7 @@ TrieExitCode trie_search(Trie *trie, Entry **entry_ptr, const char *key);
* @param entry entry to add * @param entry entry to add
* @return 0 if added, 1 if already in trie, something else if other errors * @return 0 if added, 1 if already in trie, something else if other errors
*/ */
TrieExitCode trie_add(Trie *trie, const char *key, Entry *entry); TrieExitCode trie_add(Trie *trie, const char *key, void *data);
/** /**
* Add an entry by generating a random string as the key. * Add an entry by generating a random string as the key.
@ -94,7 +100,7 @@ TrieExitCode trie_add(Trie *trie, const char *key, Entry *entry);
* @return pointer to the generated key. This pointer is safe to use after * @return pointer to the generated key. This pointer is safe to use after
* unlocking the trie, and should be freed manually. * unlocking the trie, and should be freed manually.
*/ */
TrieExitCode trie_add_random(Trie *trie, char **key_ptr, Entry *entry, TrieExitCode trie_add_random(Trie *trie, char **key_ptr, void *data,
bool secure); bool secure);
/** /**
@ -113,7 +119,7 @@ bool trie_remove(Trie *trie, const char *key);
* @param trie * @param trie
* @return the number of entries in this trie * @return the number of entries in this trie
*/ */
size_t trie_size(Trie *trie); uint64_t trie_size(Trie *trie);
/* /*
* Acquire a read lock on the trie. * Acquire a read lock on the trie.

View File

@ -10,12 +10,12 @@
typedef struct ttrie { typedef struct ttrie {
TrieNode *root; TrieNode *root;
size_t size; uint64_t size;
char *file_path; char *file_path;
pthread_rwlock_t lock; pthread_rwlock_t lock;
} Trie; } Trie;
TrieExitCode trie_add_no_lock(Trie *trie, const char *key, Entry *entry); TrieExitCode trie_add_no_lock(Trie *trie, const char *key, void *data);
/** /**
* Allocate and initialize an empty Trie * Allocate and initialize an empty Trie
@ -138,7 +138,7 @@ SearchResult trie_search_node(Trie *trie, const char *key) {
// At this point, we've either arrived at an empty child, or traversed through // At this point, we've either arrived at an empty child, or traversed through
// the entire string. Therefore, all we have to do is check whether we're at // the entire string. Therefore, all we have to do is check whether we're at
// the end of the string and if node represents a string. // the end of the string and if node represents a string.
if (key[i] == DELIMITER && (*child_ptr)->represents) { if (key[i] == DELIMITER && (*child_ptr)->data_size > 0) {
out.parent = *node_ptr; out.parent = *node_ptr;
out.child = *child_ptr; out.child = *child_ptr;
} }
@ -153,14 +153,14 @@ SearchResult trie_search_node(Trie *trie, const char *key) {
* @param string string to look up * @param string string to look up
* @return true if the string is present in the trie, false otherwise * @return true if the string is present in the trie, false otherwise
*/ */
TrieExitCode trie_search(Trie *trie, Entry **entry_ptr, const char *key) { TrieExitCode trie_search(Trie *trie, void **data_ptr, const char *key) {
SearchResult res = trie_search_node(trie, key); SearchResult res = trie_search_node(trie, key);
if (res.child == NULL) { if (res.child == NULL) {
return NotFound; return NotFound;
} }
*entry_ptr = res.child->entry; *data_ptr = res.child->data;
return Ok; return Ok;
} }
@ -173,7 +173,7 @@ TrieExitCode trie_search(Trie *trie, Entry **entry_ptr, const char *key) {
* @return true if the string wasn't present in the trie and thus added, false * @return true if the string wasn't present in the trie and thus added, false
* otherwise * otherwise
*/ */
TrieExitCode trie_add_no_lock(Trie *trie, const char *string, Entry *entry) { TrieExitCode trie_add_no_lock(Trie *trie, const char *string, void *data) {
size_t i = 0; size_t i = 0;
uint8_t offset; uint8_t offset;
TrieNode **node_ptr = &(trie->root); TrieNode **node_ptr = &(trie->root);
@ -211,8 +211,8 @@ TrieExitCode trie_add_no_lock(Trie *trie, const char *string, Entry *entry) {
continue; continue;
} }
child_node->represents = true; child_node->data_size = sizeof(data);
child_node->entry = entry; child_node->data = data;
trie->size++; trie->size++;
return Ok; return Ok;
@ -265,17 +265,17 @@ TrieExitCode trie_add_no_lock(Trie *trie, const char *string, Entry *entry) {
i += offset; i += offset;
} while (string[i] != DELIMITER); } while (string[i] != DELIMITER);
if ((*child_node_ptr)->represents) { if ((*child_node_ptr)->data_size > 0) {
return AlreadyPresent; return AlreadyPresent;
} }
(*child_node_ptr)->represents = true; (*child_node_ptr)->data_size = sizeof(data);
(*child_node_ptr)->entry = entry; (*child_node_ptr)->data = data;
trie->size++; trie->size++;
return Ok; return Ok;
} }
TrieExitCode trie_add(Trie *trie, const char *key, Entry *entry) { TrieExitCode trie_add(Trie *trie, const char *key, void *entry) {
if (trie->file_path != NULL) { if (trie->file_path != NULL) {
// Easiest way to make sure we don't add duplicate entries // Easiest way to make sure we don't add duplicate entries
// We use an internal function that doesn't require a read lock, as we're // We use an internal function that doesn't require a read lock, as we're
@ -290,12 +290,12 @@ TrieExitCode trie_add(Trie *trie, const char *key, Entry *entry) {
return FileError; return FileError;
} }
fputs(key, fp); /* fputs(key, fp); */
fputs(" ", fp); /* fputs(" ", fp); */
fputc(entry_type_to_char(entry->type), fp); /* fputc(entry_type_to_char(entry->type), fp); */
fputs(" ", fp); /* fputs(" ", fp); */
fputs(entry->string, fp); /* fputs(entry->string, fp); */
fputs("\n", fp); /* fputs("\n", fp); */
fclose(fp); fclose(fp);
} }
@ -305,7 +305,7 @@ TrieExitCode trie_add(Trie *trie, const char *key, Entry *entry) {
return trie_add_no_lock(trie, key, entry); return trie_add_no_lock(trie, key, entry);
} }
TrieExitCode trie_add_random(Trie *trie, char **key_ptr, Entry *entry, TrieExitCode trie_add_random(Trie *trie, char **key_ptr, void *data,
bool secure) { bool secure) {
// Generate random key // Generate random key
bool ok = false; bool ok = false;
@ -324,7 +324,7 @@ TrieExitCode trie_add_random(Trie *trie, char **key_ptr, Entry *entry,
ok = trie_search_node(trie, key).child == NULL; ok = trie_search_node(trie, key).child == NULL;
} }
TrieExitCode return_value = trie_add(trie, key, entry); TrieExitCode return_value = trie_add(trie, key, data);
if (return_value == Ok) { if (return_value == Ok) {
*key_ptr = key; *key_ptr = key;
@ -402,7 +402,7 @@ TrieExitCode trie_add_random(Trie *trie, char **key_ptr, Entry *entry,
* @param trie trie to return size for * @param trie trie to return size for
* @return size of the trie * @return size of the trie
*/ */
size_t trie_size(Trie *trie) { return trie->size; } uint64_t trie_size(Trie *trie) { return trie->size; }
int trie_rlock(Trie *trie) { return pthread_rwlock_rdlock(&trie->lock); } int trie_rlock(Trie *trie) { return pthread_rwlock_rdlock(&trie->lock); }

View File

@ -35,3 +35,11 @@ Entry *entry_new(EntryType type, const char *string) {
return entry; return entry;
} }
void entry_free(Entry *entry) {
if (entry->string != NULL) {
free(entry->string);
}
free(entry);
}

View File

@ -27,7 +27,8 @@ typedef struct tinode {
* and its string pointer is initialized. * and its string pointer is initialized.
*/ */
typedef struct tnode { typedef struct tnode {
Entry *entry; void *data;
uint64_t data_size;
TrieInnerNode *tree; TrieInnerNode *tree;
uint8_t tree_size; uint8_t tree_size;
@ -36,8 +37,6 @@ typedef struct tnode {
// nodes // nodes
char string[TRIE_MAX_SKIP_SIZE]; char string[TRIE_MAX_SKIP_SIZE];
uint8_t string_len; uint8_t string_len;
bool represents;
} TrieNode; } TrieNode;
// Required for recursively freeing tree structure // Required for recursively freeing tree structure
@ -67,7 +66,7 @@ TrieNode *tnode_init() {
node->tree_size = 0; node->tree_size = 0;
node->string_len = 0; node->string_len = 0;
node->represents = false; node->data_size = 0;
return node; return node;
} }
@ -105,6 +104,10 @@ void tnode_free(TrieNode *node) {
tinode_free_cascade(node->tree); tinode_free_cascade(node->tree);
} }
if (node->data_size > 0) {
free(node->data);
}
// TODO properly free entry // TODO properly free entry
/* if (node->payload != NULL) { */ /* if (node->payload != NULL) { */
/* free(node->payload); */ /* free(node->payload); */

View File

@ -2,5 +2,10 @@ add_compile_options(-Wno-incompatible-pointer-types)
add_executable(test_trie test_trie.c ../src/trie.c) add_executable(test_trie test_trie.c ../src/trie.c)
add_test(NAME test_trie COMMAND test_trie) add_test(NAME test_trie COMMAND test_trie)
add_executable(test_trie_fuzzy test_trie_fuzzy.c ../src/trie.c) add_executable(test_trie_fuzzy test_trie_fuzzy.c ../src/trie.c)
add_test(NAME test_trie_fuzzy COMMAND test_trie_fuzzy) add_test(NAME test_trie_fuzzy COMMAND test_trie_fuzzy)
add_test(NAME test_leaks COMMAND valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./test_trie)
add_test(NAME test_fuzzy_leaks COMMAND valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./test_trie_fuzzy)

View File

@ -28,6 +28,8 @@ void test_add_one() {
TEST_CHECK(trie_search(ct, &entry2, string) == Ok); TEST_CHECK(trie_search(ct, &entry2, string) == Ok);
TEST_CHECK(entry == entry2); TEST_CHECK(entry == entry2);
TEST_SIZE(ct, 1); TEST_SIZE(ct, 1);
entry_free(entry);
trie_free(ct); trie_free(ct);
} }
@ -52,6 +54,8 @@ void test_add_prefix() {
TEST_CHECK(trie_search(ct, &entry3, s2) == Ok); TEST_CHECK(trie_search(ct, &entry3, s2) == Ok);
TEST_CHECK(entry3 == entry2); TEST_CHECK(entry3 == entry2);
entry_free(entry1);
entry_free(entry2);
trie_free(ct); trie_free(ct);
} }
@ -104,6 +108,7 @@ void test_add_more() {
TEST_CHECK(trie_add(ct, twenty, NULL) == AlreadyPresent); TEST_CHECK(trie_add(ct, twenty, NULL) == AlreadyPresent);
TEST_CHECK(trie_add(ct, twentytwo, NULL) == AlreadyPresent); TEST_CHECK(trie_add(ct, twentytwo, NULL) == AlreadyPresent);
entry_free(entry);
trie_free(ct); trie_free(ct);
} }

View File

@ -10,7 +10,7 @@ void test_fuzzy() {
int counter = 0; int counter = 0;
int res; int res;
for (int len = 1; len < 25; len += 5) { for (int len = 2; len < 25; len += 5) {
for (int count = 10; count <= 500; count += 10) { for (int count = 10; count <= 500; count += 10) {
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
counter++; counter++;