]> granicus.if.org Git - transmission/commitdiff
(trunk) Use proper notation for json floating-point and bool types. For backwards...
authorCharles Kerr <charles@transmissionbt.com>
Sun, 5 Apr 2009 17:52:21 +0000 (17:52 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Sun, 5 Apr 2009 17:52:21 +0000 (17:52 +0000)
doc/rpc-spec.txt
libtransmission/bencode.c
libtransmission/bencode.h

index 40abdccbf3ee3476c837a1da2d6b205dbf533db4..ad1728968b0d700c0af6a26a44309f8c7dd31d25 100644 (file)
 
 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).
 
    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
    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
    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
    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
    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
                           | 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
    -----------------------+--------------------------------------+
    "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
    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
 
    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.
index c4571a542fd29d29a9fbafe54ff1b89594619543..902d12b5e5f9566b3029d0b0823d7a63b05e3e9a 100644 (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( b, TYPE_BOOL );
+    b->val.b = value != 0;
+}
+
+void
+tr_bencInitReal( tr_benc * b, double value )
 {
-    tr_bencInit( val, TYPE_INT );
-    val->val.i = num;
+    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;
index 2e62e882313c22710e29e3ba50346bcb75636683..2ad6a85b7bfe3818053c542636eb105fecfa245b 100644 (file)
 #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