diff --git a/test.c b/test.c index be89660..7aca1d5 100644 --- a/test.c +++ b/test.c @@ -3,14 +3,19 @@ #include "trie.h" #include +#include bool ffdb_test() { const char* d = "/tmp/ffdb-test"; + system( "rm -Rvf /tmp/ffdb-test" ); mkdir( d, 0755 ); - ffdb_trie_set( d, "https://example.com/actor", "T" ); - //ffdb_trie_set( d, "https://apogee.polaris-1.work/owner/actor", "T" ); - return false; + bool result = true; + + result &= ffdb_trie_set( d, "https://example.com/actor", "T" ); + result &= ffdb_trie_set( d, "https://apogee.polaris-1.work/owner/actor", "T" ); + + return result; } diff --git a/trie.c b/trie.c index 35507cc..dff3428 100644 --- a/trie.c +++ b/trie.c @@ -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 ) { }