feat: pave way for pastebin integration
parent
689a878978
commit
f1ec643f80
|
@ -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
|
||||||
|
|
17
src/main.cpp
17
src/main.cpp
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue