cJSON原始碼分析(三)
阿新 • • 發佈:2021-02-11
技術標籤:C++
在構建好一個JSON物件之後,如何訪問呢?
首先試著將json字串序列化,並全部打印出來看下結構再說:
char * string = "{\"name\":\"xxx\", \"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);//json字串序列化
printf("%s\n", cJSON_Print(root));//json格式化輸出
看原始碼瞭解一下cJSON_Print函式大致實現過程吧(cJSON_Parse函式實現的原始碼可翻閱前面文章)
/*
printbuffer結構體主要用作格式化或非格式化列印json資料結構的緩衝區
*/
typedef struct
{
unsigned char *buffer; //內容
size_t length; //長度
size_t offset; //偏移量
size_t depth; /* current nesting depth (for formatted printing) 當前巢狀深度(用於格式化列印)*/
cJSON_bool noalloc; //bool型別
cJSON_bool format; /*bool型別, is this print a formatted print 這是是否格式化輸出,用true,false控制*/
internal_hooks hooks; //通過hook對記憶體進行操作
} printbuffer;
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) //判斷大小,全部用括號括起來,否則如果a,b是表示式且含有比<符號級別更低的運算子就會出現不可預料的錯誤
/* Render a cJSON item/entity/structure to text.
將專案/實體/結構呈現為文字*/
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
return (char*) print(item, true, &global_hooks);//global_hooks作為全域性變數負責分配記憶體緩衝區
}
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{
static const size_t default_buffer_size = 256;
printbuffer buffer[1];
unsigned char *printed = NULL;
memset(buffer, 0, sizeof(buffer));
/* create buffer 建立緩衝區讀取字元*/
buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
buffer->length = default_buffer_size;
buffer->format = format;
buffer->hooks = *hooks;
if (buffer->buffer == NULL)
{
goto fail;
}
/* print the value */
if (!print_value(item, buffer))
{
goto fail;
}
update_offset(buffer);//更新偏移量
/* check if reallocate is available */
if (hooks->reallocate != NULL)//檢查開闢的空間是否滿足
{
printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);//動態增加或減少空間
if (printed == NULL) {
goto fail;
}
buffer->buffer = NULL;
}
else /* otherwise copy the JSON over to a new buffer
否則,將JSON複製到新的緩衝區*/
{
printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
if (printed == NULL)
{
goto fail;
}
memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
printed[buffer->offset] = '\0'; /* just to be sure 只是想確定一下,應該是保證字串有正確的結束符號*/
/* free the buffer */
hooks->deallocate(buffer->buffer);//釋放緩衝區記憶體
}
return printed;
fail:
if (buffer->buffer != NULL)
{
hooks->deallocate(buffer->buffer);
}
if (printed != NULL)
{
hooks->deallocate(printed);
}
return NULL;
}
/* Render a value to text.
將值呈現為文字。*/
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
{
unsigned char *output = NULL;
if ((item == NULL) || (output_buffer == NULL))
{
return false;
}
switch ((item->type) & 0xFF)//0xFF, 表示item->type取低8位的值作為引數,更高位的資料不參考
{
case cJSON_NULL:
output = ensure(output_buffer, 5);//確保偏移量足夠容納內容
if (output == NULL)
{
return false;
}
strcpy((char*)output, "null");
return true;
case cJSON_False:
output = ensure(output_buffer, 6);
if (output == NULL)
{
return false;
}
strcpy((char*)output, "false");
return true;
case cJSON_True:
output = ensure(output_buffer, 5);
if (output == NULL)
{
return false;
}
strcpy((char*)output, "true");
return true;
case cJSON_Number:
return print_number(item, output_buffer);
case cJSON_Raw:
{
size_t raw_length = 0;
if (item->valuestring == NULL)
{
return false;
}
raw_length = strlen(item->valuestring) + sizeof("");
output = ensure(output_buffer, raw_length);
if (output == NULL)
{
return false;
}
memcpy(output, item->valuestring, raw_length);
return true;
}
case cJSON_String:
return print_string(item, output_buffer);
case cJSON_Array:
return print_array(item, output_buffer);
case cJSON_Object:
return print_object(item, output_buffer);
default:
return false;
}
}
/* calculate the new length of the string in a printbuffer and update the offset
計算列印緩衝區中字串的新長度並更新偏移量*/
static void update_offset(printbuffer * const buffer)
{
const unsigned char *buffer_pointer = NULL;
if ((buffer == NULL) || (buffer->buffer == NULL))
{
return;
}
buffer_pointer = buffer->buffer + buffer->offset;
buffer->offset += strlen((const char*)buffer_pointer);
}
/* realloc printbuffer if necessary to have at least "needed" bytes more
重新分配printbuffer(如果需要的話)至少“需要”更多的位元組*/
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
unsigned char *newbuffer = NULL;
size_t newsize = 0;
if ((p == NULL) || (p->buffer == NULL))
{
return NULL;
}
if ((p->length > 0) && (p->offset >= p->length))
{
/* make sure that offset is valid
確保偏移量有效*/
return NULL;
}
if (needed > INT_MAX)
{
/* sizes bigger than INT_MAX are currently not supported
當前不支援大於INT_MAX的大小*/
return NULL;
}
needed += p->offset + 1;
if (needed <= p->length)
{
return p->buffer + p->offset;
}
if (p->noalloc) {
return NULL;
}
/* calculate new buffer size
計算新緩衝區大小*/
if (needed > (INT_MAX / 2))
{
/* overflow of int, use INT_MAX if possible
int會溢位,溢位原因我認為是當int數是負數的時候,符號位為1,運算之後可能會超出size_t的大小
如果可能,請使用 INT_MAX*/
if (needed <= INT_MAX)
{
newsize = INT_MAX;
}
else
{
return NULL;
}
}
else
{
newsize = needed * 2;
}
if (p->hooks.reallocate != NULL)
{
/* reallocate with realloc if available
重新分配與重新分配(如果可用)*/
newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
if (newbuffer == NULL)
{
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
}
else
{
/* otherwise reallocate manually
否則手動重新分配*/
newbuffer = (unsigned char*)p->hooks.allocate(newsize);
if (!newbuffer)
{
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
memcpy(newbuffer, p->buffer, p->offset + 1);
p->hooks.deallocate(p->buffer);
}
p->length = newsize;
p->buffer = newbuffer;
return newbuffer + p->offset;
}
序列化json字串確實繁瑣,其主要花費時間在緩衝區邊界界定,內容複製,記憶體分配處理上。
大致瞭解一下工作流程,函式呼叫順序大致如下(主要功能):
cJSON_Print ==>
print ==>
print_value ==>
ensure和print_string和print_array等