cJSON open source project learning (1)

Operating environment: Ubuntu18.04 gcc/g++7.5.0 cmake3.10.2

first day:

First, download the cJSON project from GitHub via Git:

        git clone https://github.com/DaveGamble/cJSON.git

Did not execute make and other commands according to the official statement, the main files are cJSON.c and cJSON_Utils.c, directly use the test.c file to let the project run first:

        gcc -g test.c cJSON.c -o test (-g here is to debug test with gdb)

(To mention here, I also found out later that there are many make options in the readme on GitHub. By default, cJSON_Utils is not enabled.)

After ./test, the version information and a lot of data in the form of key-value pairs will be output, as shown below:

Version: 1.7.15
{
        "name": "Jack (\"Bee\") Nimble",
        "format":       {
                "type": "rect",
                "width":        1920,
                "height":       1080,
                "interlace":    false,
                "frame rate":   24
        }
}
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

 Next, I decided to familiarize myself with the data structure of cJSON:

/* The cJSON structure: */
typedef struct cJSON
{
    struct cJSON *next;
    struct cJSON *prev;
    struct cJSON *child;
    int type;
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    double valuedouble;
    char *string;
} cJSON;

It can be seen that cJSON adopts a linked list structure, and the `next` and `prev` fields in the cJSON structure are used to build a linked list structure and link multiple cJSON objects together. This linked list structure can facilitate and access sibling nodes of cJSON objects. Access the next cJSON object through the `next` field, and access the previous cJSON object in the linked list through the `prev` field. And each object also has child nodes, which is the `child` field.

struct cJSON *next A pointer to the next cJSON structure, used to link multiple cJSON objects to form a linked list structure
struct cJSON *prev A pointer to the previous cJSON structure, used to link multiple cJSON objects to form a linked list structure
struct cJSON *child A pointer to the first child element, handling nested structures of JSON objects or JSON arrays
int type type representing a cJSON object
char * valuestring is a pointer to a character array storing the JSON string value
int valueint Is a value of integer type representing an integer value of JSON number type
double valuedouble Is a double-precision floating-point number type value, representing the floating-point value of the JSON number type
char *str Is a pointer to a character array storing the JSON key name (key-value pair key name)

type can be one of the following:

  • cJSON_Invalid(check cJSON_IsInvalid): Indicates an invalid item that does not contain any value. If an item is set to all zero bytes it automatically has this type.
  • cJSON_False(check cJSON_IsFalse): Represents a falseboolean value. You can also check general boolean values ​​with cJSON_IsBool.
  • cJSON_True(check cJSON_IsTrue): Represents a trueboolean value. You can also check general boolean values ​​with cJSON_IsBool.
  • cJSON_NULL(check cJSON_IsNull): Represents a nullvalue.
  • cJSON_Number(check cJSON_IsNumber): Represents a numeric value. The value is stored as a double in valuedoubleand also in  valueint. If the number is outside the integer range, INT_MAXor INT_MINfor valueint.
  • cJSON_String(check cJSON_IsString): Represents a string value. It is stored as a zero-terminated string in valuestring.
  • cJSON_Array(check cJSON_IsArray): Represents an array value. childThis is accomplished by pointing to a linked list of items representing the values ​​in the array . These elements are linked together using and cJSON, where the first element and the last element.nextprevprev.next == NULLnext == NULL
  • cJSON_Object(check cJSON_IsObject): Represents an object value. Objects are stored in the same way as arrays, the only difference being that the items in the object store their keys in the string.
  • cJSON_Raw(check cJSON_IsRaw): Represents any type of JSON stored as a zero-terminated character array  valuestring. This can be used, for example, to avoid printing the same static JSON over and over to save performance. cJSON never creates this type when parsing. Also note that cJSON does not check that it is valid JSON.
  • cJSON_IsReferencechild: specifies valuestringan item that points to and/or does not belong to this item, it is just a reference. So cJSON_Deleteand other functions will only release the item, not its childvaluestring.
  • cJSON_StringIsConst: This means stringpointing to a constant string. This means cJSON_Deletethat and other functions will not try to deallocate string.

                                                                                (instructions on GitHub)

Next we start with the main function:

printf("Version: %s\n", cJSON_Version());    ->

CJSON_PUBLIC(const char*) cJSON_Version(void)
{
    static char version[15];
    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);

    return version;
}
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 15

The version information of cJSON is stored in the cJSON.h file. Here I noticed that the array is decorated with `static` to ensure that the variable maintains the persistence of the value between function calls. The array is a local variable defined inside the function. If there is no `static` modifier, it will be recreated every time the `cJSON_Version` function is called, and will be destroyed after the function call. With `static` modification, the array will only be created when the function is called for the first time, and the value will remain unchanged between multiple calls. (major version number - minor version number - revision number)

        And after tracing back to CJSON_PUBLIC(const char*), it is found that:

