#include "trie.h" #include "json/json.h" #include #include struct edge { char* label; char* value; int count; }; /* { "k": "test", "t": 2, "z": 7 } */ struct trie_entry { struct { struct edge* items; int count; } edges; char* filename; }; 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, .curr_state = jpp_initial_state, }; if( !jpp.f ) { return false; } int save; if( !json_pull_parser_begin_object(&jpp,&save) ) { goto failed; } e->edges.count = 0; char* edge_label = NULL; while( edge_label = json_pull_parser_read_object_key(&jpp) ) { 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; ed->count = 1; if( !json_pull_parser_read_int( &jpp, &ed->count ) ) { char* value = json_pull_parser_read_string(&jpp); if( !value ) { goto failed; } ed->value = value; } } if( !json_pull_parser_end_object(&jpp,&save) ) { goto failed; } return true; failed: fclose(jpp.f); return false; } static bool trie_entry_load_from_file( const char* filename, struct trie_entry* e ) { e->filename = strdup(filename); 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->edges.count; ++i ) { if( i != 0 ) { fprintf( f, ",\n\t" ); } else { fprintf( f, "\n\t" ); } struct edge* ed = &e->edges.items[i]; json_write_string( f, ed->label ); fprintf( f, ": " ); if( ed->count > 1 ) { fprintf( f, "%d", ed->count ); } else { json_write_string( f, ed->value ); } } 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 ) { int size = 0; for( const char* i = str; *i; ++i ) { switch( *i ) { case '/': size += 2; break; case '%': size += 2; break; default: size += 1; break; } } char* result = malloc(size+1); char* o = result; for( const char* i = str; *i; ++i ) { switch( *i ) { case '/': o[0] = '%'; o[1] = '|'; o += 2; break; case '%': o[0] = '%'; o[1] = '%'; o += 2; break; default: *o = *i; ++o; } } *o = '\0'; return result; } static int prefix_match( const char* a, const char* b ) { int res = 0; while( *a && *b && *a == *b ) { a += 1; b += 1; res += 1; } return res; } static bool trie_entry_set( struct trie_entry* e, const char* key, const char* value ) { 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); prefix_len = prefix_match( key, ed->label ); if( prefix_len == strlen( ed->label ) ) { goto traverse_existing_edge; } else if( prefix_len != 0 ) { goto split_existing_edge; } } 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; root = load_root_node(filename); if( !trie_entry_set( root, key_escaped, value ) ) { printf( "Failed to set %s to %s\n", key, value ); goto failed; } 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 ) { return NULL; } bool ffdb_trie_remove( const char* filename, const char* key ) { return false; } int ffdb_trie_count( const char* filename ) { 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 ) { }