From 911bce88c95b8cab78d94bc076d07f162bc835ff Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 12 May 2008 17:54:57 +0000 Subject: [PATCH] add benc-to-json conversion + unit tests. --- libtransmission/bencode-test.c | 29 +++++---- libtransmission/bencode.c | 116 ++++++++++++++++++++++----------- libtransmission/bencode.h | 2 +- 3 files changed, 95 insertions(+), 52 deletions(-) diff --git a/libtransmission/bencode-test.c b/libtransmission/bencode-test.c index 1d7a779d1..7f14f4f6e 100644 --- a/libtransmission/bencode-test.c +++ b/libtransmission/bencode-test.c @@ -271,12 +271,12 @@ testParse( void ) } static int -testPHPSnippet( const char * benc_str, const char * expected ) +testJSONSnippet( const char * benc_str, const char * expected ) { tr_benc top; char * serialized; tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); - serialized = tr_bencSaveAsSerializedPHP( &top, NULL ); + serialized = tr_bencSaveAsJSON( &top, NULL ); check( !strcmp( serialized, expected ) ); tr_free( serialized ); tr_bencFree( &top ); @@ -284,25 +284,30 @@ testPHPSnippet( const char * benc_str, const char * expected ) } static int -testPHP( void ) +testJSON( void ) { int val; const char * benc_str; const char * expected; benc_str = "i6e"; - expected = "i:6;"; - if(( val = testPHPSnippet( benc_str, expected ))) + expected = "6"; + if(( val = testJSONSnippet( benc_str, expected ))) return val; - benc_str = "d3:cow3:moo4:spam4:eggse"; - expected = "a:2:{s:3:\"cow\";s:3:\"moo\";s:4:\"spam\";s:4:\"eggs\";}"; - if(( val = testPHPSnippet( benc_str, expected ))) + benc_str = "d5:helloi1e5:worldi2ee"; + expected = "{ \"hello\": 1, \"world\": 2 }"; + if(( val = testJSONSnippet( benc_str, expected ))) return val; - benc_str = "l3:cow3:moo4:spam4:eggse"; - expected = "a:4:{i:0;s:3:\"cow\";i:1;s:3:\"moo\";i:2;s:4:\"spam\";i:3;s:4:\"eggs\";}"; - if(( val = testPHPSnippet( benc_str, expected ))) + benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ee"; + expected = "{ \"foo\": [ 1, 2, 3 ], \"hello\": 1, \"world\": 2 }"; + if(( val = testJSONSnippet( benc_str, expected ))) + return val; + + benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eee"; + expected = "{ \"foo\": [ 1, 2, 3, { \"a\": 0 } ], \"hello\": 1, \"world\": 2 }"; + if(( val = testJSONSnippet( benc_str, expected ))) return val; return 0; @@ -354,7 +359,7 @@ main( void ) if(( i = testParse( ))) return i; - if(( i = testPHP( ))) + if(( i = testJSON( ))) return i; if(( i = testStackSmash( ))) diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c index d76bbcbb6..300429b35 100644 --- a/libtransmission/bencode.c +++ b/libtransmission/bencode.c @@ -1012,95 +1012,133 @@ tr_bencPrint( const tr_benc * val ) struct ParentState { - int type; - int index; + int bencType; + int childIndex; + int childCount; }; -struct phpWalk +struct jsonWalk { tr_list * parents; struct evbuffer * out; }; static void -phpChildFunc( struct phpWalk * data ) +jsonChildFunc( struct jsonWalk * data ) { if( data->parents ) { struct ParentState * parentState = data->parents->data; - if( parentState->type == TYPE_LIST ) - evbuffer_add_printf( data->out, "i:%d;", parentState->index++ ); + switch( parentState->bencType ) + { + case TYPE_DICT: { + const int i = parentState->childIndex++; + if( ! ( i % 2 ) ) + evbuffer_add_printf( data->out, ": " ); + else if( parentState->childIndex < parentState->childCount ) + evbuffer_add_printf( data->out, ", " ); + else + evbuffer_add_printf( data->out, " }" ); + break; + } + + case TYPE_LIST: { + if( ++parentState->childIndex < parentState->childCount ) + evbuffer_add_printf( data->out, ", " ); + else + evbuffer_add_printf( data->out, " ]" ); + break; + } + + default: + break; + } } } static void -phpPushParent( struct phpWalk * data, int type ) +jsonPushParent( struct jsonWalk * data, const tr_benc * benc ) { struct ParentState * parentState = tr_new( struct ParentState, 1 ); - parentState->type = type; - parentState->index = 0; + parentState->bencType = benc->type; + parentState->childIndex = 0; + parentState->childCount = benc->val.l.count; tr_list_prepend( &data->parents, parentState ); } static void -phpPopParent( struct phpWalk * data ) +jsonPopParent( struct jsonWalk * data ) { tr_free( tr_list_pop_front( &data->parents ) ); } static void -phpIntFunc( const tr_benc * val, void * vdata ) +jsonIntFunc( const tr_benc * val, void * vdata ) { - struct phpWalk * data = vdata; - phpChildFunc( data ); - evbuffer_add_printf( data->out, "i:%"PRId64";", val->val.i ); + struct jsonWalk * data = vdata; + evbuffer_add_printf( data->out, "%"PRId64, val->val.i ); + jsonChildFunc( data ); } static void -phpStringFunc( const tr_benc * val, void * vdata ) +jsonStringFunc( const tr_benc * val, void * vdata ) { - struct phpWalk * data = vdata; - phpChildFunc( data ); - evbuffer_add_printf( data->out, "s:%d:\"%s\";", val->val.s.i, val->val.s.s ); + struct jsonWalk * data = vdata; + const char *it, *end; + evbuffer_add_printf( data->out, "\"" ); + for( it=val->val.s.s, end=it+val->val.s.i; it!=end; ++it ) + { + switch( *it ) { + case '"' : evbuffer_add_printf( data->out, "\\\"" ); break; + case '/' : evbuffer_add_printf( data->out, "\\/" ); break; + case '\b': evbuffer_add_printf( data->out, "\\b" ); break; + case '\f': evbuffer_add_printf( data->out, "\\f" ); break; + case '\n': evbuffer_add_printf( data->out, "\\n" ); break; + case '\r': evbuffer_add_printf( data->out, "\\n" ); break; + case '\t': evbuffer_add_printf( data->out, "\\n" ); break; + case '\\': evbuffer_add_printf( data->out, "\\\\" ); break; + default: evbuffer_add_printf( data->out, "%c", *it ); break; + } + } + evbuffer_add_printf( data->out, "\"" ); + jsonChildFunc( data ); } static void -phpDictBeginFunc( const tr_benc * val, void * vdata ) +jsonDictBeginFunc( const tr_benc * val, void * vdata ) { - struct phpWalk * data = vdata; - phpChildFunc( data ); - phpPushParent( data, TYPE_DICT ); - evbuffer_add_printf( data->out, "a:%d:{", val->val.l.count/2 ); + struct jsonWalk * data = vdata; + jsonPushParent( data, val ); + evbuffer_add_printf( data->out, "{ " ); } static void -phpListBeginFunc( const tr_benc * val, void * vdata ) +jsonListBeginFunc( const tr_benc * val, void * vdata ) { - struct phpWalk * data = vdata; - phpChildFunc( data ); - phpPushParent( data, TYPE_LIST ); - evbuffer_add_printf( data->out, "a:%d:{", val->val.l.count ); + struct jsonWalk * data = vdata; + jsonPushParent( data, val ); + evbuffer_add_printf( data->out, "[ " ); } static void -phpContainerEndFunc( const tr_benc * val UNUSED, void * vdata ) +jsonContainerEndFunc( const tr_benc * val UNUSED, void * vdata ) { - struct phpWalk * data = vdata; - phpPopParent( data ); - evbuffer_add_printf( data->out, "}" ); + struct jsonWalk * data = vdata; + jsonPopParent( data ); + jsonChildFunc( data ); } char* -tr_bencSaveAsSerializedPHP( const tr_benc * top, int * len ) +tr_bencSaveAsJSON( const tr_benc * top, int * len ) { char * ret; struct WalkFuncs walkFuncs; - struct phpWalk data; + struct jsonWalk data; data.out = evbuffer_new( ); data.parents = NULL; - walkFuncs.intFunc = phpIntFunc; - walkFuncs.stringFunc = phpStringFunc; - walkFuncs.dictBeginFunc = phpDictBeginFunc; - walkFuncs.listBeginFunc = phpListBeginFunc; - walkFuncs.containerEndFunc = phpContainerEndFunc; + walkFuncs.intFunc = jsonIntFunc; + walkFuncs.stringFunc = jsonStringFunc; + walkFuncs.dictBeginFunc = jsonDictBeginFunc; + walkFuncs.listBeginFunc = jsonListBeginFunc; + walkFuncs.containerEndFunc = jsonContainerEndFunc; bencWalk( top, &walkFuncs, &data ); diff --git a/libtransmission/bencode.h b/libtransmission/bencode.h index ca0fe5ee0..7affbdeeb 100644 --- a/libtransmission/bencode.h +++ b/libtransmission/bencode.h @@ -116,7 +116,7 @@ tr_benc * tr_bencDictAddDict( tr_benc * dict, const char * key, int reserveCo tr_benc * tr_bencDictAddRaw( tr_benc * dict, const char * key, const void *, size_t len ); char* tr_bencSave( const tr_benc * val, int * len ); -char* tr_bencSaveAsSerializedPHP( const tr_benc * top, int * len ); +char* tr_bencSaveAsJSON( const tr_benc * top, int * len ); int tr_bencSaveFile( const char * filename, const tr_benc * ); int tr_bencLoadFile( const char * filename, tr_benc * );