CJSON_PUBLIC(const char*)    ->

#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL    ->

#define CJSON_STDCALL __stdcall

#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL This is used to define the visibility and calling convention of exported functions:

        __declspec(dllexport)is a compiler-specific keyword used on Windows platforms. It instructs the compiler to mark the decorated function as a dynamically linked library export function, accessible outside the library.

        typeIs a placeholder for the type parameter in the macro, used to specify the return type of the function.

   CJSON_STDCALLis a macro that indicates the calling convention used by the function and is also platform and compiler specific. It can be used to specify the passing order of function parameters and the way the stack is cleaned up, etc.

        __stdcallIt is a calling convention, which is a prescribed way for a function to handle function parameter passing, stack cleanup, etc. at compile time. The calling convention determines how parameters are passed when a function is called, how the stack is managed, and how the stack is cleaned up after the function is called. __stdcallis a common calling convention typically used for function calls on the Windows platform. CJSON_STDCALLWhere macros are used , __stdcallthe calling convention will be used. This means that the order in which parameters are passed to the function and how the stack is cleaned up will follow __stdcallthe rules of .

        By using this macro definition CJSON_PUBLIC, a function can be specified to be exported on the Windows platform and use a specific calling convention. In this way, functions marked with CJSON_PUBLICcan be accessed and called from outside the library.

Next I will annotate the points I think are useful directly into the code snippet:

static void create_objects(void)
{
    /* declare a few. */
    cJSON *root = NULL;    //JSON根节点
    cJSON *fmt = NULL;     //格式化信息
    cJSON *img = NULL;     //图像相关信息
    cJSON *thm = NULL;     //缩略图相关信息
    cJSON *fld = NULL;     //表示字段或其他数据信息
    int i = 0;

    /* Our "days of the week" array: */
    const char *strings[7] =
    {
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    };
    /* Our matrix: */
    int numbers[3][3] =
    {
        {0, -1, 0},
        {1, 0, 0},
        {0 ,0, 1}
    };
    /* Our "gallery" item: */
    int ids[4] = { 116, 943, 234, 38793 };
    /* Our array of "records": */
    struct record fields[2] =
    {
        {
            "zip",
            37.7668,
            -1.223959e+2,
            "",
            "SAN FRANCISCO",
            "CA",
            "94107",
            "US"
        },
        {
            "zip",
            37.371991,
            -1.22026e+2,
            "",
            "SUNNYVALE",
            "CA",
            "94085",
            "US"
        }
    };
    /*关键字 volatile 用于告诉编译器,该变量的值可能会在程序的其他地方被修改
    ,因此编译器在优化代码时不应该做出假设或进行优化。它用于标记变量具有不稳
    定的、易变的特性。*/
    volatile double zero = 0.0;
    /*在此之前定义了一些JSON的数据*/

    /* Here we construct some JSON standards, from the JSON site. */

    /* Our "Video" datatype: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
    cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
    cJSON_AddStringToObject(fmt, "type", "rect");
    cJSON_AddNumberToObject(fmt, "width", 1920);
    cJSON_AddNumberToObject(fmt, "height", 1080);
    cJSON_AddFalseToObject (fmt, "interlace");
    cJSON_AddNumberToObject(fmt, "frame rate", 24);

    /* Print to text */
    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "days of the week" array: */
    root = cJSON_CreateStringArray(strings, 7);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our matrix: */
    root = cJSON_CreateArray();
    for (i = 0; i < 3; i++)
    {
        cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3));
    }

    /* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "gallery" item: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject());
    cJSON_AddNumberToObject(img, "Width", 800);
    cJSON_AddNumberToObject(img, "Height", 600);
    cJSON_AddStringToObject(img, "Title", "View from 15th Floor");
    cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject());
    cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
    cJSON_AddNumberToObject(thm, "Height", 125);
    cJSON_AddStringToObject(thm, "Width", "100");
    cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our array of "records": */
    root = cJSON_CreateArray();
    for (i = 0; i < 2; i++)
    {
        cJSON_AddItemToArray(root, fld = cJSON_CreateObject());
        cJSON_AddStringToObject(fld, "precision", fields[i].precision);
        cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
        cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
        cJSON_AddStringToObject(fld, "Address", fields[i].address);
        cJSON_AddStringToObject(fld, "City", fields[i].city);
        cJSON_AddStringToObject(fld, "State", fields[i].state);
        cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
        cJSON_AddStringToObject(fld, "Country", fields[i].country);
    }

    /* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "number", 1.0 / zero);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);
}

The following is the call to the cJSON library to create a JSON object and add key-value pairs and sub-objects:

CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item)
    {
        item->type = cJSON_Object;
    }

    return item;
}

static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };

/*这里用static来修饰能够指定函数链接类型为内部链接,意味着该函数
只能在当前编译单元中使用,无法被其他编译单元引用,可以壁面函数名
冲突和符号重定义的问题,增强代码封装性和模块化。*/
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
    /*通过global_hooks,allocate指向internal_malloc,追溯后发现调用的stdlib中de 
    malloc函数*/
    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
    if (node)
    {
        memset(node, '\0', sizeof(cJSON));    //用0初始化node指针指向的空间
    }

    return node;
}

