cJSON开源项目详细解剖2

简介: cJSON开源项目详细解剖2

print_array

/*
功 能:将cjson数组结构体转换为字符串格式
参 数:item数组结构体指针 depth-深度  fmt-调整格式  p-输出缓冲结构体指针
返回值:
*/
static char* print_array(cJSON* item, int depth, int fmt, printbuffer* p)
{
  char** entries;// 保存一个数组的地址。该数组是指针数组。
  char* out = NULL;
  char* ptr = NULL;
  char* ret = NULL;
  int len = 5;
  cJSON* child = item->child;
  int numentries = 0;// 数组元素个数
  int i = 0;    // 循环计数器
  int fail = 0; // 处理出错标志
  size_t tmplen = 0;
  /* 计算数组元素个数 */
  while (NULL != child)
  {
    numentries++;
    child = child->next;
  }
  /* 显现的处理元素个数为0时 */
  if (0 == numentries)
  {
    if (NULL != p)
      out = ensure(p, 3);
    else
      out = (char*)cJSON_malloc(3);
    if (NULL != out)
      strcpy(out, "[]");
    return out;
  }
  if (NULL != p)
  {
    /* 组成输出数组 */
    i = p->offset;
    ptr = ensure(p, 1);
    if (NULL == ptr)
      return NULL;
    *ptr = '['; 
    p->offset++;
    child = item->child;
    while (NULL != child && 0 == fail)
    {
      print_value(child, depth + 1, fmt, p);
      p->offset = update(p);
      if (NULL != child->next)
      {
        len = fmt ? 2 : 1;
        ptr = ensure(p, len + 1);
        if (NULL == ptr)
          return NULL;
        *ptr++ = ',';
        if (0 != fmt)
          *ptr++ = ' ';
        *ptr = '\0';
        p->offset += len;
      }
      child = child->next;
    }
    ptr = ensure(p, 2);
    if (!ptr)
      return 0;
    *ptr++ = ']';
    *ptr = 0;
    out = (p->buffer) + i;
  }
  else
  {
    /* 分配一个指针数组来保存每个元素的指针*/
    entries = (char**)cJSON_malloc(numentries * sizeof(char*));
    if (NULL == entries)// 内存申请失败
      return 0;
    memset(entries, 0, numentries * sizeof(char*));// 初始化内存
    /* 检索所有结果: */
    child = item->child;
    while (NULL != child && 0 == fail)
    {
      ret = print_value(child, depth + 1, fmt, 0);
      entries[i++] = ret;
      if (NULL != ret)
        len += strlen(ret) + 2 + (fmt ? 1 : 0);
      else
        fail = 1;// 结束标记
      child = child->next;// 指向它的兄弟节点
    }
    /* 如果没有失败,尝试malloc输出字符串 */
    if (0 == fail)
      out = (char*)cJSON_malloc(len);
    if (NULL == out)
      fail = 1;
    if (0 != fail)
    {
      for (i = 0; i < numentries; i++)
      {
        if (entries[i])
          cJSON_free(entries[i]);
      }
      cJSON_free(entries);
      return NULL;
    }
    *out = '[';
    ptr = out + 1;
    *ptr = '\0';
    for (i = 0; i < numentries; i++)
    {
      tmplen = strlen(entries[i]);
      memcpy(ptr, entries[i], tmplen);
      ptr += tmplen;
      if (i != numentries - 1)
      {
        *ptr++ = ',';
        if (0 != fmt)
          *ptr++ = ' ';
        *ptr = '\0';
      }
      cJSON_free(entries[i]);
    }
    cJSON_free(entries);
    *ptr++ = ']';
    *ptr++ = '\0';
  }
  return out;
}


parse_object

