(trunk) Use proper notation for json floating-point and bool types. For backwards compatability, still allow old-style printf strings as doubles, and 0s and 1s as bools.

This commit is contained in:
Charles Kerr 2009-04-05 17:52:21 +00:00
parent bf3bdda68f
commit 8a8c8711d8
3 changed files with 196 additions and 89 deletions

View File

@ -13,15 +13,6 @@
2. Message Format
Messages are formatted in a subset of JSON easily represented
as bencoded data. Arrays, objects, strings, and whole numbers
all have one-to-one mappings between JSON and benc.
Booleans and floating-point numbers are also used in the JSON messages.
Those two types aren't native to benc, so they're encoded this way:
Booleans are encoded as numbers where 0 is false and 1 is true.
Floating-point numbers are represented as strings.
Messages are formatted as objects. There are two types:
requests (described in 2.1) and responses (described in 2.2).
@ -98,19 +89,19 @@
string | value type & description
----------------------------------+-------------------------------------------------
"downloadLimit" | number maximum download speed (in K/s)
"downloadLimited" | 'boolean' true if "downloadLimit" is honored
"downloadLimited" | boolean true if "downloadLimit" is honored
"files-wanted" | array indices of file(s) to download
"files-unwanted" | array indices of file(s) to not download
"honorsSessionLimits" | 'boolean' true if session upload limits are honored
"honorsSessionLimits" | boolean true if session upload limits are honored
"ids" | array torrent list, as described in 3.1
"peer-limit" | number maximum number of peers
"priority-high" | array indices of high-priority file(s)
"priority-low" | array indices of low-priority file(s)
"priority-normal" | array indices of normal-priority file(s)
"seedRatioLimit" | 'double' session seeding ratio
"seedRatioLimit" | double session seeding ratio
"seedRatioMode" | number which ratio to use. See tr_ratiolimit
"uploadLimit" | number maximum upload speed (in K/s)
"uploadLimited" | 'boolean' true if "uploadLimit" is honored
"uploadLimited" | boolean true if "uploadLimit" is honored
Just as an empty "ids" value is shorthand for "all ids", using an empty array
for "files-wanted", "files-unwanted", "priority-high", "priority-low", or
@ -151,7 +142,7 @@
downloadedEver | number | tr_stat
downloaders | number | tr_stat
downloadLimit | number | tr_torrent
downloadLimited | 'boolean' | tr_torrent
downloadLimited | boolean | tr_torrent
error | number | tr_stat
errorString | number | tr_stat
eta | number | tr_stat
@ -160,9 +151,9 @@
hashString | string | tr_info
haveUnchecked | number | tr_stat
haveValid | number | tr_stat
honorsSessionLimits | 'boolean' | tr_torrent
honorsSessionLimits | boolean | tr_torrent
id | number | tr_torrent
isPrivate | 'boolean' | tr_torrent
isPrivate | boolean | tr_torrent
lastAnnounceTime | number | tr_stat
lastScrapeTime | number | tr_stat
leechers | number | tr_stat
@ -179,19 +170,19 @@
peersGettingFromUs | number | tr_stat
peersKnown | number | tr_stat
peersSendingToUs | number | tr_stat
percentDone | 'double' | tr_stat
percentDone | double | tr_stat
pieces | string (see below) | tr_torrent
pieceCount | tnumber | tr_info
pieceSize | tnumber | tr_info
priorities | array (see below) | n/a
ratio | 'double' | tr_stat
ratio | double | tr_stat
rateDownload (B/s) | number | tr_stat
rateUpload (B/s) | number | tr_stat
recheckProgress | 'double' | tr_stat
recheckProgress | double | tr_stat
scrapeResponse | string | tr_stat
scrapeURL | string | tr_stat
seeders | number | tr_stat
seedRatioLimit | 'double' | tr_torrent
seedRatioLimit | double | tr_torrent
seedRatioMode | number | tr_ratiolimit
sizeWhenDone | number | tr_stat
startDate | number | tr_stat
@ -203,8 +194,8 @@
torrentFile | string | tr_info
uploadedEver | number | tr_stat
uploadLimit | number | tr_torrent
uploadLimited | 'boolean' | tr_torrent
uploadRatio | 'double' | tr_stat
uploadLimited | boolean | tr_torrent
uploadRatio | double | tr_stat
wanted | array (see below) | n/a
webseeds | array (see below) | n/a
webseedsSendingToUs | number | tr_stat
@ -223,23 +214,23 @@
| each containing: |
+-------------------------+------------+
| bytesCompleted | number | tr_torrent
| wanted | 'boolean' | tr_info
| wanted | boolean | tr_info
| priority | number | tr_info
-----------------------+--------------------------------------+
peers | array of objects, each containing: |
+-------------------------+------------+
| address | string | tr_peer_stat
| clientName | string | tr_peer_stat
| clientIsChoked | 'boolean' | tr_peer_stat
| clientIsInterested | 'boolean' | tr_peer_stat
| isDownloadingFrom | 'boolean' | tr_peer_stat
| isEncrypted | 'boolean' | tr_peer_stat
| isIncoming | 'boolean' | tr_peer_stat
| isUploadingTo | 'boolean' | tr_peer_stat
| peerIsChoked | 'boolean' | tr_peer_stat
| peerIsInterested | 'boolean' | tr_peer_stat
| clientIsChoked | boolean | tr_peer_stat
| clientIsInterested | boolean | tr_peer_stat
| isDownloadingFrom | boolean | tr_peer_stat
| isEncrypted | boolean | tr_peer_stat
| isIncoming | boolean | tr_peer_stat
| isUploadingTo | boolean | tr_peer_stat
| peerIsChoked | boolean | tr_peer_stat
| peerIsInterested | boolean | tr_peer_stat
| port | number | tr_peer_stat
| progress | 'double' | tr_peer_stat
| progress | double | tr_peer_stat
| rateToClient (B/s) | number | tr_peer_stat
| rateToPeer (B/s) | number | tr_peer_stat
-----------------------+--------------------------------------+
@ -323,7 +314,7 @@
"download-dir" | string path to download the torrent to
"filename" | string filename or URL of the .torrent file
"metainfo" | string base64-encoded .torrent content
"paused" | 'boolean' if true, don't start the torrent
"paused" | boolean if true, don't start the torrent
"peer-limit" | number maximum number of peers
"files-wanted" | array indices of file(s) to download
"files-unwanted" | array indices of file(s) to not download
@ -347,7 +338,7 @@
string | value type & description
---------------------------+-------------------------------------------------
"ids" | array torrent list, as described in 3.1
"delete-local-data" | 'boolean' delete local data. (default: false)
"delete-local-data" | boolean delete local data. (default: false)
Response arguments: none
@ -359,30 +350,30 @@
string | value type & description
---------------------------+-------------------------------------------------
"alt-speed-down" | number max global download speed (in K/s)
"alt-speed-enabled" | 'boolean' true means use the alt speeds
"alt-speed-enabled" | boolean true means use the alt speeds
"alt-speed-time-begin" | number when to turn on alt speeds (units: minutes after midnight)
"alt-speed-time-enabled" | 'boolean' true means the scheduled on/off times are used
"alt-speed-time-enabled" | boolean true means the scheduled on/off times are used
"alt-speed-time-end" | number when to turn off alt speeds (units: same)
"alt-speed-time-day" | number what day(s) to turn on alt speeds (look at tr_sched_day)
"alt-speed-up" | number max global upload speed (in K/s)
"blocklist-enabled" | 'boolean' true means enabled
"blocklist-enabled" | boolean true means enabled
"blocklist-size" | number number of rules in the blocklist
"encryption" | string "required", "preferred", "tolerated"
"download-dir" | string default path to download torrents
"peer-limit-global" | number maximum global number of peers
"peer-limit-per-torrent" | number maximum global number of peers
"pex-enabled" | 'boolean' true means allow pex in public torrents
"pex-enabled" | boolean true means allow pex in public torrents
"peer-port" | number port number
"peer-port-random-on-start"| 'boolean' true means pick a random peer port on launch
"port-forwarding-enabled" | 'boolean' true means enabled
"peer-port-random-on-start"| boolean true means pick a random peer port on launch
"port-forwarding-enabled" | boolean true means enabled
"rpc-version" | number the current RPC API version
"rpc-version-minimum" | number the minimum RPC API version supported
"seedRatioLimit" | 'double' the default seed ratio for torrents to use
"seedRatioLimited" | 'boolean' true if seedRatioLimit is honored by default
"seedRatioLimit" | double the default seed ratio for torrents to use
"seedRatioLimited" | boolean true if seedRatioLimit is honored by default
"speed-limit-down" | number max global download speed (in K/s)
"speed-limit-down-enabled" | 'boolean' true means enabled
"speed-limit-down-enabled" | boolean true means enabled
"speed-limit-up" | number max global upload speed (in K/s)
"speed-limit-up-enabled" | 'boolean' true means enabled
"speed-limit-up-enabled" | boolean true means enabled
"version" | string long version string "$version ($revision)"
"rpc-version" indicates the RPC interface version supported by the RPC server.

