提交 78b5da0e 编写于 作者: M Minghao Li

sync refactor

上级 7469d62f
......@@ -60,17 +60,27 @@ typedef struct SyncTimeout {
ESyncTimeoutType timeoutType;
uint64_t logicClock;
int32_t timerMS;
void* data;
void* data; // need optimized
} SyncTimeout;
SyncTimeout* syncTimeoutBuild();
SyncTimeout* syncTimeoutBuild2(ESyncTimeoutType timeoutType, uint64_t logicClock, int32_t timerMS, void* data);
void syncTimeoutDestroy(SyncTimeout* pMsg);
void syncTimeoutSerialize(const SyncTimeout* pMsg, char* buf, uint32_t bufLen);
void syncTimeoutDeserialize(const char* buf, uint32_t len, SyncTimeout* pMsg);
char* syncTimeoutSerialize2(const SyncTimeout* pMsg, uint32_t* len); //
SyncTimeout* syncTimeoutDeserialize2(const char* buf, uint32_t len); //
void syncTimeout2RpcMsg(const SyncTimeout* pMsg, SRpcMsg* pRpcMsg);
void syncTimeoutFromRpcMsg(const SRpcMsg* pRpcMsg, SyncTimeout* pMsg);
SyncTimeout* syncTimeoutFromRpcMsg2(const SRpcMsg* pRpcMsg); //
cJSON* syncTimeout2Json(const SyncTimeout* pMsg);
SyncTimeout* syncTimeoutBuild2(ESyncTimeoutType timeoutType, uint64_t logicClock, int32_t timerMS, void* data);
char* syncTimeout2Str(const SyncTimeout* pMsg); //
// for debug ----------------------
void syncTimeoutPrint(const SyncTimeout* pMsg);
void syncTimeoutPrint2(char* s, const SyncTimeout* pMsg);
void syncTimeoutLog(const SyncTimeout* pMsg);
void syncTimeoutLog2(char* s, const SyncTimeout* pMsg);
// ---------------------------------------------
typedef struct SyncPing {
......@@ -129,13 +139,23 @@ typedef struct SyncClientRequest {
} SyncClientRequest;
SyncClientRequest* syncClientRequestBuild(uint32_t dataLen);
SyncClientRequest* syncClientRequestBuild2(const SRpcMsg* pOriginalRpcMsg, uint64_t seqNum, bool isWeak);
void syncClientRequestDestroy(SyncClientRequest* pMsg);
void syncClientRequestSerialize(const SyncClientRequest* pMsg, char* buf, uint32_t bufLen);
void syncClientRequestDeserialize(const char* buf, uint32_t len, SyncClientRequest* pMsg);
char* syncClientRequestSerialize2(const SyncClientRequest* pMsg, uint32_t* len);
SyncClientRequest* syncClientRequestDeserialize2(const char* buf, uint32_t len);
void syncClientRequest2RpcMsg(const SyncClientRequest* pMsg, SRpcMsg* pRpcMsg);
void syncClientRequestFromRpcMsg(const SRpcMsg* pRpcMsg, SyncClientRequest* pMsg);
SyncClientRequest* syncClientRequestFromRpcMsg2(const SRpcMsg* pRpcMsg);
cJSON* syncClientRequest2Json(const SyncClientRequest* pMsg);
SyncClientRequest* syncClientRequestBuild2(const SRpcMsg* pOriginalRpcMsg, uint64_t seqNum, bool isWeak);
char* syncClientRequest2Str(const SyncClientRequest* pMsg);
// for debug ----------------------
void syncClientRequestPrint(const SyncClientRequest* pMsg);
void syncClientRequestPrint2(char* s, const SyncClientRequest* pMsg);
void syncClientRequestLog(const SyncClientRequest* pMsg);
void syncClientRequestLog2(char* s, const SyncClientRequest* pMsg);
// ---------------------------------------------
typedef struct SyncClientRequestReply {
......
......@@ -93,6 +93,15 @@ SyncTimeout* syncTimeoutBuild() {
return pMsg;
}
SyncTimeout* syncTimeoutBuild2(ESyncTimeoutType timeoutType, uint64_t logicClock, int32_t timerMS, void* data) {
SyncTimeout* pMsg = syncTimeoutBuild();
pMsg->timeoutType = timeoutType;
pMsg->logicClock = logicClock;
pMsg->timerMS = timerMS;
pMsg->data = data;
return pMsg;
}
void syncTimeoutDestroy(SyncTimeout* pMsg) {
if (pMsg != NULL) {
free(pMsg);
......@@ -109,6 +118,25 @@ void syncTimeoutDeserialize(const char* buf, uint32_t len, SyncTimeout* pMsg) {
assert(len == pMsg->bytes);
}
char* syncTimeoutSerialize2(const SyncTimeout* pMsg, uint32_t* len) {
char* buf = malloc(pMsg->bytes);
assert(buf != NULL);
syncTimeoutSerialize(pMsg, buf, pMsg->bytes);
if (len != NULL) {
*len = pMsg->bytes;
}
return buf;
}
SyncTimeout* syncTimeoutDeserialize2(const char* buf, uint32_t len) {
uint32_t bytes = *((uint32_t*)buf);
SyncTimeout* pMsg = malloc(bytes);
assert(pMsg != NULL);
syncTimeoutDeserialize(buf, len, pMsg);
assert(len == pMsg->bytes);
return pMsg;
}
void syncTimeout2RpcMsg(const SyncTimeout* pMsg, SRpcMsg* pRpcMsg) {
memset(pRpcMsg, 0, sizeof(*pRpcMsg));
pRpcMsg->msgType = pMsg->msgType;
......@@ -121,6 +149,11 @@ void syncTimeoutFromRpcMsg(const SRpcMsg* pRpcMsg, SyncTimeout* pMsg) {
syncTimeoutDeserialize(pRpcMsg->pCont, pRpcMsg->contLen, pMsg);
}
SyncTimeout* syncTimeoutFromRpcMsg2(const SRpcMsg* pRpcMsg) {
SyncTimeout* pMsg = syncTimeoutDeserialize2(pRpcMsg->pCont, pRpcMsg->contLen);
return pMsg;
}
cJSON* syncTimeout2Json(const SyncTimeout* pMsg) {
char u64buf[128];
......@@ -139,13 +172,38 @@ cJSON* syncTimeout2Json(const SyncTimeout* pMsg) {
return pJson;
}
SyncTimeout* syncTimeoutBuild2(ESyncTimeoutType timeoutType, uint64_t logicClock, int32_t timerMS, void* data) {
SyncTimeout* pMsg = syncTimeoutBuild();
pMsg->timeoutType = timeoutType;
pMsg->logicClock = logicClock;
pMsg->timerMS = timerMS;
pMsg->data = data;
return pMsg;
char* syncTimeout2Str(const SyncTimeout* pMsg) {
cJSON* pJson = syncTimeout2Json(pMsg);
char* serialized = cJSON_Print(pJson);
cJSON_Delete(pJson);
return serialized;
}
// for debug ----------------------
void syncTimeoutPrint(const SyncTimeout* pMsg) {
char* serialized = syncTimeout2Str(pMsg);
printf("syncTimeoutPrint | len:%lu | %s \n", strlen(serialized), serialized);
fflush(NULL);
free(serialized);
}
void syncTimeoutPrint2(char* s, const SyncTimeout* pMsg) {
char* serialized = syncTimeout2Str(pMsg);
printf("syncTimeoutPrint2 | len:%lu | %s | %s \n", strlen(serialized), s, serialized);
fflush(NULL);
free(serialized);
}
void syncTimeoutLog(const SyncTimeout* pMsg) {
char* serialized = syncTimeout2Str(pMsg);
sTrace("syncTimeoutLog | len:%lu | %s", strlen(serialized), serialized);
free(serialized);
}
void syncTimeoutLog2(char* s, const SyncTimeout* pMsg) {
char* serialized = syncTimeout2Str(pMsg);
sTrace("syncTimeoutLog2 | len:%lu | %s | %s", strlen(serialized), s, serialized);
free(serialized);
}
// ---- message process SyncPing----
......@@ -359,6 +417,15 @@ SyncClientRequest* syncClientRequestBuild(uint32_t dataLen) {
return pMsg;
}
SyncClientRequest* syncClientRequestBuild2(const SRpcMsg* pOriginalRpcMsg, uint64_t seqNum, bool isWeak) {
SyncClientRequest* pMsg = syncClientRequestBuild(pOriginalRpcMsg->contLen);
pMsg->originalRpcType = pOriginalRpcMsg->msgType;
pMsg->seqNum = seqNum;
pMsg->isWeak = isWeak;
memcpy(pMsg->data, pOriginalRpcMsg->pCont, pOriginalRpcMsg->contLen);
return pMsg;
}
void syncClientRequestDestroy(SyncClientRequest* pMsg) {
if (pMsg != NULL) {
free(pMsg);
......@@ -375,6 +442,25 @@ void syncClientRequestDeserialize(const char* buf, uint32_t len, SyncClientReque
assert(len == pMsg->bytes);
}
char* syncClientRequestSerialize2(const SyncClientRequest* pMsg, uint32_t* len) {
char* buf = malloc(pMsg->bytes);
assert(buf != NULL);
syncClientRequestSerialize(pMsg, buf, pMsg->bytes);
if (len != NULL) {
*len = pMsg->bytes;
}
return buf;
}
SyncClientRequest* syncClientRequestDeserialize2(const char* buf, uint32_t len) {
uint32_t bytes = *((uint32_t*)buf);
SyncClientRequest* pMsg = malloc(bytes);
assert(pMsg != NULL);
syncClientRequestDeserialize(buf, len, pMsg);
assert(len == pMsg->bytes);
return pMsg;
}
void syncClientRequest2RpcMsg(const SyncClientRequest* pMsg, SRpcMsg* pRpcMsg) {
memset(pRpcMsg, 0, sizeof(*pRpcMsg));
pRpcMsg->msgType = pMsg->msgType;
......@@ -387,6 +473,11 @@ void syncClientRequestFromRpcMsg(const SRpcMsg* pRpcMsg, SyncClientRequest* pMsg
syncClientRequestDeserialize(pRpcMsg->pCont, pRpcMsg->contLen, pMsg);
}
SyncClientRequest* syncClientRequestFromRpcMsg2(const SRpcMsg* pRpcMsg) {
SyncClientRequest* pMsg = syncClientRequestDeserialize2(pRpcMsg->pCont, pRpcMsg->contLen);
return pMsg;
}
cJSON* syncClientRequest2Json(const SyncClientRequest* pMsg) {
char u64buf[128];
......@@ -399,18 +490,51 @@ cJSON* syncClientRequest2Json(const SyncClientRequest* pMsg) {
cJSON_AddNumberToObject(pRoot, "isWeak", pMsg->isWeak);
cJSON_AddNumberToObject(pRoot, "dataLen", pMsg->dataLen);
char* s;
s = syncUtilprintBin((char*)(pMsg->data), pMsg->dataLen);
cJSON_AddStringToObject(pRoot, "data", s);
free(s);
s = syncUtilprintBin2((char*)(pMsg->data), pMsg->dataLen);
cJSON_AddStringToObject(pRoot, "data2", s);
free(s);
cJSON* pJson = cJSON_CreateObject();
cJSON_AddItemToObject(pJson, "SyncClientRequest", pRoot);
return pJson;
}
SyncClientRequest* syncClientRequestBuild2(const SRpcMsg* pOriginalRpcMsg, uint64_t seqNum, bool isWeak) {
SyncClientRequest* pMsg = syncClientRequestBuild(pOriginalRpcMsg->contLen);
pMsg->originalRpcType = pOriginalRpcMsg->msgType;
pMsg->seqNum = seqNum;
pMsg->isWeak = isWeak;
memcpy(pMsg->data, pOriginalRpcMsg->pCont, pOriginalRpcMsg->contLen);
return pMsg;
char* syncClientRequest2Str(const SyncClientRequest* pMsg) {
cJSON* pJson = syncClientRequest2Json(pMsg);
char* serialized = cJSON_Print(pJson);
cJSON_Delete(pJson);
return serialized;
}
// for debug ----------------------
void syncClientRequestPrint(const SyncClientRequest* pMsg) {
char* serialized = syncClientRequest2Str(pMsg);
printf("syncClientRequestPrint | len:%lu | %s \n", strlen(serialized), serialized);
fflush(NULL);
free(serialized);
}
void syncClientRequestPrint2(char* s, const SyncClientRequest* pMsg) {
char* serialized = syncClientRequest2Str(pMsg);
printf("syncClientRequestPrint2 | len:%lu | %s | %s \n", strlen(serialized), s, serialized);
fflush(NULL);
free(serialized);
}
void syncClientRequestLog(const SyncClientRequest* pMsg) {
char* serialized = syncClientRequest2Str(pMsg);
sTrace("syncClientRequestLog | len:%lu | %s", strlen(serialized), serialized);
free(serialized);
}
void syncClientRequestLog2(char* s, const SyncClientRequest* pMsg) {
char* serialized = syncClientRequest2Str(pMsg);
sTrace("syncClientRequestLog2 | len:%lu | %s | %s", strlen(serialized), s, serialized);
free(serialized);
}
// ---- message process SyncRequestVote----
......
......@@ -21,6 +21,8 @@ add_executable(syncRequestVoteTest "")
add_executable(syncRequestVoteReplyTest "")
add_executable(syncAppendEntriesTest "")
add_executable(syncAppendEntriesReplyTest "")
add_executable(syncClientRequestTest "")
add_executable(syncTimeoutTest "")
target_sources(syncTest
......@@ -115,6 +117,14 @@ target_sources(syncAppendEntriesReplyTest
PRIVATE
"syncAppendEntriesReplyTest.cpp"
)
target_sources(syncClientRequestTest
PRIVATE
"syncClientRequestTest.cpp"
)
target_sources(syncTimeoutTest
PRIVATE
"syncTimeoutTest.cpp"
)
target_include_directories(syncTest
......@@ -232,6 +242,16 @@ target_include_directories(syncAppendEntriesReplyTest
"${CMAKE_SOURCE_DIR}/include/libs/sync"
"${CMAKE_CURRENT_SOURCE_DIR}/../inc"
)
target_include_directories(syncClientRequestTest
PUBLIC
"${CMAKE_SOURCE_DIR}/include/libs/sync"
"${CMAKE_CURRENT_SOURCE_DIR}/../inc"
)
target_include_directories(syncTimeoutTest
PUBLIC
"${CMAKE_SOURCE_DIR}/include/libs/sync"
"${CMAKE_CURRENT_SOURCE_DIR}/../inc"
)
target_link_libraries(syncTest
......@@ -326,6 +346,14 @@ target_link_libraries(syncAppendEntriesReplyTest
sync
gtest_main
)
target_link_libraries(syncClientRequestTest
sync
gtest_main
)
target_link_libraries(syncTimeoutTest
sync
gtest_main
)
enable_testing()
......
......@@ -32,12 +32,13 @@ void test1() {
void test2() {
SyncAppendEntriesReply *pMsg = createMsg();
uint32_t len = pMsg->bytes;
char * serialized = (char *)malloc(len);
uint32_t len = pMsg->bytes;
char * serialized = (char *)malloc(len);
syncAppendEntriesReplySerialize(pMsg, serialized, len);
SyncAppendEntriesReply *pMsg2 = syncAppendEntriesReplyBuild();
syncAppendEntriesReplyDeserialize(serialized, len, pMsg2);
syncAppendEntriesReplyPrint2((char *)"test2: syncAppendEntriesReplySerialize -> syncAppendEntriesReplyDeserialize ", pMsg2);
syncAppendEntriesReplyPrint2((char *)"test2: syncAppendEntriesReplySerialize -> syncAppendEntriesReplyDeserialize ",
pMsg2);
free(serialized);
syncAppendEntriesReplyDestroy(pMsg);
......@@ -46,11 +47,11 @@ void test2() {
void test3() {
SyncAppendEntriesReply *pMsg = createMsg();
uint32_t len;
char * serialized = syncAppendEntriesReplySerialize2(pMsg, &len);
uint32_t len;
char * serialized = syncAppendEntriesReplySerialize2(pMsg, &len);
SyncAppendEntriesReply *pMsg2 = syncAppendEntriesReplyDeserialize2(serialized, len);
syncAppendEntriesReplyPrint2((char *)"test3: syncAppendEntriesReplySerialize3 -> syncAppendEntriesReplyDeserialize2 ",
pMsg2);
pMsg2);
free(serialized);
syncAppendEntriesReplyDestroy(pMsg);
......@@ -59,11 +60,12 @@ void test3() {
void test4() {
SyncAppendEntriesReply *pMsg = createMsg();
SRpcMsg rpcMsg;
SRpcMsg rpcMsg;
syncAppendEntriesReply2RpcMsg(pMsg, &rpcMsg);
SyncAppendEntriesReply *pMsg2 = syncAppendEntriesReplyBuild();
syncAppendEntriesReplyFromRpcMsg(&rpcMsg, pMsg2);
syncAppendEntriesReplyPrint2((char *)"test4: syncAppendEntriesReply2RpcMsg -> syncAppendEntriesReplyFromRpcMsg ", pMsg2);
syncAppendEntriesReplyPrint2((char *)"test4: syncAppendEntriesReply2RpcMsg -> syncAppendEntriesReplyFromRpcMsg ",
pMsg2);
syncAppendEntriesReplyDestroy(pMsg);
syncAppendEntriesReplyDestroy(pMsg2);
......@@ -71,10 +73,11 @@ void test4() {
void test5() {
SyncAppendEntriesReply *pMsg = createMsg();
SRpcMsg rpcMsg;
SRpcMsg rpcMsg;
syncAppendEntriesReply2RpcMsg(pMsg, &rpcMsg);
SyncAppendEntriesReply *pMsg2 = syncAppendEntriesReplyFromRpcMsg2(&rpcMsg);
syncAppendEntriesReplyPrint2((char *)"test5: syncAppendEntriesReply2RpcMsg -> syncAppendEntriesReplyFromRpcMsg2 ", pMsg2);
syncAppendEntriesReplyPrint2((char *)"test5: syncAppendEntriesReply2RpcMsg -> syncAppendEntriesReplyFromRpcMsg2 ",
pMsg2);
syncAppendEntriesReplyDestroy(pMsg);
syncAppendEntriesReplyDestroy(pMsg2);
......
#include <gtest/gtest.h>
#include <stdio.h>
#include "syncIO.h"
#include "syncInt.h"
#include "syncMessage.h"
#include "syncUtil.h"
void logTest() {
sTrace("--- sync log test: trace");
sDebug("--- sync log test: debug");
sInfo("--- sync log test: info");
sWarn("--- sync log test: warn");
sError("--- sync log test: error");
sFatal("--- sync log test: fatal");
}
SyncClientRequest *createMsg() {
SRpcMsg rpcMsg;
memset(&rpcMsg, 0, sizeof(rpcMsg));
rpcMsg.msgType = 12345;
rpcMsg.contLen = 20;
rpcMsg.pCont = rpcMallocCont(rpcMsg.contLen);
strcpy((char*)rpcMsg.pCont, "hello rpc");
SyncClientRequest *pMsg = syncClientRequestBuild2(&rpcMsg, 123, true);
return pMsg;
}
void test1() {
SyncClientRequest *pMsg = createMsg();
syncClientRequestPrint2((char *)"test1:", pMsg);
syncClientRequestDestroy(pMsg);
}
void test2() {
SyncClientRequest *pMsg = createMsg();
uint32_t len = pMsg->bytes;
char * serialized = (char *)malloc(len);
syncClientRequestSerialize(pMsg, serialized, len);
SyncClientRequest *pMsg2 = syncClientRequestBuild(pMsg2->dataLen);
syncClientRequestDeserialize(serialized, len, pMsg2);
syncClientRequestPrint2((char *)"test2: syncClientRequestSerialize -> syncClientRequestDeserialize ", pMsg2);
free(serialized);
syncClientRequestDestroy(pMsg);
syncClientRequestDestroy(pMsg2);
}
void test3() {
SyncClientRequest *pMsg = createMsg();
uint32_t len;
char * serialized = syncClientRequestSerialize2(pMsg, &len);
SyncClientRequest *pMsg2 = syncClientRequestDeserialize2(serialized, len);
syncClientRequestPrint2((char *)"test3: syncClientRequestSerialize3 -> syncClientRequestDeserialize2 ", pMsg2);
free(serialized);
syncClientRequestDestroy(pMsg);
syncClientRequestDestroy(pMsg2);
}
void test4() {
SyncClientRequest *pMsg = createMsg();
SRpcMsg rpcMsg;
syncClientRequest2RpcMsg(pMsg, &rpcMsg);
SyncClientRequest *pMsg2 = (SyncClientRequest *)malloc(rpcMsg.contLen);
syncClientRequestFromRpcMsg(&rpcMsg, pMsg2);
syncClientRequestPrint2((char *)"test4: syncClientRequest2RpcMsg -> syncClientRequestFromRpcMsg ", pMsg2);
syncClientRequestDestroy(pMsg);
syncClientRequestDestroy(pMsg2);
}
void test5() {
SyncClientRequest *pMsg = createMsg();
SRpcMsg rpcMsg;
syncClientRequest2RpcMsg(pMsg, &rpcMsg);
SyncClientRequest *pMsg2 = syncClientRequestFromRpcMsg2(&rpcMsg);
syncClientRequestPrint2((char *)"test5: syncClientRequest2RpcMsg -> syncClientRequestFromRpcMsg2 ", pMsg2);
syncClientRequestDestroy(pMsg);
syncClientRequestDestroy(pMsg2);
}
int main() {
// taosInitLog((char *)"syncTest.log", 100000, 10);
tsAsyncLog = 0;
sDebugFlag = 143 + 64;
logTest();
test1();
test2();
test3();
test4();
test5();
return 0;
}
#include <gtest/gtest.h>
#include <stdio.h>
#include "syncIO.h"
#include "syncInt.h"
#include "syncMessage.h"
#include "syncUtil.h"
void logTest() {
sTrace("--- sync log test: trace");
sDebug("--- sync log test: debug");
sInfo("--- sync log test: info");
sWarn("--- sync log test: warn");
sError("--- sync log test: error");
sFatal("--- sync log test: fatal");
}
int gg = 0;
SyncTimeout *createMsg() {
SyncTimeout *pMsg = syncTimeoutBuild2(SYNC_TIMEOUT_PING, 999, 333, &gg);
return pMsg;
}
void test1() {
SyncTimeout *pMsg = createMsg();
syncTimeoutPrint2((char *)"test1:", pMsg);
syncTimeoutDestroy(pMsg);
}
void test2() {
SyncTimeout *pMsg = createMsg();
uint32_t len = pMsg->bytes;
char * serialized = (char *)malloc(len);
syncTimeoutSerialize(pMsg, serialized, len);
SyncTimeout *pMsg2 = syncTimeoutBuild();
syncTimeoutDeserialize(serialized, len, pMsg2);
syncTimeoutPrint2((char *)"test2: syncTimeoutSerialize -> syncTimeoutDeserialize ", pMsg2);
free(serialized);
syncTimeoutDestroy(pMsg);
syncTimeoutDestroy(pMsg2);
}
void test3() {
SyncTimeout *pMsg = createMsg();
uint32_t len;
char * serialized = syncTimeoutSerialize2(pMsg, &len);
SyncTimeout *pMsg2 = syncTimeoutDeserialize2(serialized, len);
syncTimeoutPrint2((char *)"test3: syncTimeoutSerialize3 -> syncTimeoutDeserialize2 ", pMsg2);
free(serialized);
syncTimeoutDestroy(pMsg);
syncTimeoutDestroy(pMsg2);
}
void test4() {
SyncTimeout *pMsg = createMsg();
SRpcMsg rpcMsg;
syncTimeout2RpcMsg(pMsg, &rpcMsg);
SyncTimeout *pMsg2 = (SyncTimeout *)malloc(rpcMsg.contLen);
syncTimeoutFromRpcMsg(&rpcMsg, pMsg2);
syncTimeoutPrint2((char *)"test4: syncTimeout2RpcMsg -> syncTimeoutFromRpcMsg ", pMsg2);
syncTimeoutDestroy(pMsg);
syncTimeoutDestroy(pMsg2);
}
void test5() {
SyncTimeout *pMsg = createMsg();
SRpcMsg rpcMsg;
syncTimeout2RpcMsg(pMsg, &rpcMsg);
SyncTimeout *pMsg2 = syncTimeoutFromRpcMsg2(&rpcMsg);
syncTimeoutPrint2((char *)"test5: syncTimeout2RpcMsg -> syncTimeoutFromRpcMsg2 ", pMsg2);
syncTimeoutDestroy(pMsg);
syncTimeoutDestroy(pMsg2);
}
int main() {
// taosInitLog((char *)"syncTest.log", 100000, 10);
tsAsyncLog = 0;
sDebugFlag = 143 + 64;
logTest();
test1();
test2();
test3();
test4();
test5();
return 0;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册