/*
功 能:从文本构建一个对象(就是{....}包含的)
参 数:item-结构体 value-字符串指针
返回值:字符串新的指针
*/
static const char* parse_object(cJSON* item, const char* value)
{
  cJSON* child = NULL;
  if ('{' != *value)      // 不是对象
  {
    ep = value;       // 将后面不能解析的字符串给到ep
    return NULL;
  }
  item->type = cJSON_Object;  // 设置类型
  value = skip(value + 1);  // 从{后面开始跳过控制字符和空格
  if ('}' == *value)      // 空对象
  {
    return value + 1;
  }
  child = cJSON_New_Item(); // 确定{}中间有数据,就申请空间
  if (NULL == child)      // 申请空间失败
  {
    return NULL;
  }
  item->child = child;    // 连接当前节点
  // 先将指针跳过控制字符和空格,然后将字符串放入堆区,
  // 最后再将指针跳过控制字符和空格
  value = skip(parse_string(child, skip(value)));
  if ('\0' == value)      // 指针走到头了
  {
    return NULL;
  }
  child->string = child->valuestring; // 将key存入string中,
  child->valuestring = '\0';
  if (':' != *value)      // 没找到":" 
  {
    ep = value;
    return NULL;
  }
  // 先将指针跳过控制字符和空格,然后将接下来字符串指针找匹配的函数,
  // 然后将其放入孩纸结构体中,最后再将指针跳过控制字符和空格
  value = skip(parse_value(child, skip(value + 1)));
  if ('\0' == value)      // 指针走到头了
  {
    return NULL;
  }
  while (',' == *value)   // 还有,表示还有数据
  {
    cJSON* new_item;
    new_item = cJSON_New_Item();
    if (NULL == new_item) // 申请空间失败
    {
      return NULL;
    }
    // 将兄弟节点连接起来
    child->next = new_item;
    new_item->prev = child;
    child = new_item;   // 更新节点指针
    // 先将指针跳过控制字符和空格,然后将接下来字符串拷贝到堆区
    // 最后再将指针跳过控制字符和空格
    value = skip(parse_string(child, skip(value + 1)));
    if ('\0' == value)    // 字符串指针遇到'\0'
    {
      return NULL;
    }
    child->string = child->valuestring;// 将key放入string中
    child->valuestring = '\0';
    if (':' != *value)    // 未找到":"
    {
      ep = value;
      return NULL;
    }
    // 先将指针跳过控制字符和空格,然后将接下来对字符串做相应处理
    // 最后再将指针跳过控制字符和空格
    value = skip(parse_value(child, skip(value + 1)));
    if (value == NULL)
      return NULL;
  }
  if ('}' == *value)      // {}结束标记
  {
    return value + 1;
  }
  ep = value;         // 匹配到'}' 发生错误
  return NULL;
}


print_object

