refactor: start using void* with trie
parent
01eb5ece55
commit
c99bc83015
45
src/main.cpp
45
src/main.cpp
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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); }
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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); */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
Loading…
Reference in New Issue