#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
//以宏的方式定义出的几种cJson对象的类型
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
//cJSON的数据结构
typedef struct cJSON
{
/* next/prev 用来遍历所有的数组或者对象链表. 一般来说可以调用GetArraySize/GetArrayItem/GetObjectItem 进行操作*/
struct cJSON *next,*prev;
/* 一个数组或者对象会有一个孩子节点指针指向一个对象或者数组链 */
struct cJSON *child;
/* 这个节点的类型, 为上面定义的宏 */
int type;
/* 是节点的值 如果节点的类型是cJSON_String的话 */
char *valuestring;
/* 是节点的值 如果节点的类型是cJSON_Number的话 */
int valueint;
/* 是节点的值 如果节点的类型是cJSON_Number的话 */
double valuedouble;
/* 节点的名字*/
char *string;
} cJSON;
//钩子?将申请内存和释放内存的接口进行管理。
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
//为cJSON提供 malloc realloc 和 free函数
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
//提供一个JSON的内存块,返回出从value传入的字符串中携带的json信息使你后续可以进行提取。在完成工作之后要使用cJSON_Delete进行释放
extern cJSON *cJSON_Parse(const char *value);
//将一个json数据转换为文本数据,用来方便转发和存储。在完成工作之后要释放char *
extern char *cJSON_Print(cJSON *item);
//将一个json数据转换为不含有任何格式的文本数据,用来方便转发和存储。在完成工作之后要释放char *
extern char *cJSON_PrintUnformatted(cJSON *item);
/* 使用缓存的策略将json数据打印到缓冲区中. prebuffer是预测的缓存大小. 认为可以很好的减少内存重复分配.
* fmt=0 不含有任何格式,
* fmt=1 含有格式
*/
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* 删除整个json结构体和其所有子项 */
extern void cJSON_Delete(cJSON *c);
/* 返回一个对象或者数组中所有的元素个数*/
extern int cJSON_GetArraySize(cJSON *array);
/* 检索数组array中第item个元素,不成功则返回NULL */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/*获取"string"指定的对象. 不区分大小写. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* 用来分析错误的解析.返回一个指向解析错误位置的指针。你可能需要从这个位置往回检查几个字符. 解析成功则返回0. */
extern const char *cJSON_GetErrorPtr(void);
/*下面的这些调用用来根据指定的类型创建cjson的节点。*/
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/*下面的这些用来建立count个节点*/
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/*将指定的节点添加到数组或者对象中 */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* 当字符串是常量的时候使用下面这个接口 */
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);
/*添加指定的节点到指定的对象或者数组中. 把一个存在的cJSON添加到一个新的cJSON但是又不想销毁已经存在的这个cJSON使用这一组接口*/
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* 从一个数组或者对象中删除指定的节点 */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* 更新数组中的节点. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* 将原有的节点右移. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* 复制一个cJSON对象 */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate会创建一个与传入参数完全相同的对象, 在新的内存中需要释放。
*当recurse!=0,将会复制这个对象中的所有的孩子节点。
*返回对象中的item->next 和 ->prev指针通常是0。
*/
/* ParseWithOpts允许你去指定或者检查字符串是否以NULL结尾, 同时可以检索解析后的字符串的最后一个位置. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
//一个精简后的解析框架
extern void cJSON_Minify(char *json);
/* 宏,用来做快速建立并添加操作. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* 当赋值一个整数的时候, 需要对浮点数也进行同时赋值. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif
{
"name": "Jack ("Bee") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
/* ep静态全局指针,指向字符串,*/
static const char *ep;
/*返回ep所指向字符串的地址,使用const修饰返回值,说明ep所指向的字符串是常量,
应该是写死在出错方式上。所以不需要释放其返回的指针的*/
const char *cJSON_GetErrorPtr(void)
{
return ep;
}
/*忽略大小写比较字符串s1和s2, 参数使用const进行修饰,说明内部不会修改这两个值。
*static说明文件内作用域,返回整数,
*=0 - 相等;
*>0 - s1>s2;
*<0 - s1<s2;
*/
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
//s1==NULL的情况下,如果s2也是NULL就相等,不然就是s1<s2;
if (!s1)
return (s1==s2)?0:1;
//s1不是NULL,但是s2是NULL,那么s1>s2
if (!s2)
return 1;
//不区分大小写,即都以小写形式进行比较,循环比较每个字符。不相等则跳出循环。
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
if(*s1 == 0)//如果这个条件为真,说明s1==s2,s1==NULL。即,两个字串不区分大小写相同
return 0;
//将不相同的那个字符的小写形式进行相减,可以得到两个串的大小。
//强制转换防止报错吧。
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
/*对静态全局函数指针变量进行赋值,使cJSON_malloc=malloc,使cJSON_free=free*/
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
/*静态作用域,返回char*类型指针,该指针指向从str中复制出内容的一块新申请的内存地址,str为const,不可修改*/
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
//获取字符串长度,并考虑了最后一个结束符号。
len = strlen(str) + 1;
//申请len个长度内存并类型转换和测试是否申请成功,不成功就返回NULL。
if (!(copy = (char*)cJSON_malloc(len))) return 0;
//申请成功就拷贝len长度个串进去。然后将首地址返回
memcpy(copy,str,len);
return copy;
}
/*初始化钩子,其实所谓钩子在这里也就是内存申请和释放接口*/
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
//判断传入的指针为NULL,那么就使用系统的申请和释放接口
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
//然后根据传入参数中是否携带指定的申请和释放接口,进行选择使用哪一个内存接口。
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/*申请一个cJSON结构体大小的内存,初始化为0,静态作用域*/
static cJSON *cJSON_New_Item(void)
{
//申请内存,测试,赋值为0,返回指针。
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node)
memset(node,0,sizeof(cJSON));
return node;
}
/*删除一个cJSON结构体,还应该循环将它的所有子项进行删除清理*/
/*对于c->type的0、1、2、3、4、5、6和256以及512做与预算到底是为什么要这么做呢?*/
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
//记录c节点的下一个节点。
next=c->next;
//c->type&cJSON_IsReference将type与256做与运算,判断有孩子节点则进行递归
if (!(c->type&cJSON_IsReference) && c->child)
cJSON_Delete(c->child);
//valuestring不为NULL,需要释放内存
if (!(c->type&cJSON_IsReference) && c->valuestring)
cJSON_free(c->valuestring);
//string不为NULL,需要释放内存
if (!(c->type&cJSON_StringIsConst) && c->string)
cJSON_free(c->string);
//删除这个节点
cJSON_free(c);
//继续下一个节点
c=next;
}
}
/*将输入的num解析成一个数字,然后将结果填充到节点中
*静态作用域,const返回值为传入字串解析完后第一个不为数值的位置,解析num到item中
*/
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;
int subscale=0,signsubscale=1;
//是否为负数
if (*num=='-')
sign=-1,num++;
//是否为0
if (*num=='0')
num++;
//如果是十进制就进行解析,临时变量存到n中
if (*num>='1' && *num<='9')
do
n=(n*10.0)+(*num++ -'0');
while (*num>='0' && *num<='9');
//如果存在小数点,将数字继续转存到n的末尾以整数方式,但是使用scale记录有几位小数
if (*num=='.' && num[1]>='0' && num[1]<='9')
{
num++;
do
n=(n*10.0)+(*num++ -'0'),scale--;
while (*num>='0' && *num<='9');
}
//如果是指数计数方式,记录指数符号,然后将数字进行转存到subscale中
if (*num=='e' || *num=='E')
{
num++;
if (*num=='+')
num++;
else if (*num=='-')
signsubscale=-1,num++;
while (*num>='0' && *num<='9')
subscale=(subscale*10)+(*num++ - '0');
}
/* number = +/- number.fraction * 10^+/- exponent */
n=sign*n*pow(10.0,(scale+subscale*signsubscale));
//将计算到的值赋值到item中,int和double项都要赋值。类型赋值为NUM
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/*返回大于x的最小的2的幂,静态作用域*/
/*在实现上就是把x占用到的最高位为1的位到第0位,都置位为1*/
static int pow2gt (int x)
{
//--x是为了防止x直接就是2的幂的情况。
--x;
x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16;
return x+1;
}
//定义一个printbuffer类型,主要是用来将json数据打印到缓冲区时,进行提供缓存空间的信息。
typedef struct
{
char *buffer; //缓存地址指针
int length; //缓存当前的长度
int offset; //缓存当前已经使用到的位置。
} printbuffer;
/*
*ensure 意为确保的意思,这里可以理解为,确保p所指向的缓冲区中能够提供needed大小的缓冲给打印功能使用
*静态作用域,返回缓冲区中可以继续使用的空间的位置。needed在里面做了局部变量,修改使用均无碍,
*减少了一个中间变量。
*/
static char* ensure(printbuffer *p,int needed)
{
char *newbuffer;int newsize;
//如果p=NULL,或者p->buffer=NULL,那么为运行时检查错误,返回空指针
if (!p || !p->buffer)
return 0;
//计算将当前所需的空间加上之前已用的空间一共需要内存的数目
needed+=p->offset;
//如果现在p->buffer足够容下所有的值,那么就返回当前缓存中最后一个可用的位置
if (needed<=p->length)
return p->buffer+p->offset;
/*下面为处理当前缓存不能存下所有的信息的时候*/
//计算大于当前所需数目的最小2de幂,用来分配内存数目
newsize=pow2gt(needed);
//申请内存并做错误检查,如果失败那么就置空后返回空指针。
newbuffer=(char*)cJSON_malloc(newsize);
if (!newbuffer)
{
cJSON_free(p->buffer);
p->length=0,
p->buffer=0;
return 0;
}
//申请成功后将原有数据拷贝到新空间中。
if (newbuffer)
memcpy(newbuffer,p->buffer,p->length);
//释放原指针,并更新新的缓存信息,然后返回缓存中第一个可用的内存位置。
cJSON_free(p->buffer);
p->length=newsize;
p->buffer=newbuffer;
return newbuffer+p->offset;
}
/*静态作用域函数,意为更新,传入参数为缓存结构,返回当前缓存区已使用的内存偏移量*/
static int update(printbuffer *p)
{
char *str;
//运行时错误检查
if (!p || !p->buffer)
return 0;
//将str定义到新加入缓存的数据的首地址。然后使用strlen计算新添加长度后加上原有的偏移量进行返回。
str=p->buffer+p->offset;
return p->offset+strlen(str);
}
/*静态作用域,将item中的数字打印成字符串,
*当p不为NULL时,使用的是p所指向的内存缓冲区,当p为NULL时,使用的是单独申请的内存
*/
static char *print_number(cJSON *item,printbuffer *p)
{
char *str=0;
double d=item->valuedouble;
//如果item的数值为0,使用两个字节,根据p是否为空,决定使用从哪里分配的缓存,并将字符串"0"拷贝到缓存中。
if (d==0)
{
if (p) str=ensure(p,2);
else str=(char*)cJSON_malloc(2); /* special case for 0. */
if (str) strcpy(str,"0");
}
//如果item的数值为整数,21个char肯定装的下,并验证数值的正确性。
//(fabs(((double)item->valueint)-d)<=DBL_EPSILON,标示差小于最小误差值,即可以理解为整数,并用INT_MAX、INT_MIN,验证合法性数据。
else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
if (p) str=ensure(p,21);
else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
//走到这里,肯定是小数,选用64个字节较为合适。
else
{
if (p) str=ensure(p,64);
else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
//如果小数值特别接近零,并且整数部分值特别大,那么就以xxxxx.0方式输出
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
//如果数值比1.0e-6小或者比1.0e9数值大,那么比较适合用科学计数法标示
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
//剩余部分直接用小数点形式进行输出
else sprintf(str,"%f",d);
}
}
return str;
}
/*从16进制整数的字符串表达方式转换成无符号整数*/
/*静态作用域,返回无符号整数,参数不可修改*/
static unsigned parse_hex4(const char *str)
{
//将字符串的字符逐个取出进行分析,然后计算到整数中。然后将h左移一个直接再进行下一个数字的解析。最后完成4个字节的整数解析
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
return h;
}
/*将输入的文本解析为非转意的c的字符串,然后填充到item中,应该保证str是已经去除开头空字符的串,
*,此处的静态字符数组,是用来做utf格式转换的,返回值为解析出一个字符串之后的首地址
*/
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;
char *ptr2;
char *out;
int len=0;
unsigned uc,uc2;
//如果str不以"引号,开头,那么不是一个字符串。
if (*str!='"') {ep=str;return 0;} /* not a string! */
while (*ptr!='"' && *ptr && ++len)
if (*ptr++ == '\')
ptr++; /* Skip escaped quotes. */
//分配内存并进行检查
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='"' && *ptr)
{
if (*ptr!='\')
*ptr2++=*ptr++;
else
{//如果以反斜杠开头的转义字符,则进行下诉的语义转换。只有utf格式转换不是很了解。
ptr++;
switch (*ptr)
{
case 'b': *ptr2++=''; break;
case 'f': *ptr2++='f'; break;
case 'n': *ptr2++=' '; break;
case 'r': *ptr2++=' '; break;
case 't': *ptr2++=' '; break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
//utf16和utf8之间格式的转换
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
uc2=parse_hex4(ptr+3);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
//在结尾填充上'