|
|
|
@ -7,7 +7,9 @@
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/file.h>
|
|
|
|
|
|
|
|
|
|
//#define DEBUG
|
|
|
|
|
|
|
|
|
@ -235,12 +237,19 @@ enum {
|
|
|
|
|
trie_entry_set_result_deleted_existing = 3,
|
|
|
|
|
trie_entry_set_result_no_change = 4,
|
|
|
|
|
};
|
|
|
|
|
static int trie_entry_set( struct trie_entry* e, const char* key, const char* value )
|
|
|
|
|
static int trie_entry_set( struct trie_entry* e, const char* key, const char* value, int parent_count )
|
|
|
|
|
{
|
|
|
|
|
int prefix_len = 0;
|
|
|
|
|
struct edge* ed;
|
|
|
|
|
int key_length = strlen(key);
|
|
|
|
|
|
|
|
|
|
int this_count = 0;
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
ed = &e->edges.items[i];
|
|
|
|
|
this_count += ed->count;
|
|
|
|
|
}
|
|
|
|
|
DEBUG_printf( "root this_count = %d, parent_count = %d\n", this_count, parent_count );
|
|
|
|
|
|
|
|
|
|
DEBUG_printf( "key: %s\n", key );
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
ed = &e->edges.items[i];
|
|
|
|
@ -264,10 +273,11 @@ static int trie_entry_set( struct trie_entry* e, const char* key, const char* va
|
|
|
|
|
goto add_new_edge;
|
|
|
|
|
|
|
|
|
|
add_new_edge:
|
|
|
|
|
DEBUG_printf( "add_new_edge %s\n", key );
|
|
|
|
|
if( !value ) {
|
|
|
|
|
DEBUG_printf( "value is null, not adding\n" );
|
|
|
|
|
return trie_entry_set_result_no_change;
|
|
|
|
|
}
|
|
|
|
|
DEBUG_printf( "add_new_edge %s\n", key );
|
|
|
|
|
{
|
|
|
|
|
e->edges.count += 1;
|
|
|
|
|
e->edges.items = realloc( e->edges.items, sizeof(struct edge) * e->edges.count );
|
|
|
|
@ -288,6 +298,9 @@ update_existing:
|
|
|
|
|
}
|
|
|
|
|
ed->value = strdup(value);
|
|
|
|
|
trie_entry_save_to_file( e->filename, e );
|
|
|
|
|
if( this_count > parent_count ) {
|
|
|
|
|
DEBUG_printf( "size mismatch detected. TODO: correct\n" );
|
|
|
|
|
}
|
|
|
|
|
return trie_entry_set_result_updated_existing;
|
|
|
|
|
|
|
|
|
|
delete_existing:
|
|
|
|
@ -316,12 +329,12 @@ traverse_existing_edge:
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
snprintf( filename, sizeof(filename), "%s%s", e->filename, ed->label );
|
|
|
|
|
DEBUG_printf( "Traversing down %s (filename=%s) key=%s, remaining=%s\n", ed->label, filename, key, &key[prefix_len] );
|
|
|
|
|
DEBUG_printf( "Traversing down %s (filename=%s) key=%s, remaining=%s, count=%d\n", ed->label, filename, key, &key[prefix_len], ed->count );
|
|
|
|
|
|
|
|
|
|
struct trie_entry branch;
|
|
|
|
|
memset(&branch,0,sizeof(branch));
|
|
|
|
|
trie_entry_load_from_file( filename, &branch );
|
|
|
|
|
int result = trie_entry_set( &branch, &key[prefix_len], value );
|
|
|
|
|
int result = trie_entry_set( &branch, &key[prefix_len], value, this_count );
|
|
|
|
|
DEBUG_printf( "result=%d\n", result );
|
|
|
|
|
|
|
|
|
|
// update count
|
|
|
|
@ -347,6 +360,15 @@ traverse_existing_edge:
|
|
|
|
|
}
|
|
|
|
|
trie_entry_free_composite( &branch );
|
|
|
|
|
|
|
|
|
|
DEBUG_printf( "Traverse result = %d\n", result );
|
|
|
|
|
/*
|
|
|
|
|
if( parent_count > this_count ) {
|
|
|
|
|
if( result == trie_entry_set_result_updated_existing ) {
|
|
|
|
|
DEBUG_printf( "Fixing up parent count parent_count = %d, this_count = %d\n", parent_count, this_count );
|
|
|
|
|
return trie_entry_set_result_deleted_existing;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
split_existing_edge:
|
|
|
|
@ -408,14 +430,24 @@ static struct trie_entry* load_root_node( const char* filename )
|
|
|
|
|
|
|
|
|
|
bool ffdb_trie_set( const char* filename, const char* key, const char* value )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_printf( "ffdb_trie_set( filename = '%s', key = '%s', value = '%s' )\n", filename, key, value );
|
|
|
|
|
struct trie_entry* root = NULL;
|
|
|
|
|
char* key_escaped = escape(key);
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
|
|
mkdir( filename, 0755 );
|
|
|
|
|
char buffer[512];
|
|
|
|
|
snprintf( buffer,sizeof(buffer), "%s/.lock", filename );
|
|
|
|
|
int fd = open( buffer, O_CREAT );
|
|
|
|
|
flock( fd, LOCK_EX );
|
|
|
|
|
root = load_root_node(filename);
|
|
|
|
|
|
|
|
|
|
int res = trie_entry_set( root, key_escaped, value );
|
|
|
|
|
int this_count = 0;
|
|
|
|
|
for( int i = 0; i < root->edges.count; ++i ) {
|
|
|
|
|
this_count += root->edges.items[i].count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int res = trie_entry_set( root, key_escaped, value, this_count );
|
|
|
|
|
if( !res ) {
|
|
|
|
|
printf( "Failed to set %s to %s\n", key, value );
|
|
|
|
|
goto failed;
|
|
|
|
@ -428,6 +460,8 @@ bool ffdb_trie_set( const char* filename, const char* key, const char* value )
|
|
|
|
|
cleanup:
|
|
|
|
|
free(key_escaped);
|
|
|
|
|
trie_entry_free(root);
|
|
|
|
|
flock( fd, LOCK_UN );
|
|
|
|
|
close(fd);
|
|
|
|
|
return result;
|
|
|
|
|
failed:
|
|
|
|
|
result = false;
|
|
|
|
@ -551,14 +585,18 @@ struct string_array {
|
|
|
|
|
|
|
|
|
|
static void load_items( struct trie_entry* e, int offset, int limit, struct string_array* keys, struct string_array* values )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_printf( "Looking at edges in '%s'\n", e->prefix );
|
|
|
|
|
for( int i = 0; i < e->edges.count; ++i ) {
|
|
|
|
|
struct edge* ed = &e->edges.items[i];
|
|
|
|
|
DEBUG_printf( "ed = { .label = '%s', .value = '%s', .count = %d }\n", ed->label, ed->value, ed->count );
|
|
|
|
|
if( ed->count <= offset ) {
|
|
|
|
|
printf( "Skipping all for '%s' offset=%d\n", e->prefix, offset );
|
|
|
|
|
// branch - skip all
|
|
|
|
|
offset -= ed->count;
|
|
|
|
|
} else if( ed->count > 1 ) {
|
|
|
|
|
// branch - skip part, include part
|
|
|
|
|
int branch_count = ed->count - offset;
|
|
|
|
|
DEBUG_printf( "Skipping part of '%s', offset=%d, branch_count=%d, limit=%d\n", e->prefix, offset, branch_count, limit );
|
|
|
|
|
if( branch_count > limit ) {
|
|
|
|
|
branch_count = limit;
|
|
|
|
|
}
|
|
|
|
@ -577,6 +615,7 @@ static void load_items( struct trie_entry* e, int offset, int limit, struct stri
|
|
|
|
|
limit -= branch_count;
|
|
|
|
|
} else if( limit > 0 ) {
|
|
|
|
|
// leaf - include
|
|
|
|
|
DEBUG_printf( "Loading from '%s', offset=%d\n", e->prefix, offset );
|
|
|
|
|
if( keys ) {
|
|
|
|
|
char* str = aformat( "%s%s", e->prefix, ed->label );
|
|
|
|
|
array_append( keys, sizeof(str), &str );
|
|
|
|
@ -588,6 +627,7 @@ static void load_items( struct trie_entry* e, int offset, int limit, struct stri
|
|
|
|
|
limit -= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
DEBUG_printf( "Out of edges in '%s'\n", e->prefix );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffdb_trie_list( const char* filename, int offset, int limit, void* keys_ptr, void* values_ptr )
|
|
|
|
@ -599,28 +639,34 @@ void ffdb_trie_list( const char* filename, int offset, int limit, void* keys_pt
|
|
|
|
|
}
|
|
|
|
|
bool ffdb_trie_get_index( const char* filename, int offset, char** key, char** value )
|
|
|
|
|
{
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
|
|
/// Convenience function to get single item at offset
|
|
|
|
|
struct string_array keys;
|
|
|
|
|
struct string_array values;
|
|
|
|
|
memset(&keys,0,sizeof(keys));
|
|
|
|
|
memset(&values,0,sizeof(values));
|
|
|
|
|
|
|
|
|
|
ffdb_trie_list( filename, offset, 1, key, value ? &values : NULL );
|
|
|
|
|
ffdb_trie_list( filename, offset, 1, &keys, value ? &values : NULL );
|
|
|
|
|
if( keys.count == 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( key ) {
|
|
|
|
|
*key = keys.items[0];
|
|
|
|
|
result = false;
|
|
|
|
|
} else {
|
|
|
|
|
free( keys.items[0] );
|
|
|
|
|
}
|
|
|
|
|
if( value ) {
|
|
|
|
|
*value = values.items[0];
|
|
|
|
|
result = true;
|
|
|
|
|
|
|
|
|
|
if( key ) {
|
|
|
|
|
*key = keys.items[0];
|
|
|
|
|
} else {
|
|
|
|
|
free( keys.items[0] );
|
|
|
|
|
}
|
|
|
|
|
if( value ) {
|
|
|
|
|
*value = values.items[0];
|
|
|
|
|
} else {
|
|
|
|
|
free( values.items[0] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(keys.items);
|
|
|
|
|
free(values.items);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|