|
|
|
@ -22,11 +22,27 @@ struct edge {
|
|
|
|
|
|
|
|
|
|
struct trie_entry
|
|
|
|
|
{
|
|
|
|
|
struct edge* edges;
|
|
|
|
|
int count;
|
|
|
|
|
struct {
|
|
|
|
|
struct edge* items;
|
|
|
|
|
int count;
|
|
|
|
|
} edges;
|
|
|
|
|
char* filename;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool load_entry( FILE* f, struct trie_entry* e )
|
|
|
|
|
static void trie_entry_free( struct trie_entry* e )
|
|
|
|
|
{
|
|
|
|
|
if( !e ) { return; }
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
free(e->edges.items[i].label);
|
|
|
|
|
free(e->edges.items[i].value);
|
|
|
|
|
}
|
|
|
|
|
free(e->edges.items);
|
|
|
|
|
free(e->filename);
|
|
|
|
|
free(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool trie_entry_load( FILE* f, struct trie_entry* e )
|
|
|
|
|
{
|
|
|
|
|
struct json_pull_parser jpp = {
|
|
|
|
|
.f = f,
|
|
|
|
@ -37,13 +53,13 @@ static bool load_entry( FILE* f, struct trie_entry* e )
|
|
|
|
|
int save;
|
|
|
|
|
if( !json_pull_parser_begin_object(&jpp,&save) ) { goto failed; }
|
|
|
|
|
|
|
|
|
|
e->count = 0;
|
|
|
|
|
e->edges.count = 0;
|
|
|
|
|
|
|
|
|
|
char* edge_label = NULL;
|
|
|
|
|
while( edge_label = json_pull_parser_read_object_key(&jpp) ) {
|
|
|
|
|
e->count += 1;
|
|
|
|
|
e->edges = realloc( e->edges, sizeof(struct edge) * e->count );
|
|
|
|
|
struct edge* ed = &e->edges[ e->count-1 ];
|
|
|
|
|
e->edges.count += 1;
|
|
|
|
|
e->edges.items = realloc( e->edges.items, sizeof(struct edge) * e->edges.count );
|
|
|
|
|
struct edge* ed = &e->edges.items[ e->edges.count-1 ];
|
|
|
|
|
memset(ed,0,sizeof(*ed));
|
|
|
|
|
|
|
|
|
|
ed->label = edge_label;
|
|
|
|
@ -63,18 +79,30 @@ failed:
|
|
|
|
|
fclose(jpp.f);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
static bool trie_entry_load_from_file( const char* filename, struct trie_entry* e )
|
|
|
|
|
{
|
|
|
|
|
e->filename = strdup(filename);
|
|
|
|
|
|
|
|
|
|
static void save_entry( FILE* f, struct trie_entry* e )
|
|
|
|
|
FILE* f = fopen( filename, "r" );
|
|
|
|
|
if( !f ) { return false; }
|
|
|
|
|
|
|
|
|
|
bool result = trie_entry_load( f, e );
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void trie_entry_save( FILE* f, struct trie_entry* e )
|
|
|
|
|
{
|
|
|
|
|
fprintf( f, "{" );
|
|
|
|
|
for( int i = 0; i < e->count; ++i ) {
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
if( i != 0 ) {
|
|
|
|
|
fprintf( f, ",\n\t" );
|
|
|
|
|
} else {
|
|
|
|
|
fprintf( f, "\n\t" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct edge* ed = &e->edges[i];
|
|
|
|
|
struct edge* ed = &e->edges.items[i];
|
|
|
|
|
json_write_string( f, ed->label );
|
|
|
|
|
fprintf( f, ": " );
|
|
|
|
|
if( ed->count > 1 ) {
|
|
|
|
@ -85,6 +113,19 @@ static void save_entry( FILE* f, struct trie_entry* e )
|
|
|
|
|
}
|
|
|
|
|
fprintf( f, "\n}\n" );
|
|
|
|
|
}
|
|
|
|
|
static bool trie_entry_save_to_file( const char* filename, struct trie_entry* e )
|
|
|
|
|
{
|
|
|
|
|
char tmp_filename[512];
|
|
|
|
|
snprintf( tmp_filename, sizeof(tmp_filename), "%s.tmp", filename );
|
|
|
|
|
FILE* f = fopen(tmp_filename,"w");
|
|
|
|
|
if( !f ) { return false; }
|
|
|
|
|
|
|
|
|
|
trie_entry_save( f, e );
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
rename( tmp_filename, filename );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Escape '/' as "%|" and '%' as "%%"
|
|
|
|
|
static char* escape( const char* str )
|
|
|
|
@ -119,7 +160,7 @@ static char* escape( const char* str )
|
|
|
|
|
}
|
|
|
|
|
*o = '\0';
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int prefix_match( const char* a, const char* b )
|
|
|
|
@ -133,62 +174,119 @@ static int prefix_match( const char* a, const char* b )
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool trie_set( struct trie_entry* e, const char* key, const char* value )
|
|
|
|
|
static bool trie_entry_set( struct trie_entry* e, const char* key, const char* value )
|
|
|
|
|
{
|
|
|
|
|
for( int i = 0; i < e->count; ++i ) {
|
|
|
|
|
struct edge* ed = &e->edges[i];
|
|
|
|
|
int prefix_len = 0;
|
|
|
|
|
struct edge* ed;
|
|
|
|
|
|
|
|
|
|
printf( "key: %s\n", key );
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
ed = &e->edges.items[i];
|
|
|
|
|
printf( "label[%d]: %s\n", i, ed->label );
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
|
|
int prefix_len = prefix_match( key, ed->label );
|
|
|
|
|
prefix_len = prefix_match( key, ed->label );
|
|
|
|
|
if( prefix_len == strlen( ed->label ) ) {
|
|
|
|
|
if( ed->count == 1 ) {
|
|
|
|
|
free(ed->value);
|
|
|
|
|
ed->value = strdup(value);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
printf( "TODO: exact match, go down this node\n" );
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
goto traverse_existing_edge;
|
|
|
|
|
} else if( prefix_len != 0 ) {
|
|
|
|
|
printf( "TODO: create new node" );
|
|
|
|
|
return false;
|
|
|
|
|
goto split_existing_edge;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No match in node, add
|
|
|
|
|
printf( "Adding edge %s\n", key );
|
|
|
|
|
e->count += 1;
|
|
|
|
|
e->edges = realloc( e->edges, sizeof(struct edge) * e->count );
|
|
|
|
|
struct edge* ed = &e->edges[e->count-1];
|
|
|
|
|
ed->label = strdup(key);
|
|
|
|
|
ed->value = strdup(value);
|
|
|
|
|
ed->count = 1;
|
|
|
|
|
goto add_new_edge;
|
|
|
|
|
|
|
|
|
|
add_new_edge:
|
|
|
|
|
{
|
|
|
|
|
printf( "Adding edge %s\n", key );
|
|
|
|
|
e->edges.count += 1;
|
|
|
|
|
e->edges.items = realloc( e->edges.items, sizeof(struct edge) * e->edges.count );
|
|
|
|
|
|
|
|
|
|
struct edge* ed2 = &e->edges.items[e->edges.count-1];
|
|
|
|
|
ed2->label = strdup(key);
|
|
|
|
|
ed2->value = strdup(value);
|
|
|
|
|
ed2->count = 1;
|
|
|
|
|
}
|
|
|
|
|
return trie_entry_save_to_file( e->filename, e );
|
|
|
|
|
|
|
|
|
|
traverse_existing_edge:
|
|
|
|
|
if( ed->count == 1 ) {
|
|
|
|
|
free(ed->value);
|
|
|
|
|
ed->value = strdup(value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf( "TODO: exact match, go down this node\n" );
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
split_existing_edge:
|
|
|
|
|
{
|
|
|
|
|
char* new_prefix = strndup( key, prefix_len );
|
|
|
|
|
char* new_filename = malloc(strlen(e->filename)+prefix_len+1);
|
|
|
|
|
strcpy(new_filename,e->filename);
|
|
|
|
|
strcat(new_filename,new_prefix);
|
|
|
|
|
|
|
|
|
|
// Create the new node
|
|
|
|
|
struct trie_entry* new_e = malloc(sizeof(struct trie_entry));
|
|
|
|
|
memset(new_e,0,sizeof(*new_e));
|
|
|
|
|
new_e->filename = new_filename;
|
|
|
|
|
|
|
|
|
|
new_e->edges.items = malloc( sizeof(struct edge) * 2 );
|
|
|
|
|
new_e->edges.items[0].label = strdup(&ed->label[prefix_len]);
|
|
|
|
|
new_e->edges.items[0].value = ed->value;
|
|
|
|
|
new_e->edges.items[0].count = 1;
|
|
|
|
|
|
|
|
|
|
new_e->edges.items[1].label = strdup(&key[prefix_len]);
|
|
|
|
|
new_e->edges.items[1].value = strdup(value);
|
|
|
|
|
new_e->edges.items[1].count = 1;
|
|
|
|
|
new_e->edges.count = 2;
|
|
|
|
|
|
|
|
|
|
// Save to file
|
|
|
|
|
trie_entry_save_to_file( new_e->filename, new_e );
|
|
|
|
|
trie_entry_free(new_e);
|
|
|
|
|
|
|
|
|
|
// Update the existing edge
|
|
|
|
|
free(ed->label);
|
|
|
|
|
ed->label = new_prefix;
|
|
|
|
|
ed->value = NULL;
|
|
|
|
|
ed->count = 2;
|
|
|
|
|
trie_entry_save_to_file( e->filename, e );
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct trie_entry* load_root_node( const char* filename )
|
|
|
|
|
{
|
|
|
|
|
struct trie_entry* root = malloc(sizeof(*root));
|
|
|
|
|
memset(root,0,sizeof(*root));
|
|
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
|
snprintf( buffer, sizeof(buffer), "%s/%ROOT|", filename );
|
|
|
|
|
trie_entry_load_from_file( buffer, root );
|
|
|
|
|
|
|
|
|
|
return root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ffdb_trie_set( const char* filename, const char* key, const char* value )
|
|
|
|
|
{
|
|
|
|
|
struct trie_entry* root = NULL;
|
|
|
|
|
char* key_escaped = escape(key);
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
|
|
struct trie_entry root;
|
|
|
|
|
memset(&root,0,sizeof(root));
|
|
|
|
|
root = load_root_node(filename);
|
|
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
|
snprintf( buffer, sizeof(buffer), "%s/%ROOT", filename );
|
|
|
|
|
FILE* f = fopen( buffer, "r" );
|
|
|
|
|
if( f ) {
|
|
|
|
|
load_entry( f, &root );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !trie_set( &root, key, value ) ) {
|
|
|
|
|
if( !trie_entry_set( root, key_escaped, value ) ) {
|
|
|
|
|
printf( "Failed to set %s to %s\n", key, value );
|
|
|
|
|
return false;
|
|
|
|
|
goto failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f = fopen( buffer, "w" );
|
|
|
|
|
save_entry( f, &root );
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
result = true;
|
|
|
|
|
cleanup:
|
|
|
|
|
free(key_escaped);
|
|
|
|
|
trie_entry_free(root);
|
|
|
|
|
return result;
|
|
|
|
|
failed:
|
|
|
|
|
result = false;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
char* ffdb_trie_get( const char* filename, const char* key )
|
|
|
|
|
{
|
|
|
|
@ -200,8 +298,23 @@ bool ffdb_trie_remove( const char* filename, const char* key )
|
|
|
|
|
}
|
|
|
|
|
int ffdb_trie_count( const char* filename )
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
struct trie_entry* root = NULL;
|
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
|
|
root = load_root_node(filename);
|
|
|
|
|
if( !root ) { goto failed; }
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < root->edges.count; ++i ) {
|
|
|
|
|
result += root->edges.items[i].count;
|
|
|
|
|
}
|
|
|
|
|
cleanup:
|
|
|
|
|
trie_entry_free(root);
|
|
|
|
|
return result;
|
|
|
|
|
failed:
|
|
|
|
|
result = -1;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffdb_trie_clean( const char* filename )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|