From 00f8021bf8913597822fd536abb002801e305f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Sun, 15 Jun 2025 16:11:38 +0800 Subject: [PATCH 1/8] add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 1 + interfaces/kits/hyperaio/src/hyperaio_trace.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index bd4d38d2a..ed3442740 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -302,6 +302,7 @@ int32_t HyperAio::DestroyCtx() if (harvestThread_.joinable()) { HILOGI("start harvest thread join"); harvestThread_.join(); + // No print this log means join failed HILOGI("join success"); } diff --git a/interfaces/kits/hyperaio/src/hyperaio_trace.cpp b/interfaces/kits/hyperaio/src/hyperaio_trace.cpp index b51c24b34..4dfce0b0c 100644 --- a/interfaces/kits/hyperaio/src/hyperaio_trace.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio_trace.cpp @@ -23,13 +23,13 @@ HyperaioTrace::HyperaioTrace(const std::string& value, bool isShowLog) : value_( if (isShowLog) { HILOGI("%{public}s", value_.c_str()); } - StartTrace(HITRACE_TAG_OHOS, "[HyperAio]" + value); + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "[HyperAio]" + value); } void HyperaioTrace::End() { if (!isFinished_) { - FinishTrace(HITRACE_TAG_OHOS); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); isFinished_ = true; } } -- Gitee From 5798c36f5e76822ccc67f9b599cc8533aaa6bce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Sun, 15 Jun 2025 16:32:09 +0800 Subject: [PATCH 2/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index ed3442740..3adb59efb 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -277,7 +277,7 @@ void HyperAio::HarvestRes() } cqeCount_++; auto response = std::make_unique(cqe->user_data, cqe->res, cqe->flags); - HILOGI("get cqe, user_data = %{public}lld, res = %{public}d, flags = %{public}u", + HILOGI("get cqe, user_data = %{public}lu, res = %{public}d, flags = %{public}u", cqe->user_data, cqe->res, cqe->flags); HyperaioTrace trace("harvest: userdata " + std::to_string(cqe->user_data) + " res " + std::to_string(cqe->res) + "flags " + std::to_string(cqe->flags)); -- Gitee From a0d585788c7f4f515b66fa736649f0b1cf9265e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Sun, 15 Jun 2025 16:42:46 +0800 Subject: [PATCH 3/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index 3adb59efb..458baed1b 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -277,7 +277,7 @@ void HyperAio::HarvestRes() } cqeCount_++; auto response = std::make_unique(cqe->user_data, cqe->res, cqe->flags); - HILOGI("get cqe, user_data = %{public}lu, res = %{public}d, flags = %{public}u", + HILOGI("get cqe, user_data = %{public}llu, res = %{public}d, flags = %{public}u", cqe->user_data, cqe->res, cqe->flags); HyperaioTrace trace("harvest: userdata " + std::to_string(cqe->user_data) + " res " + std::to_string(cqe->res) + "flags " + std::to_string(cqe->flags)); -- Gitee From 665221054e6fc825512c15f58fe0544bd127ecf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Sun, 15 Jun 2025 17:07:32 +0800 Subject: [PATCH 4/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index 458baed1b..a9b5629dc 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -277,8 +277,6 @@ void HyperAio::HarvestRes() } cqeCount_++; auto response = std::make_unique(cqe->user_data, cqe->res, cqe->flags); - HILOGI("get cqe, user_data = %{public}llu, res = %{public}d, flags = %{public}u", - cqe->user_data, cqe->res, cqe->flags); HyperaioTrace trace("harvest: userdata " + std::to_string(cqe->user_data) + " res " + std::to_string(cqe->res) + "flags " + std::to_string(cqe->flags)); io_uring_cqe_seen(&pImpl_->uring_, cqe); -- Gitee From 6cd75df9f369c05bc6ed057c7f1c25470493bca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Mon, 16 Jun 2025 17:30:37 +0800 Subject: [PATCH 5/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 47 +++++++++-------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index a9b5629dc..baadeab4f 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -53,6 +53,10 @@ static bool HasAccessIouringPermission() return true; } +static bool ValidateReqNum(uint32_t reqNum) { + return reqNum > 0 && reqNum <= URING_QUEUE_SIZE; +} + uint32_t HyperAio::SupportIouring() { HyperaioTrace trace("SupportIouring"); @@ -121,6 +125,10 @@ int32_t HyperAio::StartOpenReqs(OpenReqs *req) HILOGE("HyperAio is not initialized"); return -EPERM; } + if (!ValidateReqNum(req->reqNum)) { + HILOGE("reqNum is out of range: %{public}u", req->reqNum); + return -EINVAL; + } HyperaioTrace trace("StartOpenReqs" + std::to_string(req->reqNum)); uint32_t totalReqs = req->reqNum; uint32_t count = 0; @@ -139,7 +147,7 @@ int32_t HyperAio::StartOpenReqs(OpenReqs *req) HyperaioTrace trace("open flags:" + std::to_string(openInfo->flags) + "mode:" + std::to_string(openInfo->mode) + "userData:" + std::to_string(openInfo->userData)); count++; - if (count >= BATCH_SIZE) { + if (count >= BATCH_SIZE || i == totalReqs - 1) { int32_t ret = io_uring_submit(&pImpl_->uring_); if (ret < 0) { HILOGE("submit open reqs failed, ret = %{public}d", ret); @@ -149,14 +157,6 @@ int32_t HyperAio::StartOpenReqs(OpenReqs *req) count = 0; } } - if (count > 0 && count < BATCH_SIZE) { - int32_t ret = io_uring_submit(&pImpl_->uring_); - if (ret < 0) { - HILOGE("submit open reqs failed, ret = %{public}d", ret); - return ret; - } - openReqCount_ += count; - } return EOK; } @@ -172,6 +172,10 @@ int32_t HyperAio::StartReadReqs(ReadReqs *req) HILOGE("HyperAio is not initialized"); return -EPERM; } + if (!ValidateReqNum(req->reqNum)) { + HILOGE("reqNum is out of range: %{public}u", req->reqNum); + return -EINVAL; + } HyperaioTrace trace("StartReadReqs" + std::to_string(req->reqNum)); uint32_t totalReqs = req->reqNum; uint32_t count = 0; @@ -189,7 +193,7 @@ int32_t HyperAio::StartReadReqs(ReadReqs *req) HyperaioTrace trace("read len:" + std::to_string(readInfo->len) + "offset:" + std::to_string(readInfo->offset) + "userData:" + std::to_string(readInfo->userData)); count++; - if (count >= BATCH_SIZE) { + if (count >= BATCH_SIZE || i == totalReqs - 1) { int32_t ret = io_uring_submit(&pImpl_->uring_); if (ret < 0) { HILOGE("submit read reqs failed, ret = %{public}d", ret); @@ -199,15 +203,6 @@ int32_t HyperAio::StartReadReqs(ReadReqs *req) count = 0; } } - if (count > 0 && count < BATCH_SIZE) { - int32_t ret = io_uring_submit(&pImpl_->uring_); - if (ret < 0) { - HILOGE("submit read reqs failed, ret = %{public}d", ret); - return ret; - } - readReqCount_ += count; - } - return EOK; } @@ -223,6 +218,10 @@ int32_t HyperAio::StartCancelReqs(CancelReqs *req) HILOGE("HyperAio is not initialized"); return -EPERM; } + if (!ValidateReqNum(req->reqNum)) { + HILOGE("reqNum is out of range: %{public}u", req->reqNum); + return -EINVAL; + } HyperaioTrace trace("StartCancelReqs" + std::to_string(req->reqNum)); uint32_t totalReqs = req->reqNum; uint32_t count = 0; @@ -240,7 +239,7 @@ int32_t HyperAio::StartCancelReqs(CancelReqs *req) HyperaioTrace trace("cancel userData:" + std::to_string(cancelInfo->userData) + "targetUserData:" + std::to_string(cancelInfo->targetUserData)); count++; - if (count >= BATCH_SIZE) { + if (count >= BATCH_SIZE || i == totalReqs - 1) { int32_t ret = io_uring_submit(&pImpl_->uring_); if (ret < 0) { HILOGE("submit cancel reqs failed, ret = %{public}d", ret); @@ -250,14 +249,6 @@ int32_t HyperAio::StartCancelReqs(CancelReqs *req) count = 0; } } - if (count > 0 && count < BATCH_SIZE) { - int32_t ret = io_uring_submit(&pImpl_->uring_); - if (ret < 0) { - HILOGE("submit cancel reqs failed, ret = %{public}d", ret); - return ret; - } - cancelReqCount_ += count; - } return EOK; } -- Gitee From 2becbe878cb595d3f6f23a9a2f019bbf5d6124bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Mon, 16 Jun 2025 18:43:49 +0800 Subject: [PATCH 6/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- interfaces/kits/hyperaio/src/hyperaio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp index baadeab4f..c4f16cc46 100644 --- a/interfaces/kits/hyperaio/src/hyperaio.cpp +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -53,7 +53,8 @@ static bool HasAccessIouringPermission() return true; } -static bool ValidateReqNum(uint32_t reqNum) { +static bool ValidateReqNum(uint32_t reqNum) +{ return reqNum > 0 && reqNum <= URING_QUEUE_SIZE; } -- Gitee From 7dcf0fd1d05e90a9ade0949ee00e13c3ec6ffad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Mon, 16 Jun 2025 20:22:44 +0800 Subject: [PATCH 7/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- .../test/unittest/hyperaio/hyperaio_test.cpp | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/interfaces/test/unittest/hyperaio/hyperaio_test.cpp b/interfaces/test/unittest/hyperaio/hyperaio_test.cpp index 2d43b7d86..30e7d4889 100644 --- a/interfaces/test/unittest/hyperaio/hyperaio_test.cpp +++ b/interfaces/test/unittest/hyperaio/hyperaio_test.cpp @@ -35,6 +35,7 @@ namespace OHOS::HyperAio { const uint64_t userData = 12345; const uint32_t len = 1024; const uint32_t batchSize = 300; + const uint32_t Threshold = 600; HyperAio::ProcessIoResultCallBack callBack = [](std::unique_ptr response) { GTEST_LOG_(INFO) << "HyperAioTest callBack"; }; @@ -227,6 +228,36 @@ namespace OHOS::HyperAio { GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartOpenReqs_0003"; } + /** + * @tc.name: HyperAio_StartOpenReqs_0004 + * @tc.desc: Test function of StartOpenReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_StartOpenReqs_0004, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_StartOpenReqs_0004"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->CtxInit(&callBack); + EXPECT_EQ(result, 0); + auto openInfos = std::make_unique(Threshold); + for (int i = 0; i < Threshold; ++i) { + openInfos[i].dfd = 0; + openInfos[i].flags = O_RDWR; + openInfos[i].mode = 0; + openInfos[i].path = nullptr; + openInfos[i].userData = userData + i; + } + OpenReqs openReqs = {Threshold, openInfos.get()}; + result = hyperAio_->StartOpenReqs(&openReqs); + EXPECT_EQ(result, -EINVAL); + result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartOpenReqs_0004"; + } + /** * @tc.name: HyperAio_StartReadReqs_0000 * @tc.desc: Test function of StartReadReqs() interface for SUCCESS. @@ -293,6 +324,65 @@ namespace OHOS::HyperAio { GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartReadReqs_0002"; } + /** + * @tc.name: HyperAio_StartReadReqs_0003 + * @tc.desc: Test function of StartReadReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_StartReadReqs_0003, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_StartReadReqs_0003"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->CtxInit(&callBack); + EXPECT_EQ(result, 0); + auto readInfos = std::make_unique(batchSize); + for (int i = 0; i < batchSize; ++i) { + readInfos[i].fd = 0; + readInfos[i].len = len; + readInfos[i].offset = 0; + readInfos[i].buf = nullptr; + readInfos[i].userData = userData + i; + } + ReadReqs readReqs = {batchSize, readInfos.get()}; + result = hyperAio_->StartReadReqs(&readReqs); + EXPECT_EQ(result, 0); + result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartReadReqs_0003"; + } + + /** + * @tc.name: HyperAio_StartReadReqs_0004 + * @tc.desc: Test function of StartReadReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_StartReadReqs_0004, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_StartReadReqs_0004"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->CtxInit(&callBack); + EXPECT_EQ(result, 0); + auto readInfos = std::make_unique(Threshold); + for (int i = 0; i < Threshold; ++i) { + readInfos[i].fd = 0; + readInfos[i].len = len; + readInfos[i].offset = 0; + readInfos[i].buf = nullptr; + readInfos[i].userData = userData + i; + } + ReadReqs readReqs = {Threshold, readInfos.get()}; + result = hyperAio_->StartReadReqs(&readReqs); + EXPECT_EQ(result, -EINVAL); + result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartReadReqs_0004"; + } /** * @tc.name: HyperAio_StartCancelReqs_0000 * @tc.desc: Test function of StartCancelReqs() interface for SUCCESS. @@ -357,5 +447,59 @@ namespace OHOS::HyperAio { EXPECT_EQ(result, 0); GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartCancelReqs_0002"; } + + /** + * @tc.name: HyperAio_StartCancelReqs_0003 + * @tc.desc: Test function of StartCancelReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_StartCancelReqs_0003, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_StartCancelReqs_0003"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->CtxInit(&callBack); + EXPECT_EQ(result, 0); + auto cancelInfos = std::make_unique(batchSize); + for (int i = 0; i < batchSize; ++i) { + cancelInfos[i].userData = userData + i; + cancelInfos[i].targetUserData = userData + i; + } + CancelReqs cancelReqs = {batchSize, cancelInfos.get()}; + result = hyperAio_->StartCancelReqs(&cancelReqs); + EXPECT_EQ(result, 0); + result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartCancelReqs_0003"; + } + + /** + * @tc.name: HyperAio_StartCancelReqs_0004 + * @tc.desc: Test function of StartCancelReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_StartCancelReqs_0004, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_StartCancelReqs_0004"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->CtxInit(&callBack); + EXPECT_EQ(result, 0); + auto cancelInfos = std::make_unique(Threshold); + for (int i = 0; i < Threshold; ++i) { + cancelInfos[i].userData = userData + i; + cancelInfos[i].targetUserData = userData + i; + } + CancelReqs cancelReqs = {Threshold, cancelInfos.get()}; + result = hyperAio_->StartCancelReqs(&cancelReqs); + EXPECT_EQ(result, -EINVAL); + result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartCancelReqs_0004"; + } #endif } -- Gitee From 1a71098af962f3db9f908e9d8bbef59f6f758641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=AB=E5=BF=B5?= Date: Mon, 16 Jun 2025 21:33:42 +0800 Subject: [PATCH 8/8] 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 别念 --- .../test/unittest/hyperaio/hyperaio_test.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/interfaces/test/unittest/hyperaio/hyperaio_test.cpp b/interfaces/test/unittest/hyperaio/hyperaio_test.cpp index 30e7d4889..f966a16a2 100644 --- a/interfaces/test/unittest/hyperaio/hyperaio_test.cpp +++ b/interfaces/test/unittest/hyperaio/hyperaio_test.cpp @@ -343,7 +343,7 @@ namespace OHOS::HyperAio { readInfos[i].fd = 0; readInfos[i].len = len; readInfos[i].offset = 0; - readInfos[i].buf = nullptr; + readInfos[i].buf = nullptr; readInfos[i].userData = userData + i; } ReadReqs readReqs = {batchSize, readInfos.get()}; @@ -373,7 +373,7 @@ namespace OHOS::HyperAio { readInfos[i].fd = 0; readInfos[i].len = len; readInfos[i].offset = 0; - readInfos[i].buf = nullptr; + readInfos[i].buf = nullptr; readInfos[i].userData = userData + i; } ReadReqs readReqs = {Threshold, readInfos.get()}; @@ -501,5 +501,22 @@ namespace OHOS::HyperAio { EXPECT_EQ(result, 0); GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_StartCancelReqs_0004"; } + + /** + * @tc.name: HyperAio_DestoryCtx_0000 + * @tc.desc: Test function of StartCancelReqs() interface for SUCCESS. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + * @tc.require: AR000HG8M4 + */ + HWTEST_F(HyperAioTest, HyperAio_DestoryCtx_0000, testing::ext::TestSize.Level1) + { + GTEST_LOG_(INFO) << "HyperAioTest-begin HyperAio_DestoryCtx_0000"; + std::unique_ptr hyperAio_ = std::make_unique(); + int32_t result = hyperAio_->DestroyCtx(); + EXPECT_EQ(result, 0); + GTEST_LOG_(INFO) << "HyperAioTest-end HyperAio_DestoryCtx_0000"; + } #endif } -- Gitee