边缘计算网关
一、项目整体框架图
二、项目整体描述
边缘计算网关项目主要实现了智能家居场景和工业物联网场景下设备的数据采集和控制。
整个项目分为三大层:用户接口层、网关层、设备层。
其中用户层通过QT客户端、WEB界面及阿里云提供数据展示和用户接口。
网关使用虚拟机代替,基于Linux系统开发,起到数据中转作用,向下对接设备,向上对接客户端及云。
设备家居设备使用STM32单片机自主开发,工业设备使用modbus slave软件模拟。
我在整个项目中开发的模块是:设备搜索响应模块
设备搜索模块主要完成局域网内网关搜索及配置下发等功能。首先QT客户端会通过UDP广播发送搜索协议,搜索进程检测到协议匹配后,会进行相应的回复。客户端获取回复同时会获知网关设备的IP地址和端口,接下来设备会基于此IP地址和端口建立TCP服务器,QT作为客户端连入建立长连接。TCP连接成功后,即可完成后续的配置文件下发及心跳包等功能。
三、各模块功能介绍
WEB模块
移植lighttpd作为网关端服务器,实现了网关的内置网页功能。通过浏览器访问网关的IP地址,即可登录到网关内置网页界面,实现了登录、远程设备信息展示和控制等功能。网页发送http请求后,lighttpd会将请求转发给cgi,通过自行开发的cgi程序,进行解析http请求并做出相应的回复。
QT客户端
使用QT开发了桌面客户端,通过MQTT协议实现数据的采集,协议使用JSON格式,解析完成后刷新到界面上。用户操作界面组件,下发协议来控制设备。(后面的这块需要根据实际情况来描述)主界面使用堆栈窗体来布局,通过MQTT协议来实现和网关设备的通信。收到来自设备的JSON数据后,通过QJson类反序列化得到实际数据,然后刷新到相应的界面上。QT客户端主要完成了设备数据的展示、设备命令的控制及历史数据查询等功能。
阿里云模块
使用阿里云的SDK完成阿里云物模型的对接,实现了设备数据上传到阿里云平台,通过阿里云的在线调试功能,可远程实现设备的控制。
数据上报模块
数据上报模块实现了数据从设备到QT客户端的中转,首先设备对应的进程会把采集到的数据刷新到共享内存中,上报进程从共享内存中轮询数据点,根据上报策略来进行上报。上报前会把数据点信息转换为JSON格式,通过MQTT上报给QT客户端。客户端下发的JSON指令解析后,根据设备转发给具体的进程,完成设备的控制。同时,上报模块还需要根据用户的设置修改网关的配置参数,如上报模式、mqtt服务器地址等。
监控模块
移植mjpg-streamer库,实现了摄像头数据的采集以及分发。mjpg-streamer通过UVC接口实现了摄像头的图像帧采集,通过修改mjpg-streamer源码,将采集到的图像帧缓冲区分别通过http协议和UDP协议发送到了网页和QT客户端,在两个客户端上实现了视频流展示。
STM32采集模块
stm32采集模块实现了stm32设备的通信对接,向上通过串口协议采集单片机的数据,并刷新到共享内存,以便上报模块进程使用。向下接收来自上报模块的JSON控制指令,解析后,转换为相应的控制指令后通过串口发送给设,实现设备的控制。
modbus采集模块
modbus采集模块实现了modbus工业设备的通信对接,向上通过modbusTCP协议采集单片机的数据,并刷新到共享内存,以便上报模块进程使用。向下接收来自上报模块的JSON控制指令,解析后,转换为标准的modbusTCP指令后通过网络发送给设备,实现设备的控制。
单片机设备模块
单片机模拟了智能家居系统的设备场景,分别实现了传感器数据采集上报、设备的远程控制等功能。通过ADC采集XXX传感器,并按照上报协议通过串口发送给网关设备。接收来自网关的控制指令,通过JSON反序列化后,进行相应的设备控制。呼吸灯来表示设备运行状态,按键模拟墙壁开关同步模拟灯光控制。用到技术点:GPIO、串口通信、串口中断、GPIO中断、硬件定时器、ADC采集等。
四、部分代码展示
1. QT端部分代码展示
dialog.h代码:
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QtNetwork>#include <QMessageBox>
// 连接类
#include <QTcpSocket>
// 文本流类
#include <QTextStream>namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTpublic:explicit Dialog(QWidget *parent = 0);~Dialog();QTimer *getTimer() const;void setTimer(QTimer *value);public slots:void on_pushButton_clicked();void on_pushButton_2_clicked();void tcpReadReadSlot();void sendHeartbeat();private:Ui::Dialog *ui;QUdpSocket *sender;QTimer *timer;//QTcpSocket *tcpSocket;// QTcpSocket *client; // 连接类对象void btnConnClickedSlot(); // 连接按钮点击的槽函数void btnSendClickedSlot(); // 发送按钮点击的槽函数void connectedSlot(); // 连接成功的槽函数void disconnectedSlot(); // 连接失败的槽函数
};#endif // DIALOG_H
dialog.cpp代码:
#include "dialog.h"
#include "ui_dialog.h"
#include "string.h"
#include <QtNetwork>
#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QDebug>
#include <QCoreApplication>
#include <QTcpSocket>
#include <QTimer>
#include <QByteArray>
#include <QTextStream>
QTcpSocket tcpSocket;
QHostAddress host;
quint16 port;//QString message="living!";
Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{ui->setupUi(this);// 始终位于前台setWindowFlags(Qt::WindowStaysOnTopHint);sender = new QUdpSocket(this);connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));//tcpSocket= new QTcpSocket(this);connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(tcpReadReadSlot()));// 创建定时器,每隔1秒发送心跳包timer = new QTimer(this);connect(timer, &QTimer::timeout, this,&Dialog::sendHeartbeat);timer->start(1000); // 1000毫秒为间隔}Dialog::~Dialog()
{delete ui;
}void Dialog::on_pushButton_clicked()
{// qDebug() << %d",__LINE__;char buf[256] = {};QByteArray datagram = "coffee";sender->writeDatagram(datagram.data(),datagram.size(),QHostAddress("192.168.50.255"),8686);sender->readDatagram(buf,sizeof(buf),&host,&port);if(strcmp(buf, "yes") != 0){return;}// 尝试连接到主机tcpSocket.connectToHost(host,port);if (tcpSocket.waitForConnected(1000)) // 等待连接成功{// 创建JSON对象QJsonObject json;json["type"] = 1;// 添加其他键值对// 创建JSON文档QJsonDocument jsonDoc(json);// 将JSON文档转换为字节数组QByteArray jsonData = jsonDoc.toJson();// 发送JSON数据tcpSocket.write(jsonData);// 等待数据发送完毕if (tcpSocket.waitForBytesWritten()) {// qDebug() << "JSON data sent successfully1";} else {qDebug() << "Failed to send JSON data";}}}void Dialog::on_pushButton_2_clicked()
{// 创建JSON对象QJsonObject json;json["type"] = 3;// 添加其他键值对// 创建JSON文档QJsonDocument jsonDoc(json);// 将JSON文档转换为字节数组QByteArray jsonData = jsonDoc.toJson();// 发送JSON数据tcpSocket.write(jsonData);// 等待数据发送完毕if (tcpSocket.waitForBytesWritten()) {// qDebug() << "JSON data sent successfully1";} else {qDebug() << "Failed to send JSON data";}// tcpSocket.write(data); // 发送数据//tcpSocket.write(jsonData);//tcpSocket.waitForBytesWritten(); // 等待数据发送成功// tcpSocket.disconnectFromHost(); // 断开连接}void Dialog::tcpReadReadSlot()
{// /接收服务器端消息QTextStream input(&tcpSocket);QString text = input.readAll();qDebug() << text;//根据接收消息判断有无心跳if(text == "living"){// QMessageBox::information(this,"提示","持续心跳中");// QMessageBox::warning(this,"提示","type 1无内容!");return;}//根据接收消息判断有无点表,无则下发if(text=="no"){QFile file("D:/example1.json");// QFile file(filename);// QFile file(QCoreApplication::applicationDirPath()+"/example.txt");if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){qDebug() << "Failed to open file:" << file.errorString();return;}else{// 创建一个数据流QDataStream dataStream(&tcpSocket);// 设置数据流版本,用于跨平台兼容dataStream.setVersion(QDataStream::Qt_5_0);// 读取文件内容QByteArray fileData = file.readAll();qDebug()<<"read all = "<<fileData;tcpSocket.write(fileData);qDebug() << "File sent successfully";// 断开连接// tcpSocket.disconnectFromHost();//tcpSocket.waitForDisconnected();}}// //根据接收消息判断点表是否下发成功if(text == "succeed"){QMessageBox::information(this,"提示","点表下发成功");// QMessageBox::warning(this,"提示","type 1无内容!");return;}}void Dialog::sendHeartbeat()
{// 创建JSON对象QJsonObject json;json["type"] = 3;// 添加其他键值对// 创建JSON文档QJsonDocument jsonDoc(json);// 将JSON文档转换为字节数组QByteArray jsonData = jsonDoc.toJson();// 发送JSON数据tcpSocket.write(jsonData);// 等待数据发送完毕if (tcpSocket.waitForBytesWritten()) {// qDebug() << "JSON data sent successfully1";} else {qDebug() << "Failed to send JSON data";}}
dialog.ui代码
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>Dialog</class><widget class="QDialog" name="Dialog"><property name="geometry"><rect><x>0</x><y>0</y><width>486</width><height>325</height></rect></property><property name="windowTitle"><string>Dialog</string></property><layout class="QVBoxLayout" name="verticalLayout_2"><item><layout class="QVBoxLayout" name="verticalLayout"><item><widget class="QPushButton" name="pushButton"><property name="text"><string>下发点表</string></property></widget></item><item><widget class="QPushButton" name="pushButton_2"><property name="text"><string>心跳</string></property></widget></item></layout></item></layout></widget><layoutdefault spacing="6" margin="11"/><resources/><connections/>
</ui>
2. 网关层部分代码展示:
dev.c代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "cJSON.h"
#define N 64
void saveToFile(const char *data);
void parseJSON(const char *json);
int main(int argc, char const *argv[])
{int broadfd;//创建一个socket文件描述符broadfd = socket(AF_INET, SOCK_DGRAM, 0);if (broadfd < 0){perror("sock err");return -1;}//绑定套接字(ip+port)struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8686); //端口号addr.sin_addr.s_addr = INADDR_ANY;int addrlen = sizeof(addr);if (bind(broadfd, (struct sockaddr *)&addr, addrlen) < 0){perror("bind err");return -1;}ssize_t len;char buf[N] = {0};struct sockaddr_in cliaddr, caddr;//接收"coffee"搜索包bzero(buf, N);len = recvfrom(broadfd, buf, N, 0, (struct sockaddr *)&cliaddr, &addrlen);//判断是否是本公司产品:收到的数据是否"coffee"if (strcmp(buf, "coffee") != 0){printf("not my coffee\n");return -1;}//回复yes,告诉软件,我收到了搜索协议,并且回复地址sendto(broadfd, "yes", 4, 0, (struct sockaddr *)&cliaddr, addrlen);//变身为TCP服务器,准备接收软件的升级文件int tcpfd = socket(AF_INET, SOCK_STREAM, 0);if (tcpfd < 0){perror("sock err");return -1;}if (bind(tcpfd, (struct sockaddr *)&addr, addrlen) < 0){perror("bind err");return -1;}//监听套接字if (listen(tcpfd, 5) < 0){perror("listen err");return -1;}//接收客户端的连接printf("wait client connect\n");int clifd;socklen_t len1 = sizeof(caddr);clifd = accept(tcpfd, (struct sockaddr *)&caddr, &len1);if (clifd < 0){perror("accept err");return -1;}// printf("new cononect coming\n");// printf("tcpfd:%d clifd:%d\n", tcpfd, clifd);// printf("client:ip=%s port=%d\n", inet_ntoa(caddr.sin_addr),// ntohs(caddr.sin_port));char buf2[3000] = {0};char buf3[] = "no";char buf4[7] = "living";//size_t recvbyte=1;size_t recvbyte;
loop:while (1){recvbyte = recv(clifd, buf2, sizeof(buf2), 0);//printf("recvbyte = %d\n", recvbyte);if (recvbyte > 0){//printf("Received data: %s", buf2);cJSON *root = cJSON_Parse(buf2);if (cJSON_HasObjectItem(root,"version") ){char buf5[8] = "succeed";// printf("Failed to parse JSON data.\n");saveToFile(buf2);if (send(clifd, buf5, strlen(buf5), 0) < 0){perror("Sending data failed.");}printf("Succeeded in delivering the point table.\n") ;goto loop;// return 0;}cJSON *item = cJSON_GetObjectItem(root, "type");int type = item->valueint;//printf("Type: %d\n", type);if (type == 1){if (send(clifd, buf3, strlen(buf3), 0) < 0){perror("Sending data failed.");}goto loop;// printf("%d %s", strlen(buf3), buf3);//send(clifd, buf3, sizeof(bud3), 0);//saveToFile(buf2);}else if (type == 3){if (send(clifd, buf4, strlen(buf4), 0) < 0){perror("Sending data failed.");}// printf("%d %s\n", strlen(buf4), buf4);//send(clifd, buf4, strlen(buf4), 0);goto loop;//saveToFile(buf2);}}}
}void saveToFile(const char *data)
{FILE *file = fopen("/text.txt", "w+");if (file == NULL){perror("Failed to open file");exit(1);}fprintf(file, "%s", data);fclose(file);
}
Makefile代码:
#指定生成的文件名
OJB_OUT = dev#指定每一个c文件对应的.o文件
OBJS = dev.o #指定编译器
CC = gcc#指定需要的库和路径
ULDFLAGS = cJSON.c -lm###########################################
#以下的内容不需要修改
###########################################
all:$(OJB_OUT)$(OJB_OUT):$(OBJS)$(CC) -o $@ $^ $(ULDFLAGS)dep_files := $(foreach f,$(OBJS),.$(f).d)
dep_files := $(wildcard $(dep_files))ifneq ($(dep_files),)include $(dep_files)
endif$(OBJS):%.o:%.c$(CC) -Wp,-MD,.$@.d -c $< -o $@clean:rm -rf .*.o.d *.o $(OJB_OUT)
cJSON.h代码:
/*Copyright (c) 2009 Dave GamblePermission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.
*/#ifndef cJSON__h
#define cJSON__h#include <stddef.h>#ifdef __cplusplus
extern "C"
{
#endif/* cJSON Types: */
#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/* The cJSON structure: */
typedef struct cJSON {struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */int type; /* The type of the item, as above. */char *valuestring; /* The item's string, if type==cJSON_String */int valueint; /* The item's number, if type==cJSON_Number */double valuedouble; /* The item's number, if type==cJSON_Number */char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;typedef struct cJSON_Hooks {void *(*malloc_fn)(size_t sz);void (*free_fn)(void *ptr);
} cJSON_Hooks;/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
extern int cJSON_HasObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);/* These calls create a cJSON item of the appropriate type. */
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);/* These utilities create an Array of count items. */
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);/* Append item to the specified array/object. */
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); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);/* Remove/Detatch items from Arrays/Objects. */
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);/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. *//* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);extern void cJSON_Minify(char *json);/* Macros for creating things quickly. */
#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))/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))/* Macro for iterating over an array */
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)#ifdef __cplusplus
}
#endif#endif
cJSON.c代码:
/*Copyright (c) 2009 Dave GamblePermission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.
*//* 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"static const char *ep;const char *cJSON_GetErrorPtr(void) {return ep;}static int cJSON_strcasecmp(const char *s1,const char *s2)
{if (!s1) return (s1==s2)?0:1;if (!s2) return 1;for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;static char* cJSON_strdup(const char* str)
{size_t len;char* copy;len = strlen(str) + 1;if (!(copy = (char*)cJSON_malloc(len))) return 0;memcpy(copy,str,len);return copy;
}void cJSON_InitHooks(cJSON_Hooks* hooks)
{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;
}/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));if (node) memset(node,0,sizeof(cJSON));return node;
}/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{cJSON *next;while (c){next=c->next;if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);cJSON_free(c);c=next;}
}/* Parse the input text to generate a number, and populate the result into 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++; /* Has sign? */if (*num=='0') num++; /* is zero */if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */if (*num=='e' || *num=='E') /* Exponent? */{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */}n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */item->valuedouble=n;item->valueint=(int)n;item->type=cJSON_Number;return num;
}static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }typedef struct {char *buffer; int length; int offset; } printbuffer;static char* ensure(printbuffer *p,int needed)
{char *newbuffer;int newsize;if (!p || !p->buffer) return 0;needed+=p->offset;if (needed<=p->length) return p->buffer+p->offset;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=p->buffer+p->offset;return p->offset+strlen(str);
}/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item,printbuffer *p)
{char *str=0;double d=item->valuedouble;if (d==0){if (p) str=ensure(p,2);else str=(char*)cJSON_malloc(2); /* special case for 0. */if (str) strcpy(str,"0");}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);}else{if (p) str=ensure(p,64);else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */if (str){if (fpclassify(d) != FP_ZERO && !isnormal(d)) sprintf(str,"null");else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) sprintf(str,"%.0f",d);else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);else sprintf(str,"%f",d);}}return str;
}static unsigned parse_hex4(const char *str)
{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;
}/* Parse the input text into an unescaped cstring, and populate item. */
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;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{ptr++;switch (*ptr){case 'b': *ptr2++='\b'; break;case 'f': *ptr2++='\f'; break;case 'n': *ptr2++='\n'; break;case 'r': *ptr2++='\r'; break;case 't': *ptr2++='\t'; break;case 'u': /* transcode utf16 to utf8. */uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */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++;}}*ptr2=0;if (*ptr=='\"') ptr++;item->valuestring=out;item->type=cJSON_String;return ptr;
}/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str,printbuffer *p)
{const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;if (!flag){len=ptr-str;if (p) out=ensure(p,len+3);else out=(char*)cJSON_malloc(len+3);if (!out) return 0;ptr2=out;*ptr2++='\"';strcpy(ptr2,str);ptr2[len]='\"';ptr2[len+1]=0;return out;}if (!str){if (p) out=ensure(p,3);else out=(char*)cJSON_malloc(3);if (!out) return 0;strcpy(out,"\"\"");return out;}ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}if (p) out=ensure(p,len+3);else out=(char*)cJSON_malloc(len+3);if (!out) return 0;ptr2=out;ptr=str;*ptr2++='\"';while (*ptr){if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;else{*ptr2++='\\';switch (token=*ptr++){case '\\': *ptr2++='\\'; break;case '\"': *ptr2++='\"'; break;case '\b': *ptr2++='b'; break;case '\f': *ptr2++='f'; break;case '\n': *ptr2++='n'; break;case '\r': *ptr2++='r'; break;case '\t': *ptr2++='t'; break;default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */}}}*ptr2++='\"';*ptr2++=0;return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{const char *end=0;cJSON *c=cJSON_New_Item();ep=0;if (!c) return 0; /* memory fail */end=parse_value(c,skip(value));if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. *//* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}if (return_parse_end) *return_parse_end=end;return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{printbuffer p;p.buffer=(char*)cJSON_malloc(prebuffer);p.length=prebuffer;p.offset=0;return print_value(item,0,fmt,&p);return p.buffer;
}/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{if (!value) return 0; /* Fail on null. */if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }if (*value=='\"') { return parse_string(item,value); }if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }if (*value=='[') { return parse_array(item,value); }if (*value=='{') { return parse_object(item,value); }ep=value;return 0; /* failure. */
}/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{char *out=0;if (!item) return 0;if (p){switch ((item->type)&255){case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;}case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;}case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;}case cJSON_Number: out=print_number(item,p);break;case cJSON_String: out=print_string(item,p);break;case cJSON_Array: out=print_array(item,depth,fmt,p);break;case cJSON_Object: out=print_object(item,depth,fmt,p);break;}}else{switch ((item->type)&255){case cJSON_NULL: out=cJSON_strdup("null"); break;case cJSON_False: out=cJSON_strdup("false");break;case cJSON_True: out=cJSON_strdup("true"); break;case cJSON_Number: out=print_number(item,0);break;case cJSON_String: out=print_string(item,0);break;case cJSON_Array: out=print_array(item,depth,fmt,0);break;case cJSON_Object: out=print_object(item,depth,fmt,0);break;}}return out;
}/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{cJSON *child;if (*value!='[') {ep=value;return 0;} /* not an array! */item->type=cJSON_Array;value=skip(value+1);if (*value==']') return value+1; /* empty array. */item->child=child=cJSON_New_Item();if (!item->child) return 0; /* memory fail */value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */if (!value) return 0;while (*value==','){cJSON *new_item;if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */child->next=new_item;new_item->prev=child;child=new_item;value=skip(parse_value(child,skip(value+1)));if (!value) return 0; /* memory fail */}if (*value==']') return value+1; /* end of array */ep=value;return 0; /* malformed. */
}/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{char **entries;char *out=0,*ptr,*ret;int len=5;cJSON *child=item->child;int numentries=0,i=0,fail=0;size_t tmplen=0;/* How many entries in the array? */while (child) numentries++,child=child->next;/* Explicitly handle numentries==0 */if (!numentries){if (p) out=ensure(p,3);else out=(char*)cJSON_malloc(3);if (out) strcpy(out,"[]");return out;}if (p){/* Compose the output array. */i=p->offset;ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++;child=item->child;while (child && !fail){print_value(child,depth+1,fmt,p);p->offset=update(p);if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(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{/* Allocate an array to hold the values for each */entries=(char**)cJSON_malloc(numentries*sizeof(char*));if (!entries) return 0;memset(entries,0,numentries*sizeof(char*));/* Retrieve all the results: */child=item->child;while (child && !fail){ret=print_value(child,depth+1,fmt,0);entries[i++]=ret;if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;child=child->next;}/* If we didn't fail, try to malloc the output string */if (!fail) out=(char*)cJSON_malloc(len);/* If that fails, we fail. */if (!out) fail=1;/* Handle failure. */if (fail){for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);cJSON_free(entries);return 0;}/* Compose the output array. */*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(fmt)*ptr++=' ';*ptr=0;}cJSON_free(entries[i]);}cJSON_free(entries);*ptr++=']';*ptr++=0;}return out;
}/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{cJSON *child;if (*value!='{') {ep=value;return 0;} /* not an object! */item->type=cJSON_Object;value=skip(value+1);if (*value=='}') return value+1; /* empty array. */item->child=child=cJSON_New_Item();if (!item->child) return 0;value=skip(parse_string(child,skip(value)));if (!value) return 0;child->string=child->valuestring;child->valuestring=0;if (*value!=':') {ep=value;return 0;} /* fail! */value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */if (!value) return 0;while (*value==','){cJSON *new_item;if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */child->next=new_item;new_item->prev=child;child=new_item;value=skip(parse_string(child,skip(value+1)));if (!value) return 0;child->string=child->valuestring;child->valuestring=0;if (*value!=':') {ep=value;return 0;} /* fail! */value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */if (!value) return 0;}if (*value=='}') return value+1; /* end of array */ep=value;return 0; /* malformed. */
}/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{char **entries=0,**names=0;char *out=0,*ptr,*ret,*str;int len=7,i=0,j;cJSON *child=item->child;int numentries=0,fail=0;size_t tmplen=0;/* Count the number of entries. */while (child) numentries++,child=child->next;/* Explicitly handle empty object case */if (!numentries){if (p) out=ensure(p,fmt?depth+4:3);else out=(char*)cJSON_malloc(fmt?depth+4:3);if (!out) return 0;ptr=out;*ptr++='{';if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}*ptr++='}';*ptr++=0;return out;}if (p){/* Compose the output: */i=p->offset;len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0;*ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len;child=item->child;depth++;while (child){if (fmt){ptr=ensure(p,depth); if (!ptr) return 0;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 (!ptr) return 0;*ptr++=':';if (fmt) *ptr++='\t';p->offset+=len;print_value(child,depth,fmt,p);p->offset=update(p);len=(fmt?1:0)+(child->next?1:0);ptr=ensure(p,len+1); if (!ptr) return 0;if (child->next) *ptr++=',';if (fmt) *ptr++='\n';*ptr=0;p->offset+=len;child=child->next;}ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';*ptr++='}';*ptr=0;out=(p->buffer)+i;}else{/* Allocate space for the names and the objects */entries=(char**)cJSON_malloc(numentries*sizeof(char*));if (!entries) return 0;names=(char**)cJSON_malloc(numentries*sizeof(char*));if (!names) {cJSON_free(entries);return 0;}memset(entries,0,sizeof(char*)*numentries);memset(names,0,sizeof(char*)*numentries);/* Collect all the results into our arrays: */child=item->child;depth++;if (fmt) len+=depth;while (child && !fail){names[i]=str=print_string_ptr(child->string,0);entries[i++]=ret=print_value(child,depth,fmt,0);if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;child=child->next;}/* Try to allocate the output string */if (!fail) out=(char*)cJSON_malloc(len);if (!out) fail=1;/* Handle failure */if (fail){for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}cJSON_free(names);cJSON_free(entries);return 0;}/* Compose the output: */*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;for (i=0;i<numentries;i++){if (fmt) for (j=0;j<depth;j++) *ptr++='\t';tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;*ptr++=':';if (fmt) *ptr++='\t';strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);if (i!=numentries-1) *ptr++=',';if (fmt) *ptr++='\n';*ptr=0;cJSON_free(names[i]);cJSON_free(entries[i]);}cJSON_free(names);cJSON_free(entries);if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';*ptr++='}';*ptr++=0;}return out;
}/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
int cJSON_HasObjectItem(cJSON *object,const char *string) {cJSON *c=object->child;while (c ){if(cJSON_strcasecmp(c->string,string)==0){return 1;}c=c->next;}return 0;
}/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}/* Replace array/object items with new ones. */
void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{cJSON *newitem,*cptr,*nptr=0,*newchild;/* Bail on bad ptr */if (!item) return 0;/* Create new item */newitem=cJSON_New_Item();if (!newitem) return 0;/* Copy over all vars */newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}/* If non-recursive, then we're done! */if (!recurse) return newitem;/* Walk the ->next chain for the child. */cptr=item->child;while (cptr){newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */if (!newchild) {cJSON_Delete(newitem);return 0;}if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */cptr=cptr->next;}return newitem;
}void cJSON_Minify(char *json)
{char *into=json;while (*json){if (*json==' ') json++;else if (*json=='\t') json++; /* Whitespace characters. */else if (*json=='\r') json++;else if (*json=='\n') json++;else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */else *into++=*json++; /* All other characters. */}*into=0; /* and null-terminate. */
}
相关文章:
边缘计算网关
一、项目整体框架图 二、项目整体描述 边缘计算网关项目主要实现了智能家居场景和工业物联网场景下设备的数据采集和控制。 整个项目分为三大层:用户接口层、网关层、设备层。 其中用户层通过QT客户端、WEB界面及阿里云提供数据展示和用户接口。 网关使用虚拟机代替…...
1800_vim的宏录制功能尝试
全部学习信息汇总: GreyZhang/editors_skills: Summary for some common editor skills I used. (github.com) 最近5年多来,我emacs的编辑器用的还是比较多的。我的配置基本上是一个spacemacs,然后根据自己的需求增加了一丁点儿的其他配置。而…...
Ultra-Fast-Lane-Detection-v2 {后处理优化}//参考
采用三次多项式拟合生成的anchor特征点,在给定的polyfit_draw函数中,degree参数代表了拟合多项式的度数。 具体来说,当我们使用np.polyfit函数进行数据点的多项式拟合时,我们需要指定一个度数。这个度数决定了多项式的复杂度。例…...
【面试题精讲】Java静态方法和实例方法有何不同?
★ 有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top ” 首发博客地址[1] 面试题手册[2] 系列文章地址[3] Java 中的静态方法和实例方法在使用和行为上有一些不同之处。 调用方式不同: 静…...
【数据结构】布隆过滤器
布隆过滤器的提出 在注册账号设置昵称的时候,为了保证每个用户昵称的唯一性,系统必须检测你输入的昵称是否被使用过,这本质就是一个key的模型,我们只需要判断这个昵称被用过,还是没被用过。 方法一:用红黑…...
linux基础4---内存
1、什么是内存泄漏,怎么解决内存泄漏? 在嵌入式Linux中,内存泄漏是指由于疏忽或错误,导致一些对象或资源无法被垃圾回收器回收,从而导致内存占用不断增加,最终导致设备性能下降。内存泄漏对程序的影响很大,可能会导致应用程序变慢、崩溃或者消耗大量的内存,最终导致设…...
图论---拓扑排序
概念 一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。一直进行上面的处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。拓扑排序是对DAG(有向无环图)上的节…...
java Spring Boot 将日志写入文件中记录
我们之前的一套操作来讲 日志都是在控制台上的 但 如果你的项目在正式环境上跑 运维人员突然告诉你说日志报错了,但你日志只在控制台上,那公司项目如果访问量很大 那你是很难在控制台上找到某一条日志的 这时 我们就可以用文件把它记下来 我们打开项目 …...
Android 开发错误集合
🔥 开发错误集合一 🔥 Caused by: java.lang.ClassNotFoundException: Didnt find class "com.mask.app.ui.LoginRegisterActivity" on path: DexPathList[[zip file "/data/app/~~NMvHVhj8V6-HwGbh2amXDA/com.mask.app-PWbg4xIlETQ3eVY…...
VSCode个人设置习惯
账号登陆同步 点击左下角齿轮或者用户头像–>Turn on Settings Sync–>全选–>Sign in &Turn on。 可以同步配置、快捷键、插件、用户代码片段、UI状态 Windows下将powershell改为cmd 在vscode打开集成终端,点击右上角加号右边的下拉菜单,…...
代码随想录训练营二刷第四十七天 | 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数
代码随想录训练营二刷第四十七天 | 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数 一、70. 爬楼梯 (进阶) 题目链接:https://leetcode.cn/problems/climbing-stairs/ 思路:物品是楼梯1和2,…...
beego-简单项目写法--后续放到git上
Beego案例-新闻发布系统 1.注册 后台代码和昨天案例代码一致。,所以这里面只写一个注册的业务流程图。 **业务流程图 ** 2.登陆 业务流程图 登陆和注册业务和我们昨天登陆和注册基本一样,所以就不再重复写这个代码 但是我们遇到的问题是如何做代码的迁移&…...
【算法|动态规划No.9】leetcodeLCR 091. 粉刷房子
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…...
基于SpringBoot的图书进销存管理系统
目录 前言 一、技术栈 二、系统功能介绍 用户信息管理 图书类型管理 商品退货管理 客户信息管理 图书添加 客户添加 应收金额 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实…...
回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测
回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测 目录 回归预测 | MATLAB实现PSO-SVR粒子群优化支持向量机回归多输入单输出预测预测效果基本介绍模型描述程序设计预测效果 <...
vue3使用v-model控制子组件进行双向数据绑定
vue2写法: 中父组件调用子组件: <child :isShow.sync"isShow" v-show"isShow"/> 子组件想要消失, 在子组件写: this.$emit("update:isShow",false); 具体代码就不粘贴了 vue3写法: 父组件核心代码: v-model:a"xxx" 子组…...
.netCore .net5,6,7 存日志文件
如果你使用 .netCore及以上版本(.net5,.net6,.net7)... 系统默认自带日志中间件(log4net) 对,就是上次java 日志大漏洞的兄弟....... 控制台自动打印日志就是它的功劳 现在我们想存日志文件,怎么办 很简单. 1.在项目中添加日志配置文件 文件名 : log4net.config 不能…...
【数据结构---排序】很详细的哦
本篇文章介绍数据结构中的几种排序哦~ 文章目录 前言一、排序是什么?二、排序的分类 1.直接插入排序2.希尔排序3.选择排序4.冒泡排序5.快速排序6.归并排序总结 前言 排序在我们的生活当中无处不在,当然,它在计算机程序当中也是一种很重要的操…...
GitHub爬虫项目详解
前言 闲来无事浏览GitHub的时候,看到一个仓库,里边列举了Java的优秀开源项目列表,包括说明、仓库地址等,还是很具有学习意义的。但是大家也知道,国内访问GitHub的时候,经常存在访问超时的问题,…...
辅助驾驶功能开发-功能对标篇(7)-NOA领航辅助系统-上汽荣威
1.横向对标参数 厂商上汽荣威车型荣威RX5(燃油车)上市时间2022Q3方案10V3R摄像头前视摄像头1*(8M)侧视摄像头4后视摄像头1环视摄像头4DMS摄像头1雷达毫米波雷达34D毫米波雷达/超声波雷达12激光雷达/域控供应商1*(宏景智驾)辅助驾驶软件供应商地平线高精度地图中海庭芯片J3合作…...
第0次 序言
突然想起有好多书没有看,或者看了也没留下任何记录,以后有空必须得好好整理才行,这次就从《Linux命令行和shell脚本编程大全开始》 本文完全是闲聊,自娱自乐,我觉得做开发是一件很快乐的事情,但是工作是开发…...
ESP32设备驱动-OLED显示单个或多个DS18B20传感器数据
OLED显示单个或多个DS18B20传感器数据 文章目录 OLED显示单个或多个DS18B20传感器数据1、DS18B20介绍2、硬件准备3、软件准备4、代码实现4.1 读取单个DS18B20数据4.2 驱动多个DS18B20传感器4.3 OLED显示DS18B20数据在本文中,我们将介绍如何ESP32驱动单个或多个DS18B20传感器,…...
MongoDB快速上手
文章目录 1、mongodb相关概念1.1、业务应用场景1.2、MongoDB简介1.3、体系结构1.3.1 数据库 (databases) 管理语法1.3.2 集合 (collection) 管理语法 1.4、数据模型1.5、MongoDB的特点 2、单机部署3、基本常用命令3.1、案例需求3.2、数据库操作3.2.1 选择和创建数据库3.2.2 数据…...
maven 初学
1. maven 安装 配置安装 路径 maven 下载位置: D:\software\apache-maven-3.8.6 默认仓库位置: C:\Users\star-dream\.m2\repository 【已更改】 本地仓库设置为:D:\software\apache-maven-3.8.6\.m2\repository 镜像已更改为阿里云中央镜像仓库 <mirrors>…...
解决WPF+Avalonia在openKylin系统下默认字体问题
一、openKylin简介 openKylin(开放麒麟) 社区是在开源、自愿、平等和协作的基础上,由基础软硬件企业、非营利性组织、社团组织、高等院校、科研机构和个人开发者共同创立的一个开源社区,致力于通过开源、开放的社区合作ÿ…...
智能合约漏洞,Dyna 事件分析
智能合约漏洞,Dyna 事件分析 1. 漏洞简介 https://twitter.com/BlockSecTeam/status/1628319536117153794 https://twitter.com/BeosinAlert/status/1628301635834486784 2. 相关地址或交易 攻击交易 1: https://bscscan.com/tx/0x7fa89d869fd1b89e…...
Elasticsearch基础篇(四):Elasticsearch7.x的官方文档学习(Set up Elasticsearch)
Set up Elasticsearch 1 Configuring Elasticsearch(配置 Elasticsearch)1.1 Setting JVM Options(设置JVM选项)1.2 Secure Settings(安全设置)Introduction(介绍)Using the Keystore(使用密钥库)Applying Changes(应用更改)Reloadable Secure Settings(可重新加载的安全设置)R…...
二叉树的遍历方式和代码
二叉树的三种遍历和代码 1.前序遍历2.中序遍历3.后序遍历4.三种遍历方式的代码实现 1.前序遍历 学习二叉树结构,最简单的方式就是遍历。所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具…...
小样本学习——匹配网络
目录 匹配网络 (1)简单介绍: (2)专业术语 (3)主要思想 (4)训练过程 问题 回答 MANN 匹配网络 (1)简单介绍: Matching netwo…...
CSS 常用样式 之字体属性
font-weight(字体粗细) 字体粗细用于设置文本的粗细程度,可以使用如下的值: normal:正常字体(默认)bold:加粗字体bolder:更加加粗lighter:更加细 代码实例…...
wordpress单页导航模版/制作网页的步骤
点击上方蓝字,记得关注我们!猪2019年 2月年日一二三四五六123456789101112131415161718192021222324252627 28 1 2中级结构BIM预计三天总学习时长:2.5小时学习内容:1、剪力墙柱、框架柱的绘制2、梁的绘制3、梁的快速绘制技巧4、板的绘制5…...
东莞大朗网站建设/新手做外贸怎么入门
(跃迁之路)专栏 叨叨两句 技术的精进不能只是简单的刷题,而应该是不断的“刻意”练习该系列改版后正式纳入【跃迁之路】专栏,持续更新一、SQL 【跃迁之路】SQL语句刻意练习(最后更新:2018.05.04) 二、JAVA基础 IO流 【…...
做网站收费标准/靠谱的拉新平台
OSI模型、TCP/IP 技术详解前言一、名词阐述二、认识 OSI 模型、TCP/IP三、OSI 参考模型详解1、应用层2、表示层3、会话层4、传输层5、网络层6、数据链路层7、物理层四、数据的封装与解封装前言 此次系列文章,作为计算机网络初级课程分享,其中涉及到的设…...
要做网站到哪里做/免费软文发布平台有哪些
2019独角兽企业重金招聘Python工程师标准>>> java编译成.class 有两种方式 使用javac,默认使用的release方式。 release模式下对于函数参数会改变 public class Test{ private static void sayHello(){ System.out.println("Hello world"); …...
asp门户网站源码/seo优化专员工作内容
转载请注明出处:http://www.cnblogs.com/buxizhizhoum/p/8086230.html 环境: 系统:ubuntu 16.04 cpu:i5 gpu:gt920m memory:8g disk:SSD 256g 1.安装显卡驱动 首先需要保证电脑有满…...
网站模版怎么编辑器/app拉新项目推广代理
阅读文本大概需要 13 分钟。 通过之前的文章介绍,你现在应该对 pyspider 有了一定的认识。如果你还不清楚的话,可以再回顾下之前的文章「高效率爬虫框架之 pyspider」。务必要对 pysdpier 有个整体认知,这样你的学习效率才会高。现在我们用一…...