cJSON支持64位数据解析

1、问题

cJSON本身不支持64位的整形数据解析和增加,对于部分应用场景不适用,并且cJSON适用double来存储中间数据,转换为整形可能存在精度问题。

float和double精度问题,可以参考本文 《float和double的精度

2、优化

修改优化cJSON的代码以支持long long 格式的数据解析。

1)增加long long 结构字段

#ifdef CONFIG_CJSON_SUPPORT_64BIT

#define NUMBER_DOUBLE       1
#define NUMBER_LONGLONG     2
#endif

/* The cJSON structure: */
typedef struct cJSON
{
    struct cJSON *next;
    struct cJSON *prev;
    struct cJSON *child;
    int type;

    char *valuestring;
    int valueint;
    double valuedouble;
    char *string;

#ifdef CONFIG_CJSON_SUPPORT_64BIT

    /* The type of number,double,long long or ull */
    int numbertype;
    long long valuelonglong;
#endif
} cJSON;

2)优化数值解析代码流程

static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{

#ifdef CONFIG_CJSON_SUPPORT_64BIT

    int    scale = 0;
#endif

    double number = 0;
    unsigned char *after_end = NULL;
    unsigned char number_c_string[64];
    unsigned char decimal_point = get_decimal_point();
    size_t i = 0;

    if ((input_buffer == NULL) || (input_buffer->content == NULL))
    {
        return false;
    }

    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
    {
        switch (buffer_at_offset(input_buffer)[i])
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '+':
            case '-':
            case 'e':
            case 'E':
                number_c_string[i] = buffer_at_offset(input_buffer)[i];
                break;

            case '.':

#ifdef CONFIG_CJSON_SUPPORT_64BIT
                scale = 1;
#endif

                number_c_string[i] = decimal_point;
                break;

            default:
                goto loop_end;
        }
    }
loop_end:
    number_c_string[i] = '\0';

    number = strtod((const char*)number_c_string, (char**)&after_end);
    if (number_c_string == after_end)
    {
        return false; /* parse_error */
    }

    item->valuedouble = number;

#ifdef CONFIG_CJSON_SUPPORT_64BIT

    if(scale == 0)      /* check decimal point '.' */
    {
        item->valuelonglong = (long long)strtoll((const char*)number_c_string, (char**)&after_end,10);
        item->numbertype    = NUMBER_LONGLONG;
    }
    else
    {
        item->numbertype = NUMBER_DOUBLE;
    }
#endif

    /* use saturation in case of overflow */
    if (number >= INT_MAX)
    {
        item->valueint = INT_MAX;
    }
    else if (number <= (double)INT_MIN)
    {
        item->valueint = INT_MIN;
    }
    else
    {
        item->valueint = (int)number;
    }

    item->type = cJSON_Number;

    input_buffer->offset += (size_t)(after_end - number_c_string);
    return true;
}

3)优化数值打印的流程

static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    double d = item->valuedouble;
    int length = 0;
    size_t i = 0;
    unsigned char number_buffer[26]; /* temporary buffer to print the number into */
    unsigned char decimal_point = get_decimal_point();

    if (output_buffer == NULL)
    {
        return false;
    }

    /* This checks for NaN and Infinity */
    if ((d * 0) != 0)
    {
        length = sprintf((char*)number_buffer, "null");
    }
    else
    {
#if CJSON_SPRINTF_FLOAT
        double test;
        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
        length = sprintf((char*)number_buffer, "%1.15g", d);

        /* Check whether the original double can be recovered */
        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
        {
            /* If not, print with 17 decimal places of precision */
            length = sprintf((char*)number_buffer, "%1.17g", d);
        }
#else
        long long d64 = (long long)d;
        long d32 = (long)d;

        /**
         * Check if 32-bit type data is overflow.
         */

#ifdef  CONFIG_NEWLIB_NANO_FORMAT

        assert(d32 == d64 && "Library newlib of nano mode does not support double or long-long format, please enable newlib normal mode.");
#elif   defined(CONFIG_CJSON_SUPPORT_64BIT)

        if(item->numbertype == NUMBER_LONGLONG)
        {
            length = sprintf((char*)number_buffer, "%lld", item->valuelonglong);
        }
        else
#endif
        {

#ifdef  CONFIG_CJSON_THREE_PRECISION_ENABLE

            length = sprintf((char*)number_buffer, "%.3g",item->valuedouble);
#else

            length = sprintf((char*)number_buffer, "%ld", d32);

            if ((double)d32 != d) {
                size_t precision = 14;
                unsigned char *pbuf = number_buffer;

                if (d < 0.0) {
                    d = (double)d32 - d + 0.00000000000001;
                } else {
                    d = d - (double)d32;
                }

                pbuf = &number_buffer[length];
                *pbuf++ = '.';
                length++;

                while (d > 0.0 && precision--) {
                    d *= 10.0;
                    unsigned char tmp = (unsigned char)d;

                    *pbuf++ = tmp + '0';
                    length++;

                    d -= (double)tmp;
                }

                pbuf = &number_buffer[length - 1];

                while (*pbuf == '0') {
                    pbuf--;
                    length--;
                }

                *++pbuf = 0;
            }
#endif
        }
#endif
    }

    /* sprintf failed or buffer overrun occurred */
    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
    {
        return false;
    }

    /* reserve appropriate space in the output */
    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
    if (output_pointer == NULL)
    {
        return false;
    }

    /* copy the printed number to the output and replace locale
     * dependent decimal point with '.' */
    for (i = 0; i < ((size_t)length); i++)
    {
        if (number_buffer[i] == decimal_point)
        {
            output_pointer[i] = '.';
            continue;
        }

        output_pointer[i] = number_buffer[i];
    }
    output_pointer[i] = '\0';

    output_buffer->offset += (size_t)length;

    return true;
}

4)添加long long 数值解析和增加的接口

#ifdef CONFIG_CJSON_SUPPORT_64BIT

CJSON_PUBLIC(int) cJSON_Get_LongLong(const cJSON * const object, const char * key, long long* out)
{
    cJSON* sub_obj = NULL;

    sub_obj = get_object_item(object,key,false);
    if(out == NULL || sub_obj == NULL || sub_obj->numbertype != NUMBER_LONGLONG)
    {
        return -1;
    }

    *out = sub_obj->valuelonglong;

    return 0;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateLongLong(long long num)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_Number;
        item->valuedouble = (double)num;

        item->numbertype = NUMBER_LONGLONG;
        item->valuelonglong = num;
    }

    return item;
}

CJSON_PUBLIC(cJSON*) cJSON_AddLongLongToObject(cJSON * const object, const char * const name, const long long valuell)
{
    cJSON *number_item = cJSON_CreateLongLong(valuell);
    if (add_item_to_object(object, name, number_item, &global_hooks, false))
    {
        return number_item;
    }

    cJSON_Delete(number_item);
    return NULL;
}

#endif
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值