Changeset 8409


Ignore:
Timestamp:
May 15, 2009, 1:16:34 PM (14 years ago)
Author:
charles
Message:

(trunk libT) update our copy of Jean's JSON parser

Location:
trunk/libtransmission
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/JSON_parser.c

    r8372 r8409  
    2828
    2929/*
    30     Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2008.
     30    Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2009.
    3131   
    3232    For the added features the license above applies also.
    3333   
    3434    Changelog:
    35         2008/10/14
    36             - Renamed states.IN to states.IT to avoid name clash which IN macro
    37               defined in windef.h (alexey.pelykh@gmail.com)
     35        2009-05-14
     36            Fixed float parsing bug related to a locale being set that didn't
     37            use '.' as decimal point character (charles@transmissionbt.com).
    3838           
    39         2008/07/19
    40             - Removed some duplicate code & debugging variable (Charles.Kerr@noaa.gov)
    41        
    42         2008/05/28
    43             - Made JSON_value structure ansi C compliant. This bug was report by
    44               trisk@acm.jhu.edu
    45        
    46         2008/05/20
    47             - Fixed bug reported by Charles.Kerr@noaa.gov where the switching
    48               from static to dynamic parse buffer did not copy the static parse
    49               buffer's content.
     39        2008-10-14
     40            Renamed states.IN to states.IT to avoid name clash which IN macro
     41            defined in windef.h (alexey.pelykh@gmail.com)
     42           
     43        2008-07-19
     44            Removed some duplicate code & debugging variable (charles@transmissionbt.com)
     45       
     46        2008-05-28
     47            Made JSON_value structure ansi C compliant. This bug was report by
     48            trisk@acm.jhu.edu
     49       
     50        2008-05-20
     51            Fixed bug reported by charles@transmissionbt.com where the switching
     52            from static to dynamic parse buffer did not copy the static parse
     53            buffer's content.
    5054*/
    5155
     
    5559#include <ctype.h>
    5660#include <float.h>
    57 #include <locale.h>
    5861#include <stddef.h>
    5962#include <stdio.h>
    6063#include <stdlib.h>
    6164#include <string.h>
     65#include <locale.h>
    6266
    6367#include "JSON_parser.h"
     
    6569
    6670#ifdef _MSC_VER
    67  #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
    68   #pragma warning(disable:4996) /* unsecure sscanf */
    69  #endif
     71#   if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
     72#      pragma warning(disable:4996) // unsecure sscanf
     73#   endif
    7074#endif
    7175
     
    8589
    8690
    87 typedef struct JSON_parser_struct {
     91struct JSON_parser_struct {
    8892    JSON_parser_callback callback;
    8993    void* ctx;
     
    9498    signed char* stack;
    9599    long stack_capacity;
    96     signed char static_stack[JSON_PARSER_STACK_SIZE];
     100    char decimal_point;
    97101    char* parse_buffer;
    98102    size_t parse_buffer_capacity;
    99103    size_t parse_buffer_count;
    100104    size_t comment_begin_offset;
     105    signed char static_stack[JSON_PARSER_STACK_SIZE];
    101106    char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE];
    102 } * JSON_parser;
     107};
    103108
    104109#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
    105110
    106111/*
    107     Characters are mapped into these 31 character classes. This allows for
     112    Characters are mapped into these character classes. This allows for
    108113    a significant reduction in the size of the state transition table.
    109114*/
     
    433438    jc->allow_comments = config->allow_comments != 0;
    434439    jc->handle_floats_manually = config->handle_floats_manually != 0;
     440   
     441    /* set up decimal point */
     442    jc->decimal_point = *localeconv()->decimal_point;
     443   
    435444    return jc;
    436445}
     
    456465    } while (0)
    457466
     467#define assert_is_non_container_type(jc) \
     468    assert( \
     469        jc->type == JSON_T_NULL || \
     470        jc->type == JSON_T_FALSE || \
     471        jc->type == JSON_T_TRUE || \
     472        jc->type == JSON_T_FLOAT || \
     473        jc->type == JSON_T_INTEGER || \
     474        jc->type == JSON_T_STRING)
     475   
    458476
    459477static int parse_parse_buffer(JSON_parser jc)
     
    463481       
    464482        if (jc->type != JSON_T_NONE) {
    465             assert(
    466                 jc->type == JSON_T_NULL ||
    467                 jc->type == JSON_T_FALSE ||
    468                 jc->type == JSON_T_TRUE ||
    469                 jc->type == JSON_T_FLOAT ||
    470                 jc->type == JSON_T_INTEGER ||
    471                 jc->type == JSON_T_STRING);
     483            assert_is_non_container_type(jc);
    472484       
    473485            switch(jc->type) {
     
    478490                        value.vu.str.length = jc->parse_buffer_count;
    479491                    } else {
    480                         /* the json spec requires a '.' decimal point regardless of locale */
    481                         char numeric[128];
    482                         snprintf(numeric, sizeof(numeric), "%s", setlocale(LC_NUMERIC, NULL));
    483                         setlocale(LC_NUMERIC, "POSIX" );
    484                         sscanf(jc->parse_buffer, "%Lf", &value.vu.float_value);
    485                         value.vu.float_value = strtod(jc->parse_buffer, NULL);
    486                         setlocale(LC_NUMERIC, numeric);
     492                        /*sscanf(jc->parse_buffer, "%Lf", &value.vu.float_value);*/
     493                       
     494                        /* not checking with end pointer b/c there may be trailing ws */
     495                        value.vu.float_value = strtold(jc->parse_buffer, NULL);
    487496                    }
    488497                    break;
     
    577586}
    578587
     588static int add_escaped_char_to_parse_buffer(JSON_parser jc, int next_char)
     589{
     590    jc->escaped = 0;
     591    /* remove the backslash */
     592    parse_buffer_pop_back_char(jc);
     593    switch(next_char) {
     594        case 'b':
     595            parse_buffer_push_back_char(jc, '\b');
     596            break;
     597        case 'f':
     598            parse_buffer_push_back_char(jc, '\f');
     599            break;
     600        case 'n':
     601            parse_buffer_push_back_char(jc, '\n');
     602            break;
     603        case 'r':
     604            parse_buffer_push_back_char(jc, '\r');
     605            break;
     606        case 't':
     607            parse_buffer_push_back_char(jc, '\t');
     608            break;
     609        case '"':
     610            parse_buffer_push_back_char(jc, '"');
     611            break;
     612        case '\\':
     613            parse_buffer_push_back_char(jc, '\\');
     614            break;
     615        case '/':
     616            parse_buffer_push_back_char(jc, '/');
     617            break;
     618        case 'u':
     619            parse_buffer_push_back_char(jc, '\\');
     620            parse_buffer_push_back_char(jc, 'u');
     621            break;
     622        default:
     623            return false;
     624    }
     625
     626    return true;
     627}
     628
     629#define add_char_to_parse_buffer(jc, next_char, next_class) \
     630    do { \
     631        if (jc->escaped) { \
     632            if (!add_escaped_char_to_parse_buffer(jc, next_char)) \
     633                return false; \
     634        } else if (!jc->comment) { \
     635            if ((jc->type != JSON_T_NONE) | !((next_class == C_SPACE) | (next_class == C_WHITE)) /* non-white-space */) { \
     636                parse_buffer_push_back_char(jc, (char)next_char); \
     637            } \
     638        } \
     639    } while (0)
     640   
     641
     642#define assert_type_isnt_string_null_or_bool(jc) \
     643    assert(jc->type != JSON_T_FALSE); \
     644    assert(jc->type != JSON_T_TRUE); \
     645    assert(jc->type != JSON_T_NULL); \
     646    assert(jc->type != JSON_T_STRING)
     647
    579648
    580649int
     
    604673    }
    605674   
    606     if (jc->escaped) {
    607         jc->escaped = 0;
    608         /* remove the backslash */
    609         parse_buffer_pop_back_char(jc);
    610         switch(next_char) {
    611         case 'b':
    612             parse_buffer_push_back_char(jc, '\b');
    613             break;
    614         case 'f':
    615             parse_buffer_push_back_char(jc, '\f');
    616             break;
    617         case 'n':
    618             parse_buffer_push_back_char(jc, '\n');
    619             break;
    620         case 'r':
    621             parse_buffer_push_back_char(jc, '\r');
    622             break;
    623         case 't':
    624             parse_buffer_push_back_char(jc, '\t');
    625             break;
    626         case '"':
    627             parse_buffer_push_back_char(jc, '"');
    628             break;
    629         case '\\':
    630             parse_buffer_push_back_char(jc, '\\');
    631             break;
    632         case '/':
    633             parse_buffer_push_back_char(jc, '/');
    634             break;
    635         case 'u':
    636             parse_buffer_push_back_char(jc, '\\');
    637             parse_buffer_push_back_char(jc, 'u');
    638             break;
    639         default:
    640             return false;
    641         }
    642     } else if (!jc->comment) {
    643         if (jc->type != JSON_T_NONE || !(next_class == C_SPACE || next_class == C_WHITE) /* non-white-space */) {
    644             parse_buffer_push_back_char(jc, (char)next_char);
    645         }
    646     }
    647    
    648    
     675    add_char_to_parse_buffer(jc, next_char, next_class);
    649676   
    650677/*
     
    697724/* floating point number detected by exponent*/
    698725        case DE:
    699             assert(jc->type != JSON_T_FALSE);
    700             assert(jc->type != JSON_T_TRUE);
    701             assert(jc->type != JSON_T_NULL);
    702             assert(jc->type != JSON_T_STRING);
     726            assert_type_isnt_string_null_or_bool(jc);
    703727            jc->type = JSON_T_FLOAT;
    704728            jc->state = E1;
     
    707731/* floating point number detected by fraction */
    708732        case DF:
    709             assert(jc->type != JSON_T_FALSE);
    710             assert(jc->type != JSON_T_TRUE);
    711             assert(jc->type != JSON_T_NULL);
    712             assert(jc->type != JSON_T_STRING);
     733            assert_type_isnt_string_null_or_bool(jc);
     734            if (!jc->handle_floats_manually) {
     735/*
     736    Some versions of strtod (which underlies sscanf) don't support converting
     737    C-locale formated floating point values.
     738*/           
     739                assert(jc->parse_buffer[jc->parse_buffer_count-1] == '.');
     740                jc->parse_buffer[jc->parse_buffer_count-1] = jc->decimal_point;
     741            }           
    713742            jc->type = JSON_T_FLOAT;
    714743            jc->state = FX;
  • trunk/libtransmission/JSON_parser.h

    r7197 r8409  
    1 #ifndef __TRANSMISSION__
    2 #error only libtransmission should #include this header.
    3 #endif
    4 
    51#ifndef JSON_PARSER_H
    62#define JSON_PARSER_H
     
    128
    139/* Windows DLL stuff */
    14 /*
    15  #ifdef _WIN32
    16  #      ifdef JSON_PARSER_DLL_EXPORTS
    17  #              define JSON_PARSER_DLL_API __declspec(dllexport)
    18  #      else
    19  #              define JSON_PARSER_DLL_API __declspec(dllimport)
    20  #   endif
    21  #else
    22  */
    23 #define JSON_PARSER_DLL_API
    24 /*
    25  #endif
    26  */
     10#ifdef _WIN32
     11#       ifdef JSON_PARSER_DLL_EXPORTS
     12#               define JSON_PARSER_DLL_API __declspec(dllexport)
     13#       else
     14#               define JSON_PARSER_DLL_API __declspec(dllimport)
     15#   endif
     16#else
     17#       define JSON_PARSER_DLL_API
     18#endif
    2719
    28 #include <inttypes.h>
    29 typedef int64_t JSON_int_t;
    30 #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%" PRId64
    31 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%" PRId64
     20/* Determine the integer type use to parse non-floating point numbers */
     21#if __STDC_VERSION__ >= 199901L || HAVE_LONG_LONG == 1
     22typedef long long JSON_int_t;
     23#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
     24#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
     25#else
     26typedef long JSON_int_t;
     27#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
     28#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
     29#endif
    3230
    3331
    3432#ifdef __cplusplus
    3533extern "C" {
    36 #endif
     34#endif 
    3735
    38 typedef enum
     36typedef enum 
    3937{
    4038    JSON_T_NONE = 0,
     
    5351} JSON_type;
    5452
    55 typedef struct JSON_value_struct
    56 {
    57     union
    58     {
    59         JSON_int_t  integer_value;
    60 
     53typedef struct JSON_value_struct {
     54    union {
     55        JSON_int_t integer_value;
     56       
    6157        long double float_value;
    62 
    63         struct
    64         {
     58       
     59        struct {
    6560            const char* value;
    66             size_t      length;
     61            size_t length;
    6762        } str;
    6863    } vu;
    6964} JSON_value;
    7065
    71 /*! \brief JSON parser callback
     66typedef struct JSON_parser_struct* JSON_parser;
     67
     68/*! \brief JSON parser callback
    7269
    7370    \param ctx The pointer passed to new_JSON_parser.
    74     \param type An element of JSON_type but not JSON_T_NONE.
    75     \param value A representation of the parsed value. This parameter is NULL
    76    for
    77         JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN,
    78    JSON_T_OBJECT_END,
    79         JSON_T_NULL, JSON_T_TRUE, and SON_T_FALSE. String values are always
    80    returned
     71    \param type An element of JSON_type but not JSON_T_NONE.   
     72    \param value A representation of the parsed value. This parameter is NULL for
     73        JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
     74        JSON_T_NULL, JSON_T_TRUE, and SON_T_FALSE. String values are always returned
    8175        as zero-terminated C strings.
    8276
    8377    \return Non-zero if parsing should continue, else zero.
    84  */
    85 typedef int ( *JSON_parser_callback )( void* ctx, int type,
    86                                        const struct JSON_value_struct*
    87                                        value );
     78*/   
     79typedef int (*JSON_parser_callback)(void* ctx, int type, const struct JSON_value_struct* value);
    8880
    8981
    90 /*! \brief The structure used to configure a JSON parser object
    91 
    92     \param depth If negative, the parser can parse arbitrary levels of JSON,
    93    otherwise
     82/*! \brief The structure used to configure a JSON parser object
     83   
     84    \param depth If negative, the parser can parse arbitrary levels of JSON, otherwise
    9485        the depth is the limit
    95     \param Pointer to a callback. This parameter may be NULL. In this case the
    96    input is merely checked for validity.
     86    \param Pointer to a callback. This parameter may be NULL. In this case the input is merely checked for validity.
    9787    \param Callback context. This parameter may be NULL.
    98     \param depth. Specifies the levels of nested JSON to allow. Negative numbers
    99    yield unlimited nesting.
     88    \param depth. Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting.
    10089    \param allowComments. To allow C style comments in JSON, set to non-zero.
    101     \param handleFloatsManually. To decode floating point numbers manually set
    102    this parameter to non-zero.
    103 
     90    \param handleFloatsManually. To decode floating point numbers manually set this parameter to non-zero.
     91   
    10492    \return The parser object.
    105  */
    106 typedef struct JSON_config_struct
    107 {
    108     JSON_parser_callback    callback;
    109     void*                   callback_ctx;
    110     int                     depth;
    111     int                     allow_comments;
    112     int                     handle_floats_manually;
     93*/
     94typedef struct {
     95    JSON_parser_callback     callback;
     96    void*                    callback_ctx;
     97    int                      depth;
     98    int                      allow_comments;
     99    int                      handle_floats_manually;
    113100} JSON_config;
    114101
    115102
    116 /*! \brief Initializes the JSON parser configuration structure to default
    117    values.
     103/*! \brief Initializes the JSON parser configuration structure to default values.
    118104
    119105    The default configuration is
    120     - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see
    121    json_parser.c)
     106    - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c)
    122107    - no parsing, just checking for JSON syntax
    123108    - no comments
    124109
    125110    \param config. Used to configure the parser.
    126  */
    127 JSON_PARSER_DLL_API void                              init_JSON_config(
    128     JSON_config* config );
     111*/
     112JSON_PARSER_DLL_API void init_JSON_config(JSON_config* config);
    129113
    130 /*! \brief Create a JSON parser object
    131 
    132     \param config. Used to configure the parser. Set to NULL to use the default
    133    configuration.
     114/*! \brief Create a JSON parser object
     115   
     116    \param config. Used to configure the parser. Set to NULL to use the default configuration.
    134117        See init_JSON_config
    135 
     118   
    136119    \return The parser object.
    137  */
    138 JSON_PARSER_DLL_API extern struct JSON_parser_struct* new_JSON_parser(
    139     JSON_config* config );
     120*/
     121JSON_PARSER_DLL_API extern JSON_parser new_JSON_parser(JSON_config* config);
    140122
    141123/*! \brief Destroy a previously created JSON parser object. */
    142 JSON_PARSER_DLL_API extern void                       delete_JSON_parser(
    143     struct JSON_parser_struct* jc );
     124JSON_PARSER_DLL_API extern void delete_JSON_parser(JSON_parser jc);
    144125
    145126/*! \brief Parse a character.
    146127
    147     \return Non-zero, if all characters passed to this function are part of are
    148    valid JSON.
    149  */
    150 JSON_PARSER_DLL_API extern int                        JSON_parser_char(
    151     struct JSON_parser_struct* jc,
    152     int
    153                                next_char );
     128    \return Non-zero, if all characters passed to this function are part of are valid JSON.
     129*/
     130JSON_PARSER_DLL_API extern int JSON_parser_char(JSON_parser jc, int next_char);
    154131
    155132/*! \brief Finalize parsing.
    156133
    157134    Call this method once after all input characters have been consumed.
     135   
     136    \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
     137*/
     138JSON_PARSER_DLL_API extern int JSON_parser_done(JSON_parser jc);
    158139
    159     \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
    160  */
    161 JSON_PARSER_DLL_API extern int JSON_parser_done(
    162     struct JSON_parser_struct* jc );
    163 
    164 /*! \brief Determine if a given string is valid JSON white space
     140/*! \brief Determine if a given string is valid JSON white space
    165141
    166142    \return Non-zero if the string is valid, zero otherwise.
    167  */
    168 JSON_PARSER_DLL_API extern int
    169                                JSON_parser_is_legal_white_space_string(
    170     const char* s );
     143*/
     144JSON_PARSER_DLL_API extern int JSON_parser_is_legal_white_space_string(const char* s);
    171145
    172146
    173147#ifdef __cplusplus
    174148}
    175 #endif
    176 
     149#endif 
     150   
    177151
    178152#endif /* JSON_PARSER_H */
  • trunk/libtransmission/json.c

    r8159 r8409  
    145145    const unsigned char       * buf = vbuf;
    146146    const void                * bufend = buf + len;
    147     struct JSON_config_struct   config;
     147    JSON_config                 config;
    148148    struct JSON_parser_struct * checker;
    149149    struct json_benc_data       data;
Note: See TracChangeset for help on using the changeset viewer.