feat: pave way for pastebin integration

trie-skips
Jef Roosens 2022-11-21 15:28:02 +01:00
parent 689a878978
commit f1ec643f80
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
5 changed files with 112 additions and 61 deletions

View File

@ -11,5 +11,5 @@ if [ "$1" = add ]; then
"$URL/$3" "$URL/$3"
elif [ "$1" = get ]; then elif [ "$1" = get ]; then
curl -is "$URL/$2" | sed -En 's/^location: (.*)/\1/p' curl -is "$URL/$2" | sed -En 's/^[lL]ocation: (.*)/\1/p'
fi fi

View File

@ -49,10 +49,11 @@ int main() {
CROW_ROUTE(app, "/<string>") CROW_ROUTE(app, "/<string>")
.methods(crow::HTTPMethod::Get)( .methods(crow::HTTPMethod::Get)(
[trie](crow::response &res, std::string s) { [trie](crow::response &res, std::string s) {
char *payload = ternarytrie_search(trie, s.c_str()); Entry *entry = ternarytrie_search(trie, s.c_str());
if (payload != NULL) { // TODO check entry type
res.redirect(payload); if (entry != NULL) {
res.redirect(entry->string);
} else { } else {
res.code = 404; res.code = 404;
} }
@ -63,7 +64,8 @@ int main() {
[api_key, base_url, trie](const crow::request req) { [api_key, base_url, trie](const crow::request req) {
AUTH(); AUTH();
char *key = ternarytrie_add_random(trie, req.body.c_str()); Entry *new_entry = entry_new(Redirect, req.body.c_str());
char *key = ternarytrie_add_random(trie, new_entry);
if (key == NULL) { if (key == NULL) {
return crow::response(crow::status::INTERNAL_SERVER_ERROR); return crow::response(crow::status::INTERNAL_SERVER_ERROR);
@ -76,11 +78,12 @@ int main() {
}); });
CROW_ROUTE(app, "/<string>") CROW_ROUTE(app, "/<string>")
.methods(crow::HTTPMethod::Post)( .methods(crow::HTTPMethod::Post)(
[api_key, base_url, trie](const crow::request &req, std::string s) { [api_key, base_url, trie](const crow::request &req, std::string key) {
AUTH(); AUTH();
std::string key = req.body; Entry *new_entry = entry_new(Redirect, req.body.c_str());
bool added = ternarytrie_add(trie, s.c_str(), req.body.c_str());
bool added = ternarytrie_add(trie, key.c_str(), new_entry);
if (!added) { if (!added) {
return crow::response(crow::status::CONFLICT); return crow::response(crow::status::CONFLICT);

View File

@ -23,6 +23,19 @@ static const size_t charset_len = sizeof(charset) - 1;
*/ */
typedef struct ttrie TernaryTrie; typedef struct ttrie TernaryTrie;
typedef enum entry_type {
Redirect,
Paste,
Unknown
} EntryType;
typedef struct entry {
EntryType type;
char *string;
} Entry;
Entry *entry_new(EntryType type, const char *string);
/** /**
* Allocate and initialize an empty Trie. * Allocate and initialize an empty Trie.
* *
@ -47,50 +60,47 @@ int ternarytrie_populate(TernaryTrie* trie, const char* file_path);
void ternarytrie_free(TernaryTrie* trie); void ternarytrie_free(TernaryTrie* trie);
/** /**
* Search whether a string is contained in this trie. * Search for an entry in the trie.
* *
* @param trie * @param trie
* @param key * @param key key representing the entry
* @return pointer to payload string; NULL if not found * @return pointer to entry; NULL if not found
*/ */
char * ternarytrie_search(TernaryTrie* trie, const char* key); Entry *ternarytrie_search(TernaryTrie* trie, const char* key);
/** /**
* Add a string to this trie. * Add a string to this trie.
* *
* @param trie * @param trie
* @param key * @param key key to represent entry with
* @param payload payload to add * @param entry entry to add
* @return true if the trie was changed by this operation, false if it was already present * @return true if the trie was changed by this operation, false if it was already present
*/ */
bool ternarytrie_add(TernaryTrie* trie, const char* key, const char *payload); bool ternarytrie_add(TernaryTrie* trie, const char* key, Entry *entry);
/** /**
* Add a payload by generating a random string as the key. * Add an entry by generating a random string as the key.
* *
* @param trie * @param trie
* @param payload payload to add * @param entry entry to add
* @return the generated key * @return the generated key
*/ */
char *ternarytrie_add_random(TernaryTrie *trie, const char *payload); char *ternarytrie_add_random(TernaryTrie *trie, Entry *entry);
/** /**
* Remove a string from this trie. * Remove an entry from this trie given its key.
*
* Note: strings added to this trie are considered to be "owned" by the caller.
* Removing the string from the trie should not free the string's memory.
* *
* @param trie * @param trie
* @param key * @param key key representing entry
* @return true if the string was present and has been removed, false if it was not present * @return true if the entry was present and has been removed, false if it was not present
*/ */
bool ternarytrie_remove(TernaryTrie* trie, const char *key); bool ternarytrie_remove(TernaryTrie* trie, const char *key);
/** /**
* Returns the number of strings in this trie. * Returns the number of entries in this trie.
* *
* @param trie * @param trie
* @return the number of strings in this trie * @return the number of entries in this trie
*/ */
size_t ternarytrie_size(TernaryTrie* trie); size_t ternarytrie_size(TernaryTrie* trie);

View File

@ -37,7 +37,32 @@ void ternarytrie_free(TernaryTrie *trie) {
free(trie); free(trie);
} }
bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char *payload); bool ternarytrie_add_internal(TernaryTrie *trie, const char *key, Entry *entry);
EntryType entry_type_from_char(char c) {
switch(c) {
case '0': return Redirect;
case '1': return Paste;
default: return Unknown;
}
}
char entry_type_to_char(EntryType et) {
switch (et) {
case Redirect: return '0';
case Paste: return '1';
default: return '\0';
}
}
Entry *entry_new(EntryType type, const char *string) {
Entry *entry = malloc(sizeof(Entry));
entry->type = type;
entry->string = my_strdup(string);
return entry;
}
int ternarytrie_populate(TernaryTrie *trie, const char *file_path) { int ternarytrie_populate(TernaryTrie *trie, const char *file_path) {
trie->file_path = my_strdup(file_path); trie->file_path = my_strdup(file_path);
@ -51,13 +76,16 @@ int ternarytrie_populate(TernaryTrie *trie, const char *file_path) {
// We read in lines of at most 8192 characters (sounds like enough) // We read in lines of at most 8192 characters (sounds like enough)
char buffer[8192]; char buffer[8192];
EntryType type;
Entry *entry;
char *string;
int i, j; int i, j;
int entries = 0; int entries = 0;
while (fgets(buffer, 8192, fp)) { while (fgets(buffer, 8192, fp)) {
// Find index of space character
i = 0; i = 0;
// Move index in buffer until we encounter first space character
while (buffer[i] != ' ') { while (buffer[i] != ' ') {
i++; i++;
} }
@ -65,7 +93,10 @@ int ternarytrie_populate(TernaryTrie *trie, const char *file_path) {
// Split the buffer into two strings, the key and the payload // Split the buffer into two strings, the key and the payload
buffer[i] = '\0'; buffer[i] = '\0';
j = i + 1; type = entry_type_from_char(buffer[i + 1]);
// Skip type character & its surrounding spaces
j = i + 3;
// Now remove the newline character // Now remove the newline character
while (buffer[j] != '\n') { while (buffer[j] != '\n') {
@ -74,7 +105,9 @@ int ternarytrie_populate(TernaryTrie *trie, const char *file_path) {
buffer[j] = '\0'; buffer[j] = '\0';
ternarytrie_add_internal(trie, buffer, buffer + i + 1); entry = entry_new(type, buffer + i + 3);
ternarytrie_add_internal(trie, buffer, entry);
entries++; entries++;
} }
@ -88,11 +121,11 @@ typedef struct searchresult {
TernaryTrieNode *child; TernaryTrieNode *child;
} SearchResult; } SearchResult;
SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) { SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *key) {
SearchResult out = {NULL, NULL}; SearchResult out = {NULL, NULL};
// Edge case for empty string // Edge case for empty string
if (string[0] == DELIMITER) { if (key[0] == DELIMITER) {
if (trie->root->type == 1) { if (trie->root->type == 1) {
out.child = trie->root; out.child = trie->root;
} }
@ -105,7 +138,7 @@ SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) {
TernaryTrieNode **child_ptr; TernaryTrieNode **child_ptr;
do { do {
child_ptr = ttnode_search(*node_ptr, string[i], false); child_ptr = ttnode_search(*node_ptr, key[i], false);
// We don't have to check whether *node_ptr is NULL, because if it was // We don't have to check whether *node_ptr is NULL, because if it was
// NULL, it wouldn't be in the binary tree. // NULL, it wouldn't be in the binary tree.
@ -115,7 +148,7 @@ SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) {
i++; i++;
if (string[i] == DELIMITER || (*child_ptr)->type == 2) { if (key[i] == DELIMITER || (*child_ptr)->type == 2) {
break; break;
} }
@ -123,8 +156,8 @@ SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) {
} while (1); } while (1);
if ((*child_ptr)->type == 2) { if ((*child_ptr)->type == 2) {
if (string[i] != DELIMITER && if (key[i] != DELIMITER &&
strcmp(string + i, (*child_ptr)->ptr.string) == 0) { strcmp(key + i, (*child_ptr)->ptr.string) == 0) {
out.child = *child_ptr; out.child = *child_ptr;
out.parent = *node_ptr; out.parent = *node_ptr;
} }
@ -146,15 +179,15 @@ SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) {
* @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
*/ */
char * ternarytrie_search(TernaryTrie *trie, const char *string) { Entry *ternarytrie_search(TernaryTrie *trie, const char *key) {
pthread_rwlock_rdlock(&trie->lock); pthread_rwlock_rdlock(&trie->lock);
SearchResult res = ternarytrie_search_node(trie, string); SearchResult res = ternarytrie_search_node(trie, key);
char* return_value = NULL; Entry *return_value = NULL;
if (res.child != NULL) { if (res.child != NULL) {
return_value = res.child->payload; return_value = res.child->entry;
} }
pthread_rwlock_unlock(&trie->lock); pthread_rwlock_unlock(&trie->lock);
@ -170,12 +203,12 @@ char * ternarytrie_search(TernaryTrie *trie, const char *string) {
* @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
*/ */
bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char *payload) { bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, Entry *entry) {
// Edge case for empty string // Edge case for empty string
if (string[0] == DELIMITER) { if (string[0] == DELIMITER) {
if (trie->root->type == 0) { if (trie->root->type == 0) {
trie->root->type = 1; trie->root->type = 1;
trie->root->payload = my_strdup(payload); trie->root->entry = entry;
trie->size++; trie->size++;
return true; return true;
@ -222,7 +255,7 @@ bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char
new_node->type = 1; new_node->type = 1;
} }
new_node->payload = my_strdup(payload); new_node->entry = entry;
*node_ptr = new_node; *node_ptr = new_node;
@ -246,21 +279,21 @@ bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char
} }
(*node_ptr)->type = 1; (*node_ptr)->type = 1;
(*node_ptr)->payload = my_strdup(payload); (*node_ptr)->entry = entry;
trie->size++; trie->size++;
return true; return true;
} }
bool ternarytrie_add_persistent(TernaryTrie *trie, const char *string, const char *payload) { bool ternarytrie_add_persistent(TernaryTrie *trie, const char *key, Entry *entry) {
bool return_value = false; bool return_value = false;
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
// already inside a write lock // already inside a write lock
if (ternarytrie_search_node(trie, string).child != NULL) { if (ternarytrie_search_node(trie, key).child != NULL) {
return false; return false;
} }
@ -270,9 +303,11 @@ bool ternarytrie_add_persistent(TernaryTrie *trie, const char *string, const cha
return false; return false;
} }
fputs(string, fp); fputs(key, fp);
fputs(" ", fp); fputs(" ", fp);
fputs(payload, fp); fputc(entry_type_to_char(entry->type), fp);
fputs(" ", fp);
fputs(entry->string, fp);
fputs("\n", fp); fputs("\n", fp);
fclose(fp); fclose(fp);
@ -280,20 +315,20 @@ bool ternarytrie_add_persistent(TernaryTrie *trie, const char *string, const cha
// This function *should* always return true. Otherwise, the function would've // This function *should* always return true. Otherwise, the function would've
// exited because the string was found in the trie. // exited because the string was found in the trie.
return ternarytrie_add_internal(trie, string, payload); return ternarytrie_add_internal(trie, key, entry);
} }
bool ternarytrie_add(TernaryTrie *trie, const char *string, const char *payload) { bool ternarytrie_add(TernaryTrie *trie, const char *key, Entry *entry) {
pthread_rwlock_wrlock(&trie->lock); pthread_rwlock_wrlock(&trie->lock);
bool return_value = ternarytrie_add_persistent(trie, string, payload); bool return_value = ternarytrie_add_persistent(trie, key, entry);
pthread_rwlock_unlock(&trie->lock); pthread_rwlock_unlock(&trie->lock);
return return_value; return return_value;
} }
char* ternarytrie_add_random(TernaryTrie *trie, const char *payload) { char* ternarytrie_add_random(TernaryTrie *trie, Entry *entry) {
pthread_rwlock_wrlock(&trie->lock); pthread_rwlock_wrlock(&trie->lock);
// Generate random key // Generate random key
@ -312,7 +347,7 @@ char* ternarytrie_add_random(TernaryTrie *trie, const char *payload) {
ok = ternarytrie_search_node(trie, key).child == NULL; ok = ternarytrie_search_node(trie, key).child == NULL;
} }
bool res = ternarytrie_add_persistent(trie, key, payload); bool res = ternarytrie_add_persistent(trie, key, entry);
char *return_value; char *return_value;
if (res) { if (res) {

View File

@ -1,8 +1,10 @@
#include "common.c"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "ternarytrie.h"
#include "common.c"
/** /**
* Represents a node of the binary tree contained within each non-leaf * Represents a node of the binary tree contained within each non-leaf
* TernaryTrieNode. * TernaryTrieNode.
@ -30,7 +32,7 @@ typedef struct ttnode {
TernaryTrieInnerNode *root; TernaryTrieInnerNode *root;
char *string; char *string;
} ptr; } ptr;
char *payload; Entry *entry;
// What type of node this is // What type of node this is
// 0: regular non-representing node // 0: regular non-representing node
// 1: regular representing node // 1: regular representing node
@ -101,9 +103,10 @@ void ttnode_free(TernaryTrieNode *node) {
ttinode_free_cascade(node->ptr.root); ttinode_free_cascade(node->ptr.root);
} }
if (node->payload != NULL) { // TODO properly free entry
free(node->payload); /* if (node->payload != NULL) { */
} /* free(node->payload); */
/* } */
free(node); free(node);
} }
@ -218,11 +221,11 @@ void ttnode_split(TernaryTrieNode *node) {
new_node->type = 1; new_node->type = 1;
} }
new_node->payload = node->payload; new_node->entry = node->entry;
node->type = 0; node->type = 0;
node->size = 0; node->size = 0;
node->payload = NULL; node->entry = NULL;
free(node->ptr.string); free(node->ptr.string);
node->ptr.string = NULL; node->ptr.string = NULL;