/*
功 能:将对象结构体转换为字符串存储在输出缓冲区中
参 数:item-对象   depth-深度  fmt-输出格式(1-有换行符 0-无)    p-输出缓冲结构体指针
返回值:返回字符串在堆区起始地址
*/
static char* print_object(cJSON* item, int depth, int fmt, printbuffer* p)
{
  char** entries = NULL;        // 保存item中元素的value在输出缓冲区的地址
  char** names  = NULL;       // 保存item中元素的key在输出缓冲区的地址
  char* out = NULL;         // 保存item转换为文本在堆区的位置
  char* ptr = NULL;
  char* ret = NULL;
  char* str = NULL;
  int len = 7;            // 字符串的长度 最少 "","" 7个字节
  int i = 0, j = 0;         // 循环计数器
  cJSON* child = item->child;     // 指向item的第一个儿子
  int numentries = 0;         // 保存对象中对的个数
  int fail = 0;           // 保存到输出缓冲区的出错标志
  size_t tmplen = 0;
  while (NULL != child)       // 计算有多少对元素
  {
    numentries++;
    child = child->next;
  }
  if (0 == numentries)        // 键值对的个数为0
  {
    if (NULL != p)          // 输出缓冲结构体指针不为NULL
    {
      // 判断当前偏移量下比较len+3(因为需要放俩个""和一个\0)长度的字符是否越界并作出保护措施
      out = ensure(p, fmt ? depth + 4 : 3);
    }
    else
    {
      out = (char*)cJSON_malloc(fmt ? depth + 4 : 3);
    }
    if (NULL == out)        // 申请空间失败
    {
      return 0;
    }
    ptr = out;
    *ptr++ = '{';         // 大括号开
    if (0 != fmt)
    {
      *ptr++ = '\n';
      for (i = 0; i < depth - 1; i++)
        *ptr++ = '\t';
    }
    *ptr++ = '}';         // 大括号闭
    *ptr++ = '\0';
    return out;
  }
  if (NULL != p)            // 输出缓冲结构体指针不为NULL
  {
    /* 组成的输出: */
    i = p->offset;
    len = fmt ? 2 : 1;        // 有换行符多一个字符"\n\0"
    // 判断当前偏移量下比较len+1(有换行符三个字节,无换行符俩个字节)长度的字符是否越界并作出保护措施
    ptr = ensure(p, len + 1);
    if (NULL == ptr)        // 地址无效
      return NULL;
    *ptr++ = '{';         // 大括号开
    if (1 == fmt)         // 判断有无换行符
      *ptr++ = '\n';
    *ptr = '\0';
    p->offset += len;       // 输出缓冲结构体的偏移量更新
    child = item->child;      // 指向下一个儿子
    depth++;            // 深度+1
    while (NULL != child)
    {
      if (1 == fmt)       // 有换行符
      {
        ptr = ensure(p, depth); // 安全检查
        if (NULL == ptr)    // 指针无效
          return NULL;
        for (j = 0; j < depth; j++)
          *ptr++ = '\t';
        p->offset += depth;   // 更新输出缓冲结构体指针的偏移量
      }
      print_string_ptr(child->string, p);// 将字符串转换成文本模式
      p->offset = update(p);    // 获取缓冲区的字符串长度
      len = fmt ? 2 : 1;      // 计算长度
      ptr = ensure(p, len);   // 安全检查
      if (NULL == ptr)      // 指针无效
        return NULL;
      *ptr++ = ':';
      if (1 == fmt)
        *ptr++ = '\t';
      p->offset += len;     // 更新输出缓冲结构体指针偏移量
      print_value(child, depth, fmt, p);// 将json结构体转换为字符串。
      p->offset = update(p);    // 获取缓冲区的字符串长度
      len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
      ptr = ensure(p, len + 1); // 安全检查
      if (NULL == ptr)      // 指针无效
        return NULL;
      if (NULL != child->next)  // 判断还有无元素(节点)
        *ptr++ = ',';
      if (1 == fmt) 
        *ptr++ = '\n';
      *ptr = '\0';
      p->offset += len;     // 更新输出缓冲结构体指针偏移量
      child = child->next;    // 继续下一个节点
    }
    ptr = ensure(p, fmt ? (depth + 1) : 2); // 安全检查 考虑了换行符和tab
    if (NULL == ptr)        // 指针无效
      return 0;
    if (1 == fmt)
    {
      for (i = 0; i < depth - 1; i++)   // 深度
        *ptr++ = '\t';
    }
    *ptr++ = '}';
    *ptr = '\0';
    out = (p->buffer) + i;
  }
  else  // 输出缓冲结构体指针不为NULL
  {
    entries = (char**)cJSON_malloc(numentries * sizeof(char*)); // 给value申请空间
    if (NULL == entries)      // 申请空间失败
    {
      return NULL;
    }
    names = (char**)cJSON_malloc(numentries * sizeof(char*)); // 给key申请空间
    if (NULL == names)        // 申请空间失败
    {
      cJSON_free(entries);    // 释放前面申请成功的空间
      return NULL;
    }
    memset(entries, 0, sizeof(char*) * numentries);       // 给value空间初始化
    memset(names, 0, sizeof(char*) * numentries);       // 给key空间初始化
    /* 将所有结果收集到我们的数组中: */
    child = item->child;      // 指向数组第一个元素
    depth++;            // 深度增加 \t
    if (0 != fmt)         
      len += depth;
    while (NULL != child)
    {
      names[i] = str = print_string_ptr(child->string, 0);  // 返回该元素key字符串在输出缓冲区的起始位置
      entries[i++] = ret = print_value(child, depth, fmt, 0); // 返回该元素value字符串在输出缓冲区的起始位置
      if (NULL != str && NULL != ret)
      {
        // fmt为换行符 depth为深度\t 深度可叠加 但是换行符最多有一个
        // key为一个字符串 value为一个字符串 所以会多出来俩个\0 因为strlen不算\0 
        len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0);
      }
      else            // 出错了
      {
        fail = 1;
      }
      child = child->next;    // 处理下一个元素(下一个节点)
    }
    /* 尝试分配输出字符串 */
    if (0 == fail)
      out = (char*)cJSON_malloc(len);
    if (NULL == out)        // 申请失败
      fail = 1;
    /* 处理失败 */
    if (1 == fail)
    {
      for (i = 0; i < numentries; i++)  // 将俩个数组中保存堆区开辟的空间依次释放掉
      {
        if (NULL != names[i])
          cJSON_free(names[i]);
        if (NULL != entries[i])
          cJSON_free(entries[i]);
      }
      cJSON_free(names);
      cJSON_free(entries);
      return NULL;
    }
    /* 组成的输出: */
    *out = '{';               // 存入大括开
    ptr = out + 1;              // 指向未填充的地址
    if (1 == fmt)             // 有换行符
      *ptr++ = '\n';
    *ptr = '\0';          
    for (i = 0; i < numentries; i++)
    {
      if (1 == fmt)
      {
        for (j = 0; j < depth; j++)   // 深度
          *ptr++ = '\t';
      }
      tmplen = strlen(names[i]);      // 计算当前key字符串的长度
      memcpy(ptr, names[i], tmplen);    // 拷贝
      ptr += tmplen;            // 指针更新
      *ptr++ = ':';         
      if (1 == fmt)
        *ptr++ = '\t';
      strcpy(ptr, entries[i]);      // 拷贝当前value字符串
      ptr += strlen(entries[i]);      // 指针更新
      if (i != numentries - 1)
        *ptr++ = ',';
      if (1 == fmt)
        *ptr++ = '\n';
      *ptr = '\0';
      cJSON_free(names[i]);       // 释放掉当前键值对所占的内存
      cJSON_free(entries[i]);
    }
    cJSON_free(names);
    cJSON_free(entries);
    if (1 == fmt)             // 格式操作
    {
      for (i = 0; i < depth - 1; i++) 
        *ptr++ = '\t';
    }
    *ptr++ = '}';             // 大括号闭
    *ptr++ = '\0';
  }
  return out;                 // 返回字符串在堆区起始地址
}