View File

@ -414,10 +414,17 @@ tr_bool
tr_bencGetInt( const tr_benc * val,
int64_t * setme )
{
const tr_bool success = tr_bencIsInt( val );
tr_bool success = FALSE;
if( success && setme )
*setme = val->val.i;
if( !success && (( success = tr_bencIsInt( val ))))
if( setme )
*setme = val->val.i;
if( !success && (( success = tr_bencIsBool( val )))) {
fprintf( stderr, "warning: reading bool as an int\n" );
if( setme )
*setme = val->val.b ? 1 : 0;
}
return success;
}
@ -439,6 +446,9 @@ tr_bencGetBool( const tr_benc * val, tr_bool * setme )
{
tr_bool success = FALSE;
if(( success = tr_bencIsBool( val )))
*setme = val->val.b;
if( !success && tr_bencIsInt( val ) )
if(( success = ( val->val.i==0 || val->val.i==1 ) ))
*setme = val->val.i!=0;
@ -455,6 +465,12 @@ tr_bencGetReal( const tr_benc * val, double * setme )
{
tr_bool success = FALSE;
if( !success && (( success = tr_bencIsReal( val ))))
*setme = val->val.d;
if( !success && (( success = tr_bencIsInt( val ))))
*setme = val->val.i;
if( !success && tr_bencIsString(val) )
{
char * endptr;
@ -472,11 +488,6 @@ tr_bencGetReal( const tr_benc * val, double * setme )
*setme = d;
}
if( !success && tr_bencIsInt(val) )
{
success = TRUE;
*setme = val->val.i;
}
return success;
}
@ -598,43 +609,52 @@ tr_bencInitStr( tr_benc * val,
}
void
tr_bencInitInt( tr_benc * val,
int64_t num )
tr_bencInitBool( tr_benc * b, int value )
{
tr_bencInit( val, TYPE_INT );
val->val.i = num;
tr_bencInit( b, TYPE_BOOL );
b->val.b = value != 0;
}
void
tr_bencInitReal( tr_benc * b, double value )
{
tr_bencInit( b, TYPE_REAL );
b->val.d = value;
}
void
tr_bencInitInt( tr_benc * b, int64_t value )
{
tr_bencInit( b, TYPE_INT );
b->val.i = value;
}
int
tr_bencInitList( tr_benc * val,
size_t reserveCount )
tr_bencInitList( tr_benc * b, size_t reserveCount )
{
tr_bencInit( val, TYPE_LIST );
return tr_bencListReserve( val, reserveCount );
tr_bencInit( b, TYPE_LIST );
return tr_bencListReserve( b, reserveCount );
}
int
tr_bencListReserve( tr_benc * val,
size_t count )
tr_bencListReserve( tr_benc * b, size_t count )
{
assert( tr_bencIsList( val ) );
return makeroom( val, count );
assert( tr_bencIsList( b ) );
return makeroom( b, count );
}
int
tr_bencInitDict( tr_benc * val,
size_t reserveCount )
tr_bencInitDict( tr_benc * b, size_t reserveCount )
{
tr_bencInit( val, TYPE_DICT );
return tr_bencDictReserve( val, reserveCount );
tr_bencInit( b, TYPE_DICT );
return tr_bencDictReserve( b, reserveCount );
}
int
tr_bencDictReserve( tr_benc * val,
size_t reserveCount )
tr_bencDictReserve( tr_benc * b, size_t reserveCount )
{
assert( tr_bencIsDict( val ) );
return makeroom( val, reserveCount * 2 );
assert( tr_bencIsDict( b ) );
return makeroom( b, reserveCount * 2 );
}
tr_benc *
@ -716,16 +736,14 @@ tr_bencDictAdd( tr_benc * dict,
return itemval;
}
tr_benc*
tr_bencDictAddInt( tr_benc * dict,
const char * key,
int64_t val )
static tr_benc*
dictFindOrAdd( tr_benc * dict, const char * key, int type )
{
tr_benc * child;
/* see if it already exists, and if so, try to reuse it */
if(( child = tr_bencDictFind( dict, key ))) {
if( !tr_bencIsInt( child ) ) {
if( !tr_bencIsType( child, type ) ) {
tr_bencDictRemove( dict, key );
child = NULL;
}
@ -735,18 +753,33 @@ tr_bencDictAddInt( tr_benc * dict,
if( child == NULL )
child = tr_bencDictAdd( dict, key );
/* set it */
tr_bencInitInt( child, val );
return child;
}
tr_benc*
tr_bencDictAddInt( tr_benc * dict,
const char * key,
int64_t val )
{
tr_benc * child = dictFindOrAdd( dict, key, TYPE_INT );
tr_bencInitInt( child, val );
return child;
}
tr_benc*
tr_bencDictAddBool( tr_benc * dict, const char * key, tr_bool val )
{
assert( tr_isBool( val ) );
tr_benc * child = dictFindOrAdd( dict, key, TYPE_BOOL );
tr_bencInitBool( child, val );
return child;
}
return tr_bencDictAddInt( dict, key, val );
tr_benc*
tr_bencDictAddReal( tr_benc * dict, const char * key, double val )
{
tr_benc * child = dictFindOrAdd( dict, key, TYPE_REAL );
tr_bencInitReal( child, val );
return child;
}
tr_benc*
@ -774,9 +807,11 @@ tr_bencDictAddStr( tr_benc * dict, const char * key, const char * val )
return child;
}
#if 0
tr_benc*
tr_bencDictAddReal( tr_benc * dict, const char * key, double d )
{
ccc
char buf[128];
char * locale;
@ -789,6 +824,7 @@ tr_bencDictAddReal( tr_benc * dict, const char * key, double d )
return tr_bencDictAddStr( dict, key, buf );
}
#endif
tr_benc*
tr_bencDictAddList( tr_benc * dict,
@ -960,6 +996,8 @@ typedef void ( *BencWalkFunc )( const tr_benc * val, void * user_data );
struct WalkFuncs
{
BencWalkFunc intFunc;
BencWalkFunc boolFunc;
BencWalkFunc realFunc;
BencWalkFunc stringFunc;
BencWalkFunc dictBeginFunc;
BencWalkFunc listBeginFunc;
@ -1011,6 +1049,14 @@ bencWalk( const tr_benc * top,
walkFuncs->intFunc( val, user_data );
break;
case TYPE_BOOL:
walkFuncs->boolFunc( val, user_data );
break;
case TYPE_REAL:
walkFuncs->realFunc( val, user_data );
break;
case TYPE_STR:
walkFuncs->stringFunc( val, user_data );
break;
@ -1050,6 +1096,31 @@ saveIntFunc( const tr_benc * val,
evbuffer_add_printf( evbuf, "i%" PRId64 "e", val->val.i );
}
static void
saveBoolFunc( const tr_benc * val, void * evbuf )
{
evbuffer_add_printf( evbuf, "i%de", val->val.b?1:0 );
}
static void
saveRealFunc( const tr_benc * val, void * evbuf )
{
char buf[128];
char * locale;
size_t len;
/* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
setlocale( LC_NUMERIC, "POSIX" );
tr_snprintf( buf, sizeof( buf ), "%f", val->val.d );
setlocale( LC_NUMERIC, locale );
tr_free( locale );
len = strlen( buf );
evbuffer_add_printf( evbuf, "%lu:", (unsigned long)buf );
evbuffer_add( evbuf, buf, len );
}
static void
saveStringFunc( const tr_benc * val,
void * vevbuf )
@ -1090,6 +1161,8 @@ tr_bencSave( const tr_benc * top,
struct evbuffer * out = tr_getBuffer( );
walkFuncs.intFunc = saveIntFunc;
walkFuncs.boolFunc = saveBoolFunc;
walkFuncs.realFunc = saveRealFunc;
walkFuncs.stringFunc = saveStringFunc;
walkFuncs.dictBeginFunc = saveDictBeginFunc;
walkFuncs.listBeginFunc = saveListBeginFunc;
@ -1136,6 +1209,8 @@ tr_bencFree( tr_benc * val )
struct WalkFuncs walkFuncs;
walkFuncs.intFunc = freeDummyFunc;
walkFuncs.boolFunc = freeDummyFunc;
walkFuncs.realFunc = freeDummyFunc;
walkFuncs.stringFunc = freeStringFunc;
walkFuncs.dictBeginFunc = freeContainerBeginFunc;
walkFuncs.listBeginFunc = freeContainerBeginFunc;
@ -1235,6 +1310,31 @@ jsonIntFunc( const tr_benc * val,
jsonChildFunc( data );
}
static void
jsonBoolFunc( const tr_benc * val, void * vdata )
{
struct jsonWalk * data = vdata;
evbuffer_add_printf( data->out, "%s", (val->val.b?"true":"false") );
jsonChildFunc( data );
}
static void
jsonRealFunc( const tr_benc * val, void * vdata )
{
struct jsonWalk * data = vdata;
char * locale;
/* json requires a '.' decimal point regardless of locale */
locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
setlocale( LC_NUMERIC, "POSIX" );
evbuffer_add_printf( data->out, "%f", val->val.d );
setlocale( LC_NUMERIC, locale );
tr_free( locale );
jsonChildFunc( data );
}
static void
jsonStringFunc( const tr_benc * val,
void * vdata )
@ -1366,6 +1466,8 @@ tr_bencSaveAsJSON( const tr_benc * top, struct evbuffer * out )
data.parents = NULL;
walkFuncs.intFunc = jsonIntFunc;
walkFuncs.boolFunc = jsonBoolFunc;
walkFuncs.realFunc = jsonRealFunc;
walkFuncs.stringFunc = jsonStringFunc;
walkFuncs.dictBeginFunc = jsonDictBeginFunc;
walkFuncs.listBeginFunc = jsonListBeginFunc;

View File

@ -13,6 +13,10 @@
#ifndef TR_BENCODE_H
#define TR_BENCODE_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h> /* for int64_t */
struct evbuffer;
@ -22,7 +26,9 @@ enum
TYPE_INT = 1,
TYPE_STR = 2,
TYPE_LIST = 4,
TYPE_DICT = 8
TYPE_DICT = 8,
TYPE_BOOL = 16,
TYPE_REAL = 32
};
typedef struct tr_benc
@ -30,13 +36,19 @@ typedef struct tr_benc
char type;
union
{
int64_t i;
struct
uint8_t b; /* bool type */
double d; /* double type */
int64_t i; /* int type */
struct /* string type */
{
size_t i;
char * s;
} s;
struct
struct /* list & dict types */
{
size_t alloc;
size_t count;
@ -45,10 +57,6 @@ typedef struct tr_benc
} val;
} tr_benc;
#ifdef __cplusplus
extern "C" {
#endif
/***
****
***/
@ -96,6 +104,10 @@ int tr_bencInitDict( tr_benc *, size_t reserveCount );
int tr_bencInitList( tr_benc *, size_t reserveCount );
void tr_bencInitBool( tr_benc *, int value );
void tr_bencInitReal( tr_benc *, double value );
/***
****
***/
@ -172,6 +184,8 @@ static TR_INLINE tr_bool tr_bencIsInt ( const tr_benc * b ) { return tr_bencIs
static TR_INLINE tr_bool tr_bencIsDict ( const tr_benc * b ) { return tr_bencIsType( b, TYPE_DICT ); }
static TR_INLINE tr_bool tr_bencIsList ( const tr_benc * b ) { return tr_bencIsType( b, TYPE_LIST ); }
static TR_INLINE tr_bool tr_bencIsString( const tr_benc * b ) { return tr_bencIsType( b, TYPE_STR ); }
static TR_INLINE tr_bool tr_bencIsBool ( const tr_benc * b ) { return tr_bencIsType( b, TYPE_BOOL ); }
static TR_INLINE tr_bool tr_bencIsReal ( const tr_benc * b ) { return tr_bencIsType( b, TYPE_REAL ); }
/**
*** Treat these as private -- they're only made public here