typedef struct internal_hooks
{
    /*下面是函数指针,
    void *(CJSON_CDECL *allocate)(size_t size);                    //分配内存函数
    void (CJSON_CDECL *deallocate)(void *pointer);                 //释放内存函数
    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);   //重新分配内存函数
} internal_hooks;

/*左移运算符:a<<b,将a左移b位,这里用到的cJSON_Object左移后对应十进制是64
不直接定义整数是为了使语义更加清晰和易读并且有一定语义意义
例如,使用 1 << 6 来表示 cJSON_Object,我们可以明确地知道它是一个二进制数,
在第 7 位上有一个 1,而其他位都是 0。这样的表达方式更加直观且易于阅读。

此外,通过左移运算符,我们可以方便地组合多个标识位,使用按位或运算符将它们合
并在一起,形成复合类型的标识。这在处理复杂的标识位掩码时非常有用,使代码更
具可读性和可维护性。
*/
#define cJSON_Invalid (0)
#define cJSON_False  (1 << 0)
#define cJSON_True   (1 << 1)
#define cJSON_NULL   (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array  (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw    (1 << 7) /* raw json */

The functional relationship among them is shown in the figure below:

 So far we have initialized root, now add item (cJSON) data to it:

CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
    return add_item_to_object(object, string, item, &global_hooks, false);
}

/目标对象,键名,要添加的项,内部钩子,键名是否为常量*/
static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{
    char *new_key = NULL;
    int new_type = cJSON_Invalid;

    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
    {
        return false;
    }

    /*此处if和else如果是常量就设置标志位,反之取消标志位*/
    if (constant_key)
    {
        /*将`string`转换为非常量字符指针,赋值给new_key*/
        new_key = (char*)cast_away_const(string);
        /*这里用`|`是按位或,可以同时保存原始类型和标志位,将多个信息组合到一个整数
        例如,`item->type`=1000,`cJSON_STRINGIsConst`=0100,或的结果就是1100用
        一位二进制保存了状态信息*/
        new_type = item->type | cJSON_StringIsConst;
    }
    else
    {
        /*将`string`复制到一个新的动态分配的内存空间并检查是否成功分配*/
        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
        if (new_key == NULL)
        {
            return false;
        }

        new_type = item->type & ~cJSON_StringIsConst;
    }

    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
    {
        hooks->deallocate(item->string);
    }

    item->string = new_key;
    item->type = new_type;

    return add_item_to_array(object, item);
}

static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
    size_t length = 0;
    unsigned char *copy = NULL;

    if (string == NULL)
    {
        return NULL;
    }

    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*)hooks->allocate(length);
    if (copy == NULL)
    {
        return NULL;
    }
    memcpy(copy, string, length);

    return copy;
}

static void* cast_away_const(const void* string)
{
    return (void*)string;
}


/*这里的加入item,注意childe并不是一个头节点,只是它的prev并没有指向根节点(root)
而是指向child链表中的最后一个节点,简直是米奇妙妙屋-妙到家了!*/
static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{
    cJSON *child = NULL;

    if ((item == NULL) || (array == NULL) || (array == item))
    {
        return false;
    }
    
    child = array->child;
    /*
     * To find the last item in array quickly, we use prev in array
     */
    if (child == NULL)
    {
        /* list is empty, start new one */
        array->child = item;
        item->prev = item;
        item->next = NULL;
    }
    else
    {
        /* append to the end */
        if (child->prev)
        {
            suffix_object(child->prev, item);
            array->child->prev = item;
        }
    }

    return true;
}

static void suffix_object(cJSON *prev, cJSON *item)
{
    prev->next = item;
    item->prev = prev;
}

The functional relationship is shown in the figure below:

The next step is to add string, number, false and other data types, the steps are the same (note that the cJSON_AddItemToObject function is to add the attributes of this JSON, and the cJSON_Add*ToObject function is to add nested attributes):

Let me mention here, when executing the above function, if there is a memory allocation failure, the Delete function will be called to release the memory! !

 

Guess you like

Origin blog.csdn.net/pan_1214_/article/details/130694094