cJSON_GetArraySize

/* 获取数组大小/对象个数 */
int cJSON_GetArraySize(cJSON* array)
{
  cJSON* c = array->child;  // 指向第一个儿子
  int i = 0;
  while (NULL != c)
  {
    i++;
    c = c->next;
  }
  return i;       
}


cJSON_GetArrayItem

/* 查找数组结构体第item(从0开始)个子结构体的地址并返回 */
cJSON* cJSON_GetArrayItem(cJSON* array, int item)
{
  cJSON* c = array->child;
  while (NULL != c && item > 0)
  {
    item--;
    c = c->next;
  }
  return c;
}

cJSON_GetObjectItem

/* 查找对象结构体中key为string的子结构体的地址并返回 */
cJSON* cJSON_GetObjectItem(cJSON* object, const char* string)
{
  cJSON* c = object->child;
  while (NULL != c && 0 != cJSON_strcasecmp(c->string, string))
  {
    c = c->next;
  }
  return c;
}


suffix_object

/* 将兄弟节点连接起来  类似与链表结构 */
static void suffix_object(cJSON* prev, cJSON* item)
{
  prev->next = item;
  item->prev = prev;
}


create_reference

/* 用于处理引用的实用程序(拷贝一份item结构体)  */
static cJSON* create_reference(cJSON* item)
{
  cJSON* ref = cJSON_New_Item();
  if (NULL == ref)// 申请失败
  {
    return NULL;
  }
  memcpy(ref, item, sizeof(cJSON));// 内存初始化
  ref->string = '\0';
  ref->type |= cJSON_IsReference;
  ref->next = NULL;
  ref->prev = NULL;
  return ref;
}


