diff --git a/.gitignore b/.gitignore index dfc138d13f281ec7a0cba4173f32b8bc7aa13089..ba8611030b65b2138349e920c159bc77c20864c1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,6 @@ tests/test/ tests/taoshebei/ tests/taoscsv/ tests/taosdalipu/ -tests/sim/ -tests/script/ tests/pytest/ tests/jenkins/ tests/hdfs/ @@ -33,4 +31,6 @@ taoshebei/ taosdalipu/ Target/ *.failed -*.sql \ No newline at end of file +*.sql +sim/ + diff --git a/src/inc/trpc.h b/src/inc/trpc.h index e545abfed378f661c6ab58278590784c985b2672..a34d10747441757cb29bf07963702dc1585d9b40 100644 --- a/src/inc/trpc.h +++ b/src/inc/trpc.h @@ -85,6 +85,7 @@ void rpcSendRequest(void *thandle, SRpcIpSet *pIpSet, SRpcMsg *pMsg); void rpcSendResponse(SRpcMsg *pMsg); void rpcSendRedirectRsp(void *pConn, SRpcIpSet *pIpSet); int rpcGetConnInfo(void *thandle, SRpcConnInfo *pInfo); +void rpcSendRecv(void *shandle, SRpcIpSet *pIpSet, SRpcMsg *pOut, SRpcMsg *pRsp); #ifdef __cplusplus } diff --git a/src/mnode/src/mgmtUser.c b/src/mnode/src/mgmtUser.c index 56fe6c5419681285ac1b860effb472b2a9e3c49e..a44afc02bc311cb488f4392a80215b03a3126fb5 100644 --- a/src/mnode/src/mgmtUser.c +++ b/src/mnode/src/mgmtUser.c @@ -252,7 +252,7 @@ static int32_t mgmtGetUserMeta(STableMetaMsg *pMeta, SShowObj *pShow, void *pCon pShow->bytes[cols] = 8; pSchema[cols].type = TSDB_DATA_TYPE_TIMESTAMP; - strcpy(pSchema[cols].name, "created_time"); + strcpy(pSchema[cols].name, "create time"); pSchema[cols].bytes = htons(pShow->bytes[cols]); cols++; diff --git a/src/rpc/src/rpcMain.c b/src/rpc/src/rpcMain.c index 78952e22095c6a5cebf6bb4b6de4c25009e9f3bd..21ce7ee60b951a5bed391ef68e204c5508fb824e 100755 --- a/src/rpc/src/rpcMain.c +++ b/src/rpc/src/rpcMain.c @@ -83,6 +83,9 @@ typedef struct { int8_t oldInUse; // server IP inUse passed by app int8_t redirect; // flag to indicate redirect int8_t connType; // connection type + SRpcMsg *pRsp; // for synchronous API + tsem_t *pSem; // for synchronous API + SRpcIpSet *pSet; // for synchronous API char msg[0]; // RpcHead starts from here } SRpcReqContext; @@ -183,6 +186,7 @@ static void rpcSendReqToServer(SRpcInfo *pRpc, SRpcReqContext *pContext); static void rpcSendQuickRsp(SRpcConn *pConn, int32_t code); static void rpcSendErrorMsgToPeer(SRecvInfo *pRecv, int32_t code); static void rpcSendMsgToPeer(SRpcConn *pConn, void *data, int dataLen); +static void rpcSendReqHead(SRpcConn *pConn); static void *rpcProcessMsgFromPeer(SRecvInfo *pRecv); static void rpcProcessIncomingMsg(SRpcConn *pConn, SRpcHead *pHead); @@ -415,12 +419,12 @@ void rpcSendResponse(SRpcMsg *pMsg) { rpcFreeMsg(pConn->pRspMsg); pConn->pRspMsg = msg; pConn->rspMsgLen = msgLen; - if (pHead->content[0] == TSDB_CODE_ACTION_IN_PROGRESS) pConn->inTranId--; + if (pMsg->code == TSDB_CODE_ACTION_IN_PROGRESS) pConn->inTranId--; rpcUnlockConn(pConn); taosTmrStopA(&pConn->pTimer); - taosTmrReset(rpcProcessIdleTimer, pRpc->idleTime, pConn, pRpc->tmrCtrl, &pConn->pIdleTimer); + // taosTmrReset(rpcProcessIdleTimer, pRpc->idleTime, pConn, pRpc->tmrCtrl, &pConn->pIdleTimer); rpcSendMsgToPeer(pConn, msg, msgLen); pConn->secured = 1; // connection shall be secured @@ -456,6 +460,26 @@ int rpcGetConnInfo(void *thandle, SRpcConnInfo *pInfo) { return 0; } +void rpcSendRecv(void *shandle, SRpcIpSet *pIpSet, SRpcMsg *pMsg, SRpcMsg *pRsp) { + SRpcReqContext *pContext; + pContext = (SRpcReqContext *) (pMsg->pCont-sizeof(SRpcHead)-sizeof(SRpcReqContext)); + + memset(pRsp, 0, sizeof(SRpcMsg)); + + tsem_t sem; + tsem_init(&sem, 0, 0); + pContext->pSem = &sem; + pContext->pRsp = pRsp; + pContext->pSet = pIpSet; + + rpcSendRequest(shandle, pIpSet, pMsg); + + tsem_wait(&sem); + tsem_destroy(&sem); + + return; +} + static void rpcFreeMsg(void *msg) { if ( msg ) { char *temp = (char *)msg - sizeof(SRpcReqContext); @@ -661,8 +685,12 @@ static int rpcProcessReqHead(SRpcConn *pConn, SRpcHead *pHead) { if (pConn->inTranId == pHead->tranId) { if (pConn->inType == pHead->msgType) { - tTrace("%s %p, %s is retransmitted", pRpc->label, pConn, taosMsg[pHead->msgType]); - rpcSendQuickRsp(pConn, TSDB_CODE_ACTION_IN_PROGRESS); + if (pHead->code == 0) { + tTrace("%s %p, %s is retransmitted", pRpc->label, pConn, taosMsg[pHead->msgType]); + rpcSendQuickRsp(pConn, TSDB_CODE_ACTION_IN_PROGRESS); + } else { + // do nothing, it is heart beat from client + } } else if (pConn->inType == 0) { tTrace("%s %p, %s is already processed, tranId:%d", pRpc->label, pConn, taosMsg[pHead->msgType], pConn->inTranId); @@ -703,22 +731,23 @@ static int rpcProcessRspHead(SRpcConn *pConn, SRpcHead *pHead) { return TSDB_CODE_INVALID_RESPONSE_TYPE; } - if (*pHead->content == TSDB_CODE_NOT_READY) { + if (pHead->code == TSDB_CODE_NOT_READY) { return TSDB_CODE_ALREADY_PROCESSED; } taosTmrStopA(&pConn->pTimer); pConn->retry = 0; - if (*pHead->content == TSDB_CODE_ACTION_IN_PROGRESS) { + if (pHead->code == TSDB_CODE_ACTION_IN_PROGRESS) { if (pConn->tretry <= tsRpcMaxRetry) { - pConn->tretry++; tTrace("%s %p, peer is still processing the transaction", pRpc->label, pConn); - taosTmrReset(rpcProcessRetryTimer, tsRpcProgressTime, pConn, pRpc->tmrCtrl, &pConn->pTimer); + pConn->tretry++; + rpcSendReqHead(pConn); + taosTmrReset(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl, &pConn->pTimer); return TSDB_CODE_ALREADY_PROCESSED; } else { // peer still in processing, give up - *pHead->content = TSDB_CODE_TOO_SLOW; + return TSDB_CODE_TOO_SLOW; } } @@ -779,6 +808,7 @@ static SRpcConn *rpcProcessMsgHead(SRpcInfo *pRpc, SRecvInfo *pRecv) { if ( rpcIsReq(pHead->msgType) ) { terrno = rpcProcessReqHead(pConn, pHead); pConn->connType = pRecv->connType; + taosTmrReset(rpcProcessIdleTimer, pRpc->idleTime, pConn, pRpc->tmrCtrl, &pConn->pIdleTimer); } else { terrno = rpcProcessRspHead(pConn, pHead); } @@ -800,6 +830,18 @@ static void rpcProcessBrokenLink(SRpcConn *pConn) { pContext->code = TSDB_CODE_NETWORK_UNAVAIL; taosTmrStart(rpcProcessConnError, 0, pContext, pRpc->tmrCtrl); } + + if (pConn->inType) { + // if there are pending request, notify the app + tTrace("%s %p, connection is gone, notify the app", pRpc->label, pConn); + SRpcMsg rpcMsg; + rpcMsg.pCont = NULL; + rpcMsg.contLen = 0; + rpcMsg.handle = pConn; + rpcMsg.msgType = pConn->inType; + rpcMsg.code = TSDB_CODE_NETWORK_UNAVAIL; + (*(pRpc->cfp))(&rpcMsg); + } rpcCloseConn(pConn); } @@ -824,7 +866,7 @@ static void *rpcProcessMsgFromPeer(SRecvInfo *pRecv) { pConn = rpcProcessMsgHead(pRpc, pRecv); if (pHead->msgType < TSDB_MSG_TYPE_CM_HEARTBEAT || (rpcDebugFlag & 16)) { - tTrace("%s %p, %s received from 0x%x:%hu, parse code:%x len:%d sig:0x%08x:0x%08x:%d", + tTrace("%s %p, %s received from 0x%x:%hu, parse code:0x%x len:%d sig:0x%08x:0x%08x:%d", pRpc->label, pConn, taosMsg[pHead->msgType], pRecv->ip, pRecv->port, terrno, pRecv->msgLen, pHead->sourceId, pHead->destId, pHead->tranId, pHead->port); } @@ -845,6 +887,26 @@ static void *rpcProcessMsgFromPeer(SRecvInfo *pRecv) { return pConn; } +static void rpcNotifyClient(SRpcReqContext *pContext, SRpcMsg *pMsg) { + SRpcInfo *pRpc = pContext->pRpc; + + if (pContext->pRsp) { + // for synchronous API + tsem_post(pContext->pSem); + memcpy(pContext->pSet, &pContext->ipSet, sizeof(SRpcIpSet)); + memcpy(pContext->pRsp, pMsg, sizeof(SRpcMsg)); + } else { + // for asynchronous API + if (pRpc->ufp && (pContext->ipSet.inUse != pContext->oldInUse || pContext->redirect)) + (*pRpc->ufp)(pContext->ahandle, &pContext->ipSet); // notify the update of ipSet + + (*pRpc->cfp)(pMsg); + } + + // free the request message + rpcFreeCont(pContext->pCont); +} + static void rpcProcessIncomingMsg(SRpcConn *pConn, SRpcHead *pHead) { SRpcInfo *pRpc = pConn->pRpc; @@ -877,10 +939,7 @@ static void rpcProcessIncomingMsg(SRpcConn *pConn, SRpcHead *pHead) { tTrace("%s %p, redirect is received, numOfIps:%d", pRpc->label, pConn, pContext->ipSet.numOfIps); rpcSendReqToServer(pRpc, pContext); } else { - if ( pRpc->ufp && (pContext->ipSet.inUse != pContext->oldInUse || pContext->redirect) ) - (*pRpc->ufp)(pContext->ahandle, &pContext->ipSet); // notify the update of ipSet - (*pRpc->cfp)(&rpcMsg); - rpcFreeCont(pContext->pCont); // free the request msg + rpcNotifyClient(pContext, &rpcMsg); } } } @@ -894,7 +953,7 @@ static void rpcSendQuickRsp(SRpcConn *pConn, int32_t code) { pHead = (SRpcHead *)msg; pHead->version = 1; pHead->msgType = pConn->inType+1; - pHead->spi = 0; + pHead->spi = pConn->spi; pHead->encrypt = 0; pHead->tranId = pConn->inTranId; pHead->sourceId = pConn->ownId; @@ -903,7 +962,29 @@ static void rpcSendQuickRsp(SRpcConn *pConn, int32_t code) { memcpy(pHead->user, pConn->user, tListLen(pHead->user)); pHead->code = htonl(code); - rpcSendMsgToPeer(pConn, msg, 0); + rpcSendMsgToPeer(pConn, msg, sizeof(SRpcHead)); + pConn->secured = 1; // connection shall be secured +} + +static void rpcSendReqHead(SRpcConn *pConn) { + char msg[RPC_MSG_OVERHEAD]; + SRpcHead *pHead; + + // set msg header + memset(msg, 0, sizeof(SRpcHead)); + pHead = (SRpcHead *)msg; + pHead->version = 1; + pHead->msgType = pConn->outType; + pHead->spi = pConn->spi; + pHead->encrypt = 0; + pHead->tranId = pConn->outTranId; + pHead->sourceId = pConn->ownId; + pHead->destId = pConn->peerId; + pHead->linkUid = pConn->linkUid; + memcpy(pHead->user, pConn->user, tListLen(pHead->user)); + pHead->code = 1; + + rpcSendMsgToPeer(pConn, msg, sizeof(SRpcHead)); } static void rpcSendErrorMsgToPeer(SRecvInfo *pRecv, int32_t code) { @@ -999,9 +1080,9 @@ static void rpcSendMsgToPeer(SRpcConn *pConn, void *msg, int msgLen) { pConn->peerPort, msgLen, pHead->sourceId, pHead->destId, pHead->tranId); } else { if (pHead->msgType < TSDB_MSG_TYPE_CM_HEARTBEAT || (rpcDebugFlag & 16)) - tTrace( "%s %p, %s is sent to %s:%hu, code:%u len:%d sig:0x%08x:0x%08x:%d", + tTrace( "%s %p, %s is sent to %s:%hu, code:0x%x len:%d sig:0x%08x:0x%08x:%d", pRpc->label, pConn, taosMsg[pHead->msgType], pConn->peerIpstr, pConn->peerPort, - (uint8_t)pHead->content[0], msgLen, pHead->sourceId, pHead->destId, pHead->tranId); + pHead->code, msgLen, pHead->sourceId, pHead->destId, pHead->tranId); } writtenLen = (*taosSendData[pConn->connType])(pConn->peerIp, pConn->peerPort, pHead, msgLen, pConn->chandle); @@ -1027,8 +1108,8 @@ static void rpcProcessConnError(void *param, void *id) { rpcMsg.code = pContext->code; rpcMsg.pCont = NULL; rpcMsg.contLen = 0; - (*(pRpc->cfp))(&rpcMsg); - rpcFreeCont(pContext->pCont); // free the request msg + + rpcNotifyClient(pContext, &rpcMsg); } else { // move to next IP pContext->ipSet.inUse++; @@ -1079,6 +1160,17 @@ static void rpcProcessIdleTimer(void *param, void *tmrId) { if (pConn->user[0]) { tTrace("%s %p, close the connection since no activity", pRpc->label, pConn); + if (pConn->inType && pRpc->cfp) { + // if there are pending request, notify the app + tTrace("%s %p, notify the app, connection is gone", pRpc->label, pConn); + SRpcMsg rpcMsg; + rpcMsg.pCont = NULL; + rpcMsg.contLen = 0; + rpcMsg.handle = pConn; + rpcMsg.msgType = pConn->inType; + rpcMsg.code = TSDB_CODE_NETWORK_UNAVAIL; + (*(pRpc->cfp))(&rpcMsg); + } rpcCloseConn(pConn); } else { tTrace("%s %p, idle timer:%p not processed", pRpc->label, pConn, tmrId); diff --git a/src/rpc/test/CMakeLists.txt b/src/rpc/test/CMakeLists.txt index 15780a396c1b5de49f7a2c7af408369b67b6e088..b519ae757829f47c8d8467abba69834e35453686 100644 --- a/src/rpc/test/CMakeLists.txt +++ b/src/rpc/test/CMakeLists.txt @@ -11,6 +11,10 @@ IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM)) ADD_EXECUTABLE(rclient ${CLIENT_SRC}) TARGET_LINK_LIBRARIES(rclient trpc) + LIST(APPEND SCLIENT_SRC ./rsclient.c) + ADD_EXECUTABLE(rsclient ${SCLIENT_SRC}) + TARGET_LINK_LIBRARIES(rsclient trpc) + LIST(APPEND SERVER_SRC ./rserver.c) ADD_EXECUTABLE(rserver ${SERVER_SRC}) TARGET_LINK_LIBRARIES(rserver trpc) diff --git a/src/rpc/test/rclient.c b/src/rpc/test/rclient.c index 562d0fff96acab47d8121e21ed10e9fb984c105f..f000ab91a273251972e0145ff431a21bc0c8b519 100644 --- a/src/rpc/test/rclient.c +++ b/src/rpc/test/rclient.c @@ -40,7 +40,7 @@ typedef struct { void *pRpc; } SInfo; -void processResponse(SRpcMsg *pMsg) { +static void processResponse(SRpcMsg *pMsg) { SInfo *pInfo = (SInfo *)pMsg->handle; tTrace("thread:%d, response is received, type:%d contLen:%d code:0x%x", pInfo->index, pMsg->msgType, pMsg->contLen, pMsg->code); @@ -49,16 +49,16 @@ void processResponse(SRpcMsg *pMsg) { sem_post(&pInfo->rspSem); } -void processUpdateIpSet(void *handle, SRpcIpSet *pIpSet) { +static void processUpdateIpSet(void *handle, SRpcIpSet *pIpSet) { SInfo *pInfo = (SInfo *)handle; tTrace("thread:%d, ip set is changed, index:%d", pInfo->index, pIpSet->inUse); pInfo->ipSet = *pIpSet; } -int tcount = 0; +static int tcount = 0; -void *sendRequest(void *param) { +static void *sendRequest(void *param) { SInfo *pInfo = (SInfo *)param; SRpcMsg rpcMsg; diff --git a/src/rpc/test/rsclient.c b/src/rpc/test/rsclient.c new file mode 100644 index 0000000000000000000000000000000000000000..b99387e097df6930810d2176e2526d76f823f3fa --- /dev/null +++ b/src/rpc/test/rsclient.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "os.h" +#include "tlog.h" +#include "trpc.h" +#include "taoserror.h" +#include +#include + +typedef struct { + int index; + SRpcIpSet ipSet; + int num; + int numOfReqs; + int msgSize; + sem_t rspSem; + sem_t *pOverSem; + pthread_t thread; + void *pRpc; +} SInfo; + +static void processUpdateIpSet(void *handle, SRpcIpSet *pIpSet) { + SInfo *pInfo = (SInfo *)handle; + + tTrace("thread:%d, ip set is changed, index:%d", pInfo->index, pIpSet->inUse); + pInfo->ipSet = *pIpSet; +} + +static int tcount = 0; +static int terror = 0; + +static void *sendRequest(void *param) { + SInfo *pInfo = (SInfo *)param; + SRpcMsg rpcMsg, rspMsg; + + tTrace("thread:%d, start to send request", pInfo->index); + + while ( pInfo->numOfReqs == 0 || pInfo->num < pInfo->numOfReqs) { + pInfo->num++; + rpcMsg.pCont = rpcMallocCont(pInfo->msgSize); + rpcMsg.contLen = pInfo->msgSize; + rpcMsg.handle = pInfo; + rpcMsg.msgType = 1; + tTrace("thread:%d, send request, contLen:%d num:%d", pInfo->index, pInfo->msgSize, pInfo->num); + + rpcSendRecv(pInfo->pRpc, &pInfo->ipSet, &rpcMsg, &rspMsg); + + // handle response + if (rspMsg.code != 0) terror++; + + tTrace("thread:%d, rspLen:%d code:%d", pInfo->index, rspMsg.contLen, rspMsg.code); + + rpcFreeCont(rspMsg.pCont); + + if ( pInfo->num % 20000 == 0 ) + tPrint("thread:%d, %d requests have been sent", pInfo->index, pInfo->num); + } + + tTrace("thread:%d, it is over", pInfo->index); + tcount++; + + return NULL; +} + +int main(int argc, char *argv[]) { + SRpcInit rpcInit; + SRpcIpSet ipSet; + int msgSize = 128; + int numOfReqs = 0; + int appThreads = 1; + char serverIp[40] = "127.0.0.1"; + struct timeval systemTime; + int64_t startTime, endTime; + pthread_attr_t thattr; + + // server info + ipSet.numOfIps = 1; + ipSet.inUse = 0; + ipSet.port = 7000; + ipSet.ip[0] = inet_addr(serverIp); + ipSet.ip[1] = inet_addr("192.168.0.1"); + + // client info + memset(&rpcInit, 0, sizeof(rpcInit)); + rpcInit.localIp = "0.0.0.0"; + rpcInit.localPort = 0; + rpcInit.label = "APP"; + rpcInit.numOfThreads = 1; + // rpcInit.cfp = processResponse; + rpcInit.ufp = processUpdateIpSet; + rpcInit.sessions = 100; + rpcInit.idleTime = tsShellActivityTimer*1000; + rpcInit.user = "michael"; + rpcInit.secret = "mypassword"; + rpcInit.ckey = "key"; + rpcInit.spi = 1; + rpcInit.connType = TAOS_CONN_CLIENT; + + for (int i=1; iindex = i; + pInfo->ipSet = ipSet; + pInfo->numOfReqs = numOfReqs; + pInfo->msgSize = msgSize; + sem_init(&pInfo->rspSem, 0, 0); + pInfo->pRpc = pRpc; + pthread_create(&pInfo->thread, &thattr, sendRequest, pInfo); + pInfo++; + } + + do { + usleep(1); + } while ( tcount < appThreads); + + gettimeofday(&systemTime, NULL); + endTime = systemTime.tv_sec*1000000 + systemTime.tv_usec; + float usedTime = (endTime - startTime)/1000.0; // mseconds + + tPrint("it takes %.3f mseconds to send %d requests to server, error num:%d", usedTime, numOfReqs*appThreads, terror); + tPrint("Performance: %.3f requests per second, msgSize:%d bytes", 1000.0*numOfReqs*appThreads/usedTime, msgSize); + + taosCloseLogger(); + + return 0; +} + + diff --git a/src/rpc/test/rserver.c b/src/rpc/test/rserver.c index 6c5b320809b8c714a3f30caefa952e289b57a36d..48ae02a1d57089852ad5964a058f0ad145410b8e 100644 --- a/src/rpc/test/rserver.c +++ b/src/rpc/test/rserver.c @@ -72,7 +72,7 @@ void processShellMsg() { rpcMsg.pCont = rpcMallocCont(msgSize); rpcMsg.contLen = msgSize; rpcMsg.handle = pRpcMsg->handle; - rpcMsg.code = 1; + rpcMsg.code = 0; rpcSendResponse(&rpcMsg); taosFreeQitem(pRpcMsg); @@ -126,9 +126,10 @@ void processRequestMsg(SRpcMsg *pMsg) { int main(int argc, char *argv[]) { SRpcInit rpcInit; char dataName[20] = "server.data"; + char localIp[40] = "0.0.0.0"; memset(&rpcInit, 0, sizeof(rpcInit)); - rpcInit.localIp = "0.0.0.0"; + rpcInit.localIp = localIp; rpcInit.localPort = 7000; rpcInit.label = "SER"; rpcInit.numOfThreads = 1; @@ -201,5 +202,3 @@ int main(int argc, char *argv[]) { return 0; } - - diff --git a/src/vnode/wal/CMakeLists.txt b/src/vnode/wal/CMakeLists.txt index 1de958f84e65198c94f8a946d5379f02ce0d2fa0..d77a235bb952cb736f63d6b6f87195b321af61c5 100644 --- a/src/vnode/wal/CMakeLists.txt +++ b/src/vnode/wal/CMakeLists.txt @@ -1,4 +1,15 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(TDengine) + +INCLUDE_DIRECTORIES(${TD_OS_DIR}/inc) +INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/inc) +INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/util/inc) +INCLUDE_DIRECTORIES(inc) + AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/src SRC) -ADD_LIBRARY(wal ${SRC}) -TARGET_INCLUDE_DIRECTORIES(wal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) \ No newline at end of file +ADD_LIBRARY(twal ${SRC}) +TARGET_LINK_LIBRARIES(twal tutil) + +ADD_SUBDIRECTORY(test) + diff --git a/src/vnode/wal/inc/vnodeWal.h b/src/vnode/wal/inc/twal.h similarity index 54% rename from src/vnode/wal/inc/vnodeWal.h rename to src/vnode/wal/inc/twal.h index 7753e4ecca5401bfc8434465f90cdb415d5757a7..49fcde9e28548da05c73a0eca658640a99eb67c4 100644 --- a/src/vnode/wal/inc/vnodeWal.h +++ b/src/vnode/wal/inc/twal.h @@ -14,19 +14,37 @@ */ #ifndef _TD_WAL_H_ #define _TD_WAL_H_ -#include #ifdef __cplusplus extern "C" { #endif -typedef void walh; // WAL HANDLE +#define TAOS_WAL_NOLOG 0 +#define TAOS_WAL_WRITE 1 +#define TAOS_WAL_FSYNC 2 + +typedef struct { + int8_t msgType; + int8_t reserved[3]; + int32_t len; + uint64_t version; + uint32_t signature; + uint32_t cksum; + char cont[]; +} SWalHead; + +typedef void* twal_h; // WAL HANDLE + +twal_h walOpen(char *path, int max, int level); +void walClose(twal_h); +int walRenew(twal_h); +int walWrite(twal_h, SWalHead *); +void walFsync(twal_h); +int walRestore(twal_h, void *pVnode, int (*writeFp)(void *ahandle, void *pWalHead)); +int walGetWalFile(twal_h, char *name, uint32_t *index); + +extern int wDebugFlag; -walh *vnodeOpenWal(int vnode, uint8_t op); -int vnodeCloseWal(walh *pWal); -int vnodeRenewWal(walh *pWal); -int vnodeWriteWal(walh *pWal, void *cont, int contLen); -int vnodeSyncWal(walh *pWal); #ifdef __cplusplus } diff --git a/src/vnode/wal/src/vnodeWal.c b/src/vnode/wal/src/vnodeWal.c deleted file mode 100644 index 528cc97ed6030bbbf7941b05a3ff9c4cf5b81e75..0000000000000000000000000000000000000000 --- a/src/vnode/wal/src/vnodeWal.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include - -#include "vnodeWal.h" - -typedef struct { - /* TODO */ -} SWal; - -walh *vnodeOpenWal(int vnode, uint8_t op) { return NULL; } -int vnodeCloseWal(walh *pWal) { return 0; } -int vnodeRenewWal(walh *pWal) { return 0; } -int vnodeWriteWal(walh *pWal, void *cont, int contLen) { return 0; } -int vnodeSyncWal(walh *pWal) { return 0; } \ No newline at end of file diff --git a/src/vnode/wal/src/walMain.c b/src/vnode/wal/src/walMain.c new file mode 100644 index 0000000000000000000000000000000000000000..f327c28ce32e554a694f26d1adb4a4eb0c046407 --- /dev/null +++ b/src/vnode/wal/src/walMain.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "os.h" +#include "tlog.h" +#include "tchecksum.h" +#include "tutil.h" +#include "twal.h" + +#define walPrefix "wal" +#define wError(...) if (wDebugFlag & DEBUG_ERROR) {tprintf("ERROR WAL ", wDebugFlag, __VA_ARGS__);} +#define wWarn(...) if (wDebugFlag & DEBUG_WARN) {tprintf("WARN WAL ", wDebugFlag, __VA_ARGS__);} +#define wTrace(...) if (wDebugFlag & DEBUG_TRACE) {tprintf("WAL ", wDebugFlag, __VA_ARGS__);} +#define wPrint(...) {tprintf("WAL ", 255, __VA_ARGS__);} + +typedef struct { + int fd; + int level; + int max; // maximum number of wal files + uint32_t id; // increase continuously + int num; // number of wal files + char path[TSDB_FILENAME_LEN]; + char name[TSDB_FILENAME_LEN]; + pthread_mutex_t mutex; +} SWal; + +int wDebugFlag = 135; + +static uint32_t walSignature = 0xFAFBFDFE; +static int walHandleExistingFiles(char *path); +static int walRestoreWalFile(char *name, void *pVnode, int (*writeFp)(void *, void *)); +static int walRemoveWalFiles(char *path); + +void *walOpen(char *path, int max, int level) { + SWal *pWal = calloc(sizeof(SWal), 1); + if (pWal == NULL) return NULL; + + pWal->fd = -1; + pWal->max = max; + pWal->id = 0; + pWal->num = 0; + pWal->level = level; + strcpy(pWal->path, path); + pthread_mutex_init(&pWal->mutex, NULL); + + if (access(path, F_OK) != 0) mkdir(path, 0755); + + if (walHandleExistingFiles(path) == 0) + walRenew(pWal); + + if (pWal->fd <0) { + wError("wal:%s, failed to open", path); + pthread_mutex_destroy(&pWal->mutex); + free(pWal); + pWal = NULL; + } + + return pWal; +} + +void walClose(void *handle) { + + SWal *pWal = (SWal *)handle; + + close(pWal->fd); + + // remove all files in the directory + for (int i=0; inum; ++i) { + sprintf(pWal->name, "%s/%s%d", pWal->path, walPrefix, pWal->id-i); + if (remove(pWal->name) <0) { + wError("wal:%s, failed to remove", pWal->name); + } else { + wTrace("wal:%s, it is removed", pWal->name); + } + } + + pthread_mutex_destroy(&pWal->mutex); + + free(pWal); +} + +int walRenew(twal_h handle) { + SWal *pWal = (SWal *)handle; + int code = 0; + + pthread_mutex_lock(&pWal->mutex); + + if (pWal->fd >=0) { + close(pWal->fd); + pWal->id++; + wTrace("wal:%s, it is closed", pWal->name); + } + + pWal->num++; + + sprintf(pWal->name, "%s/%s%d", pWal->path, walPrefix, pWal->id); + pWal->fd = open(pWal->name, O_WRONLY | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); + + if (pWal->fd < 0) { + wError("wal:%d, failed to open(%s)", pWal->name, strerror(errno)); + code = -1; + } else { + wTrace("wal:%s, it is created", pWal->name); + + if (pWal->num > pWal->max) { + // remove the oldest wal file + char name[TSDB_FILENAME_LEN]; + sprintf(name, "%s/%s%d", pWal->path, walPrefix, pWal->id - pWal->max); + if (remove(name) <0) { + wError("wal:%s, failed to remove(%s)", name, strerror(errno)); + } else { + wTrace("wal:%s, it is removed", name); + } + + pWal->num--; + } + } + + pthread_mutex_unlock(&pWal->mutex); + + return code; +} + +int walWrite(void *handle, SWalHead *pHead) { + SWal *pWal = (SWal *)handle; + int code = 0; + + // no wal + if (pWal->level == TAOS_WAL_NOLOG) return 0; + + pHead->signature = walSignature; + taosCalcChecksumAppend(0, (uint8_t *)pHead, sizeof(SWal)); + int contLen = pHead->len + sizeof(SWalHead); + + if(write(pWal->fd, pHead, contLen) != contLen) { + wError("wal:%s, failed to write(%s)", pWal->name, strerror(errno)); + code = -1; + } + + return code; +} + +void walFsync(void *handle) { + + SWal *pWal = (SWal *)handle; + + if (pWal->level == TAOS_WAL_FSYNC) + fsync(pWal->fd); +} + +int walRestore(void *handle, void *pVnode, int (*writeFp)(void *, void *)) { + SWal *pWal = (SWal *)handle; + int code = 0; + struct dirent *ent; + int count = 0; + uint32_t maxId = 0, minId = -1, index =0; + + int plen = strlen(walPrefix); + char opath[TSDB_FILENAME_LEN]; + sprintf(opath, "%s/old", pWal->path); + + // is there old directory? + if (access(opath, F_OK)) return 0; + + DIR *dir = opendir(opath); + while ((ent = readdir(dir))!= NULL) { + if ( strncmp(ent->d_name, walPrefix, plen) == 0) { + index = atol(ent->d_name + plen); + if (index > maxId) maxId = index; + if (index < minId) minId = index; + count++; + } + } + + if ( count != (maxId-minId+1) ) { + wError("wal:%s, messed up, count:%d max:%d min:%d", opath, count, maxId, minId); + code = -1; + } else { + wTrace("wal:%s, %d files will be restored", opath, count); + + for (index = minId; index<=maxId; ++index) { + sprintf(pWal->name, "%s/old/%s%d", pWal->path, walPrefix, index); + code = walRestoreWalFile(pWal->name, pVnode, writeFp); + if (code < 0) break; + } + } + + if (code == 0) { + code = walRemoveWalFiles(opath); + if (code == 0) { + if (remove(opath) < 0) { + wError("wal:%s, failed to remove directory(%s)", opath, strerror(errno)); + code = -1; + } + } + } + + closedir(dir); + + return code; +} + +int walGetWalFile(void *handle, char *name, uint32_t *index) { + SWal *pWal = (SWal *)handle; + int code = 1; + int32_t first = 0; + + name[0] = 0; + if (pWal == NULL || pWal->num == 0) return 0; + + pthread_mutex_lock(&(pWal->mutex)); + + first = pWal->id + 1 - pWal->num; + if (*index == 0) *index = first; // set to first one + + if (*index < first && *index > pWal->id) { + code = -1; // index out of range + } else { + sprintf(name, "%s/%s%d", pWal->path, walPrefix, *index); + code = (*index == pWal->id) ? 0:1; + } + + pthread_mutex_unlock(&(pWal->mutex)); + + return code; +} + +static int walRestoreWalFile(char *name, void *pVnode, int (*writeFp)(void *, void *)) { + SWalHead walHead; + int code = 0; + + int fd = open(name, O_RDONLY); + if (fd < 0) { + wError("wal:%s, failed to open for restore(%s)", name, strerror(errno)); + return -1; + } + + wTrace("wal:%s, start to restore", name); + + while (1) { + int ret = read(fd, &walHead, sizeof(walHead)); + if ( ret == 0) { code = 0; break;} + + if (ret != sizeof(walHead)) { + wWarn("wal:%s, failed to read head, skip, ret:%d(%s)", name, ret, strerror(errno)); + break; + } + + if (taosCheckChecksumWhole((uint8_t *)&walHead, sizeof(walHead))) { + wWarn("wal:%s, cksum is messed up, skip the rest of file", name); + break; + } + + char *buffer = malloc(sizeof(SWalHead) + walHead.len); + memcpy(buffer, &walHead, sizeof(walHead)); + + ret = read(fd, buffer+sizeof(walHead), walHead.len); + if ( ret != walHead.len) { + wWarn("wal:%s, failed to read body, skip, len:%d ret:%d", name, walHead.len, ret); + break; + } + + // write into queue + (*writeFp)(pVnode, buffer); + } + + return code; +} + +int walHandleExistingFiles(char *path) { + int code = 0; + char oname[TSDB_FILENAME_LEN]; + char nname[TSDB_FILENAME_LEN]; + char opath[TSDB_FILENAME_LEN]; + + sprintf(opath, "%s/old", path); + + struct dirent *ent; + DIR *dir = opendir(path); + int plen = strlen(walPrefix); + + if (access(opath, F_OK) == 0) { + // old directory is there, it means restore process is not finished + walRemoveWalFiles(path); + + } else { + // move all files to old directory + int count = 0; + while ((ent = readdir(dir))!= NULL) { + if ( strncmp(ent->d_name, walPrefix, plen) == 0) { + if (access(opath, F_OK) != 0) mkdir(opath, 0755); + + sprintf(oname, "%s/%s", path, ent->d_name); + sprintf(nname, "%s/old/%s", path, ent->d_name); + if (rename(oname, nname) < 0) { + wError("wal:%s, failed to move to new:%s", oname, nname); + code = -1; + break; + } + + count++; + } + } + + wTrace("wal:%s, %d files are moved for restoration", path, count); + } + + closedir(dir); + return code; +} + +static int walRemoveWalFiles(char *path) { + int plen = strlen(walPrefix); + char name[TSDB_FILENAME_LEN]; + int code = 0; + + if (access(path, F_OK) != 0) return 0; + + struct dirent *ent; + DIR *dir = opendir(path); + + while ((ent = readdir(dir))!= NULL) { + if ( strncmp(ent->d_name, walPrefix, plen) == 0) { + sprintf(name, "%s/%s", path, ent->d_name); + if (remove(name) <0) { + wError("wal:%s, failed to remove(%s)", name, strerror(errno)); + code = -1; break; + } + } + } + + closedir(dir); + + return code; +} + + diff --git a/src/vnode/wal/test/CMakeLists.txt b/src/vnode/wal/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..06591def40ab0c7a8013553e8187ee8ad2d0ba8a --- /dev/null +++ b/src/vnode/wal/test/CMakeLists.txt @@ -0,0 +1,16 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(TDengine) + +IF ((TD_LINUX_64) OR (TD_LINUX_32 AND TD_ARM)) + INCLUDE_DIRECTORIES(${TD_OS_DIR}/inc) + INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/inc) + INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/util/inc) + INCLUDE_DIRECTORIES(../inc) + + LIST(APPEND WALTEST_SRC ./waltest.c) + ADD_EXECUTABLE(waltest ${WALTEST_SRC}) + TARGET_LINK_LIBRARIES(waltest twal) + +ENDIF () + + diff --git a/src/vnode/wal/test/waltest.c b/src/vnode/wal/test/waltest.c new file mode 100644 index 0000000000000000000000000000000000000000..e90b54d1f326789d846bc65bf5b909d90e8eb3dc --- /dev/null +++ b/src/vnode/wal/test/waltest.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +//#define _DEFAULT_SOURCE +#include +#include "tlog.h" +#include "twal.h" + +int64_t ver = 0; +void *pWal = NULL; + +int writeToQueue(void *pVnode, void *data) { + SWalHead *pHead = (SWalHead *)data; + + // do nothing + if (pHead->version > ver) + ver = pHead->version; + + walWrite(pWal, pHead); + + free(data); + + return 0; +} + +int main(int argc, char *argv[]) { + char path[128] = "/home/jhtao/test/wal"; + int max = 3; + int level = 2; + int total = 5; + int rows = 10000; + int size = 128; + + for (int i=1; iversion = ++ver; + pHead->len = size; + walWrite(pWal, pHead); + } + + printf("renew a wal, i:%d\n", i); + walRenew(pWal); + } + + printf("%d wal files are written\n", total); + + uint32_t index = 0; + char name[256]; + + while (1) { + int code = walGetWalFile(pWal, name, &index); + if (code == -1) { + printf("failed to get wal file, index:%d\n", index); + break; + } + + printf("index:%d wal:%s\n", index, name); + if (code == 0) break; + + index++; + } + + getchar(); + + walClose(pWal); + + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7afaf7f8b17d7793b61fe237a76163cfd7fed1c4..872201f1788d95f0cd1d59df38be20131e2d6305 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,3 +10,4 @@ SET(CMAKE_C_STANDARD 11) SET(CMAKE_VERBOSE_MAKEFILE ON) ADD_SUBDIRECTORY(examples/c) +ADD_SUBDIRECTORY(tsim) diff --git a/tests/script/basicSuite.sim b/tests/script/basicSuite.sim new file mode 100644 index 0000000000000000000000000000000000000000..009abf99e263302a02cd7e2876cffb6b4e846e17 --- /dev/null +++ b/tests/script/basicSuite.sim @@ -0,0 +1,3 @@ +################################# +run general/user/testSuite.sim +################################## diff --git a/tests/script/general/user/basic.sim b/tests/script/general/user/basic.sim new file mode 100644 index 0000000000000000000000000000000000000000..1b8c4c84d8682d020718c20d20ade67b307d2189 --- /dev/null +++ b/tests/script/general/user/basic.sim @@ -0,0 +1,71 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -m 192.168.0.1 -i 192.168.0.1 +system sh/exec.sh -n dnode1 -s start +sql connect + +print =============== show users +sql show users +if $rows != 3 then + return -1 +endi + +print $data00 $data01 $data02 +print $data10 $data11 $data22 +print $data20 $data11 $data22 + +print =============== create user1 +sql create user user1 PASS 'user1' +sql show users +if $rows != 4 then + return -1 +endi + +print $data00 $data01 $data02 +print $data10 $data11 $data22 +print $data20 $data11 $data22 +print $data30 $data31 $data32 + +print =============== create user2 +sql create user user2 PASS 'user2' +sql show users +if $rows != 5 then + return -1 +endi + +print $data00 $data01 $data02 +print $data10 $data11 $data22 +print $data20 $data11 $data22 +print $data30 $data31 $data32 +print $data40 $data41 $data42 + +print =============== drop user1 +sql drop user user1 +sql show users +if $rows != 4 then + return -1 +endi + +print $data00 $data01 $data02 +print $data10 $data11 $data22 +print $data20 $data11 $data22 +print $data30 $data31 $data32 + +print =============== restart taosd +system sh/exec.sh -n dnode1 -s stop +sleep 1000 +system sh/exec.sh -n dnode1 -s start + +print =============== show users +sql show users +if $rows != 4 then + return -1 +endi + +print $data00 $data01 $data02 +print $data10 $data11 $data22 +print $data20 $data11 $data22 +print $data30 $data31 $data32 + + + + diff --git a/tests/script/general/user/testSuite.sim b/tests/script/general/user/testSuite.sim new file mode 100644 index 0000000000000000000000000000000000000000..cb953b67ace2eacd7c290244d0df33653fbd761d --- /dev/null +++ b/tests/script/general/user/testSuite.sim @@ -0,0 +1,3 @@ +################################# +run general/user/basic.sim +################################## diff --git a/tests/script/sh/cfg.sh b/tests/script/sh/cfg.sh new file mode 100755 index 0000000000000000000000000000000000000000..f28b401b78431356c091ab9b9c2c6164d8b8c755 --- /dev/null +++ b/tests/script/sh/cfg.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +if [ $# != 6 ]; then + echo "argument list need input : " + echo " -n nodeName" + echo " -c configName" + echo " -v configValue" + exit 1 +fi + +NODE_NAME= +CONFIG_NAME= +CONFIG_VALUE= +while getopts "n:v:c:" arg +do + case $arg in + n) + NODE_NAME=$OPTARG + ;; + c) + CONFIG_NAME=$OPTARG + ;; + v) + CONFIG_VALUE=$OPTARG + ;; + ?) + echo "unkonw argument" + ;; + esac +done + +SCRIPT_DIR=`dirname $0` +cd $SCRIPT_DIR/../ +SCRIPT_DIR=`pwd` + +cd ../../ +TAOS_DIR=`pwd` + +BUILD_DIR=$TAOS_DIR/debug/build +SIM_DIR=$TAOS_DIR/sim + +NODE_DIR=$SIM_DIR/$NODE_NAME +TAOS_CFG=$NODE_DIR/cfg/taos.cfg +TAOS_FLAG=$SIM_DIR/tsim/flag +if [ -f "$TAOS_FLAG" ] ; then + TAOS_CFG=/etc/taos/taos.cfg +fi + +echo "$CONFIG_NAME $CONFIG_VALUE" >> $TAOS_CFG diff --git a/tests/script/sh/clear.sh b/tests/script/sh/clear.sh new file mode 100755 index 0000000000000000000000000000000000000000..714ff4358036c4aa432d9f0c24ba0a6ee17781fe --- /dev/null +++ b/tests/script/sh/clear.sh @@ -0,0 +1,117 @@ +#!/bin/sh + +echo "Executing clear.sh" + +if [ $# != 6 ]; then + echo "argument list need input : " + echo " -n nodeName" + echo " -i nodeIp" + echo " -m masterIp" + exit 1 +fi + +NODE_NAME= +NODE_IP= +MSATER_IP= +while getopts "n:i:m:" arg +do + case $arg in + n) + NODE_NAME=$OPTARG + ;; + i) + NODE_IP=$OPTARG + ;; + m) + MASTER_IP=$OPTARG + ;; + ?) + echo "unkonw argument" + ;; + esac +done + +SCRIPT_DIR=`dirname $0` +cd $SCRIPT_DIR/../ +SCRIPT_DIR=`pwd` +echo "SCRIPT_DIR: $SCRIPT_DIR" + +cd ../../ +TAOS_DIR=`pwd` + +BUILD_DIR=$TAOS_DIR/debug/build +SIM_DIR=$TAOS_DIR/sim + +NODE_DIR=$SIM_DIR/$NODE_NAME +EXE_DIR=$BUILD_DIR/bin +CFG_DIR=$NODE_DIR/cfg +LOG_DIR=$NODE_DIR/log +DATA_DIR=$NODE_DIR/data + +#echo ============ deploy $NODE_NAME +#echo === masterIp : $MASTER_IP +#echo === nodeIp : $NODE_IP +#echo === nodePath : $EXE_DIR +#echo === cfgPath : $CFG_DIR +#echo === logPath : $LOG_DIR +#echo === dataPath : $DATA_DIR + +# rm -rf $NODE_DIR + +mkdir -p $SIM_DIR +mkdir -p $NODE_DIR +mkdir -p $LOG_DIR +rm -rf $DATA_DIR +mkdir -p $DATA_DIR + +#cp -rf $TAOS_DIR/cfg $NODE_DIR/ +rm -rf $CFG_DIR +mkdir -p $CFG_DIR + +#allow normal user to read/write log +chmod -R 777 $NODE_DIR + +TAOS_CFG=$NODE_DIR/cfg/taos.cfg +touch -f $TAOS_CFG + +TAOS_FLAG=$SIM_DIR/tsim/flag +if [ -f "$TAOS_FLAG" ] ; then + TAOS_CFG=/etc/taos/taos.cfg + DATA_DIR=/var/lib/taos + LOG_DIR=/var/log/taos + sudo rm -f /etc/taos/*.cfg + sudo cp -rf $TAOS_DIR/cfg/*.cfg /etc/taos + sudo rm -rf $DATA_DIR + sudo rm -rf $LOG_DIR +fi + +echo " " >> $TAOS_CFG +echo "masterIp $MASTER_IP" >> $TAOS_CFG +echo "dataDir $DATA_DIR" >> $TAOS_CFG +echo "logDir $LOG_DIR" >> $TAOS_CFG +echo "publicIp $NODE_IP" >> $TAOS_CFG +echo "internalIp $NODE_IP" >> $TAOS_CFG +echo "privateIp $NODE_IP" >> $TAOS_CFG +echo "dDebugFlag 135" >> $TAOS_CFG +echo "mDebugFlag 135" >> $TAOS_CFG +echo "sdbDebugFlag 135" >> $TAOS_CFG +echo "rpcDebugFlag 131" >> $TAOS_CFG +echo "tmrDebugFlag 131" >> $TAOS_CFG +echo "cDebugFlag 135" >> $TAOS_CFG +echo "httpDebugFlag 131" >> $TAOS_CFG +echo "monitorDebugFlag 131" >> $TAOS_CFG +echo "udebugFlag 131" >> $TAOS_CFG +echo "jnidebugFlag 131" >> $TAOS_CFG +echo "monitor 0" >> $TAOS_CFG +echo "numOfThreadsPerCore 2.0" >> $TAOS_CFG +echo "defaultPass taosdata" >> $TAOS_CFG +echo "numOfLogLines 100000000" >> $TAOS_CFG +echo "mgmtEqualVnodeNum 0" >> $TAOS_CFG +echo "clog 0" >> $TAOS_CFG +echo "statusInterval 1" >> $TAOS_CFG +echo "numOfTotalVnodes 4" >> $TAOS_CFG +echo "asyncLog 0" >> $TAOS_CFG +echo "numOfMPeers 1" >> $TAOS_CFG +echo "locale en_US.UTF-8" >> $TAOS_CFG + + diff --git a/tests/script/sh/deploy.sh b/tests/script/sh/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..49dd90c66f944bc18f2a5eacb826efff37dbbffd --- /dev/null +++ b/tests/script/sh/deploy.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +echo "Executing deploy.sh" + +if [ $# != 6 ]; then + echo "argument list need input : " + echo " -n nodeName" + echo " -i nodeIp" + echo " -m masterIp" + exit 1 +fi + +NODE_NAME= +NODE_IP= +MSATER_IP= +while getopts "n:i:m:" arg +do + case $arg in + n) + NODE_NAME=$OPTARG + ;; + i) + NODE_IP=$OPTARG + ;; + m) + MASTER_IP=$OPTARG + ;; + ?) + echo "unkonw argument" + ;; + esac +done + +SCRIPT_DIR=`dirname $0` +cd $SCRIPT_DIR/../ +SCRIPT_DIR=`pwd` +echo "SCRIPT_DIR: $SCRIPT_DIR" + +cd ../../ +TAOS_DIR=`pwd` + +BUILD_DIR=$TAOS_DIR/debug/build +SIM_DIR=$TAOS_DIR/sim + +NODE_DIR=$SIM_DIR/$NODE_NAME +EXE_DIR=$BUILD_DIR/bin +CFG_DIR=$NODE_DIR/cfg +LOG_DIR=$NODE_DIR/log +DATA_DIR=$NODE_DIR/data + +#echo ============ deploy $NODE_NAME +#echo === masterIp : $MASTER_IP +#echo === nodeIp : $NODE_IP +#echo === nodePath : $EXE_DIR +#echo === cfgPath : $CFG_DIR +#echo === logPath : $LOG_DIR +#echo === dataPath : $DATA_DIR + +rm -rf $NODE_DIR + +mkdir -p $SIM_DIR +mkdir -p $NODE_DIR +mkdir -p $LOG_DIR +mkdir -p $DATA_DIR + +#cp -rf $TAOS_DIR/cfg $NODE_DIR/ +mkdir -p $CFG_DIR + +#allow normal user to read/write log +chmod -R 777 $NODE_DIR + +TAOS_CFG=$NODE_DIR/cfg/taos.cfg +touch -f $TAOS_CFG + +TAOS_FLAG=$SIM_DIR/tsim/flag +if [ -f "$TAOS_FLAG" ] ; then + TAOS_CFG=/etc/taos/taos.cfg + DATA_DIR=/var/lib/taos + LOG_DIR=/var/log/taos + sudo rm -f /etc/taos/*.cfg + sudo cp -rf $TAOS_DIR/cfg/*.cfg /etc/taos + sudo rm -rf $DATA_DIR + sudo rm -rf $LOG_DIR +fi + +echo " " >> $TAOS_CFG +echo "masterIp $MASTER_IP" >> $TAOS_CFG +echo "dataDir $DATA_DIR" >> $TAOS_CFG +echo "logDir $LOG_DIR" >> $TAOS_CFG +echo "publicIp $NODE_IP" >> $TAOS_CFG +echo "internalIp $NODE_IP" >> $TAOS_CFG +echo "privateIp $NODE_IP" >> $TAOS_CFG +echo "dDebugFlag 135" >> $TAOS_CFG +echo "mDebugFlag 135" >> $TAOS_CFG +echo "sdbDebugFlag 135" >> $TAOS_CFG +echo "rpcDebugFlag 135" >> $TAOS_CFG +echo "tmrDebugFlag 131" >> $TAOS_CFG +echo "cDebugFlag 135" >> $TAOS_CFG +echo "httpDebugFlag 131" >> $TAOS_CFG +echo "monitorDebugFlag 131" >> $TAOS_CFG +echo "udebugFlag 131" >> $TAOS_CFG +echo "jnidebugFlag 131" >> $TAOS_CFG +echo "monitor 0" >> $TAOS_CFG +echo "numOfThreadsPerCore 2.0" >> $TAOS_CFG +echo "defaultPass taosdata" >> $TAOS_CFG +echo "numOfLogLines 100000000" >> $TAOS_CFG +echo "mgmtEqualVnodeNum 0" >> $TAOS_CFG +echo "clog 0" >> $TAOS_CFG +echo "statusInterval 1" >> $TAOS_CFG +echo "numOfTotalVnodes 4" >> $TAOS_CFG +echo "asyncLog 0" >> $TAOS_CFG +echo "numOfMPeers 1" >> $TAOS_CFG +echo "locale en_US.UTF-8" >> $TAOS_CFG +echo "anyIp 0" >> $TAOS_CFG + + diff --git a/tests/script/sh/exec.sh b/tests/script/sh/exec.sh new file mode 100755 index 0000000000000000000000000000000000000000..86d78a441354f13316303787f6967108533450b8 --- /dev/null +++ b/tests/script/sh/exec.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +# if [ $# != 4 || $# != 5 ]; then + # echo "argument list need input : " + # echo " -n nodeName" + # echo " -s start/stop" + # echo " -c clear" + # exit 1 +# fi + +NODE_NAME= +EXEC_OPTON= +CLEAR_OPTION="false" +while getopts "n:s:u:x:ct" arg +do + case $arg in + n) + NODE_NAME=$OPTARG + ;; + s) + EXEC_OPTON=$OPTARG + ;; + c) + CLEAR_OPTION="clear" + ;; + t) + SHELL_OPTION="true" + ;; + u) + USERS=$OPTARG + ;; + x) + SIGNAL=$OPTARG + ;; + ?) + echo "unkown argument" + ;; + esac +done + +SCRIPT_DIR=`dirname $0` +cd $SCRIPT_DIR/../ +SCRIPT_DIR=`pwd` + +cd ../../ +TAOS_DIR=`pwd` + +BUILD_DIR=$TAOS_DIR/debug/build +SIM_DIR=$TAOS_DIR/sim +NODE_DIR=$SIM_DIR/$NODE_NAME +EXE_DIR=$BUILD_DIR/bin +CFG_DIR=$NODE_DIR/cfg +LOG_DIR=$NODE_DIR/log +DATA_DIR=$NODE_DIR/data +MGMT_DIR=$NODE_DIR/data/mgmt +TSDB_DIR=$NODE_DIR/data/tsdb + +TAOS_CFG=$NODE_DIR/cfg/taos.cfg + +echo ------------ $EXEC_OPTON $NODE_NAME + +TAOS_FLAG=$SIM_DIR/tsim/flag +if [ -f "$TAOS_FLAG" ]; then + EXE_DIR=/usr/local/bin/taos +fi + +if [ "$CLEAR_OPTION" = "clear" ]; then + echo rm -rf $MGMT_DIR $TSDB_DIR + rm -rf $TSDB_DIR + rm -rf $MGMT_DIR +fi + +if [ "$SHELL_OPTION" = "true" ]; then + if [ "$EXEC_OPTON" = "start" ]; then + echo "ExcuteCmd:" $EXE_DIR/taos -c $CFG_DIR -u $USERS -p + $EXE_DIR/taos -c $CFG_DIR -u $USERS -p + else + #relative path + RCFG_DIR=sim/$NODE_NAME/cfg + PID=`ps -ef|grep -v taosd | grep taos | grep $RCFG_DIR | grep -v grep | awk '{print $2}'` + if [ -n "$PID" ]; then + sudo kill -9 $PID + fi + fi + return +fi + +if [ "$EXEC_OPTON" = "start" ]; then + echo "ExcuteCmd:" $EXE_DIR/taosd -c $CFG_DIR + nohup $EXE_DIR/taosd -c $CFG_DIR > /dev/null 2>&1 & + #TT=`date +%s` + #mkdir ${LOG_DIR}/${TT} + #echo valgrind --log-file=${LOG_DIR}/${TT}/valgrind.log --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes $EXE_DIR/taosd -c $CFG_DIR + #nohup valgrind --log-file=${LOG_DIR}/${TT}/valgrind.log --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes $EXE_DIR/taosd -c $CFG_DIR > /dev/null 2>&1 & + +else + #relative path + RCFG_DIR=sim/$NODE_NAME/cfg + PID=`ps -ef|grep taosd | grep $RCFG_DIR | grep -v grep | awk '{print $2}'` + if [ -n "$PID" ]; then + if [ "$SIGNAL" = "SIGINT" ]; then + echo killed by signal + sudo kill -sigint $PID + else + sudo kill -9 $PID + fi + fi +fi + diff --git a/tests/script/sh/ip.sh b/tests/script/sh/ip.sh new file mode 100755 index 0000000000000000000000000000000000000000..9a0cde163a78cdd691724b3e1faf15aa7b48d456 --- /dev/null +++ b/tests/script/sh/ip.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +#already create real card, such as 192.168.0.1-5 +#exit 0 + +if [ $# != 4 ]; then + echo "argument list need input : " + echo " -i if use [192.168.0.70 ] then input [70]" + echo " -s up/down" + exit 1 +fi + +#just create ip like 192.168.0.* + +IP_ADDRESS= +EXEC_OPTON= +while getopts "i:s:" arg +do + case $arg in + i) + IP_ADDRESS=$OPTARG + ;; + s) + EXEC_OPTON=$OPTARG + ;; + ?) + echo "unkonw argument" + ;; + esac +done + +echo ============ $EXEC_OPTON $IP_ADDRESS =========== +sudo ifconfig lo:$IP_ADDRESS 192.168.0.$IP_ADDRESS $EXEC_OPTON diff --git a/tests/script/sh/stop_dnodes.sh b/tests/script/sh/stop_dnodes.sh new file mode 100755 index 0000000000000000000000000000000000000000..acdfbe9e6ad74d2493e442207a98442d5c5e3144 --- /dev/null +++ b/tests/script/sh/stop_dnodes.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +PID=`ps -ef|grep /usr/bin/taosd | grep -v grep | awk '{print $2}'` +if [ -n "$PID" ]; then + echo sudo systemctl stop taosd + sudo systemctl stop taosd +fi + +PID=`ps -ef|grep taosd | grep -v grep | awk '{print $2}'` +if [ -n "$PID" ]; then + echo sudo kill -9 $PID + sudo kill -9 $PID +fi diff --git a/tests/script/test.sh b/tests/script/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..5fd80d39098dfc45c63e78c4f13291322f7af81b --- /dev/null +++ b/tests/script/test.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +################################################## +# +# Do simulation test +# +################################################## + +set +e + +FILE_NAME= +RELEASE=0 +ASYNC=0 +while getopts "f:a" arg +do + case $arg in + f) + FILE_NAME=$OPTARG + ;; + a) + ASYNC=1 + ;; + ?) + echo "unknow argument" + ;; + esac +done + +cd . +sh/ip.sh -i 1 -s up > /dev/null 2>&1 & +sh/ip.sh -i 2 -s up > /dev/null 2>&1 & +sh/ip.sh -i 3 -s up > /dev/null 2>&1 & + +# Get responsible directories +CODE_DIR=`dirname $0` +CODE_DIR=`pwd` +cd ../../ +TOP_DIR=`pwd` +BUILD_DIR=$TOP_DIR/debug/build +SIM_DIR=$TOP_DIR/sim + +if [ $ASYNC -eq 0 ]; then + PROGRAM=$BUILD_DIR/bin/tsim +else + PROGRAM="$BUILD_DIR/bin/tsim -a" +fi + +PRG_DIR=$SIM_DIR/tsim +CFG_DIR=$PRG_DIR/cfg +LOG_DIR=$PRG_DIR/log +DATA_DIR=$PRG_DIR/data + +chmod -R 777 $PRG_DIR +echo "------------------------------------------------------------------------" +echo "Start TDengine Testing Case ..." +echo "BUILD_DIR: $BUILD_DIR" +echo "SIM_DIR : $SIM_DIR" +echo "CODE_DIR : $CODE_DIR" +echo "CFG_DIR : $CFG_DIR" + +rm -rf $LOG_DIR +rm -rf $CFG_DIR +mkdir -p $PRG_DIR +mkdir -p $LOG_DIR +mkdir -p $CFG_DIR + +TAOS_CFG=$PRG_DIR/cfg/taos.cfg +touch -f $TAOS_CFG +TAOS_FLAG=$PRG_DIR/flag + +echo " " >> $TAOS_CFG +echo "scriptDir ${CODE_DIR}/../script">> $TAOS_CFG +echo "masterIp 192.168.0.1" >> $TAOS_CFG +echo "secondIp 192.168.0.2" >> $TAOS_CFG +echo "localIp 127.0.0.1" >> $TAOS_CFG +echo "dataDir $DATA_DIR" >> $TAOS_CFG +echo "logDir $LOG_DIR" >> $TAOS_CFG +echo "numOfLogLines 100000000" >> $TAOS_CFG +echo "dDebugFlag 135" >> $TAOS_CFG +echo "mDebugFlag 135" >> $TAOS_CFG +echo "sdbDebugFlag 135" >> $TAOS_CFG +echo "rpcDebugFlag 135" >> $TAOS_CFG +echo "tmrDebugFlag 131" >> $TAOS_CFG +echo "cDebugFlag 135" >> $TAOS_CFG +echo "httpDebugFlag 135" >> $TAOS_CFG +echo "monitorDebugFlag 135" >> $TAOS_CFG +echo "udebugFlag 135" >> $TAOS_CFG +echo "clog 0" >> $TAOS_CFG +echo "asyncLog 0" >> $TAOS_CFG +echo "locale en_US.UTF-8" >> $TAOS_CFG +echo " " >> $TAOS_CFG + +ulimit -n 600000 +ulimit -c unlimited + +#sudo sysctl -w kernel.core_pattern=$TOP_DIR/core.%p.%e + +if [ -n "$FILE_NAME" ]; then + echo "ExcuteCmd:" $PROGRAM -c $CFG_DIR -f $FILE_NAME + echo "------------------------------------------------------------------------" + #valgrind --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes --log-file=valgrind.log $PROGRAM -c $CFG_DIR -f $FILE_NAME + $PROGRAM -c $CFG_DIR -f $FILE_NAME +else + echo "ExcuteCmd:" $PROGRAM -c $CFG_DIR -f basicSuite.sim + echo "------------------------------------------------------------------------" + $PROGRAM -c $CFG_DIR -f basicSuite.sim +fi + diff --git a/tests/script/tmp/dnode1.sim b/tests/script/tmp/dnode1.sim new file mode 100644 index 0000000000000000000000000000000000000000..7b6e3723cb8d27378150774f29ed46ac65968a94 --- /dev/null +++ b/tests/script/tmp/dnode1.sim @@ -0,0 +1,4 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -m 192.168.0.1 -i 192.168.0.1 +system sh/exec.sh -n dnode1 -s start +sql connect \ No newline at end of file diff --git a/tests/tsim/CMakeLists.txt b/tests/tsim/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2fec751cec4019b82d10760f13989e82894d262a --- /dev/null +++ b/tests/tsim/CMakeLists.txt @@ -0,0 +1,15 @@ +PROJECT(TDengine) + +INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/inc) +INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/util/inc) +INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/src/client/inc) +INCLUDE_DIRECTORIES(${TD_OS_DIR}/inc) +INCLUDE_DIRECTORIES(inc) + +IF (TD_WINDOWS_64) + INCLUDE_DIRECTORIES(${TD_COMMUNITY_DIR}/deps/pthread) +ENDIF () + +AUX_SOURCE_DIRECTORY(src SRC) +ADD_EXECUTABLE(tsim ${SRC}) +TARGET_LINK_LIBRARIES(tsim taos_static trpc tutil pthread ) diff --git a/tests/tsim/inc/cJSON.h b/tests/tsim/inc/cJSON.h new file mode 100644 index 0000000000000000000000000000000000000000..cdd5faa52399e83dd2c07174a025eae52a3c1b61 --- /dev/null +++ b/tests/tsim/inc/cJSON.h @@ -0,0 +1,267 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, 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 IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 5 +#define CJSON_VERSION_PATCH 9 + +#include +#include + +/* cJSON Types: */ +#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 */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int64_t valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; + + //Keep the original string of number + char numberstring[13]; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const 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 */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const 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. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(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. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* 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. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool 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. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(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)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/tsim/inc/sim.h b/tests/tsim/inc/sim.h new file mode 100644 index 0000000000000000000000000000000000000000..9d5227ff1e6124d717b1d0877b904666c30e2523 --- /dev/null +++ b/tests/tsim/inc/sim.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef __SIM_H__ +#define __SIM_H__ + +#include +#include +#include + +#include "taos.h" +#include "tidpool.h" +#include "tlog.h" +#include "tmodule.h" +#include "tutil.h" + +#define MAX_MAIN_SCRIPT_NUM 10 +#define MAX_BACKGROUND_SCRIPT_NUM 10 +#define MAX_FILE_NAME_LEN 256 +#define MAX_ERROR_LEN 1024 +#define MAX_QUERY_VALUE_LEN 40 +#define MAX_QUERY_COL_NUM 10 +#define MAX_QUERY_ROW_NUM 10 +#define MAX_SYSTEM_RESULT_LEN 2048 +#define MAX_VAR_LEN 100 +#define MAX_VAR_NAME_LEN 32 +#define MAX_VAR_VAL_LEN 80 +#define MAX_OPT_NAME_LEN 32 +#define MAX_SIM_CMD_NAME_LEN 40 + +#ifdef LINUX +#define SUCCESS_PREFIX "\033[44;32;1m" +#define SUCCESS_POSTFIX "\033[0m" +#define FAILED_PREFIX "\033[44;31;1m" +#define FAILED_POSTFIX "\033[0m" +#else +#define SUCCESS_PREFIX "" +#define SUCCESS_POSTFIX "" +#define FAILED_PREFIX "" +#define FAILED_POSTFIX "" +#endif + +#define simError(...) \ + if (simDebugFlag & DEBUG_ERROR) { \ + tprintf("ERROR SIM ", 255, __VA_ARGS__); \ + } +#define simWarn(...) \ + if (simDebugFlag & DEBUG_WARN) { \ + tprintf("WARN SIM ", simDebugFlag, __VA_ARGS__); \ + } +#define simTrace(...) \ + if (simDebugFlag & DEBUG_TRACE) { \ + tprintf("SIM ", simDebugFlag, __VA_ARGS__); \ + } +#define simDump(x, y) \ + if (simDebugFlag & DEBUG_DUMP) { \ + taosDumpData(x, y); \ + } +#define simPrint(...) \ + { tprintf("SIM ", 255, __VA_ARGS__); } + +enum { SIM_SCRIPT_TYPE_MAIN, SIM_SCRIPT_TYPE_BACKGROUND }; + +enum { + SIM_CMD_EXP, + SIM_CMD_IF, + SIM_CMD_ELIF, + SIM_CMD_ELSE, + SIM_CMD_ENDI, + SIM_CMD_WHILE, + SIM_CMD_ENDW, + SIM_CMD_SWITCH, + SIM_CMD_CASE, + SIM_CMD_DEFAULT, + SIM_CMD_CONTINUE, + SIM_CMD_BREAK, + SIM_CMD_ENDS, + SIM_CMD_SLEEP, + SIM_CMD_GOTO, + SIM_CMD_RUN, + SIM_CMD_RUN_BACK, + SIM_CMD_PRINT, + SIM_CMD_SYSTEM, + SIM_CMD_SYSTEM_CONTENT, + SIM_CMD_SQL, + SIM_CMD_SQL_ERROR, + SIM_CMD_SQL_SLOW, + SIM_CMD_TEST, + SIM_CMD_RETURN, + SIM_CMD_END +}; + +enum { + SQL_JUMP_FALSE, + SQL_JUMP_TRUE, +}; + +struct _script_t; +typedef struct _cmd_t { + short cmdno; + short nlen; + char name[MAX_SIM_CMD_NAME_LEN]; + bool (*parseCmd)(char *, struct _cmd_t *, int); + bool (*executeCmd)(struct _script_t *script, char *option); + struct _cmd_t *next; +} SCommand; + +typedef struct { + short cmdno; + short jump; // jump position + short errorJump; // sql jump flag, while '-x' exist in sql cmd, this flag + // will be SQL_JUMP_TRUE, otherwise is SQL_JUMP_FALSE */ + short lineNum; // correspodning line number in original file + int optionOffset; // relative option offset +} SCmdLine; + +typedef struct _var_t { + char varName[MAX_VAR_NAME_LEN]; + char varValue[MAX_VAR_VAL_LEN]; + char varNameLen; +} SVariable; + +typedef struct _script_t { + int type; + bool killed; + + void *taos; + char rows[12]; // number of rows data retrieved + char data[MAX_QUERY_ROW_NUM][MAX_QUERY_COL_NUM] + [MAX_QUERY_VALUE_LEN]; // query results + char system_exit_code[12]; + char system_ret_content[MAX_SYSTEM_RESULT_LEN]; + + int varLen; + int linePos; // current cmd position + int numOfLines; // number of lines in the script + int bgScriptLen; + char fileName[MAX_FILE_NAME_LEN]; // script file name + char error[MAX_ERROR_LEN]; + char *optionBuffer; + SCmdLine *lines; // command list + SVariable variables[MAX_VAR_LEN]; + struct _script_t *bgScripts[MAX_BACKGROUND_SCRIPT_NUM]; + char auth[128]; +} SScript; + +extern SScript *simScriptList[MAX_MAIN_SCRIPT_NUM]; +extern SCommand simCmdList[]; +extern int simScriptPos; +extern int simScriptSucced; +extern int simDebugFlag; +extern char scriptDir[]; +extern bool simAsyncQuery; + +SScript *simParseScript(char *fileName); + +SScript *simProcessCallOver(SScript *script); +void *simExecuteScript(void *script); +void simInitsimCmdList(); +bool simSystemInit(); +void simSystemCleanUp(); +char *simGetVariable(SScript *script, char *varName, int varLen); +bool simExecuteExpCmd(SScript *script, char *option); +bool simExecuteTestCmd(SScript *script, char *option); +bool simExecuteGotoCmd(SScript *script, char *option); +bool simExecuteRunCmd(SScript *script, char *option); +bool simExecuteRunBackCmd(SScript *script, char *option); +bool simExecuteSystemCmd(SScript *script, char *option); +bool simExecuteSystemContentCmd(SScript *script, char *option); +bool simExecutePrintCmd(SScript *script, char *option); +bool simExecuteSleepCmd(SScript *script, char *option); +bool simExecuteReturnCmd(SScript *script, char *option); +bool simExecuteSqlCmd(SScript *script, char *option); +bool simExecuteSqlErrorCmd(SScript *script, char *rest); +bool simExecuteSqlSlowCmd(SScript *script, char *option); +void simVisuallizeOption(SScript *script, char *src, char *dst); + +#endif \ No newline at end of file diff --git a/tests/tsim/inc/simParse.h b/tests/tsim/inc/simParse.h new file mode 100644 index 0000000000000000000000000000000000000000..cff66e6c4f50e4f5d2573eed16422004cc520bf3 --- /dev/null +++ b/tests/tsim/inc/simParse.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef __SIM_PARSE_H__ +#define __SIM_PARSE_H__ + +#define MAX_NUM_CMD 64 +#define MAX_NUM_LABLES 100 +#define MAX_LABEL_LEN 40 +#define MAX_NUM_BLOCK 100 +#define MAX_NUM_JUMP 100 +#define MAX_LINE_LEN 3000 +#define MAX_CMD_LINES 2048 +#define MAX_OPTION_BUFFER 64000 + +enum { + BLOCK_IF, + BLOCK_WHILE, + BLOCK_SWITCH, +}; + +/* label stack */ +typedef struct { + char top; /* number of labels */ + short pos[MAX_NUM_LABLES]; /* the position of the label */ + char label[MAX_NUM_LABLES][MAX_LABEL_LEN]; /* name of the label */ +} SLabel; + +/* block definition */ +typedef struct { + char top; /* the number of blocks stacked */ + char type[MAX_NUM_BLOCK]; /* the block type */ + short *pos[MAX_NUM_BLOCK]; /* position of the jump for if/elif/case */ + short back[MAX_NUM_BLOCK]; /* go back, endw and continue */ + char numJump[MAX_NUM_BLOCK]; + short *jump[MAX_NUM_BLOCK][MAX_NUM_JUMP]; /* break or elif */ + char sexp[MAX_NUM_BLOCK][40]; /*switch expression */ + char sexpLen[MAX_NUM_BLOCK]; /*switch expression length */ +} SBlock; + +bool simParseExpression(char *token, int lineNum); + +#endif \ No newline at end of file diff --git a/tests/tsim/src/cJSON.c b/tests/tsim/src/cJSON.c new file mode 100644 index 0000000000000000000000000000000000000000..b2e2f47bdac787b82ca0b93e50e69009e8a5f64c --- /dev/null +++ b/tests/tsim/src/cJSON.c @@ -0,0 +1,2702 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, 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 IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 9) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +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; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +static internal_hooks global_hooks = { malloc, free, realloc }; + +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(""); + if (!(copy = (unsigned char*)hooks->allocate(length))) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define cannot_read(buffer, size) (!can_read(buffer, size)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= LLONG_MAX) + { + item->valueint = LLONG_MAX; + } + else if (number <= LLONG_MIN) + { + item->valueint = LLONG_MIN; + } + else + { + item->valueint = (int64_t)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + + strncpy(item->numberstring, number_c_string, 12); + + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= LLONG_MAX) + { + object->valueint = LLONG_MAX; + } + else if (number <= LLONG_MIN) + { + object->valueint = LLONG_MIN; + } + else + { + object->valueint = (int64_t)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +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 > LLONG_MAX) + { + /* sizes bigger than LLONG_MAX are currently not supported */ + 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 > (LLONG_MAX / 2)) + { + /* overflow of int, use LLONG_MAX if possible */ + if (needed <= LLONG_MAX) + { + newsize = LLONG_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; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* 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); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + uint64_t codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(&buffer))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + 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->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + 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 cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + + return false; +} + +/* 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) + { + 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) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + 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; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 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(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (item == NULL) + { + return; + } + + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if ((item == NULL) || (string == NULL)) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= LLONG_MAX) + { + item->valueint = LLONG_MAX; + } + else if (num <= LLONG_MIN) + { + item->valueint = LLONG_MIN; + } + else + { + item->valueint = (int64_t)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/tests/tsim/src/simExe.c b/tests/tsim/src/simExe.c new file mode 100644 index 0000000000000000000000000000000000000000..d844f3b7860ea51b4a69715ad7d5c476896fbffd --- /dev/null +++ b/tests/tsim/src/simExe.c @@ -0,0 +1,916 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "os.h" +#include "sim.h" +#include "taos.h" +#include "taoserror.h" +#include "tglobalcfg.h" +#include "tutil.h" +#include "cJSON.h" + +void simLogSql(char *sql) { + static FILE *fp = NULL; + char filename[256]; + sprintf(filename, "%s/sim.sql", scriptDir); + if (fp == NULL) { + fp = fopen(filename, "w"); + if (fp == NULL) { + fprintf(stderr, "ERROR: failed to open file: %s\n", filename); + return; + } + } + fprintf(fp, "%s;\n", sql); + fflush(fp); +} + +char *simGetVariable(SScript *script, char *varName, int varLen) { + if (strncmp(varName, "error", varLen) == 0) return script->error; + + if (strncmp(varName, "rows", varLen) == 0) return script->rows; + + if (strncmp(varName, "system_exit", varLen) == 0) + return script->system_exit_code; + + if (strncmp(varName, "system_content", varLen) == 0) + return script->system_ret_content; + + // variable like data2_192.168.0.1 + if (strncmp(varName, "data", 4) == 0) { + if (varLen < 6) { + return "null"; + } + + if (varName[5] == '_') { + int col = varName[4] - '0'; + if (col < 0 || col >= MAX_QUERY_COL_NUM) { + return "null"; + } + + char *keyName; + int keyLen; + paGetToken(varName + 6, &keyName, &keyLen); + + for (int i = 0; i < MAX_QUERY_ROW_NUM; ++i) { + if (strncmp(keyName, script->data[i][0], keyLen) == 0) { + simTrace("script:%s, keyName:%s, keyValue:%s", script->fileName, script->data[i][0], script->data[i][col]); + return script->data[i][col]; + } + } + return "null"; + } else { + int row = varName[4] - '0'; + int col = varName[5] - '0'; + if (row < 0 || row >= MAX_QUERY_ROW_NUM) { + return "null"; + } + if (col < 0 || col >= MAX_QUERY_COL_NUM) { + return "null"; + } + + simTrace("script:%s, data[%d][%d]=%s", script->fileName, row, col, script->data[row][col]); + return script->data[row][col]; + } + } + + for (int i = 0; i < script->varLen; ++i) { + SVariable *var = &script->variables[i]; + if (var->varNameLen != varLen) { + continue; + } + if (strncmp(varName, var->varName, varLen) == 0) { + // if (strlen(var->varValue) != 0) + // simTrace("script:%s, var:%s, value:%s", script->fileName, + // var->varName, var->varValue); + return var->varValue; + } + } + + if (script->varLen >= MAX_VAR_LEN) { + simError("script:%s, too many varialbes:%d", script->fileName, script->varLen); + exit(0); + } + + SVariable *var = &script->variables[script->varLen]; + script->varLen++; + strncpy(var->varName, varName, varLen); + var->varNameLen = varLen; + var->varValue[0] = 0; + return var->varValue; +} + +int simExecuteExpression(SScript *script, char *exp) { + char *op1, *op2, *var1, *var2, *var3, *rest; + int op1Len, op2Len, var1Len, var2Len, var3Len, val0, val1; + char t0[512], t1[512], t2[512], t3[512]; + int result; + + rest = paGetToken(exp, &var1, &var1Len); + rest = paGetToken(rest, &op1, &op1Len); + rest = paGetToken(rest, &var2, &var2Len); + rest = paGetToken(rest, &op2, &op2Len); + + if (var1[0] == '$') + strcpy(t0, simGetVariable(script, var1 + 1, var1Len - 1)); + else { + memcpy(t0, var1, var1Len); + t0[var1Len] = 0; + } + + if (var2[0] == '$') + strcpy(t1, simGetVariable(script, var2 + 1, var2Len - 1)); + else { + memcpy(t1, var2, var2Len); + t1[var2Len] = 0; + } + + if (op2Len != 0) { + rest = paGetToken(rest, &var3, &var3Len); + + if (var3[0] == '$') + strcpy(t2, simGetVariable(script, var3 + 1, var3Len - 1)); + else { + memcpy(t2, var3, var3Len); + t2[var3Len] = 0; + } + + if (op2[0] == '+') { + sprintf(t3, "%lld", atoll(t1) + atoll(t2)); + } else if (op2[0] == '-') { + sprintf(t3, "%lld", atoll(t1) - atoll(t2)); + } else if (op2[0] == '*') { + sprintf(t3, "%lld", atoll(t1) * atoll(t2)); + } else if (op2[0] == '/') { + sprintf(t3, "%lld", atoll(t1) / atoll(t2)); + } else if (op2[0] == '.') { + sprintf(t3, "%s%s", t1, t2); + } + } else { + strcpy(t3, t1); + } + + result = 0; + + if (op1Len == 1) { + if (op1[0] == '=') { + strcpy(simGetVariable(script, var1 + 1, var1Len - 1), t3); + } else if (op1[0] == '<') { + val0 = atoi(t0); + val1 = atoi(t3); + if (val0 >= val1) result = -1; + } else if (op1[0] == '>') { + val0 = atoi(t0); + val1 = atoi(t3); + if (val0 <= val1) result = -1; + } + } else { + if (op1[0] == '=' && op1[1] == '=') { + if (strcmp(t0, t3) != 0) result = -1; + } else if (op1[0] == '!' && op1[1] == '=') { + if (strcmp(t0, t3) == 0) result = -1; + } else if (op1[0] == '<' && op1[1] == '=') { + val0 = atoi(t0); + val1 = atoi(t3); + if (val0 > val1) result = -1; + } else if (op1[0] == '>' && op1[1] == '=') { + val0 = atoi(t0); + val1 = atoi(t3); + if (val0 < val1) result = -1; + } + } + + return result; +} + +bool simExecuteExpCmd(SScript *script, char *option) { + simExecuteExpression(script, option); + script->linePos++; + return true; +} + +bool simExecuteTestCmd(SScript *script, char *option) { + int result; + result = simExecuteExpression(script, option); + + if (result >= 0) + script->linePos++; + else + script->linePos = script->lines[script->linePos].jump; + + return true; +} + +bool simExecuteGotoCmd(SScript *script, char *option) { + script->linePos = script->lines[script->linePos].jump; + return true; +} + +bool simExecuteRunCmd(SScript *script, char *option) { + char *fileName = option; + if (fileName == NULL || strlen(fileName) == 0) { + sprintf(script->error, "lineNum:%d. script file is null", script->lines[script->linePos].lineNum); + return false; + } + + SScript *newScript = simParseScript(option); + if (newScript == NULL) { + sprintf(script->error, "lineNum:%d. parse file:%s error", script->lines[script->linePos].lineNum, fileName); + return false; + } + + simPrint("script:%s, start to execute", newScript->fileName); + + newScript->type = SIM_SCRIPT_TYPE_MAIN; + simScriptPos++; + simScriptList[simScriptPos] = newScript; + + script->linePos++; + return true; +} + +bool simExecuteRunBackCmd(SScript *script, char *option) { + char *fileName = option; + if (fileName == NULL || strlen(fileName) == 0) { + sprintf(script->error, "lineNum:%d. script file is null", script->lines[script->linePos].lineNum); + return false; + } + + SScript *newScript = simParseScript(option); + if (newScript == NULL) { + sprintf(script->error, "lineNum:%d. parse file:%s error", script->lines[script->linePos].lineNum, fileName); + return false; + } + simPrint("script:%s, start to execute in background", newScript->fileName); + + newScript->type = SIM_SCRIPT_TYPE_BACKGROUND; + script->bgScripts[script->bgScriptLen++] = newScript; + + pthread_t pid; + if (pthread_create(&pid, NULL, simExecuteScript, (void *)newScript) != 0) { + sprintf(script->error, "lineNum:%d. create background thread failed", script->lines[script->linePos].lineNum); + return false; + } + + script->linePos++; + return true; +} + +bool simExecuteSystemCmd(SScript *script, char *option) { + char buf[4096] = {0}; + + sprintf(buf, "cd %s; ", scriptDir); + simVisuallizeOption(script, option, buf + strlen(buf)); + + int code = system(buf); + int repeatTimes = 0; + while (code < 0) { + simError("script:%s, failed to execute %s , code %d, errno:%d %s, repeatTimes:%d", + script->fileName, buf, code, errno, strerror(errno), repeatTimes); + taosMsleep(1000); + signal(SIGCHLD, SIG_DFL); + if (repeatTimes++ >= 10) { + exit(0); + } + } + + sprintf(script->system_exit_code, "%d", code); + script->linePos++; + return true; +} + +void simStoreSystemContentResult(SScript *script, char *filename) { + memset(script->system_ret_content, 0, MAX_SYSTEM_RESULT_LEN); + + FILE *fd; + if ((fd = fopen(filename, "r")) != NULL) { + fread(script->system_ret_content, 1, MAX_SYSTEM_RESULT_LEN - 1, fd); + fclose(fd); + char rmCmd[MAX_FILE_NAME_LEN] = {0}; + sprintf(rmCmd, "rm -f %s", filename); + system(rmCmd); + } +} + +bool simExecuteSystemContentCmd(SScript *script, char *option) { + char buf[4096] = {0}; + char filename[400] = {0}; + sprintf(filename, "%s/%s.tmp", scriptDir, script->fileName); + + sprintf(buf, "cd %s; ", scriptDir); + simVisuallizeOption(script, option, buf + strlen(buf)); + sprintf(buf, "%s > %s 2>/dev/null", buf, filename); + + sprintf(script->system_exit_code, "%d", system(buf)); + simStoreSystemContentResult(script, filename); + + script->linePos++; + return true; +} + +bool simExecutePrintCmd(SScript *script, char *rest) { + char buf[65536]; + + simVisuallizeOption(script, rest, buf); + rest = buf; + + simPrint("script:%s, %s", script->fileName, rest); + script->linePos++; + return true; +} + +bool simExecuteSleepCmd(SScript *script, char *option) { + int delta; + char buf[1024]; + + simVisuallizeOption(script, option, buf); + option = buf; + + delta = atoi(option); + if (delta <= 0) delta = 5; + + simPrint("script:%s, sleep %dms begin", script->fileName, delta); + taosMsleep(delta); + simPrint("script:%s, sleep %dms finished", script->fileName, delta); + + script->linePos++; + return true; +} + +bool simExecuteReturnCmd(SScript *script, char *option) { + char buf[1024]; + + simVisuallizeOption(script, option, buf); + option = buf; + + int ret = 1; + if (option && option[0] != 0) ret = atoi(option); + + if (ret < 0) { + sprintf(script->error, "lineNum:%d. error return %s", script->lines[script->linePos].lineNum, option); + return false; + } else { + simPrint("script:%s, return cmd execute with:%d", script->fileName, ret); + script->linePos = script->numOfLines; + } + + script->linePos++; + return true; +} + +void simVisuallizeOption(SScript *script, char *src, char *dst) { + char *var, *token, *value; + int dstLen, srcLen, tokenLen; + + dst[0] = 0, dstLen = 0; + + while (1) { + var = strchr(src, '$'); + if (var == NULL) break; + if (var && ((var - src - 1) > 0) && *(var - 1) == '\\') { + srcLen = var - src - 1; + memcpy(dst + dstLen, src, srcLen); + dstLen += srcLen; + src = var; + break; + } + + srcLen = var - src; + memcpy(dst + dstLen, src, srcLen); + dstLen += srcLen; + + src = paGetToken(var + 1, &token, &tokenLen); + value = simGetVariable(script, token, tokenLen); + + strcpy(dst + dstLen, value); + dstLen += strlen(value); + } + + strcpy(dst + dstLen, src); +} + +void simCloseRestFulConnect(SScript *script) { + memset(script->auth, 0, sizeof(script->auth)); +} + +void simCloseNativeConnect(SScript *script) { + if (script->taos == NULL) return; + + simTrace("script:%s, taos:%p closed", script->fileName, script->taos); + taos_close(script->taos); + + script->taos = NULL; +} + +void simCloseTaosdConnect(SScript *script) { + if (simAsyncQuery) { + return simCloseRestFulConnect(script); + } else { + return simCloseNativeConnect(script); + } +} +// {"status":"succ","code":0,"desc":"/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04"} +// {"status":"succ","head":["affected_rows"],"data":[[1]],"rows":1} +// {"status":"succ","head":["ts","i"],"data":[["2017-12-25 21:28:41.022",1],["2017-12-25 21:28:42.022",2],["2017-12-25 21:28:43.022",3],["2017-12-25 21:28:44.022",4],["2017-12-25 21:28:45.022",5],["2017-12-25 21:28:46.022",6],["2017-12-25 21:28:47.022",7],["2017-12-25 21:28:48.022",8],["2017-12-25 21:28:49.022",9],["2017-12-25 21:28:50.022",10]],"rows":10} +int simParseHttpCommandResult(SScript *script, char *command) { + cJSON* root = cJSON_Parse(command); + if (root == NULL) { + simError("script:%s, failed to parse json, response:%s", script->fileName, command); + return -1; + } + + cJSON *status = cJSON_GetObjectItem(root, "status"); + if (status == NULL) { + simError("script:%s, failed to parse json, status is null, response:%s", script->fileName, command); + cJSON_Delete(root); + return -1; + } + + if (status->valuestring == NULL || strlen(status->valuestring) == 0) { + simError("script:%s, failed to parse json, status value is null, response:%s", script->fileName, command); + cJSON_Delete(root); + return -1; + } + + if (strcmp(status->valuestring, "succ") != 0) { + cJSON *code = cJSON_GetObjectItem(root, "code"); + if (code == NULL) { + simError("script:%s, failed to parse json, code is null, response:%s", script->fileName, command); + cJSON_Delete(root); + return -1; + } + int retcode = (int)code->valueint; + if (retcode != 1017) { + simError("script:%s, json:status:%s not equal to succ, response:%s", script->fileName, status->valuestring, command); + cJSON_Delete(root); + return retcode; + } else { + simTrace("script:%s, json:status:%s not equal to succ, but code is %d, response:%s", script->fileName, + status->valuestring, retcode, command); + cJSON_Delete(root); + return 0; + } + } + + cJSON *desc = cJSON_GetObjectItem(root, "desc"); + if (desc != NULL) { + if (desc->valuestring == NULL || strlen(desc->valuestring) == 0) { + simError("script:%s, failed to parse json, desc value is null, response:%s", script->fileName, command); + cJSON_Delete(root); + return -1; + } + strcpy(script->auth, desc->valuestring); + cJSON_Delete(root); + return 0; + } + + cJSON *data = cJSON_GetObjectItem(root, "data"); + if (data == NULL) { + simError("script:%s, failed to parse json, data is null, response:%s", script->fileName, command); + cJSON_Delete(root); + return -1; + } + + int rowsize = cJSON_GetArraySize(data); + if (rowsize < 0) { + simError("script:%s, failed to parse json:data, data size %d, response:%s", script->fileName, rowsize, command); + cJSON_Delete(root); + return -1; + } + + int rowIndex = 0; + sprintf(script->rows, "%d", rowsize); + for (int r = 0; r < rowsize; ++r) { + cJSON *row = cJSON_GetArrayItem(data, r); + if (row == NULL) continue; + if (rowIndex++ >= 10) break; + + int colsize = cJSON_GetArraySize(row); + if (colsize < 0) { + break; + } + + colsize = MIN(10, colsize); + for (int c = 0; c < colsize; ++c) { + cJSON *col = cJSON_GetArrayItem(row, c); + if (col->valuestring != NULL) { + strcpy(script->data[r][c], col->valuestring); + } else { + if (col->numberstring[0] == 0) { + strcpy(script->data[r][c], "null"); + } else { + strcpy(script->data[r][c], col->numberstring); + } + } + } + } + + return 0; +} + +int simExecuteRestFulCommand(SScript *script, char *command) { + char buf[5000] = {0}; + sprintf(buf, "%s 2>/dev/null", command); + + FILE *fp = popen(buf, "r"); + if (fp == NULL) { + simError("failed to execute %s", buf); + return TSDB_CODE_OTHERS; + } + + int mallocSize = 2000; + int alreadyReadSize = 0; + char* content = malloc(mallocSize); + + while (!feof(fp)) { + int availSize = mallocSize - alreadyReadSize; + int len = fread(content + alreadyReadSize, 1, availSize, fp); + if (len >= availSize) { + alreadyReadSize += len; + mallocSize *= 2; + content = realloc(content, mallocSize); + } + } + + pclose(fp); + + return simParseHttpCommandResult(script, content); +} + +bool simCreateRestFulConnect(SScript *script, char *user, char *pass) { + char command[4096]; + sprintf(command, "curl 127.0.0.1:6020/rest/login/%s/%s", user, pass); + + bool success = false; + for (int attempt = 0; attempt < 10; ++attempt) { + success = simExecuteRestFulCommand(script, command) == 0; + if (!success) { + simTrace("script:%s, user:%s connect taosd failed:%s, attempt:%d", script->fileName, user, taos_errstr(NULL), attempt); + taosMsleep(1000); + } else { + simTrace("script:%s, user:%s connect taosd successed, attempt:%d", script->fileName, user, attempt); + break; + } + } + + if (!success) { + sprintf(script->error, "lineNum:%d. connect taosd failed:%s", script->lines[script->linePos].lineNum, taos_errstr(NULL)); + return false; + } + + simTrace("script:%s, connect taosd successed, auth:%p", script->fileName, script->auth); + return true; +} + +bool simCreateNativeConnect(SScript *script, char *user, char *pass) { + simCloseTaosdConnect(script); + void *taos = NULL; + for (int attempt = 0; attempt < 10; ++attempt) { + taos = taos_connect(NULL, user, pass, NULL, tsMnodeShellPort); + if (taos == NULL) { + simTrace("script:%s, user:%s connect taosd failed:%s, attempt:%d", script->fileName, user, taos_errstr(NULL), attempt); + taosMsleep(1000); + } else { + simTrace("script:%s, user:%s connect taosd successed, attempt:%d", script->fileName, user, attempt); + break; + } + } + + if (taos == NULL) { + sprintf(script->error, "lineNum:%d. connect taosd failed:%s", script->lines[script->linePos].lineNum, taos_errstr(NULL)); + return false; + } + + script->taos = taos; + simTrace("script:%s, connect taosd successed, taos:%p", script->fileName, taos); + + return true; +} + +bool simCreateTaosdConnect(SScript *script, char *rest) { + char *user = tsDefaultUser; + char *token; + int tokenLen; + rest = paGetToken(rest, &token, &tokenLen); + rest = paGetToken(rest, &token, &tokenLen); + if (tokenLen != 0) { + user = token; + } + + if (simAsyncQuery) { + return simCreateRestFulConnect(script, user, tsDefaultPass); + } else { + return simCreateNativeConnect(script, user, tsDefaultPass); + } +} + +bool simExecuteNativeSqlCommand(SScript *script, char *rest, bool isSlow) { + char timeStr[30] = {0}; + time_t tt; + struct tm *tp; + SCmdLine *line = &script->lines[script->linePos]; + int ret = -1; + + for (int attempt = 0; attempt < 3; ++attempt) { + simLogSql(rest); + ret = taos_query(script->taos, rest); + if (ret == TSDB_CODE_TABLE_ALREADY_EXIST || + ret == TSDB_CODE_DB_ALREADY_EXIST) { + simTrace("script:%s, taos:%p, %s success, ret:%d:%s", script->fileName, script->taos, rest, ret, tstrerror(ret)); + ret = 0; + break; + } else if (ret != 0) { + simTrace("script:%s, taos:%p, %s failed, ret:%d:%s, error:%s", + script->fileName, script->taos, rest, ret, tstrerror(ret), taos_errstr(script->taos)); + + if (line->errorJump == SQL_JUMP_TRUE) { + script->linePos = line->jump; + return true; + } + taosMsleep(1000); + } else { + break; + } + } + + if (ret) { + sprintf(script->error, "lineNum:%d. sql:%s failed, ret:%d:%s", line->lineNum, rest, ret, tstrerror(ret)); + return false; + } + + int numOfRows = 0; + int num_fields = taos_field_count(script->taos); + if (num_fields != 0) { + TAOS_RES *result = taos_use_result(script->taos); + if (result == NULL) { + simTrace("script:%s, taos:%p, %s failed, result is null", script->fileName, script->taos, rest); + if (line->errorJump == SQL_JUMP_TRUE) { + script->linePos = line->jump; + return true; + } + + sprintf(script->error, "lineNum:%d. result set null, sql:%s", line->lineNum, rest); + return false; + } + + TAOS_ROW row; + + while ((row = taos_fetch_row(result))) { + if (numOfRows < MAX_QUERY_ROW_NUM) { + TAOS_FIELD *fields = taos_fetch_fields(result); + for (int i = 0; i < num_fields; i++) { + char *value = NULL; + if (i < MAX_QUERY_COL_NUM) { + value = script->data[numOfRows][i]; + } + if (value == NULL) { + continue; + } + + if (row[i] == 0) { + strcpy(value, "null"); + continue; + } + + switch (fields[i].type) { + case TSDB_DATA_TYPE_BOOL: + sprintf(value, "%s", + ((((int)(*((char *)row[i]))) == 1) ? "1" : "0")); + break; + case TSDB_DATA_TYPE_TINYINT: + sprintf(value, "%d", (int)(*((char *)row[i]))); + break; + case TSDB_DATA_TYPE_SMALLINT: + sprintf(value, "%d", (int)(*((short *)row[i]))); + break; + case TSDB_DATA_TYPE_INT: + sprintf(value, "%d", *((int *)row[i])); + break; + case TSDB_DATA_TYPE_BIGINT: +#ifdef _TD_ARM_32_ + sprintf(value, "%lld", *((int64_t *)row[i])); +#else + sprintf(value, "%ld", *((int64_t *)row[i])); +#endif + break; + case TSDB_DATA_TYPE_FLOAT:{ +#ifdef _TD_ARM_32_ + float fv = 0; + *(int32_t*)(&fv) = *(int32_t*)row[i]; + sprintf(value, "%.5f", fv); +#else + sprintf(value, "%.5f", *((float *)row[i])); +#endif + } + break; + case TSDB_DATA_TYPE_DOUBLE: { +#ifdef _TD_ARM_32_ + double dv = 0; + *(int64_t*)(&dv) = *(int64_t*)row[i]; + sprintf(value, "%.9lf", dv); +#else + sprintf(value, "%.9lf", *((double *)row[i])); +#endif + } + break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: + memcpy(value, row[i], fields[i].bytes); + value[fields[i].bytes] = 0; + // snprintf(value, fields[i].bytes, "%s", (char *)row[i]); + break; + case TSDB_DATA_TYPE_TIMESTAMP: + tt = *(int64_t *)row[i] / 1000; + tp = localtime(&tt); + strftime(timeStr, 64, "%y-%m-%d %H:%M:%S", tp); + sprintf(value, "%s.%03d", timeStr, + (int)(*((int64_t *)row[i]) % 1000)); + break; + default: + break; + } // end of switch + } // end of for + } // end of if + numOfRows++; + if (isSlow && numOfRows % 100 == 0) { + taosMsleep(200); + } + if (numOfRows > 2000000000) { + simError("script:%s, too many rows return from query", script->fileName); + break; + } + } + + taos_free_result(result); + } else { + numOfRows = taos_affected_rows(script->taos); + } + + sprintf(script->rows, "%d", numOfRows); + + script->linePos++; + return true; +} + +bool simExecuteRestFulSqlCommand(SScript *script, char *rest) { + SCmdLine *line = &script->lines[script->linePos]; + char command[4096]; + sprintf(command, "curl -H 'Authorization: Taosd %s' -d \"%s\" 127.0.0.1:6020/rest/sql", script->auth, rest); + + int ret = -1; + for (int attempt = 0; attempt < 10; ++attempt) { + ret = simExecuteRestFulCommand(script, command); + if (ret == TSDB_CODE_TABLE_ALREADY_EXIST || + ret == TSDB_CODE_DB_ALREADY_EXIST) { + simTrace("script:%s, taos:%p, %s success, ret:%d:%s", script->fileName, script->taos, rest, ret, tstrerror(ret)); + ret = 0; + break; + } else if (ret != 0) { + simTrace("script:%s, taos:%p, %s failed, ret:%d", + script->fileName, script->taos, rest, ret); + + if (line->errorJump == SQL_JUMP_TRUE) { + script->linePos = line->jump; + return true; + } + taosMsleep(1000); + } else { + break; + } + } + + if (ret) { + sprintf(script->error, "lineNum:%d. sql:%s failed, ret:%d", line->lineNum, rest, ret); + return false; + } + + script->linePos++; + return true; +} + +bool simExecuteSqlImpCmd(SScript *script, char *rest, bool isSlow) { + char buf[3000]; + SCmdLine *line = &script->lines[script->linePos]; + + simVisuallizeOption(script, rest, buf); + rest = buf; + + simTrace("script:%s, exec:%s", script->fileName, rest); + strcpy(script->rows, "-1"); + for (int row = 0; row < MAX_QUERY_ROW_NUM; ++row) { + for (int col = 0; col < MAX_QUERY_COL_NUM; ++col) { + strcpy(script->data[row][col], "null"); + } + } + + if (strncmp(rest, "connect", 7) == 0) { + if (!simCreateTaosdConnect(script, rest)) { + return false; + } + script->linePos++; + return true; + } + + if ((!simAsyncQuery && script->taos == NULL) || (simAsyncQuery && script->auth[0] == 0)) { + if (!simCreateTaosdConnect(script, "connect root")) { + if (line->errorJump == SQL_JUMP_TRUE) { + script->linePos = line->jump; + return true; + } + return false; + } + } + + if (strncmp(rest, "close", 5) == 0) { + simCloseTaosdConnect(script); + script->linePos++; + return true; + } + + if (simAsyncQuery) { + return simExecuteRestFulSqlCommand(script, rest); + } else { + return simExecuteNativeSqlCommand(script, rest, isSlow); + } +} + +bool simExecuteSqlCmd(SScript *script, char *rest) { + bool isSlow = false; + return simExecuteSqlImpCmd(script, rest, isSlow); +} + +bool simExecuteSqlSlowCmd(SScript *script, char *rest) { + bool isSlow = true; + return simExecuteSqlImpCmd(script, rest, isSlow); +} + +bool simExecuteSqlErrorCmd(SScript *script, char *rest) { + char buf[3000]; + SCmdLine *line = &script->lines[script->linePos]; + + simVisuallizeOption(script, rest, buf); + rest = buf; + + simTrace("script:%s, exec:%s", script->fileName, rest); + strcpy(script->rows, "-1"); + for (int row = 0; row < MAX_QUERY_ROW_NUM; ++row) { + for (int col = 0; col < MAX_QUERY_COL_NUM; ++col) { + strcpy(script->data[row][col], "null"); + } + } + + if (strncmp(rest, "connect", 7) == 0) { + if (!simCreateTaosdConnect(script, rest)) { + return false; + } + script->linePos++; + return true; + } + + if ((!simAsyncQuery && script->taos == NULL) || (simAsyncQuery && script->auth[0] == 0)) { + if (!simCreateTaosdConnect(script, "connect root")) { + if (line->errorJump == SQL_JUMP_TRUE) { + script->linePos = line->jump; + return true; + } + return false; + } + } + + if (strncmp(rest, "close", 5) == 0) { + simCloseTaosdConnect(script); + script->linePos++; + return true; + } + + int ret; + if (simAsyncQuery) { + char command[4096]; + sprintf(command, "curl -H 'Authorization: Taosd %s' -d '%s' 127.0.0.1:6020/rest/sql", script->auth, rest); + ret = simExecuteRestFulCommand(script, command); + } + else { + ret = taos_query(script->taos, rest); + } + + if (ret != TSDB_CODE_SUCCESS) { + simTrace("script:%s, taos:%p, %s execute, expect failed, so success, ret:%d:%s", + script->fileName, script->taos, rest, ret, tstrerror(ret)); + script->linePos++; + return true; + } + sprintf(script->error, "lineNum:%d. sql:%s expect failed, but success, ret:%d:%s", line->lineNum, rest, ret, tstrerror(ret)); + + return false; +} diff --git a/tests/tsim/src/simMain.c b/tests/tsim/src/simMain.c new file mode 100644 index 0000000000000000000000000000000000000000..9cab6fb02b27a6fb45c2a8c3dcec4a9426895cd0 --- /dev/null +++ b/tests/tsim/src/simMain.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "sim.h" +#include +#include +#include +#include + +bool simAsyncQuery = false; + +void simHandleSignal(int signo) { + simSystemCleanUp(); + exit(1); +} + +int main(int argc, char *argv[]) { + char scriptFile[MAX_FILE_NAME_LEN] = "sim_main_test.sim"; + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-c") == 0 && i < argc - 1) { + strncpy(configDir, argv[++i], MAX_FILE_NAME_LEN); + } else if (strcmp(argv[i], "-f") == 0 && i < argc - 1) { + strcpy(scriptFile, argv[++i]); + } else if (strcmp(argv[i], "-a") == 0) { + simAsyncQuery = true; + } else { + printf("usage: %s [options] \n", argv[0]); + printf(" [-c config]: config directory, default is: %s\n", + configDir); + printf(" [-f script]: script filename\n"); + exit(0); + } + } + + if (!simSystemInit()) { + simError("failed to initialize the system"); + simSystemCleanUp(); + exit(1); + } + + simPrint("simulator is running ..."); + signal(SIGINT, simHandleSignal); + + SScript *script = simParseScript(scriptFile); + if (script == NULL) { + simError("parse script file:%s failed", scriptFile); + exit(-1); + } + + simScriptList[++simScriptPos] = script; + simExecuteScript(script); + + return 0; +} \ No newline at end of file diff --git a/tests/tsim/src/simParse.c b/tests/tsim/src/simParse.c new file mode 100644 index 0000000000000000000000000000000000000000..62f5c1efb8fcfd713644a35dbf8edbf9aa653319 --- /dev/null +++ b/tests/tsim/src/simParse.c @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* Thi file is to parse the simScriptPos, function file and default file, + * + * syntax + * + * expression is format like: + * $var [=|<|>|==] var + * or: + * $var = var [+|-|*|/|.] var + * + * if expression + * command + * + * if expression then + * commands + * elif expression + * commands + * elif expression + * commands + * else + * commands + * endi + * + * while expression + * commands + * continue + * break + * endw + * + * switch expression + * case 1 + * commands + * break + * case 2 + * commands + * break + * default + * commands + * ends + * + * label: + * goto label + * + */ + +#include "sim.h" +#include +#include +#include +#include "simParse.h" +#include "tutil.h" + +static SCommand *cmdHashList[MAX_NUM_CMD]; +static SCmdLine cmdLine[MAX_CMD_LINES]; +static char parseErr[MAX_ERROR_LEN]; +static char optionBuffer[MAX_OPTION_BUFFER]; +static int numOfLines, optionOffset; +static SLabel label, dest; +static SBlock block; + +int simHashCmd(char *token, int tokenLen) { + int i; + int hash = 0; + + for (i = 0; i < tokenLen; ++i) hash += token[i]; + + hash = hash % MAX_NUM_CMD; + + return hash; +} + +SCommand *simCheckCmd(char *token, int tokenLen) { + int hash; + SCommand *node; + + hash = simHashCmd(token, tokenLen); + + node = cmdHashList[hash]; + + while (node) { + if (node->nlen == tokenLen && strncmp(node->name, token, tokenLen) == 0) { + return node; + } else { + node = node->next; + } + } + + return NULL; +} + +void simAddCmdIntoHash(SCommand *pCmd) { + int hash; + SCommand *node; + + hash = simHashCmd(pCmd->name, strlen(pCmd->name)); + node = cmdHashList[hash]; + pCmd->next = node; + cmdHashList[hash] = pCmd; +} + +void simResetParser() { + optionOffset = 4; + numOfLines = 0; + memset(cmdLine, 0, sizeof(cmdLine)); + memset(optionBuffer, 0, sizeof(optionBuffer)); + memset(&label, 0, sizeof(label)); + memset(&block, 0, sizeof(block)); + memset(&dest, 0, sizeof(dest)); +} + +SScript *simBuildScriptObj(char *fileName) { + int i, destPos; + + /* process labels */ + + cmdLine[numOfLines].cmdno = SIM_CMD_RETURN; + numOfLines++; + + for (i = 0; i < numOfLines; ++i) { + cmdLine[i].errorJump = SQL_JUMP_FALSE; + } + + for (--dest.top; dest.top >= 0; --dest.top) { + for (i = 0; i < label.top; ++i) { + if (strcmp(label.label[i], dest.label[(uint8_t)dest.top]) == 0) break; + } + + if (i == label.top) { + sprintf(parseErr, "label:%s not defined", dest.label[(uint8_t)dest.top]); + return NULL; + } + + destPos = dest.pos[(uint8_t)dest.top]; + cmdLine[destPos].jump = label.pos[i]; + if (cmdLine[destPos].cmdno == SIM_CMD_SQL) { + cmdLine[destPos].errorJump = SQL_JUMP_TRUE; + } + } + + if (block.top != 0) { + sprintf(parseErr, "mismatched block"); + return NULL; + } + + for (i = 0; i < numOfLines; ++i) { + if (cmdLine[i].jump == 0) cmdLine[i].jump = numOfLines; + } + + SScript *script = malloc(sizeof(SScript)); + memset(script, 0, sizeof(SScript)); + + script->type = SIM_SCRIPT_TYPE_MAIN; + script->numOfLines = numOfLines; + strncpy(script->fileName, fileName, MAX_FILE_NAME_LEN); + + script->optionBuffer = malloc(optionOffset); + memcpy(script->optionBuffer, optionBuffer, optionOffset); + + script->lines = malloc(sizeof(SCmdLine) * numOfLines); + memcpy(script->lines, cmdLine, sizeof(SCmdLine) * numOfLines); + + return script; +} + +SScript *simParseScript(char *fileName) { + FILE *fd; + int tokenLen, lineNum = 0; + char buffer[MAX_LINE_LEN], name[128], *token, *rest; + SCommand *pCmd; + SScript *script; + + if ((fileName[0] == '.') || (fileName[0] == '/')) { + strcpy(name, fileName); + } else { + sprintf(name, "%s/%s", scriptDir, fileName); + } + + if ((fd = fopen(name, "r")) == NULL) { + simError("failed to open file:%s", name); + return NULL; + } + + simResetParser(); + + while (!feof(fd)) { + if (fgets(buffer, sizeof(buffer), fd) == NULL) continue; + + lineNum++; + int cmdlen = strlen(buffer); + if (buffer[cmdlen - 1] == '\r' || buffer[cmdlen - 1] == '\n') + buffer[cmdlen - 1] = 0; + rest = buffer; + + for (int i = 0; i < cmdlen; ++i) { + if (buffer[i] == '\r' || buffer[i] == '\n') { + buffer[i] = ' '; + } + } + + again: + rest = paGetToken(rest, &token, &tokenLen); + if (tokenLen == 0) continue; + + if (token[tokenLen - 1] == ':') { + strncpy(label.label[(uint8_t)label.top], token, tokenLen - 1); + label.pos[(uint8_t)label.top] = numOfLines; + label.top++; + goto again; + } + + if (token[0] == '$') { + if (!simParseExpression(token, lineNum)) { + simError("script:%s line:%d %s", fileName, lineNum, parseErr); + return NULL; + } + continue; + } + + if ((pCmd = simCheckCmd(token, tokenLen)) == NULL) { + token[tokenLen] = 0; + simError("script:%s line:%d invalid cmd:%s", fileName, lineNum, token); + return NULL; + } + + if (!pCmd->parseCmd(rest, pCmd, lineNum)) { + simError("script:%s line:%d %s", fileName, lineNum, parseErr); + return NULL; + } + } + + fclose(fd); + + script = simBuildScriptObj(fileName); + if (script == NULL) simError("script:%s %s", fileName, parseErr); + + return script; +} + +int simCheckExpression(char *exp) { + char *op1, *op2, *op, *rest; + int op1Len, op2Len, opLen; + + rest = paGetToken(exp, &op1, &op1Len); + if (op1Len == 0) { + sprintf(parseErr, "expression is required"); + return -1; + } + + rest = paGetToken(rest, &op, &opLen); + if (opLen == 0) { + sprintf(parseErr, "operator is missed"); + return -1; + } + + rest = paGetToken(rest, &op2, &op2Len); + if (op2Len == 0) { + sprintf(parseErr, "operand is missed"); + return -1; + } + + if (opLen == 1) { + if (op[0] != '=' && op[0] != '<' && op[0] != '>') { + sprintf(parseErr, "invalid operator:%s", op); + return -1; + } + + if (op[0] == '=' && op1[0] != '$') { + sprintf(parseErr, "left side of assignment must be variable"); + return -1; + } + } else if (opLen == 2) { + if (op[1] != '=' || + (op[0] != '=' && op[0] != '<' && op[0] != '>' && op[0] != '!')) { + sprintf(parseErr, "left side of assignment must be variable"); + return -1; + } + } else { + sprintf(parseErr, "invalid operator:%s", op); + return -1; + } + + rest = paGetToken(rest, &op, &opLen); + + if (opLen == 0) return rest - exp; + + /* if it is key word "then" */ + if (strncmp(op, "then", 4) == 0) return op - exp; + + rest = paGetToken(rest, &op2, &op2Len); + if (op2Len == 0) { + sprintf(parseErr, "operand is missed"); + return -1; + } + + if (opLen > 1) { + sprintf(parseErr, "invalid operator:%s", op); + return -1; + } + + if (op[0] == '+' || op[0] == '-' || op[0] == '*' || op[0] == '/' || + op[0] == '.') { + return rest - exp; + } + + return -1; +} + +bool simParseExpression(char *token, int lineNum) { + int expLen; + + expLen = simCheckExpression(token); + if (expLen <= 0) return -1; + + cmdLine[numOfLines].cmdno = SIM_CMD_EXP; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, token, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseIfCmd(char *rest, SCommand *pCmd, int lineNum) { + char *ret; + int expLen; + + expLen = simCheckExpression(rest); + + if (expLen <= 0) return -1; + + ret = rest + expLen; + + if (strncmp(ret, "then", 4) == 0) { + block.type[(uint8_t)block.top] = BLOCK_IF; + block.pos[(uint8_t)block.top] = &cmdLine[numOfLines].jump; + block.top++; + } else { + cmdLine[numOfLines].jump = numOfLines + 2; + } + + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + cmdLine[numOfLines].cmdno = SIM_CMD_TEST; + cmdLine[numOfLines].lineNum = lineNum; + + numOfLines++; + return true; +} + +bool simParseElifCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + expLen = simCheckExpression(rest); + + if (expLen <= 0) return -1; + + if (block.top < 1) { + sprintf(parseErr, "no matching if"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_IF) { + sprintf(parseErr, "no matched if block"); + return false; + } + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + block.jump[block.top - 1][(uint8_t)block.numJump[block.top - 1]] = + &(cmdLine[numOfLines].jump); + block.numJump[block.top - 1]++; + + numOfLines++; + + *(block.pos[block.top - 1]) = numOfLines; + block.pos[block.top - 1] = &cmdLine[numOfLines].jump; + + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + cmdLine[numOfLines].cmdno = SIM_CMD_TEST; + cmdLine[numOfLines].lineNum = lineNum; + + numOfLines++; + return true; +} + +bool simParseElseCmd(char *rest, SCommand *pCmd, int lineNum) { + if (block.top < 1) { + sprintf(parseErr, "no matching if"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_IF) { + sprintf(parseErr, "no matched if block"); + return false; + } + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + block.jump[block.top - 1][(uint8_t)block.numJump[block.top - 1]] = + &(cmdLine[numOfLines].jump); + block.numJump[block.top - 1]++; + + numOfLines++; + + *(block.pos[block.top - 1]) = numOfLines; + block.pos[block.top - 1] = NULL; + + return true; +} + +bool simParseEndiCmd(char *rest, SCommand *pCmd, int lineNum) { + int i; + + if (block.top < 1) { + sprintf(parseErr, "no matching if"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_IF) { + sprintf(parseErr, "no matched if block"); + return false; + } + + if (block.pos[block.top - 1]) *(block.pos[block.top - 1]) = numOfLines; + + for (i = 0; i < block.numJump[block.top - 1]; ++i) + *(block.jump[block.top - 1][i]) = numOfLines; + + block.numJump[block.top - 1] = 0; + block.top--; + + return true; +} + +bool simParseWhileCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + expLen = simCheckExpression(rest); + + if (expLen <= 0) return false; + + block.type[(uint8_t)block.top] = BLOCK_WHILE; + block.pos[(uint8_t)block.top] = &(cmdLine[numOfLines].jump); + block.back[(uint8_t)block.top] = numOfLines; + block.top++; + + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + cmdLine[numOfLines].cmdno = SIM_CMD_TEST; + cmdLine[numOfLines].lineNum = lineNum; + + numOfLines++; + return true; +} + +bool simParseEndwCmd(char *rest, SCommand *pCmd, int lineNum) { + int i; + + if (block.top < 1) { + sprintf(parseErr, "no matching while"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_WHILE) { + sprintf(parseErr, "no matched while block"); + return false; + } + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + cmdLine[numOfLines].jump = block.back[block.top - 1]; + cmdLine[numOfLines].lineNum = lineNum; + numOfLines++; + + *(block.pos[block.top - 1]) = numOfLines; + + for (i = 0; i < block.numJump[block.top - 1]; ++i) + *(block.jump[block.top - 1][i]) = numOfLines; + + block.top--; + + return true; +} + +bool simParseSwitchCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + rest = paGetToken(rest, &token, &tokenLen); + if (tokenLen == 0) { + sprintf(parseErr, "switch should be followed by variable"); + return false; + } + + if (token[0] != '$') { + sprintf(parseErr, "switch must be followed by variable"); + return false; + } + + memcpy(block.sexp[(uint8_t)block.top], token, tokenLen); + block.sexpLen[(uint8_t)block.top] = tokenLen; + block.type[(uint8_t)block.top] = BLOCK_SWITCH; + block.top++; + + return true; +} + +bool simParseCaseCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + rest = paGetToken(rest, &token, &tokenLen); + if (tokenLen == 0) { + sprintf(parseErr, "case should be followed by value"); + return false; + } + + if (block.top < 1) { + sprintf(parseErr, "no matching switch"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_SWITCH) { + sprintf(parseErr, "case not matched"); + return false; + } + + if (block.pos[block.top - 1] != NULL) + *(block.pos[block.top - 1]) = numOfLines; + + block.pos[block.top - 1] = &(cmdLine[numOfLines].jump); + + cmdLine[numOfLines].cmdno = SIM_CMD_TEST; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, block.sexp[block.top - 1], + block.sexpLen[block.top - 1]); + optionOffset += block.sexpLen[block.top - 1]; + *(optionBuffer + optionOffset++) = ' '; + *(optionBuffer + optionOffset++) = '='; + *(optionBuffer + optionOffset++) = '='; + *(optionBuffer + optionOffset++) = ' '; + memcpy(optionBuffer + optionOffset, token, tokenLen); + optionOffset += tokenLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseBreakCmd(char *rest, SCommand *pCmd, int lineNum) { + if (block.top < 1) { + sprintf(parseErr, "no blcok exists"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_SWITCH && + block.type[block.top - 1] != BLOCK_WHILE) { + sprintf(parseErr, "not in switch or while block"); + return false; + } + + block.jump[block.top - 1][(uint8_t)block.numJump[block.top - 1]] = + &(cmdLine[numOfLines].jump); + block.numJump[block.top - 1]++; + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + cmdLine[numOfLines].lineNum = lineNum; + + numOfLines++; + return true; +} + +bool simParseDefaultCmd(char *rest, SCommand *pCmd, int lineNum) { + if (block.top < 1) { + sprintf(parseErr, "no matching switch"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_SWITCH) { + sprintf(parseErr, "default should be matched with switch"); + return false; + } + + if (block.pos[block.top - 1] != NULL) + *(block.pos[block.top - 1]) = numOfLines; + + return true; +} + +bool simParseEndsCmd(char *rest, SCommand *pCmd, int lineNum) { + int i; + + if (block.top < 1) { + sprintf(parseErr, "no matching switch"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_SWITCH) { + sprintf(parseErr, "ends should be matched with switch"); + return false; + } + + for (i = 0; i < block.numJump[block.top - 1]; ++i) + *(block.jump[block.top - 1][i]) = numOfLines; + + block.numJump[block.top - 1] = 0; + block.top--; + + return true; +} + +bool simParseContinueCmd(char *rest, SCommand *pCmd, int lineNum) { + if (block.top < 1) { + sprintf(parseErr, "no matching while"); + return false; + } + + if (block.type[block.top - 1] != BLOCK_WHILE) { + sprintf(parseErr, "continue should be matched with while cmd"); + return false; + } + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].jump = block.back[block.top - 1]; + + numOfLines++; + return true; +} + +bool simParsePrintCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + rest++; + cmdLine[numOfLines].cmdno = SIM_CMD_PRINT; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + expLen = strlen(rest); + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +void simCheckSqlOption(char *rest) { + int valueLen; + char *value, *xpos; + + xpos = strstr(rest, " -x"); // need a blank + if (xpos) { + paGetToken(xpos + 3, &value, &valueLen); + if (valueLen != 0) { + memcpy(dest.label[(uint8_t)dest.top], value, valueLen); + dest.label[(uint8_t)dest.top][valueLen] = 0; + dest.pos[(uint8_t)dest.top] = numOfLines; + dest.top++; + + *xpos = 0; + } + } +} + +bool simParseSqlCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + rest++; + simCheckSqlOption(rest); + cmdLine[numOfLines].cmdno = SIM_CMD_SQL; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + expLen = strlen(rest); + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseSqlErrorCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + rest++; + cmdLine[numOfLines].cmdno = SIM_CMD_SQL_ERROR; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + expLen = strlen(rest); + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseSqlSlowCmd(char *rest, SCommand *pCmd, int lineNum) { + simParseSqlCmd(rest, pCmd, lineNum); + cmdLine[numOfLines - 1].cmdno = SIM_CMD_SQL_SLOW; + return true; +} + +bool simParseSystemCmd(char *rest, SCommand *pCmd, int lineNum) { + int expLen; + + rest++; + cmdLine[numOfLines].cmdno = SIM_CMD_SYSTEM; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + expLen = strlen(rest); + memcpy(optionBuffer + optionOffset, rest, expLen); + optionOffset += expLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseSystemContentCmd(char *rest, SCommand *pCmd, int lineNum) { + simParseSystemCmd(rest, pCmd, lineNum); + cmdLine[numOfLines - 1].cmdno = SIM_CMD_SYSTEM_CONTENT; + return true; +} + +bool simParseSleepCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + cmdLine[numOfLines].cmdno = SIM_CMD_SLEEP; + cmdLine[numOfLines].lineNum = lineNum; + + paGetToken(rest, &token, &tokenLen); + if (tokenLen > 0) { + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, token, tokenLen); + optionOffset += tokenLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + } + + numOfLines++; + return true; +} + +bool simParseReturnCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + cmdLine[numOfLines].cmdno = SIM_CMD_RETURN; + cmdLine[numOfLines].lineNum = lineNum; + + paGetToken(rest, &token, &tokenLen); + if (tokenLen > 0) { + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, token, tokenLen); + optionOffset += tokenLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + } + + numOfLines++; + return true; +} + +bool simParseGotoCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + rest = paGetToken(rest, &token, &tokenLen); + + if (tokenLen == 0) { + sprintf(parseErr, "label should be followed by goto cmd"); + return false; + } + + memcpy(dest.label[(uint8_t)dest.top], token, tokenLen); + dest.label[(uint8_t)dest.top][tokenLen] = 0; + dest.pos[(uint8_t)dest.top] = numOfLines; + dest.top++; + + cmdLine[numOfLines].cmdno = SIM_CMD_GOTO; + cmdLine[numOfLines].lineNum = lineNum; + + numOfLines++; + return true; +} + +bool simParseRunCmd(char *rest, SCommand *pCmd, int lineNum) { + char *token; + int tokenLen; + + rest = paGetToken(rest, &token, &tokenLen); + + if (tokenLen == 0) { + sprintf(parseErr, "file name should be followed by run cmd"); + return false; + } + + cmdLine[numOfLines].cmdno = SIM_CMD_RUN; + cmdLine[numOfLines].lineNum = lineNum; + cmdLine[numOfLines].optionOffset = optionOffset; + memcpy(optionBuffer + optionOffset, token, tokenLen); + optionOffset += tokenLen + 1; + *(optionBuffer + optionOffset - 1) = 0; + + numOfLines++; + return true; +} + +bool simParseRunBackCmd(char *rest, SCommand *pCmd, int lineNum) { + simParseRunCmd(rest, pCmd, lineNum); + cmdLine[numOfLines - 1].cmdno = SIM_CMD_RUN_BACK; + return true; +} + +void simInitsimCmdList() { + int cmdno; + memset(simCmdList, 0, SIM_CMD_END * sizeof(SCommand)); + + /* internal command */ + cmdno = SIM_CMD_EXP; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "exp"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = NULL; + simCmdList[cmdno].executeCmd = simExecuteExpCmd; + + cmdno = SIM_CMD_IF; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "if"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseIfCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_ELIF; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "elif"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseElifCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_ELSE; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "else"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseElseCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_ENDI; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "endi"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseEndiCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_WHILE; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "while"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseWhileCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_ENDW; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "endw"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseEndwCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SWITCH; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "switch"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSwitchCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_CASE; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "case"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseCaseCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_DEFAULT; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "default"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseDefaultCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_BREAK; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "break"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseBreakCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_CONTINUE; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "continue"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseContinueCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_ENDS; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "ends"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseEndsCmd; + simCmdList[cmdno].executeCmd = NULL; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SLEEP; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "sleep"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSleepCmd; + simCmdList[cmdno].executeCmd = simExecuteSleepCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_GOTO; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "goto"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseGotoCmd; + simCmdList[cmdno].executeCmd = simExecuteGotoCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_RUN; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "run"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseRunCmd; + simCmdList[cmdno].executeCmd = simExecuteRunCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_RUN_BACK; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "run_back"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseRunBackCmd; + simCmdList[cmdno].executeCmd = simExecuteRunBackCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SYSTEM; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "system"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSystemCmd; + simCmdList[cmdno].executeCmd = simExecuteSystemCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SYSTEM_CONTENT; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "system_content"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSystemContentCmd; + simCmdList[cmdno].executeCmd = simExecuteSystemContentCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_PRINT; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "print"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParsePrintCmd; + simCmdList[cmdno].executeCmd = simExecutePrintCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SQL; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "sql"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSqlCmd; + simCmdList[cmdno].executeCmd = simExecuteSqlCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SQL_ERROR; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "sql_error"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSqlErrorCmd; + simCmdList[cmdno].executeCmd = simExecuteSqlErrorCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + cmdno = SIM_CMD_SQL_SLOW; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "sql_slow"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseSqlSlowCmd; + simCmdList[cmdno].executeCmd = simExecuteSqlSlowCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); + + /* test is only an internal command */ + cmdno = SIM_CMD_TEST; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "test"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = NULL; + simCmdList[cmdno].executeCmd = simExecuteTestCmd; + + cmdno = SIM_CMD_RETURN; + simCmdList[cmdno].cmdno = cmdno; + strcpy(simCmdList[cmdno].name, "return"); + simCmdList[cmdno].nlen = strlen(simCmdList[cmdno].name); + simCmdList[cmdno].parseCmd = simParseReturnCmd; + simCmdList[cmdno].executeCmd = simExecuteReturnCmd; + simAddCmdIntoHash(&(simCmdList[cmdno])); +} diff --git a/tests/tsim/src/simSystem.c b/tests/tsim/src/simSystem.c new file mode 100644 index 0000000000000000000000000000000000000000..73382744cf088c5823db5a76f09c8a11c265694b --- /dev/null +++ b/tests/tsim/src/simSystem.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "os.h" +#include "sim.h" +#include "taos.h" +#include "tglobalcfg.h" +#include "ttimer.h" +#include "tutil.h" + +SScript *simScriptList[MAX_MAIN_SCRIPT_NUM]; +SCommand simCmdList[SIM_CMD_END]; +int simScriptPos = -1; +int simScriptSucced = 0; +int simDebugFlag = 135; +void simCloseTaosdConnect(SScript *script); + +bool simSystemInit() { + taos_init(); + simInitsimCmdList(); + memset(simScriptList, 0, sizeof(SScript *) * MAX_MAIN_SCRIPT_NUM); + return true; +} + +void simSystemCleanUp() {} + +void simFreeScript(SScript *script) { + if (script->type == SIM_SCRIPT_TYPE_MAIN) { + for (int i = 0; i < script->bgScriptLen; ++i) { + SScript *bgScript = script->bgScripts[i]; + bgScript->killed = true; + } + } + + taos_close(script->taos); + tfree(script->lines); + tfree(script->optionBuffer); + tfree(script); +} + +SScript *simProcessCallOver(SScript *script) { + if (script->type == SIM_SCRIPT_TYPE_MAIN) { + if (script->killed) { + simPrint("script:" FAILED_PREFIX "%s" FAILED_POSTFIX ", " FAILED_PREFIX + "failed" FAILED_POSTFIX ", error:%s", + script->fileName, script->error); + exit(-1); + } else { + simPrint("script:" SUCCESS_PREFIX "%s" SUCCESS_POSTFIX ", " SUCCESS_PREFIX + "success" SUCCESS_POSTFIX, + script->fileName); + simCloseTaosdConnect(script); + simScriptSucced++; + simScriptPos--; + if (simScriptPos == -1) { + simPrint("----------------------------------------------------------------------"); + simPrint("Simulation Test Done, " SUCCESS_PREFIX "%d" SUCCESS_POSTFIX " Passed:\n", simScriptSucced); + exit(0); + } + + simFreeScript(script); + return simScriptList[simScriptPos]; + } + } else { + simPrint("script:%s, is stopped by main script", script->fileName); + simFreeScript(script); + return NULL; + } +} + +void *simExecuteScript(void *inputScript) { + SScript *script = (SScript *)inputScript; + + while (1) { + if (script->type == SIM_SCRIPT_TYPE_MAIN) { + script = simScriptList[simScriptPos]; + } + + if (script->killed || script->linePos >= script->numOfLines) { + script = simProcessCallOver(script); + if (script == NULL) break; + } else { + SCmdLine *line = &script->lines[script->linePos]; + char *option = script->optionBuffer + line->optionOffset; + simTrace("script:%s, line:%d with option \"%s\"", script->fileName, line->lineNum, option); + + SCommand *cmd = &simCmdList[line->cmdno]; + int ret = (*(cmd->executeCmd))(script, option); + if (!ret) { + script->killed = true; + } + } + } + + return NULL; +}