cJSON_AddItemToArray

/*
功 能:添加元素(子结构体)到数组(父结构体)最后
参 数:array-数组结构体  item-元素子结构体
返回值:无
*/
void cJSON_AddItemToArray(cJSON* array, cJSON* item)
{
  cJSON* c = array->child;    // 指向父结构体的第一个儿子
  if (NULL == item)       // 若发现子结构体指针为空就直接结束
  {
    return;
  }
  if (NULL == c)          // 若发现父结构的儿子为空时,直接添加
  {
    array->child = item;
  }
  else              // 若发现父结构的儿子不为空时,就开始找最后那个儿子节点
  {
    while (NULL != c && NULL != c->next)
    {
      c = c->next;
    }
    suffix_object(c, item);   // 连接新节点函数
  }
}


cJSON_AddItemToObject

/*
功 能:添加子结构到对象中
参 数:object-对象  string-新子结构的key  item-新结构体的value
返回值:无
*/
void cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item)
{
  if (NULL == item)           // 子结构体指针为NULL
  {
    return;
  }
  if ('\0' != item->string)       // 子结构体中的key(string)不为空.就释放掉
  {
    cJSON_free(item->string);
  }
  // 先在堆区开辟空间将key(string)放入然后再将堆区地址给到key(string)保管
  item->string = cJSON_strdup(string);
  cJSON_AddItemToArray(object, item);   // 将子结构体放入对象(jbject)中
}


cJSON_AddItemToObjectCS

/*
功 能:添加元素结构体到对象结构体中
参 数:object-对象  string-新添加元素的key   item-新添加元素的value
返回值:无
*/
void cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item)
{
  if (NULL == item)
  {
    return;
  }
  if (0 == (item->type & cJSON_StringIsConst) && '\0' != item->string)  // 若item的key存在就先释放
  {
    cJSON_free(item->string);
  }
  item->string = (char*)string;   // 将key放入item
  item->type |= cJSON_StringIsConst;  // 将type放入item
  cJSON_AddItemToArray(object, item); // 再将item放入对象(数组)中
}


cJSON_AddItemReferenceToArray

/* 添加元素引用到数组中 */
void cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item)
{
  cJSON_AddItemToArray(array, create_reference(item));
}


cJSON_AddItemReferenceToObject

/* 添加元素引用到对象中 */
void cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item)
{
  cJSON_AddItemToObject(object, string, create_reference(item));
}


目录
相关文章
|
Java Apache
阿里Java开发手册一方库/二方库/三方库等概念详解
阿里Java开发手册一方库/二方库/三方库等概念详解
1534 0
|
8天前
|
存储 JSON NoSQL
cJSON项目解析
cJSON项目解析
|
8天前
|
Rust Java 编译器
从Rustup出发看看Rust语言的编译生态
1. Rust和LLVM的关系是怎样的? 2. Rustup中targets是什么,为什么可以安装多个? 3. Rust在windows上为什么需要安装Visual studio? 4. Rust工具链有哪些工具?
|
11月前
|
存储 JSON JavaScript
cJSON开源项目详细解剖1
cJSON开源项目详细解剖
121 0
|
11月前
cJSON开源项目详细解剖3
cJSON开源项目详细解剖3
124 0
|
11月前
|
JavaScript 前端开发
全解析 ESM 模块语法,出去还是进来都由你说了算
模块语法是ES6的一个重要特性,它的出现让JavaScript的模块化编程成为了可能。在JavaScript中可以直接使用import和export关键字来导入和导出模块。
133 0
|
11月前
|
前端开发 JavaScript 搜索推荐
Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】(四)
Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】
70 0
|
11月前
|
JavaScript 前端开发 API
Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】(二)
Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】
144 0
Vue3项目框架搭建封装,一次学习,终身受益【万字长文,满满干货】(二)
|
机器学习/深度学习 SQL 存储
Python数据分析库介绍及引入惯例
Python数据分析库介绍及引入惯例
115 0
|
前端开发 算法 数据处理
前端基础向~从项目出手封装工具函数
前端基础向~从项目出手封装工具函数
143 0

热门文章

最新文章