diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b85969f1..287b1268 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
run: |
set -e
cmake -S . -B build
- cmake --build build
+ cmake --build build -j $(nproc)
- name: Unit test
working-directory: '${{ github.workspace }}/build/gtests'
run: |
@@ -38,7 +38,7 @@ jobs:
run: |
set -e
cmake -DOPENSSL_ROOT_DIR="$(brew --prefix)/opt/openssl/" -S . -B build
- cmake --build build
+ cmake --build build -j $(nproc)
- name: Unit test
working-directory: '${{ github.workspace }}/build/gtests'
run: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e01b9d3..dec93d77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
## CHANGE LOG
+## v7.8.0 (2024-08-27)
+
+- 支持闲时任务和 prefop 接口
+- 调整查询区域主备域名
+- 支持缓存多个 `v4/query` 查询结果
+- 上传重试支持切换上传域名
+- 支持上传加速域名
+- 增加上传进度回调支持
+
## v7.7.0 (2023-12-25)
- 支持归档直读存储类型
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18b8ac3d..eeab57fe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ CMAKE_POLICY(SET CMP0074 NEW)
SET(PROJECT_NAME qiniu)
# 建立项目
-PROJECT(${PROJECT_NAME} VERSION 7.7.0 LANGUAGES C)
+PROJECT(${PROJECT_NAME} VERSION 7.8.0 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
@@ -19,16 +19,17 @@ ENDIF()
AUX_SOURCE_DIRECTORY(b64 DIR_SRCS_B64)
AUX_SOURCE_DIRECTORY(cJSON DIR_SRCS_CJSON)
+AUX_SOURCE_DIRECTORY(hashmap DIR_SRCS_HASHMAP)
AUX_SOURCE_DIRECTORY(qiniu DIR_SRCS_QINIU)
-MESSAGE(STATUS "Src file: ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_QINIU}")
+MESSAGE(STATUS "Src file: ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_HASHMAP} ${DIR_SRCS_QINIU}")
# 编译可执行程序
# ADD_EXECUTABLE(${PROJECT_NAME} ${DIR_SRCS})
# 如果要生成静态链接库
-ADD_LIBRARY(${PROJECT_NAME}_static STATIC ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_QINIU})
+ADD_LIBRARY(${PROJECT_NAME}_static STATIC ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_HASHMAP} ${DIR_SRCS_QINIU})
# 如果要生成动态链接库
-ADD_LIBRARY(${PROJECT_NAME} SHARED ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_QINIU})
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${DIR_SRCS_B64} ${DIR_SRCS_CJSON} ${DIR_SRCS_HASHMAP} ${DIR_SRCS_QINIU})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
diff --git a/docs/README.md b/docs/README.md
index acd00ac0..b3fe2f4e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -68,6 +68,24 @@ C-SDK 使用 [cURL](http://curl.haxx.se/) 进行网络相关操作。无论是
如果在项目构建过程中出现环境相关的编译错误和链接错误,请确认这些选项是否都已经正确配置,以及所依赖的库是否都已经正确的安装。
+#### 通过 CMake 编译
+
+如果想在 CMake 项目里使用 C-SDK,可以直接在 CMake 里导入 C-SDK:
+
+```cmake
+INCLUDE(FetchContent)
+FetchContent_Declare(
+ qiniu
+ GIT_REPOSITORY https://github.com/qiniu/c-sdk.git
+ GIT_TAG v7.8.0
+)
+FetchContent_MakeAvailable(qiniu)
+FIND_PACKAGE(CURL REQUIRED)
+FIND_PACKAGE(OpenSSL REQUIRED)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE qiniu m)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${CURL_LIBRARIES})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${OPENSSL_LIBRARIES})
+```
@@ -169,7 +187,7 @@ typedef struct _Qiniu_Error {
typedef struct _Qiniu_RS_StatRet {
const char* hash;
const char* mimeType;
- Qiniu_Int64 fsize;
+ Qiniu_Int64 fsize;
Qiniu_Int64 putTime;
} Qiniu_RS_StatRet;
```
@@ -427,7 +445,7 @@ void stat(Qiniu_Client* client, const char* bucket, const char* key)
typedef struct _Qiniu_RS_StatRet {
const char* hash;
const char* mimeType;
- Qiniu_Int64 fsize;
+ Qiniu_Int64 fsize;
Qiniu_Int64 putTime;
} Qiniu_RS_StatRet;
```
@@ -456,8 +474,8 @@ void delete(Qiniu_Client* client, const char* bucket, const char* key)
复制和移动操作,需要指定源路径和目标路径。
```{c}
-void copy(Qiniu_Client* client,
- const char* bucketSrc, const char* keySrc,
+void copy(Qiniu_Client* client,
+ const char* bucketSrc, const char* keySrc,
const char* bucketDest, const char* keyDest)
{
Qiniu_Error err = Qiniu_RS_Copy(client, bucketSrc, keySrc, bucketDest, keyDest);
@@ -470,8 +488,8 @@ void copy(Qiniu_Client* client,
```
```{c}
-void move(Qiniu_Client* client,
- const char* bucketSrc, const char* keySrc,
+void move(Qiniu_Client* client,
+ const char* bucketSrc, const char* keySrc,
const char* bucketDest, const char* keyDest)
{
Qiniu_Error err = Qiniu_RS_Move(client, bucketSrc, keySrc, bucketDest, keyDest);
@@ -494,7 +512,7 @@ void move(Qiniu_Client* client,
调用`Qiniu_RS_BatchStat`可以批量查看多个文件的属性信息。
```{c}
-void batchStat(Qiniu_Client* client,
+void batchStat(Qiniu_Client* client,
Qiniu_RS_EntryPath* entries, Qiniu_ItemCount entryCount)
{
Qiniu_RS_BatchStatRet* rets = calloc(entryCount, sizeof(Qiniu_RS_BatchStatRet));
@@ -551,7 +569,7 @@ typedef struct _Qiniu_RS_BatchStatRet {
typedef struct _Qiniu_RS_StatRet {
const char* hash;
const char* mimeType;
- Qiniu_Int64 fsize;
+ Qiniu_Int64 fsize;
Qiniu_Int64 putTime;
} Qiniu_RS_StatRet;
```
@@ -563,7 +581,7 @@ typedef struct _Qiniu_RS_StatRet {
调用`Qiniu_RS_BatchDelete`可以批量删除多个文件。
```{c}
-void batchDelete(Qiniu_Client* client,
+void batchDelete(Qiniu_Client* client,
Qiniu_RS_EntryPath* entries, Qiniu_ItemCount entryCount)
{
Qiniu_RS_BatchItemRet* rets = calloc(entryCount, sizeof(Qiniu_RS_BatchItemRet));
@@ -604,7 +622,7 @@ typedef struct _Qiniu_RS_BatchItemRet {
调用`Qiniu_RS_BatchCopy`可以批量复制多个文件。
```{c}
-void batchCopy(Qiniu_Client* client,
+void batchCopy(Qiniu_Client* client,
Qiniu_RS_EntryPathPair* entryPairs, Qiniu_ItemCount entryCount)
{
Qiniu_RS_BatchItemRet* rets = calloc(entryCount, sizeof(Qiniu_RS_BatchItemRet));
@@ -644,7 +662,7 @@ typedef struct _Qiniu_RS_EntryPathPair {
批量移动和批量复制很类似,唯一的区别就是调用`Qiniu_RS_BatchMove`。
```{c}
-void batchMove(Qiniu_Client* client,
+void batchMove(Qiniu_Client* client,
Qiniu_RS_EntryPathPair* entryPairs, Qiniu_ItemCount entryCount)
{
Qiniu_RS_BatchItemRet* rets = calloc(entryCount, sizeof(Qiniu_RS_BatchItemRet));
diff --git a/examples/fop_video_avthumb.c b/examples/fop_video_avthumb.c
index 36857c14..ed3da2bd 100644
--- a/examples/fop_video_avthumb.c
+++ b/examples/fop_video_avthumb.c
@@ -43,7 +43,16 @@ int main(int argc, char **argv) {
char *fops[] = {avthumbMp4Fop, vframeJpgFop};
- Qiniu_Error error = Qiniu_FOP_Pfop(&client, &pfopRet, bucket, key, fops, 2, pipeline, notifyURL, force);
+ Qiniu_FOP_PfopParams params = {
+ .bucket = bucket,
+ .key = key,
+ .pipeline = pipeline,
+ .notifyURL = notifyURL,
+ .fops = fops,
+ .fopCount = 2,
+ .force = force,
+ };
+ Qiniu_Error error = Qiniu_FOP_Pfop_v2(&client, &pfopRet, ¶ms);
if (error.code != 200) {
printf("video file pfop %s:%s error.\n", bucket, key);
debug_log(&client, error);
@@ -52,7 +61,41 @@ int main(int argc, char **argv) {
printf("video file pfop %s:%s success, persistentId: %s .\n\n", bucket, key, pfopRet.persistentId);
}
+ Qiniu_FOP_PrefopRet prefopRet;
+ Qiniu_FOP_PrefopItemRet prefopItemRet[2];
+ Qiniu_ItemCount itemsCount;
+ error = Qiniu_FOP_Prefop(&client, &prefopRet, (Qiniu_FOP_PrefopItemRet *)&prefopItemRet, &itemsCount, pfopRet.persistentId, 2);
+ if (error.code != 200)
+ {
+ debug_log(&client, error);
+ }
+ else
+ {
+ printf("ID: %s\n", prefopRet.id);
+ printf("Code: %d\n", prefopRet.code);
+ printf("Desc: %s\n", prefopRet.desc);
+ printf("InputBucket: %s\n", prefopRet.inputBucket);
+ printf("InputKey: %s\n", prefopRet.inputKey);
+ printf("Type: %d\n", prefopRet.type);
+ printf("CreationDate: %d-%d-%d %d:%d:%d +%d\n", prefopRet.creationDate.date.year, prefopRet.creationDate.date.month,
+ prefopRet.creationDate.date.day, prefopRet.creationDate.time.hour,
+ prefopRet.creationDate.time.minute, prefopRet.creationDate.time.second,
+ prefopRet.creationDate.time.offset);
+
+ for (Qiniu_ItemCount i = 0; i < itemsCount; i++)
+ {
+ printf("\tIndex: %d\n", i);
+ printf("\tCmd: %s\n", prefopItemRet[i].cmd);
+ printf("\tCode: %d\n", prefopItemRet[i].code);
+ printf("\tDesc: %s\n", prefopItemRet[i].desc);
+ printf("\tError: %s\n", prefopItemRet[i].error);
+ printf("\tHash: %s\n", prefopItemRet[i].hash);
+ printf("\tKey: %s\n", prefopItemRet[i].key);
+ printf("\tReturnOld: %d\n", prefopItemRet[i].returnOld);
+ }
+ }
+
Qiniu_Free(avthumbMp4Fop);
Qiniu_Free(vframeJpgFop);
Qiniu_Client_Cleanup(&client);
-}
\ No newline at end of file
+}
diff --git a/examples/up_form_upload.c b/examples/up_form_upload.c
index a593b308..ed874059 100644
--- a/examples/up_form_upload.c
+++ b/examples/up_form_upload.c
@@ -31,12 +31,6 @@ int main(int argc, char **argv) {
putPolicy.scope = bucket;
char *uptoken = Qiniu_RS_PutPolicy_Token(&putPolicy, &mac);
- //设置机房域名
- //Qiniu_Use_Zone_Beimei(Qiniu_False);
- //Qiniu_Use_Zone_Huabei(Qiniu_True);
- Qiniu_Use_Zone_Huadong(Qiniu_False);
- //Qiniu_Use_Zone_Huanan(Qiniu_True);
-
//put extra
//putExtra.upHost="http://nbxs-gate-up.qiniu.com";
@@ -60,6 +54,7 @@ int main(int argc, char **argv) {
//init
Qiniu_Client_InitMacAuth(&client, 1024, &mac);
+ Qiniu_Client_EnableAutoQuery(&client, Qiniu_True);
Qiniu_Error error = Qiniu_Io_PutFile(&client, &putRet, uptoken, key, localFile, &putExtra);
if (error.code != 200) {
printf("upload file %s:%s error.\n", bucket, key);
diff --git a/examples/up_multipart_sample.c b/examples/up_multipart_sample.c
index 9bd84b6a..ba4d593d 100644
--- a/examples/up_multipart_sample.c
+++ b/examples/up_multipart_sample.c
@@ -62,6 +62,7 @@ int main(int argc, char **argv)
char *uptoken = Qiniu_RS_PutPolicy_Token(&putPolicy, &mac);
Qiniu_Client_InitMacAuth(&client, 1024, &mac);
+ Qiniu_Client_EnableAutoQuery(&client, Qiniu_True);
Qiniu_RS_Delete(&client, bucket, key); //to avoid "file exist" err
error = Qiniu_Multipart_PutFile(&client, uptoken, key, localFile, &putExtra, &putRet);
diff --git a/examples/up_resumeable_upload.c b/examples/up_resumeable_upload.c
index a08a3faf..f7e2b956 100644
--- a/examples/up_resumeable_upload.c
+++ b/examples/up_resumeable_upload.c
@@ -23,6 +23,7 @@ static void resumableUploadWithKey(Qiniu_Mac *mac, const char *bucket, const cha
Qiniu_Client client;
Qiniu_Global_Init(-1);
Qiniu_Client_InitNoAuth(&client, 1024);
+ Qiniu_Client_EnableAutoQuery(&client, Qiniu_True);
Qiniu_Error error;
Qiniu_Rio_PutRet putRet;
diff --git a/gtests/region_test.cc b/gtests/region_test.cc
index 599d9ac9..102a7975 100644
--- a/gtests/region_test.cc
+++ b/gtests/region_test.cc
@@ -97,24 +97,21 @@ TEST(UnitTest, TestUseRegion)
EXPECT_STREQ(hosts[0], "https://rs-z0.qiniuapi.com");
hosts = Qiniu_Region_Get_Rs_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "https://rs-z0.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Rsf_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "https://rsf-z0.qiniuapi.com");
hosts = Qiniu_Region_Get_Rsf_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "https://rsf-z0.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Api_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "https://api-z0.qiniuapi.com");
hosts = Qiniu_Region_Get_Api_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "https://api-z0.qbox.me");
+ EXPECT_EQ(count, 0);
Qiniu_Region_Free(region);
@@ -126,40 +123,35 @@ TEST(UnitTest, TestUseRegion)
EXPECT_STREQ(hosts[1], "http://up-z1.qiniup.com");
hosts = Qiniu_Region_Get_Up_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "http://up-z1.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Io_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "http://iovip-z1.qiniuio.com");
hosts = Qiniu_Region_Get_Io_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "http://iovip-z1.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Rs_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "http://rs-z1.qiniuapi.com");
hosts = Qiniu_Region_Get_Rs_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "http://rs-z1.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Rsf_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "http://rsf-z1.qiniuapi.com");
hosts = Qiniu_Region_Get_Rsf_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "http://rsf-z1.qbox.me");
+ EXPECT_EQ(count, 0);
hosts = Qiniu_Region_Get_Api_Preferred_Hosts(region, &count);
EXPECT_EQ(count, 1);
EXPECT_STREQ(hosts[0], "http://api-z1.qiniuapi.com");
hosts = Qiniu_Region_Get_Api_Alternative_Hosts(region, &count);
- EXPECT_EQ(count, 1);
- EXPECT_STREQ(hosts[0], "http://api-z1.qbox.me");
+ EXPECT_EQ(count, 0);
Qiniu_Region_Free(region);
}
@@ -242,11 +234,17 @@ TEST(IntegrationTest, TestRegionAutoQuery)
Qiniu_Client_EnableAutoQuery(&client, Qiniu_True);
const char *host;
+ const char *const *hosts;
+ size_t count;
Qiniu_Error err;
- err = _Qiniu_Region_Get_Up_Host(&client, NULL, Test_bucket, &host);
+ err = _Qiniu_Region_Get_Up_Hosts(&client, NULL, Test_bucket, &hosts, &count);
EXPECT_EQ(err.code, 200);
- EXPECT_STREQ(host, "https://upload.qiniup.com");
+ EXPECT_EQ(count, 4);
+ EXPECT_STREQ(hosts[0], "https://upload.qiniup.com");
+ EXPECT_STREQ(hosts[1], "https://up.qiniup.com");
+ EXPECT_STREQ(hosts[2], "https://upload.qbox.me");
+ EXPECT_STREQ(hosts[3], "https://up.qbox.me");
err = _Qiniu_Region_Get_Io_Host(&client, NULL, Test_bucket, &host);
EXPECT_EQ(err.code, 200);
@@ -276,11 +274,15 @@ TEST(IntegrationTest, TestRegionSpecify)
Qiniu_Client_SpecifyRegion(&client, region);
const char *host;
+ const char *const *hosts;
+ size_t count;
Qiniu_Error err;
- err = _Qiniu_Region_Get_Up_Host(&client, NULL, Test_bucket, &host);
+ err = _Qiniu_Region_Get_Up_Hosts(&client, NULL, Test_bucket, &hosts, &count);
EXPECT_EQ(err.code, 200);
- EXPECT_STREQ(host, "http://upload-z1.qiniup.com");
+ EXPECT_EQ(count, 2);
+ EXPECT_STREQ(hosts[0], "http://upload-z1.qiniup.com");
+ EXPECT_STREQ(hosts[1], "http://up-z1.qiniup.com");
err = _Qiniu_Region_Get_Io_Host(&client, NULL, Test_bucket, &host);
EXPECT_EQ(err.code, 200);
@@ -300,3 +302,54 @@ TEST(IntegrationTest, TestRegionSpecify)
Qiniu_Region_Free(region);
}
+
+TEST(IntegrationTest, TestRegionSpecifyAcceleration)
+{
+ Qiniu_Client client;
+ Qiniu_Client_InitMacAuth(&client, 1024, NULL);
+ Qiniu_Client_SetTimeout(&client, 5000);
+ Qiniu_Client_SetConnectTimeout(&client, 3000);
+
+ Qiniu_Region *region = Qiniu_Use_Region("z1", Qiniu_False);
+ const char *setHosts[1] = {"http://acc.upload-z1.qiniup.com"};
+ Qiniu_Region_Set_Up_Accelerated_Hosts(region, (const char *const *)&setHosts, 1, Qiniu_False);
+ Qiniu_Client_SpecifyRegion(&client, region);
+ Qiniu_Client_EnableUploadingAcceleration(&client);
+
+ const char *host;
+ const char *const *hosts;
+ size_t count;
+ Qiniu_Error err;
+
+ err = _Qiniu_Region_Get_Up_Hosts(&client, NULL, Test_bucket, &hosts, &count);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_EQ(count, 3);
+ EXPECT_STREQ(hosts[0], "http://acc.upload-z1.qiniup.com");
+ EXPECT_STREQ(hosts[1], "http://upload-z1.qiniup.com");
+ EXPECT_STREQ(hosts[2], "http://up-z1.qiniup.com");
+
+ err = _Qiniu_Region_Get_Io_Host(&client, NULL, Test_bucket, &host);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_STREQ(host, "http://iovip-z1.qiniuio.com");
+
+ err = _Qiniu_Region_Get_Rs_Host(&client, NULL, Test_bucket, &host);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_STREQ(host, "http://rs-z1.qiniuapi.com");
+
+ err = _Qiniu_Region_Get_Rsf_Host(&client, NULL, Test_bucket, &host);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_STREQ(host, "http://rsf-z1.qiniuapi.com");
+
+ err = _Qiniu_Region_Get_Api_Host(&client, NULL, Test_bucket, &host);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_STREQ(host, "http://api-z1.qiniuapi.com");
+
+ Qiniu_Client_DisableUploadingAcceleration(&client);
+ err = _Qiniu_Region_Get_Up_Hosts(&client, NULL, Test_bucket, &hosts, &count);
+ EXPECT_EQ(err.code, 200);
+ EXPECT_EQ(count, 2);
+ EXPECT_STREQ(hosts[0], "http://upload-z1.qiniup.com");
+ EXPECT_STREQ(hosts[1], "http://up-z1.qiniup.com");
+
+ Qiniu_Region_Free(region);
+}
diff --git a/gtests/rfc3339_test.cc b/gtests/rfc3339_test.cc
new file mode 100644
index 00000000..fa4a4897
--- /dev/null
+++ b/gtests/rfc3339_test.cc
@@ -0,0 +1,53 @@
+#include
+#include "test.h"
+#include "qiniu/rfc3339.h"
+
+#ifdef _WIN32
+#include
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ void _Qiniu_Parse_Date_Time(char *datetime_string, Qiniu_DateTime *dt);
+#ifdef __cplusplus
+}
+#endif
+
+TEST(UnitTest, TestQiniuParseDateTime)
+{
+ Qiniu_DateTime dt;
+ Qiniu_Zero(dt);
+ _Qiniu_Parse_Date_Time((char *)"1937-02-02T12:00:27.87+00:20", &dt);
+ EXPECT_TRUE(dt.ok);
+ EXPECT_TRUE(dt.date.ok);
+ EXPECT_EQ(dt.date.year, 1937);
+ EXPECT_EQ(dt.date.month, 2);
+ EXPECT_EQ(dt.date.day, 2);
+ EXPECT_TRUE(dt.time.ok);
+ EXPECT_EQ(dt.time.hour, 12);
+ EXPECT_EQ(dt.time.minute, 0);
+ EXPECT_EQ(dt.time.second, 27);
+ EXPECT_EQ(dt.time.fraction, 870000);
+ EXPECT_EQ(dt.time.offset, 20);
+
+ _Qiniu_Parse_Date_Time((char *)"1937-02-29T12:00:27.87+00:20", &dt);
+ EXPECT_FALSE(dt.ok);
+
+ _Qiniu_Parse_Date_Time((char *)"1936-02-29T12:00:27.87Z", &dt);
+ EXPECT_TRUE(dt.ok);
+ EXPECT_TRUE(dt.date.ok);
+ EXPECT_EQ(dt.date.year, 1936);
+ EXPECT_EQ(dt.date.month, 2);
+ EXPECT_EQ(dt.date.day, 29);
+ EXPECT_TRUE(dt.time.ok);
+ EXPECT_EQ(dt.time.hour, 12);
+ EXPECT_EQ(dt.time.minute, 0);
+ EXPECT_EQ(dt.time.second, 27);
+ EXPECT_EQ(dt.time.fraction, 870000);
+ EXPECT_EQ(dt.time.offset, 0);
+
+ _Qiniu_Parse_Date_Time((char *)"1900-02-29T12:00:27.87+00:20", &dt);
+ EXPECT_FALSE(dt.ok);
+}
diff --git a/hashmap/hashmap.c b/hashmap/hashmap.c
new file mode 100644
index 00000000..d70b1627
--- /dev/null
+++ b/hashmap/hashmap.c
@@ -0,0 +1,1154 @@
+// Copyright 2020 Joshua J Baker. All rights reserved.
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+#include
+#include
+#include
+#include
+#include
+#include "hashmap.h"
+
+#define GROW_AT 0.60 /* 60% */
+#define SHRINK_AT 0.10 /* 10% */
+
+#ifndef HASHMAP_LOAD_FACTOR
+#define HASHMAP_LOAD_FACTOR GROW_AT
+#endif
+
+static void *(*__malloc)(size_t) = NULL;
+static void *(*__realloc)(void *, size_t) = NULL;
+static void (*__free)(void *) = NULL;
+
+// hashmap_set_allocator allows for configuring a custom allocator for
+// all hashmap library operations. This function, if needed, should be called
+// only once at startup and a prior to calling hashmap_new().
+void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*)) {
+ __malloc = malloc;
+ __free = free;
+}
+
+struct bucket {
+ uint64_t hash:48;
+ uint64_t dib:16;
+};
+
+// hashmap is an open addressed hash map using robinhood hashing.
+struct hashmap {
+ void *(*malloc)(size_t);
+ void *(*realloc)(void *, size_t);
+ void (*free)(void *);
+ size_t elsize;
+ size_t cap;
+ uint64_t seed0;
+ uint64_t seed1;
+ uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1);
+ int (*compare)(const void *a, const void *b, void *udata);
+ void (*elfree)(void *item);
+ void *udata;
+ size_t bucketsz;
+ size_t nbuckets;
+ size_t count;
+ size_t mask;
+ size_t growat;
+ size_t shrinkat;
+ uint8_t loadfactor;
+ uint8_t growpower;
+ bool oom;
+ void *buckets;
+ void *spare;
+ void *edata;
+};
+
+void hashmap_set_grow_by_power(struct hashmap *map, size_t power) {
+ map->growpower = power < 1 ? 1 : power > 16 ? 16 : power;
+}
+
+static double clamp_load_factor(double factor, double default_factor) {
+ // Check for NaN and clamp between 50% and 90%
+ return factor != factor ? default_factor :
+ factor < 0.50 ? 0.50 :
+ factor > 0.95 ? 0.95 :
+ factor;
+}
+
+void hashmap_set_load_factor(struct hashmap *map, double factor) {
+ factor = clamp_load_factor(factor, map->loadfactor / 100.0);
+ map->loadfactor = factor * 100;
+ map->growat = map->nbuckets * (map->loadfactor / 100.0);
+}
+
+static struct bucket *bucket_at0(void *buckets, size_t bucketsz, size_t i) {
+ return (struct bucket*)(((char*)buckets)+(bucketsz*i));
+}
+
+static struct bucket *bucket_at(struct hashmap *map, size_t index) {
+ return bucket_at0(map->buckets, map->bucketsz, index);
+}
+
+static void *bucket_item(struct bucket *entry) {
+ return ((char*)entry)+sizeof(struct bucket);
+}
+
+static uint64_t clip_hash(uint64_t hash) {
+ return hash & 0xFFFFFFFFFFFF;
+}
+
+static uint64_t get_hash(struct hashmap *map, const void *key) {
+ return clip_hash(map->hash(key, map->seed0, map->seed1));
+}
+
+
+// hashmap_new_with_allocator returns a new hash map using a custom allocator.
+// See hashmap_new for more information information
+struct hashmap *hashmap_new_with_allocator(void *(*_malloc)(size_t),
+ void *(*_realloc)(void*, size_t), void (*_free)(void*),
+ size_t elsize, size_t cap, uint64_t seed0, uint64_t seed1,
+ uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
+ int (*compare)(const void *a, const void *b, void *udata),
+ void (*elfree)(void *item),
+ void *udata)
+{
+ _malloc = _malloc ? _malloc : __malloc ? __malloc : malloc;
+ _realloc = _realloc ? _realloc : __realloc ? __realloc : realloc;
+ _free = _free ? _free : __free ? __free : free;
+ size_t ncap = 16;
+ if (cap < ncap) {
+ cap = ncap;
+ } else {
+ while (ncap < cap) {
+ ncap *= 2;
+ }
+ cap = ncap;
+ }
+ size_t bucketsz = sizeof(struct bucket) + elsize;
+ while (bucketsz & (sizeof(uintptr_t)-1)) {
+ bucketsz++;
+ }
+ // hashmap + spare + edata
+ size_t size = sizeof(struct hashmap)+bucketsz*2;
+ struct hashmap *map = _malloc(size);
+ if (!map) {
+ return NULL;
+ }
+ memset(map, 0, sizeof(struct hashmap));
+ map->elsize = elsize;
+ map->bucketsz = bucketsz;
+ map->seed0 = seed0;
+ map->seed1 = seed1;
+ map->hash = hash;
+ map->compare = compare;
+ map->elfree = elfree;
+ map->udata = udata;
+ map->spare = ((char*)map)+sizeof(struct hashmap);
+ map->edata = (char*)map->spare+bucketsz;
+ map->cap = cap;
+ map->nbuckets = cap;
+ map->mask = map->nbuckets-1;
+ map->buckets = _malloc(map->bucketsz*map->nbuckets);
+ if (!map->buckets) {
+ _free(map);
+ return NULL;
+ }
+ memset(map->buckets, 0, map->bucketsz*map->nbuckets);
+ map->growpower = 1;
+ map->loadfactor = clamp_load_factor(HASHMAP_LOAD_FACTOR, GROW_AT) * 100;
+ map->growat = map->nbuckets * (map->loadfactor / 100.0);
+ map->shrinkat = map->nbuckets * SHRINK_AT;
+ map->malloc = _malloc;
+ map->realloc = _realloc;
+ map->free = _free;
+ return map;
+}
+
+// hashmap_new returns a new hash map.
+// Param `elsize` is the size of each element in the tree. Every element that
+// is inserted, deleted, or retrieved will be this size.
+// Param `cap` is the default lower capacity of the hashmap. Setting this to
+// zero will default to 16.
+// Params `seed0` and `seed1` are optional seed values that are passed to the
+// following `hash` function. These can be any value you wish but it's often
+// best to use randomly generated values.
+// Param `hash` is a function that generates a hash value for an item. It's
+// important that you provide a good hash function, otherwise it will perform
+// poorly or be vulnerable to Denial-of-service attacks. This implementation
+// comes with two helper functions `hashmap_sip()` and `hashmap_murmur()`.
+// Param `compare` is a function that compares items in the tree. See the
+// qsort stdlib function for an example of how this function works.
+// The hashmap must be freed with hashmap_free().
+// Param `elfree` is a function that frees a specific item. This should be NULL
+// unless you're storing some kind of reference data in the hash.
+struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0,
+ uint64_t seed1,
+ uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
+ int (*compare)(const void *a, const void *b, void *udata),
+ void (*elfree)(void *item),
+ void *udata)
+{
+ return hashmap_new_with_allocator(NULL, NULL, NULL, elsize, cap, seed0,
+ seed1, hash, compare, elfree, udata);
+}
+
+static void free_elements(struct hashmap *map) {
+ if (map->elfree) {
+ for (size_t i = 0; i < map->nbuckets; i++) {
+ struct bucket *bucket = bucket_at(map, i);
+ if (bucket->dib) map->elfree(bucket_item(bucket));
+ }
+ }
+}
+
+// hashmap_clear quickly clears the map.
+// Every item is called with the element-freeing function given in hashmap_new,
+// if present, to free any data referenced in the elements of the hashmap.
+// When the update_cap is provided, the map's capacity will be updated to match
+// the currently number of allocated buckets. This is an optimization to ensure
+// that this operation does not perform any allocations.
+void hashmap_clear(struct hashmap *map, bool update_cap) {
+ map->count = 0;
+ free_elements(map);
+ if (update_cap) {
+ map->cap = map->nbuckets;
+ } else if (map->nbuckets != map->cap) {
+ void *new_buckets = map->malloc(map->bucketsz*map->cap);
+ if (new_buckets) {
+ map->free(map->buckets);
+ map->buckets = new_buckets;
+ }
+ map->nbuckets = map->cap;
+ }
+ memset(map->buckets, 0, map->bucketsz*map->nbuckets);
+ map->mask = map->nbuckets-1;
+ map->growat = map->nbuckets * (map->loadfactor / 100.0) ;
+ map->shrinkat = map->nbuckets * SHRINK_AT;
+}
+
+static bool resize0(struct hashmap *map, size_t new_cap) {
+ struct hashmap *map2 = hashmap_new_with_allocator(map->malloc, map->realloc,
+ map->free, map->elsize, new_cap, map->seed0, map->seed1, map->hash,
+ map->compare, map->elfree, map->udata);
+ if (!map2) return false;
+ for (size_t i = 0; i < map->nbuckets; i++) {
+ struct bucket *entry = bucket_at(map, i);
+ if (!entry->dib) {
+ continue;
+ }
+ entry->dib = 1;
+ size_t j = entry->hash & map2->mask;
+ while(1) {
+ struct bucket *bucket = bucket_at(map2, j);
+ if (bucket->dib == 0) {
+ memcpy(bucket, entry, map->bucketsz);
+ break;
+ }
+ if (bucket->dib < entry->dib) {
+ memcpy(map2->spare, bucket, map->bucketsz);
+ memcpy(bucket, entry, map->bucketsz);
+ memcpy(entry, map2->spare, map->bucketsz);
+ }
+ j = (j + 1) & map2->mask;
+ entry->dib += 1;
+ }
+ }
+ map->free(map->buckets);
+ map->buckets = map2->buckets;
+ map->nbuckets = map2->nbuckets;
+ map->mask = map2->mask;
+ map->growat = map2->growat;
+ map->shrinkat = map2->shrinkat;
+ map->free(map2);
+ return true;
+}
+
+static bool resize(struct hashmap *map, size_t new_cap) {
+ return resize0(map, new_cap);
+}
+
+// hashmap_set_with_hash works like hashmap_set but you provide your
+// own hash. The 'hash' callback provided to the hashmap_new function
+// will not be called
+const void *hashmap_set_with_hash(struct hashmap *map, const void *item,
+ uint64_t hash)
+{
+ hash = clip_hash(hash);
+ map->oom = false;
+ if (map->count >= map->growat) {
+ if (!resize(map, map->nbuckets*(1<growpower))) {
+ map->oom = true;
+ return NULL;
+ }
+ }
+
+ struct bucket *entry = map->edata;
+ entry->hash = hash;
+ entry->dib = 1;
+ void *eitem = bucket_item(entry);
+ memcpy(eitem, item, map->elsize);
+
+ void *bitem;
+ size_t i = entry->hash & map->mask;
+ while(1) {
+ struct bucket *bucket = bucket_at(map, i);
+ if (bucket->dib == 0) {
+ memcpy(bucket, entry, map->bucketsz);
+ map->count++;
+ return NULL;
+ }
+ bitem = bucket_item(bucket);
+ if (entry->hash == bucket->hash && (!map->compare ||
+ map->compare(eitem, bitem, map->udata) == 0))
+ {
+ memcpy(map->spare, bitem, map->elsize);
+ memcpy(bitem, eitem, map->elsize);
+ return map->spare;
+ }
+ if (bucket->dib < entry->dib) {
+ memcpy(map->spare, bucket, map->bucketsz);
+ memcpy(bucket, entry, map->bucketsz);
+ memcpy(entry, map->spare, map->bucketsz);
+ eitem = bucket_item(entry);
+ }
+ i = (i + 1) & map->mask;
+ entry->dib += 1;
+ }
+}
+
+// hashmap_set inserts or replaces an item in the hash map. If an item is
+// replaced then it is returned otherwise NULL is returned. This operation
+// may allocate memory. If the system is unable to allocate additional
+// memory then NULL is returned and hashmap_oom() returns true.
+const void *hashmap_set(struct hashmap *map, const void *item) {
+ return hashmap_set_with_hash(map, item, get_hash(map, item));
+}
+
+// hashmap_get_with_hash works like hashmap_get but you provide your
+// own hash. The 'hash' callback provided to the hashmap_new function
+// will not be called
+const void *hashmap_get_with_hash(struct hashmap *map, const void *key,
+ uint64_t hash)
+{
+ hash = clip_hash(hash);
+ size_t i = hash & map->mask;
+ while(1) {
+ struct bucket *bucket = bucket_at(map, i);
+ if (!bucket->dib) return NULL;
+ if (bucket->hash == hash) {
+ void *bitem = bucket_item(bucket);
+ if (!map->compare || map->compare(key, bitem, map->udata) == 0) {
+ return bitem;
+ }
+ }
+ i = (i + 1) & map->mask;
+ }
+}
+
+// hashmap_get returns the item based on the provided key. If the item is not
+// found then NULL is returned.
+const void *hashmap_get(struct hashmap *map, const void *key) {
+ return hashmap_get_with_hash(map, key, get_hash(map, key));
+}
+
+// hashmap_probe returns the item in the bucket at position or NULL if an item
+// is not set for that bucket. The position is 'moduloed' by the number of
+// buckets in the hashmap.
+const void *hashmap_probe(struct hashmap *map, uint64_t position) {
+ size_t i = position & map->mask;
+ struct bucket *bucket = bucket_at(map, i);
+ if (!bucket->dib) {
+ return NULL;
+ }
+ return bucket_item(bucket);
+}
+
+// hashmap_delete_with_hash works like hashmap_delete but you provide your
+// own hash. The 'hash' callback provided to the hashmap_new function
+// will not be called
+const void *hashmap_delete_with_hash(struct hashmap *map, const void *key,
+ uint64_t hash)
+{
+ hash = clip_hash(hash);
+ map->oom = false;
+ size_t i = hash & map->mask;
+ while(1) {
+ struct bucket *bucket = bucket_at(map, i);
+ if (!bucket->dib) {
+ return NULL;
+ }
+ void *bitem = bucket_item(bucket);
+ if (bucket->hash == hash && (!map->compare ||
+ map->compare(key, bitem, map->udata) == 0))
+ {
+ memcpy(map->spare, bitem, map->elsize);
+ bucket->dib = 0;
+ while(1) {
+ struct bucket *prev = bucket;
+ i = (i + 1) & map->mask;
+ bucket = bucket_at(map, i);
+ if (bucket->dib <= 1) {
+ prev->dib = 0;
+ break;
+ }
+ memcpy(prev, bucket, map->bucketsz);
+ prev->dib--;
+ }
+ map->count--;
+ if (map->nbuckets > map->cap && map->count <= map->shrinkat) {
+ // Ignore the return value. It's ok for the resize operation to
+ // fail to allocate enough memory because a shrink operation
+ // does not change the integrity of the data.
+ resize(map, map->nbuckets/2);
+ }
+ return map->spare;
+ }
+ i = (i + 1) & map->mask;
+ }
+}
+
+// hashmap_delete removes an item from the hash map and returns it. If the
+// item is not found then NULL is returned.
+const void *hashmap_delete(struct hashmap *map, const void *key) {
+ return hashmap_delete_with_hash(map, key, get_hash(map, key));
+}
+
+// hashmap_count returns the number of items in the hash map.
+size_t hashmap_count(struct hashmap *map) {
+ return map->count;
+}
+
+// hashmap_free frees the hash map
+// Every item is called with the element-freeing function given in hashmap_new,
+// if present, to free any data referenced in the elements of the hashmap.
+void hashmap_free(struct hashmap *map) {
+ if (!map) return;
+ free_elements(map);
+ map->free(map->buckets);
+ map->free(map);
+}
+
+// hashmap_oom returns true if the last hashmap_set() call failed due to the
+// system being out of memory.
+bool hashmap_oom(struct hashmap *map) {
+ return map->oom;
+}
+
+// hashmap_scan iterates over all items in the hash map
+// Param `iter` can return false to stop iteration early.
+// Returns false if the iteration has been stopped early.
+bool hashmap_scan(struct hashmap *map,
+ bool (*iter)(const void *item, void *udata), void *udata)
+{
+ for (size_t i = 0; i < map->nbuckets; i++) {
+ struct bucket *bucket = bucket_at(map, i);
+ if (bucket->dib && !iter(bucket_item(bucket), udata)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// hashmap_iter iterates one key at a time yielding a reference to an
+// entry at each iteration. Useful to write simple loops and avoid writing
+// dedicated callbacks and udata structures, as in hashmap_scan.
+//
+// map is a hash map handle. i is a pointer to a size_t cursor that
+// should be initialized to 0 at the beginning of the loop. item is a void
+// pointer pointer that is populated with the retrieved item. Note that this
+// is NOT a copy of the item stored in the hash map and can be directly
+// modified.
+//
+// Note that if hashmap_delete() is called on the hashmap being iterated,
+// the buckets are rearranged and the iterator must be reset to 0, otherwise
+// unexpected results may be returned after deletion.
+//
+// This function has not been tested for thread safety.
+//
+// The function returns true if an item was retrieved; false if the end of the
+// iteration has been reached.
+bool hashmap_iter(struct hashmap *map, size_t *i, void **item) {
+ struct bucket *bucket;
+ do {
+ if (*i >= map->nbuckets) return false;
+ bucket = bucket_at(map, *i);
+ (*i)++;
+ } while (!bucket->dib);
+ *item = bucket_item(bucket);
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// SipHash reference C implementation
+//
+// Copyright (c) 2012-2016 Jean-Philippe Aumasson
+//
+// Copyright (c) 2012-2014 Daniel J. Bernstein
+//
+// To the extent possible under law, the author(s) have dedicated all copyright
+// and related and neighboring rights to this software to the public domain
+// worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along
+// with this software. If not, see
+// .
+//
+// default: SipHash-2-4
+//-----------------------------------------------------------------------------
+static uint64_t SIP64(const uint8_t *in, const size_t inlen, uint64_t seed0,
+ uint64_t seed1)
+{
+#define U8TO64_LE(p) \
+ { (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
+ ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
+ ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
+ ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) }
+#define U64TO8_LE(p, v) \
+ { U32TO8_LE((p), (uint32_t)((v))); \
+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); }
+#define U32TO8_LE(p, v) \
+ { (p)[0] = (uint8_t)((v)); \
+ (p)[1] = (uint8_t)((v) >> 8); \
+ (p)[2] = (uint8_t)((v) >> 16); \
+ (p)[3] = (uint8_t)((v) >> 24); }
+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+#define SIPROUND \
+ { v0 += v1; v1 = ROTL(v1, 13); \
+ v1 ^= v0; v0 = ROTL(v0, 32); \
+ v2 += v3; v3 = ROTL(v3, 16); \
+ v3 ^= v2; \
+ v0 += v3; v3 = ROTL(v3, 21); \
+ v3 ^= v0; \
+ v2 += v1; v1 = ROTL(v1, 17); \
+ v1 ^= v2; v2 = ROTL(v2, 32); }
+ uint64_t k0 = U8TO64_LE((uint8_t*)&seed0);
+ uint64_t k1 = U8TO64_LE((uint8_t*)&seed1);
+ uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
+ uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0;
+ uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1;
+ uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0;
+ const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
+ for (; in != end; in += 8) {
+ uint64_t m = U8TO64_LE(in);
+ v3 ^= m;
+ SIPROUND; SIPROUND;
+ v0 ^= m;
+ }
+ const int left = inlen & 7;
+ uint64_t b = ((uint64_t)inlen) << 56;
+ switch (left) {
+ case 7: b |= ((uint64_t)in[6]) << 48; /* fall through */
+ case 6: b |= ((uint64_t)in[5]) << 40; /* fall through */
+ case 5: b |= ((uint64_t)in[4]) << 32; /* fall through */
+ case 4: b |= ((uint64_t)in[3]) << 24; /* fall through */
+ case 3: b |= ((uint64_t)in[2]) << 16; /* fall through */
+ case 2: b |= ((uint64_t)in[1]) << 8; /* fall through */
+ case 1: b |= ((uint64_t)in[0]); break;
+ case 0: break;
+ }
+ v3 ^= b;
+ SIPROUND; SIPROUND;
+ v0 ^= b;
+ v2 ^= 0xff;
+ SIPROUND; SIPROUND; SIPROUND; SIPROUND;
+ b = v0 ^ v1 ^ v2 ^ v3;
+ uint64_t out = 0;
+ U64TO8_LE((uint8_t*)&out, b);
+ return out;
+}
+
+//-----------------------------------------------------------------------------
+// MurmurHash3 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+//
+// Murmur3_86_128
+//-----------------------------------------------------------------------------
+static uint64_t MM86128(const void *key, const int len, uint32_t seed) {
+#define ROTL32(x, r) ((x << r) | (x >> (32 - r)))
+#define FMIX32(h) h^=h>>16; h*=0x85ebca6b; h^=h>>13; h*=0xc2b2ae35; h^=h>>16;
+ const uint8_t * data = (const uint8_t*)key;
+ const int nblocks = len / 16;
+ uint32_t h1 = seed;
+ uint32_t h2 = seed;
+ uint32_t h3 = seed;
+ uint32_t h4 = seed;
+ uint32_t c1 = 0x239b961b;
+ uint32_t c2 = 0xab0e9789;
+ uint32_t c3 = 0x38b34ae5;
+ uint32_t c4 = 0xa1e38b93;
+ const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
+ for (int i = -nblocks; i; i++) {
+ uint32_t k1 = blocks[i*4+0];
+ uint32_t k2 = blocks[i*4+1];
+ uint32_t k3 = blocks[i*4+2];
+ uint32_t k4 = blocks[i*4+3];
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
+ k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
+ h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
+ k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
+ h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
+ k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
+ h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
+ }
+ const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
+ uint32_t k1 = 0;
+ uint32_t k2 = 0;
+ uint32_t k3 = 0;
+ uint32_t k4 = 0;
+ switch(len & 15) {
+ case 15: k4 ^= tail[14] << 16; /* fall through */
+ case 14: k4 ^= tail[13] << 8; /* fall through */
+ case 13: k4 ^= tail[12] << 0;
+ k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
+ /* fall through */
+ case 12: k3 ^= tail[11] << 24; /* fall through */
+ case 11: k3 ^= tail[10] << 16; /* fall through */
+ case 10: k3 ^= tail[ 9] << 8; /* fall through */
+ case 9: k3 ^= tail[ 8] << 0;
+ k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
+ /* fall through */
+ case 8: k2 ^= tail[ 7] << 24; /* fall through */
+ case 7: k2 ^= tail[ 6] << 16; /* fall through */
+ case 6: k2 ^= tail[ 5] << 8; /* fall through */
+ case 5: k2 ^= tail[ 4] << 0;
+ k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
+ /* fall through */
+ case 4: k1 ^= tail[ 3] << 24; /* fall through */
+ case 3: k1 ^= tail[ 2] << 16; /* fall through */
+ case 2: k1 ^= tail[ 1] << 8; /* fall through */
+ case 1: k1 ^= tail[ 0] << 0;
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ /* fall through */
+ };
+ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
+ h1 += h2; h1 += h3; h1 += h4;
+ h2 += h1; h3 += h1; h4 += h1;
+ FMIX32(h1); FMIX32(h2); FMIX32(h3); FMIX32(h4);
+ h1 += h2; h1 += h3; h1 += h4;
+ h2 += h1; h3 += h1; h4 += h1;
+ return (((uint64_t)h2)<<32)|h1;
+}
+
+//-----------------------------------------------------------------------------
+// xxHash Library
+// Copyright (c) 2012-2021 Yann Collet
+// All rights reserved.
+//
+// BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
+//
+// xxHash3
+//-----------------------------------------------------------------------------
+#define XXH_PRIME_1 11400714785074694791ULL
+#define XXH_PRIME_2 14029467366897019727ULL
+#define XXH_PRIME_3 1609587929392839161ULL
+#define XXH_PRIME_4 9650029242287828579ULL
+#define XXH_PRIME_5 2870177450012600261ULL
+
+static uint64_t XXH_read64(const void* memptr) {
+ uint64_t val;
+ memcpy(&val, memptr, sizeof(val));
+ return val;
+}
+
+static uint32_t XXH_read32(const void* memptr) {
+ uint32_t val;
+ memcpy(&val, memptr, sizeof(val));
+ return val;
+}
+
+static uint64_t XXH_rotl64(uint64_t x, int r) {
+ return (x << r) | (x >> (64 - r));
+}
+
+static uint64_t xxh3(const void* data, size_t len, uint64_t seed) {
+ const uint8_t* p = (const uint8_t*)data;
+ const uint8_t* const end = p + len;
+ uint64_t h64;
+
+ if (len >= 32) {
+ const uint8_t* const limit = end - 32;
+ uint64_t v1 = seed + XXH_PRIME_1 + XXH_PRIME_2;
+ uint64_t v2 = seed + XXH_PRIME_2;
+ uint64_t v3 = seed + 0;
+ uint64_t v4 = seed - XXH_PRIME_1;
+
+ do {
+ v1 += XXH_read64(p) * XXH_PRIME_2;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= XXH_PRIME_1;
+
+ v2 += XXH_read64(p + 8) * XXH_PRIME_2;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= XXH_PRIME_1;
+
+ v3 += XXH_read64(p + 16) * XXH_PRIME_2;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= XXH_PRIME_1;
+
+ v4 += XXH_read64(p + 24) * XXH_PRIME_2;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= XXH_PRIME_1;
+
+ p += 32;
+ } while (p <= limit);
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) +
+ XXH_rotl64(v4, 18);
+
+ v1 *= XXH_PRIME_2;
+ v1 = XXH_rotl64(v1, 31);
+ v1 *= XXH_PRIME_1;
+ h64 ^= v1;
+ h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4;
+
+ v2 *= XXH_PRIME_2;
+ v2 = XXH_rotl64(v2, 31);
+ v2 *= XXH_PRIME_1;
+ h64 ^= v2;
+ h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4;
+
+ v3 *= XXH_PRIME_2;
+ v3 = XXH_rotl64(v3, 31);
+ v3 *= XXH_PRIME_1;
+ h64 ^= v3;
+ h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4;
+
+ v4 *= XXH_PRIME_2;
+ v4 = XXH_rotl64(v4, 31);
+ v4 *= XXH_PRIME_1;
+ h64 ^= v4;
+ h64 = h64 * XXH_PRIME_1 + XXH_PRIME_4;
+ }
+ else {
+ h64 = seed + XXH_PRIME_5;
+ }
+
+ h64 += (uint64_t)len;
+
+ while (p + 8 <= end) {
+ uint64_t k1 = XXH_read64(p);
+ k1 *= XXH_PRIME_2;
+ k1 = XXH_rotl64(k1, 31);
+ k1 *= XXH_PRIME_1;
+ h64 ^= k1;
+ h64 = XXH_rotl64(h64, 27) * XXH_PRIME_1 + XXH_PRIME_4;
+ p += 8;
+ }
+
+ if (p + 4 <= end) {
+ h64 ^= (uint64_t)(XXH_read32(p)) * XXH_PRIME_1;
+ h64 = XXH_rotl64(h64, 23) * XXH_PRIME_2 + XXH_PRIME_3;
+ p += 4;
+ }
+
+ while (p < end) {
+ h64 ^= (*p) * XXH_PRIME_5;
+ h64 = XXH_rotl64(h64, 11) * XXH_PRIME_1;
+ p++;
+ }
+
+ h64 ^= h64 >> 33;
+ h64 *= XXH_PRIME_2;
+ h64 ^= h64 >> 29;
+ h64 *= XXH_PRIME_3;
+ h64 ^= h64 >> 32;
+
+ return h64;
+}
+
+// hashmap_sip returns a hash value for `data` using SipHash-2-4.
+uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0,
+ uint64_t seed1)
+{
+ return SIP64((uint8_t*)data, len, seed0, seed1);
+}
+
+// hashmap_murmur returns a hash value for `data` using Murmur3_86_128.
+uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0,
+ uint64_t seed1)
+{
+ (void)seed1;
+ return MM86128(data, len, seed0);
+}
+
+uint64_t hashmap_xxhash3(const void *data, size_t len, uint64_t seed0,
+ uint64_t seed1)
+{
+ (void)seed1;
+ return xxh3(data, len ,seed0);
+}
+
+//==============================================================================
+// TESTS AND BENCHMARKS
+// $ cc -DHASHMAP_TEST hashmap.c && ./a.out # run tests
+// $ cc -DHASHMAP_TEST -O3 hashmap.c && BENCH=1 ./a.out # run benchmarks
+//==============================================================================
+#ifdef HASHMAP_TEST
+
+static size_t deepcount(struct hashmap *map) {
+ size_t count = 0;
+ for (size_t i = 0; i < map->nbuckets; i++) {
+ if (bucket_at(map, i)->dib) {
+ count++;
+ }
+ }
+ return count;
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-warning-option"
+#pragma GCC diagnostic ignored "-Wcompound-token-split-by-macro"
+#pragma GCC diagnostic ignored "-Wgnu-statement-expression-from-macro-expansion"
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include "hashmap.h"
+
+static bool rand_alloc_fail = false;
+static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail.
+static uintptr_t total_allocs = 0;
+static uintptr_t total_mem = 0;
+
+static void *xmalloc(size_t size) {
+ if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) {
+ return NULL;
+ }
+ void *mem = malloc(sizeof(uintptr_t)+size);
+ assert(mem);
+ *(uintptr_t*)mem = size;
+ total_allocs++;
+ total_mem += size;
+ return (char*)mem+sizeof(uintptr_t);
+}
+
+static void xfree(void *ptr) {
+ if (ptr) {
+ total_mem -= *(uintptr_t*)((char*)ptr-sizeof(uintptr_t));
+ free((char*)ptr-sizeof(uintptr_t));
+ total_allocs--;
+ }
+}
+
+static void shuffle(void *array, size_t numels, size_t elsize) {
+ char tmp[elsize];
+ char *arr = array;
+ for (size_t i = 0; i < numels - 1; i++) {
+ int j = i + rand() / (RAND_MAX / (numels - i) + 1);
+ memcpy(tmp, arr + j * elsize, elsize);
+ memcpy(arr + j * elsize, arr + i * elsize, elsize);
+ memcpy(arr + i * elsize, tmp, elsize);
+ }
+}
+
+static bool iter_ints(const void *item, void *udata) {
+ int *vals = *(int**)udata;
+ vals[*(int*)item] = 1;
+ return true;
+}
+
+static int compare_ints_udata(const void *a, const void *b, void *udata) {
+ return *(int*)a - *(int*)b;
+}
+
+static int compare_strs(const void *a, const void *b, void *udata) {
+ return strcmp(*(char**)a, *(char**)b);
+}
+
+static uint64_t hash_int(const void *item, uint64_t seed0, uint64_t seed1) {
+ return hashmap_xxhash3(item, sizeof(int), seed0, seed1);
+ // return hashmap_sip(item, sizeof(int), seed0, seed1);
+ // return hashmap_murmur(item, sizeof(int), seed0, seed1);
+}
+
+static uint64_t hash_str(const void *item, uint64_t seed0, uint64_t seed1) {
+ return hashmap_xxhash3(*(char**)item, strlen(*(char**)item), seed0, seed1);
+ // return hashmap_sip(*(char**)item, strlen(*(char**)item), seed0, seed1);
+ // return hashmap_murmur(*(char**)item, strlen(*(char**)item), seed0, seed1);
+}
+
+static void free_str(void *item) {
+ xfree(*(char**)item);
+}
+
+static void all(void) {
+ int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
+ int N = getenv("N")?atoi(getenv("N")):2000;
+ printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
+ srand(seed);
+
+ rand_alloc_fail = true;
+
+ // test sip and murmur hashes
+ assert(hashmap_sip("hello", 5, 1, 2) == 2957200328589801622);
+ assert(hashmap_murmur("hello", 5, 1, 2) == 1682575153221130884);
+ assert(hashmap_xxhash3("hello", 5, 1, 2) == 2584346877953614258);
+
+ int *vals;
+ while (!(vals = xmalloc(N * sizeof(int)))) {}
+ for (int i = 0; i < N; i++) {
+ vals[i] = i;
+ }
+
+ struct hashmap *map;
+
+ while (!(map = hashmap_new(sizeof(int), 0, seed, seed,
+ hash_int, compare_ints_udata, NULL, NULL))) {}
+ shuffle(vals, N, sizeof(int));
+ for (int i = 0; i < N; i++) {
+ // // printf("== %d ==\n", vals[i]);
+ assert(map->count == (size_t)i);
+ assert(map->count == hashmap_count(map));
+ assert(map->count == deepcount(map));
+ const int *v;
+ assert(!hashmap_get(map, &vals[i]));
+ assert(!hashmap_delete(map, &vals[i]));
+ while (true) {
+ assert(!hashmap_set(map, &vals[i]));
+ if (!hashmap_oom(map)) {
+ break;
+ }
+ }
+
+ for (int j = 0; j < i; j++) {
+ v = hashmap_get(map, &vals[j]);
+ assert(v && *v == vals[j]);
+ }
+ while (true) {
+ v = hashmap_set(map, &vals[i]);
+ if (!v) {
+ assert(hashmap_oom(map));
+ continue;
+ } else {
+ assert(!hashmap_oom(map));
+ assert(v && *v == vals[i]);
+ break;
+ }
+ }
+ v = hashmap_get(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ v = hashmap_delete(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ assert(!hashmap_get(map, &vals[i]));
+ assert(!hashmap_delete(map, &vals[i]));
+ assert(!hashmap_set(map, &vals[i]));
+ assert(map->count == (size_t)(i+1));
+ assert(map->count == hashmap_count(map));
+ assert(map->count == deepcount(map));
+ }
+
+ int *vals2;
+ while (!(vals2 = xmalloc(N * sizeof(int)))) {}
+ memset(vals2, 0, N * sizeof(int));
+ assert(hashmap_scan(map, iter_ints, &vals2));
+
+ // Test hashmap_iter. This does the same as hashmap_scan above.
+ size_t iter = 0;
+ void *iter_val;
+ while (hashmap_iter (map, &iter, &iter_val)) {
+ assert (iter_ints(iter_val, &vals2));
+ }
+ for (int i = 0; i < N; i++) {
+ assert(vals2[i] == 1);
+ }
+ xfree(vals2);
+
+ shuffle(vals, N, sizeof(int));
+ for (int i = 0; i < N; i++) {
+ const int *v;
+ v = hashmap_delete(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ assert(!hashmap_get(map, &vals[i]));
+ assert(map->count == (size_t)(N-i-1));
+ assert(map->count == hashmap_count(map));
+ assert(map->count == deepcount(map));
+ for (int j = N-1; j > i; j--) {
+ v = hashmap_get(map, &vals[j]);
+ assert(v && *v == vals[j]);
+ }
+ }
+
+ for (int i = 0; i < N; i++) {
+ while (true) {
+ assert(!hashmap_set(map, &vals[i]));
+ if (!hashmap_oom(map)) {
+ break;
+ }
+ }
+ }
+
+ assert(map->count != 0);
+ size_t prev_cap = map->cap;
+ hashmap_clear(map, true);
+ assert(prev_cap < map->cap);
+ assert(map->count == 0);
+
+
+ for (int i = 0; i < N; i++) {
+ while (true) {
+ assert(!hashmap_set(map, &vals[i]));
+ if (!hashmap_oom(map)) {
+ break;
+ }
+ }
+ }
+
+ prev_cap = map->cap;
+ hashmap_clear(map, false);
+ assert(prev_cap == map->cap);
+
+ hashmap_free(map);
+
+ xfree(vals);
+
+
+ while (!(map = hashmap_new(sizeof(char*), 0, seed, seed,
+ hash_str, compare_strs, free_str, NULL)));
+
+ for (int i = 0; i < N; i++) {
+ char *str;
+ while (!(str = xmalloc(16)));
+ snprintf(str, 16, "s%i", i);
+ while(!hashmap_set(map, &str));
+ }
+
+ hashmap_clear(map, false);
+ assert(hashmap_count(map) == 0);
+
+ for (int i = 0; i < N; i++) {
+ char *str;
+ while (!(str = xmalloc(16)));
+ snprintf(str, 16, "s%i", i);
+ while(!hashmap_set(map, &str));
+ }
+
+ hashmap_free(map);
+
+ if (total_allocs != 0) {
+ fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
+ exit(1);
+ }
+}
+
+#define bench(name, N, code) {{ \
+ if (strlen(name) > 0) { \
+ printf("%-14s ", name); \
+ } \
+ size_t tmem = total_mem; \
+ size_t tallocs = total_allocs; \
+ uint64_t bytes = 0; \
+ clock_t begin = clock(); \
+ for (int i = 0; i < N; i++) { \
+ (code); \
+ } \
+ clock_t end = clock(); \
+ double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \
+ double bytes_sec = (double)bytes/elapsed_secs; \
+ printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", \
+ N, elapsed_secs, \
+ elapsed_secs/(double)N*1e9, \
+ (double)N/elapsed_secs \
+ ); \
+ if (bytes > 0) { \
+ printf(", %.1f GB/sec", bytes_sec/1024/1024/1024); \
+ } \
+ if (total_mem > tmem) { \
+ size_t used_mem = total_mem-tmem; \
+ printf(", %.2f bytes/op", (double)used_mem/N); \
+ } \
+ if (total_allocs > tallocs) { \
+ size_t used_allocs = total_allocs-tallocs; \
+ printf(", %.2f allocs/op", (double)used_allocs/N); \
+ } \
+ printf("\n"); \
+}}
+
+static void benchmarks(void) {
+ int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
+ int N = getenv("N")?atoi(getenv("N")):5000000;
+ printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
+ srand(seed);
+
+
+ int *vals = xmalloc(N * sizeof(int));
+ for (int i = 0; i < N; i++) {
+ vals[i] = i;
+ }
+
+ shuffle(vals, N, sizeof(int));
+
+ struct hashmap *map;
+ shuffle(vals, N, sizeof(int));
+
+ map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata,
+ NULL, NULL);
+ bench("set", N, {
+ const int *v = hashmap_set(map, &vals[i]);
+ assert(!v);
+ })
+ shuffle(vals, N, sizeof(int));
+ bench("get", N, {
+ const int *v = hashmap_get(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ })
+ shuffle(vals, N, sizeof(int));
+ bench("delete", N, {
+ const int *v = hashmap_delete(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ })
+ hashmap_free(map);
+
+ map = hashmap_new(sizeof(int), N, seed, seed, hash_int, compare_ints_udata,
+ NULL, NULL);
+ bench("set (cap)", N, {
+ const int *v = hashmap_set(map, &vals[i]);
+ assert(!v);
+ })
+ shuffle(vals, N, sizeof(int));
+ bench("get (cap)", N, {
+ const int *v = hashmap_get(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ })
+ shuffle(vals, N, sizeof(int));
+ bench("delete (cap)" , N, {
+ const int *v = hashmap_delete(map, &vals[i]);
+ assert(v && *v == vals[i]);
+ })
+
+ hashmap_free(map);
+
+
+ xfree(vals);
+
+ if (total_allocs != 0) {
+ fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
+ exit(1);
+ }
+}
+
+int main(void) {
+ hashmap_set_allocator(xmalloc, xfree);
+
+ if (getenv("BENCH")) {
+ printf("Running hashmap.c benchmarks...\n");
+ benchmarks();
+ } else {
+ printf("Running hashmap.c tests...\n");
+ all();
+ printf("PASSED\n");
+ }
+}
+
+
+#endif
+
+
+
diff --git a/hashmap/hashmap.h b/hashmap/hashmap.h
new file mode 100644
index 00000000..e22990e0
--- /dev/null
+++ b/hashmap/hashmap.h
@@ -0,0 +1,62 @@
+// Copyright 2020 Joshua J Baker. All rights reserved.
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+#ifndef HASHMAP_H
+#define HASHMAP_H
+
+#include
+#include
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif // __cplusplus
+
+struct hashmap;
+
+struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0,
+ uint64_t seed1,
+ uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
+ int (*compare)(const void *a, const void *b, void *udata),
+ void (*elfree)(void *item),
+ void *udata);
+
+struct hashmap *hashmap_new_with_allocator(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void*), size_t elsize,
+ size_t cap, uint64_t seed0, uint64_t seed1,
+ uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
+ int (*compare)(const void *a, const void *b, void *udata),
+ void (*elfree)(void *item),
+ void *udata);
+
+void hashmap_free(struct hashmap *map);
+void hashmap_clear(struct hashmap *map, bool update_cap);
+size_t hashmap_count(struct hashmap *map);
+bool hashmap_oom(struct hashmap *map);
+const void *hashmap_get(struct hashmap *map, const void *item);
+const void *hashmap_set(struct hashmap *map, const void *item);
+const void *hashmap_delete(struct hashmap *map, const void *item);
+const void *hashmap_probe(struct hashmap *map, uint64_t position);
+bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata);
+bool hashmap_iter(struct hashmap *map, size_t *i, void **item);
+
+uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
+uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
+uint64_t hashmap_xxhash3(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
+
+const void *hashmap_get_with_hash(struct hashmap *map, const void *key, uint64_t hash);
+const void *hashmap_delete_with_hash(struct hashmap *map, const void *key, uint64_t hash);
+const void *hashmap_set_with_hash(struct hashmap *map, const void *item, uint64_t hash);
+void hashmap_set_grow_by_power(struct hashmap *map, size_t power);
+void hashmap_set_load_factor(struct hashmap *map, double load_factor);
+
+
+// DEPRECATED: use `hashmap_new_with_allocator`
+void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*));
+
+#if defined(__cplusplus)
+}
+#endif // __cplusplus
+
+#endif // HASHMAP_H
diff --git a/qiniu/base.c b/qiniu/base.c
index 76b1f313..00adda91 100644
--- a/qiniu/base.c
+++ b/qiniu/base.c
@@ -130,7 +130,7 @@ static char *Qiniu_escape(const char *s, escapeMode mode, Qiniu_Bool *fesc)
{
int spaceCount = 0;
int hexCount = 0;
- int i, j, len = strlen(s);
+ size_t i, j, len = strlen(s);
int c;
char *t;
@@ -674,8 +674,7 @@ Qiniu_Writer Qiniu_FILE_Writer(FILE *fp)
/*============================================================================*/
/* func Qiniu_Copy */
-Qiniu_Error Qiniu_OK = {
- 200, "OK"};
+Qiniu_Error Qiniu_OK = {200, "OK"};
Qiniu_Error Qiniu_Copy(Qiniu_Writer w, Qiniu_Reader r, void *buf, size_t n, Qiniu_Int64 *ret)
{
diff --git a/qiniu/base.h b/qiniu/base.h
index e57cac61..d3d85875 100644
--- a/qiniu/base.h
+++ b/qiniu/base.h
@@ -43,7 +43,9 @@ typedef off_t Qiniu_Off_T;
#endif
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -357,7 +359,9 @@ typedef struct stat Qiniu_FileInfo;
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/base_io.c b/qiniu/base_io.c
index 42ea25e3..572fef29 100644
--- a/qiniu/base_io.c
+++ b/qiniu/base_io.c
@@ -135,7 +135,7 @@ static ssize_t Qiniu_ReadBuf_ReadAt(Qiniu_ReadBuf *self, void *buf, size_t n, Qi
n = max & (~(size_t)0L);
}
memcpy(buf, self->buf + off, n);
- return n;
+ return (ssize_t)n;
}
Qiniu_Reader Qiniu_BufReader(Qiniu_ReadBuf *self, const char *buf, size_t bytes)
diff --git a/qiniu/cdn.c b/qiniu/cdn.c
index fa74dce9..13d932a8 100644
--- a/qiniu/cdn.c
+++ b/qiniu/cdn.c
@@ -13,7 +13,7 @@ char *Qiniu_CDN_CreateTimestampAntiLeechURL(const char *host, const char *fileNa
char *queryStrEscaped = NULL;
char expireHex[20];
- sprintf(expireHex, "%0llx", deadline);
+ snprintf(expireHex, 20, "%0llx", deadline);
if (queryStr != NULL && strcmp("", queryStr) != 0) {
queryStrEscaped = Qiniu_PathEscape(queryStr, &queryStrEscapeOk);
@@ -611,4 +611,4 @@ void Qiniu_Free_CDNLogListRet(Qiniu_CDN_LogListRet *ret) {
}
Qiniu_Free(ret->data);
}
-}
\ No newline at end of file
+}
diff --git a/qiniu/cdn.h b/qiniu/cdn.h
index efd17924..1a883333 100644
--- a/qiniu/cdn.h
+++ b/qiniu/cdn.h
@@ -14,6 +14,10 @@
#include "macro.h"
#include "http.h"
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
#ifdef __cplusplus
extern "C"
{
@@ -164,6 +168,10 @@ QINIU_DLLAPI extern void Qiniu_Free_CDNLogListRet(Qiniu_CDN_LogListRet *ret);
//=====================================================================
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/qiniu/code.c b/qiniu/code.c
new file mode 100644
index 00000000..c8d00333
--- /dev/null
+++ b/qiniu/code.c
@@ -0,0 +1,47 @@
+#include
+#include "private/code.h"
+
+#define CURLE_WEIRD_SERVER_REPLY 8
+#define CURLE_HTTP2_STREAM 92
+#define CURLE_HTTP3 95
+#define CURLE_QUIC_CONNECT_ERROR 96
+
+Qiniu_Retry_Decision _Qiniu_Should_Retry(int code)
+{
+ if (code / 100 == 4 && code != 406 && code != 429)
+ {
+ return QINIU_DONT_RETRY;
+ }
+ switch (code)
+ {
+ case CURLE_COULDNT_RESOLVE_HOST:
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_WEIRD_SERVER_REPLY:
+ case CURLE_PARTIAL_FILE:
+ case CURLE_UPLOAD_FAILED:
+ case CURLE_OPERATION_TIMEDOUT:
+ case CURLE_SSL_CONNECT_ERROR:
+ case CURLE_SEND_ERROR:
+ case CURLE_RECV_ERROR:
+ case CURLE_SSL_CERTPROBLEM:
+ case CURLE_SSL_CIPHER:
+ case CURLE_PEER_FAILED_VERIFICATION:
+ case CURLE_HTTP2_STREAM:
+ case CURLE_HTTP3:
+ case CURLE_QUIC_CONNECT_ERROR:
+ case 406:
+ case 429:
+ case 500:
+ case 502:
+ case 503:
+ case 504:
+ case 509:
+ case 571:
+ case 573:
+ case 599:
+ return QINIU_TRY_NEXT_DOMAIN;
+
+ default:
+ return QINIU_DONT_RETRY;
+ }
+}
diff --git a/qiniu/conf.c b/qiniu/conf.c
index 5090a9dc..e356220b 100644
--- a/qiniu/conf.c
+++ b/qiniu/conf.c
@@ -22,8 +22,9 @@ const char *QINIU_RS_HOST = "http://rs.qbox.me";
const char *QINIU_RSF_HOST = "http://rsf.qbox.me";
const char *QINIU_API_HOST = "http://api.qiniu.com";
const char *QINIU_FUSION_HOST = "http://fusion.qiniuapi.com";
-const char *QINIU_UC_HOST = "http://kodo-config.qiniuapi.com";
-const char *QINIU_UC_HOST_BACKUP = "http://uc.qbox.me";
+const char *QINIU_UC_HOST = "http://uc.qiniuapi.com";
+const char *QINIU_UC_HOST_BACKUP = "http://kodo-config.qiniuapi.com";
+const char *QINIU_UC_HOST_BACKUP_2 = "http://uc.qbox.me";
// 默认华东机房
//
diff --git a/qiniu/conf.h b/qiniu/conf.h
index b8aaba60..f662d530 100644
--- a/qiniu/conf.h
+++ b/qiniu/conf.h
@@ -13,6 +13,10 @@
#include "macro.h"
#include "base.h"
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
#ifdef __cplusplus
extern "C"
{
@@ -64,6 +68,10 @@ extern "C"
// 不再推荐使用,建议使用 Qiniu_Use_Region("cn-east-2") 方法替代
QINIU_DLLAPI extern void Qiniu_Use_Zone_Cn_East_2(Qiniu_Bool useHttps);
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/qiniu/emu_posix.h b/qiniu/emu_posix.h
index 5580865a..fee1acff 100644
--- a/qiniu/emu_posix.h
+++ b/qiniu/emu_posix.h
@@ -14,6 +14,10 @@
#include "macro.h"
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
#ifdef __cplusplus
extern "C"
{
@@ -51,6 +55,10 @@ QINIU_DLLAPI extern char* Qiniu_Posix_strndup(const char *, size_t n);
/*============================================================================*/
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/qiniu/fop.c b/qiniu/fop.c
index 3fdefe90..dc387448 100644
--- a/qiniu/fop.c
+++ b/qiniu/fop.c
@@ -13,8 +13,18 @@
#include "private/region.h"
#include "../cJSON/cJSON.h"
-Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const char *bucket, const char *key,
- char *fops[], int fopCount, const char *pipeline, const char *notifyURL, int force)
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void _Qiniu_Parse_Date_Time(char *datetime_string, Qiniu_DateTime *dt);
+
+#ifdef __cplusplus
+}
+#endif
+
+Qiniu_Error Qiniu_FOP_Pfop_v2(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, Qiniu_FOP_PfopParams *params)
{
Qiniu_Error err;
cJSON *root;
@@ -25,6 +35,7 @@ Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const cha
char *encodedNotifyURL = NULL;
char *encodedPipeline = NULL;
char *forceStr = NULL;
+ char *typeStr = NULL;
char *url = NULL;
char *body = NULL;
Qiniu_Bool escapeBucketOk;
@@ -34,29 +45,29 @@ Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const cha
Qiniu_Bool escapeNotifyURLOk;
// Add encoded bucket
- encodedBucket = Qiniu_QueryEscape(bucket, &escapeBucketOk);
- encodedKey = Qiniu_QueryEscape(key, &escapeKeyOk);
- fopsStr = Qiniu_String_Join(";", fops, fopCount);
+ encodedBucket = Qiniu_QueryEscape(params->bucket, &escapeBucketOk);
+ encodedKey = Qiniu_QueryEscape(params->key, &escapeKeyOk);
+ fopsStr = Qiniu_String_Join(";", params->fops, params->fopCount);
encodedFops = Qiniu_QueryEscape(fopsStr, &escapeFopsOk);
Qiniu_Free(fopsStr);
- if (pipeline)
+ if (params->pipeline)
{
- encodedPipeline = Qiniu_QueryEscape(pipeline, &escapePipelineOk);
+ encodedPipeline = Qiniu_QueryEscape(params->pipeline, &escapePipelineOk);
}
else
{
encodedPipeline = "";
}
- if (notifyURL)
+ if (params->notifyURL)
{
- encodedNotifyURL = Qiniu_QueryEscape(notifyURL, &escapeNotifyURLOk);
+ encodedNotifyURL = Qiniu_QueryEscape(params->notifyURL, &escapeNotifyURLOk);
}
else
{
encodedNotifyURL = "";
}
- if (force == 1)
+ if (params->force == 1)
{
forceStr = "1";
}
@@ -64,9 +75,18 @@ Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const cha
{
forceStr = "0";
}
+ if (params->type == 1)
+ {
+ typeStr = "1";
+ }
+ else
+ {
+ typeStr = "0";
+ }
body = Qiniu_String_Concat("bucket=", encodedBucket, "&key=", encodedKey, "&fops=", encodedFops,
- "&pipeline=", encodedPipeline, "¬ifyURL=", encodedNotifyURL, "&force=", forceStr, NULL);
+ "&pipeline=", encodedPipeline, "¬ifyURL=", encodedNotifyURL, "&force=", forceStr,
+ "&type=", typeStr, NULL);
if (escapeBucketOk)
{
Qiniu_Free(encodedBucket);
@@ -82,23 +102,22 @@ Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const cha
Qiniu_Free(encodedFops);
}
- if (pipeline && escapePipelineOk)
+ if (params->pipeline && escapePipelineOk)
{
Qiniu_Free(encodedPipeline);
}
- if (notifyURL && escapeNotifyURLOk)
+ if (params->notifyURL && escapeNotifyURLOk)
{
Qiniu_Free(encodedNotifyURL);
}
const char *apiHost;
- err = _Qiniu_Region_Get_Api_Host(self, NULL, bucket, &apiHost);
+ err = _Qiniu_Region_Get_Api_Host(self, NULL, NULL, &apiHost);
if (err.code != 200)
{
- goto error;
+ return err;
}
-
url = Qiniu_String_Concat2(apiHost, "/pfop/");
err = Qiniu_Client_CallWithBuffer(
self,
@@ -107,13 +126,94 @@ Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const cha
body,
strlen(body),
"application/x-www-form-urlencoded");
+ Qiniu_Free((void *)body);
+ Qiniu_Free((void *)url);
if (err.code == 200)
{
ret->persistentId = Qiniu_Json_GetString(root, "persistentId", 0);
}
-error:
- Qiniu_Free(body);
- Qiniu_Free(url);
return err;
+}
+
+Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const char *bucket, const char *key,
+ char *fops[], int fopCount, const char *pipeline, const char *notifyURL, int force)
+{
+ Qiniu_FOP_PfopParams params;
+ Qiniu_Zero(params);
+ params.bucket = bucket;
+ params.key = key;
+ params.fops = (char **)fops;
+ params.fopCount = fopCount;
+ params.pipeline = pipeline;
+ params.notifyURL = notifyURL;
+ params.force = force;
+ return Qiniu_FOP_Pfop_v2(self, ret, ¶ms);
} // Qiniu_FOP_Pfop
+
+Qiniu_Error Qiniu_FOP_Prefop(Qiniu_Client *self, Qiniu_FOP_PrefopRet *ret, Qiniu_FOP_PrefopItemRet *itemsRet, Qiniu_ItemCount *itemsCount, const char *persistentId, Qiniu_ItemCount maxItemsCount)
+{
+ Qiniu_Error err;
+ cJSON *root, *items, *item;
+ Qiniu_ItemCount curIndex;
+ char *encodedPersistentId = NULL, *url = NULL, *creationDateStr = NULL;
+ Qiniu_ReadBuf emptyBodyBuf = {NULL, 0, 0};
+ Qiniu_Reader emptyBody = Qiniu_BufReader(&emptyBodyBuf, 0, 0);
+ Qiniu_Bool escapePersistentIdOk;
+
+ encodedPersistentId = Qiniu_QueryEscape(persistentId, &escapePersistentIdOk);
+
+ const char *apiHost;
+ err = _Qiniu_Region_Get_Api_Host(self, NULL, NULL, &apiHost);
+ if (err.code != 200)
+ {
+ return err;
+ }
+ url = Qiniu_String_Concat(apiHost, "/status/get/prefop?id=", encodedPersistentId, NULL);
+ if (escapePersistentIdOk)
+ {
+ Qiniu_Free(encodedPersistentId);
+ }
+
+ err = Qiniu_Client_CallWithMethod(
+ self,
+ &root,
+ url,
+ emptyBody,
+ 0,
+ "application/x-www-form-urlencoded",
+ "GET",
+ NULL);
+ Qiniu_Free(url);
+ if (err.code != 200)
+ {
+ return err;
+ }
+
+ ret->id = Qiniu_Json_GetString(root, "id", NULL);
+ ret->code = Qiniu_Json_GetInt(root, "code", 0);
+ ret->desc = Qiniu_Json_GetString(root, "desc", NULL);
+ ret->inputBucket = Qiniu_Json_GetString(root, "inputBucket", NULL);
+ ret->inputKey = Qiniu_Json_GetString(root, "inputBucket", NULL);
+ ret->type = Qiniu_Json_GetInt(root, "type", 0);
+ creationDateStr = (char *)Qiniu_Json_GetString(root, "creationDate", NULL);
+ if (creationDateStr != NULL)
+ {
+ _Qiniu_Parse_Date_Time(creationDateStr, &ret->creationDate);
+ }
+
+ *itemsCount = Qiniu_Json_GetArraySize(root, "items", 0);
+ items = Qiniu_Json_GetObjectItem(root, "items", 0);
+ for (curIndex = 0; curIndex < *itemsCount && curIndex < maxItemsCount; curIndex++)
+ {
+ item = cJSON_GetArrayItem(items, curIndex);
+ itemsRet[curIndex].cmd = Qiniu_Json_GetString(item, "cmd", NULL);
+ itemsRet[curIndex].code = Qiniu_Json_GetInt(item, "code", 0);
+ itemsRet[curIndex].desc = Qiniu_Json_GetString(item, "desc", NULL);
+ itemsRet[curIndex].error = Qiniu_Json_GetString(item, "error", NULL);
+ itemsRet[curIndex].hash = Qiniu_Json_GetString(item, "hash", NULL);
+ itemsRet[curIndex].key = Qiniu_Json_GetString(item, "key", NULL);
+ itemsRet[curIndex].returnOld = Qiniu_Json_GetInt(item, "returnOld", 0);
+ }
+ return err;
+}
diff --git a/qiniu/fop.h b/qiniu/fop.h
index 9300671f..27d4554d 100644
--- a/qiniu/fop.h
+++ b/qiniu/fop.h
@@ -3,16 +3,19 @@
Name : fop.h
Author : Qiniu.com
Copyright : 2012(c) Shanghai Qiniu Information Technologies Co., Ltd.
- Description :
+ Description :
============================================================================
*/
#ifndef QINIU_FOP_H
#define QINIU_FOP_H
-#include "http.h"
+#include "rs.h"
+#include "rfc3339.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -22,6 +25,22 @@ extern "C"
/*============================================================================*/
/* func Qiniu_FOP_Pfop */
+/* @gist pfop */
+
+typedef struct _Qiniu_FOP_PfopParams
+{
+ const char *bucket;
+ const char *key;
+ const char *pipeline;
+ const char *notifyURL;
+ char **fops;
+ int fopCount;
+ int force;
+ int type;
+} Qiniu_FOP_PfopParams;
+
+/* @endgist */
+
/* @gist pfopret */
typedef struct _Qiniu_FOP_PfopRet {
@@ -30,13 +49,46 @@ typedef struct _Qiniu_FOP_PfopRet {
/* @endgist */
-QINIU_DLLAPI extern Qiniu_Error Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const char *bucket,
- const char *key, char *fops[], int fopCount, const char *pipeline,
- const char *notifyURL, int force);
+/* @gist prefopret */
+
+typedef struct _Qiniu_FOP_PrefopRet
+{
+ const char *id;
+ const char *desc;
+ const char *inputBucket;
+ const char *inputKey;
+ int code;
+ int type;
+ Qiniu_DateTime creationDate;
+} Qiniu_FOP_PrefopRet;
+
+typedef struct _Qiniu_FOP_PrefopItemRet
+{
+ const char *cmd;
+ const char *desc;
+ const char *error;
+ const char *hash;
+ const char *key;
+ int code;
+ int returnOld;
+} Qiniu_FOP_PrefopItemRet;
+
+/* @endgist */
+
+QINIU_DLLAPI extern Qiniu_Error
+Qiniu_FOP_Pfop(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, const char *bucket,
+ const char *key, char *fops[], int fopCount, const char *pipeline,
+ const char *notifyURL, int force);
+QINIU_DLLAPI extern Qiniu_Error Qiniu_FOP_Pfop_v2(Qiniu_Client *self, Qiniu_FOP_PfopRet *ret, Qiniu_FOP_PfopParams *params);
+QINIU_DLLAPI extern Qiniu_Error
+Qiniu_FOP_Prefop(Qiniu_Client *self, Qiniu_FOP_PrefopRet *ret, Qiniu_FOP_PrefopItemRet *itemsRet,
+ Qiniu_ItemCount *itemsCount, const char *persistentId, Qiniu_ItemCount maxItemsCount);
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/http.c b/qiniu/http.c
index 2068ace5..ce5c31e0 100644
--- a/qiniu/http.c
+++ b/qiniu/http.c
@@ -9,6 +9,7 @@
#include "http.h"
#include "../cJSON/cJSON.h"
+#include "../hashmap/hashmap.h"
#include
Qiniu_Error Qiniu_Client_config(Qiniu_Client *self);
@@ -347,6 +348,7 @@ void Qiniu_Client_InitEx(Qiniu_Client *self, Qiniu_Auth auth, size_t bufSize)
Qiniu_Zero_Ptr(self);
self->curl = curl_easy_init();
self->auth = auth;
+ self->hostsRetriesMax = 3;
Qiniu_Buffer_Init(&self->b, bufSize);
Qiniu_Buffer_Init(&self->respHeader, bufSize);
@@ -377,8 +379,12 @@ void Qiniu_Client_Cleanup(Qiniu_Client *self)
Qiniu_Buffer_Cleanup(&self->b);
Qiniu_Buffer_Cleanup(&self->respHeader);
- Qiniu_FreeV2((void **)&self->cachedRegion);
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
+ if (self->cachedRegions != NULL)
+ {
+ hashmap_free(self->cachedRegions);
+ self->cachedRegions = NULL;
+ }
+ self->enableUploadingAcceleration = Qiniu_False;
}
void Qiniu_Client_BindNic(Qiniu_Client *self, const char *nic)
@@ -392,6 +398,11 @@ void Qiniu_Client_SetLowSpeedLimit(Qiniu_Client *self, long lowSpeedLimit, long
self->lowSpeedTime = lowSpeedTime;
} // Qiniu_Client_SetLowSpeedLimit
+void Qiniu_Client_SetMaximumHostsRetries(Qiniu_Client *self, size_t hostsRetriesMax)
+{
+ self->hostsRetriesMax = hostsRetriesMax;
+} // Qiniu_Client_SetMaximumHostsRetries
+
void Qiniu_Client_SetTimeout(Qiniu_Client *self, long timeoutMs)
{
self->timeoutMs = timeoutMs;
@@ -408,6 +419,16 @@ void Qiniu_Client_EnableAutoQuery(Qiniu_Client *self, Qiniu_Bool useHttps)
self->autoQueryHttpsRegion = useHttps;
}
+void Qiniu_Client_EnableUploadingAcceleration(Qiniu_Client *self)
+{
+ self->enableUploadingAcceleration = Qiniu_True;
+}
+
+void Qiniu_Client_DisableUploadingAcceleration(Qiniu_Client *self)
+{
+ self->enableUploadingAcceleration = Qiniu_False;
+}
+
void Qiniu_Client_SpecifyRegion(Qiniu_Client *self, Qiniu_Region *region)
{
self->specifiedRegion = region;
@@ -471,7 +492,7 @@ static Qiniu_Error Qiniu_Client_callWithBody(
Qiniu_Error err;
const char *ctxType;
char ctxLength[64], userAgent[64];
- Qiniu_Header *headers = NULL;
+ Qiniu_Header *headers;
CURL *curl = (CURL *)self->curl;
err = Qiniu_Client_config(self);
if (err.code != 200)
@@ -532,12 +553,30 @@ static Qiniu_Error Qiniu_Client_callWithBody(
Qiniu_Error Qiniu_Client_CallWithMethod(
Qiniu_Client *self, Qiniu_Json **ret, const char *url,
Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType, const char *httpMethod, const char *md5)
+{
+ return Qiniu_Client_CallWithMethodAndProgressCallback(self, ret, url, body, bodyLen, mimeType, httpMethod, md5, NULL, NULL);
+}
+
+Qiniu_Error Qiniu_Client_CallWithMethodAndProgressCallback(
+ Qiniu_Client *self, Qiniu_Json **ret, const char *url,
+ Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType, const char *httpMethod, const char *md5,
+ int (*callback)(void *, double, double, double, double), void *callbackData)
{
CURL *curl = Qiniu_Client_initcall_withMethod(self, url, httpMethod);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, bodyLen);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, body.Read);
curl_easy_setopt(curl, CURLOPT_READDATA, body.self);
+ if (callback != NULL)
+ {
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, callback);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, callbackData);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ }
+ else
+ {
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
+ }
return Qiniu_Client_callWithBody(self, ret, url, NULL, bodyLen, mimeType, md5);
}
@@ -545,12 +584,30 @@ Qiniu_Error Qiniu_Client_CallWithMethod(
Qiniu_Error Qiniu_Client_CallWithBinary(
Qiniu_Client *self, Qiniu_Json **ret, const char *url,
Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType)
+{
+ return Qiniu_Client_CallWithBinaryAndProgressCallback(self, ret, url, body, bodyLen, mimeType, NULL, NULL);
+}
+
+Qiniu_Error Qiniu_Client_CallWithBinaryAndProgressCallback(
+ Qiniu_Client *self, Qiniu_Json **ret, const char *url,
+ Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType,
+ int (*callback)(void *, double, double, double, double), void *callbackData)
{
CURL *curl = Qiniu_Client_initcall(self, url);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, bodyLen);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, body.Read);
curl_easy_setopt(curl, CURLOPT_READDATA, body.self);
+ if (callback != NULL)
+ {
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, callback);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, callbackData);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ }
+ else
+ {
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
+ }
return Qiniu_Client_callWithBody(self, ret, url, NULL, bodyLen, mimeType, NULL);
}
diff --git a/qiniu/http.h b/qiniu/http.h
index 240082b1..c64f1600 100644
--- a/qiniu/http.h
+++ b/qiniu/http.h
@@ -79,7 +79,9 @@ extern "C"
/*============================================================================*/
/* type Qiniu_Auth */
+#if defined(_WIN32)
#pragma pack(1)
+#endif
typedef struct curl_slist Qiniu_Header;
@@ -106,7 +108,9 @@ extern "C"
typedef struct _Qiniu_Region Qiniu_Region;
QINIU_DLLAPI extern void Qiniu_Region_Free(Qiniu_Region *region);
- struct _Qiniu_Client
+ struct hashmap;
+
+ typedef struct _Qiniu_Client
{
void *curl;
Qiniu_Auth auth;
@@ -116,8 +120,7 @@ extern "C"
Qiniu_Bool autoQueryRegion;
Qiniu_Bool autoQueryHttpsRegion;
- const char *cachedRegionBucketName;
- Qiniu_Region *cachedRegion;
+ struct hashmap *cachedRegions;
Qiniu_Region *specifiedRegion;
// Use the following field to specify which NIC to use for sending packets.
@@ -138,16 +141,24 @@ extern "C"
// Millisecond timeout for the connection phase.
long connectTimeoutMs;
- };
- typedef struct _Qiniu_Client Qiniu_Client;
+
+ // Max retries count.
+ size_t hostsRetriesMax;
+
+ // Is uploading acceleration enabled.
+ Qiniu_Bool enableUploadingAcceleration;
+ } Qiniu_Client;
QINIU_DLLAPI extern void Qiniu_Client_InitEx(Qiniu_Client *self, Qiniu_Auth auth, size_t bufSize);
QINIU_DLLAPI extern void Qiniu_Client_Cleanup(Qiniu_Client *self);
QINIU_DLLAPI extern void Qiniu_Client_BindNic(Qiniu_Client *self, const char *nic);
QINIU_DLLAPI extern void Qiniu_Client_SetLowSpeedLimit(Qiniu_Client *self, long lowSpeedLimit, long lowSpeedTime);
+ QINIU_DLLAPI extern void Qiniu_Client_SetMaximumHostsRetries(Qiniu_Client *self, size_t retries);
QINIU_DLLAPI extern void Qiniu_Client_SetTimeout(Qiniu_Client *self, long timeoutMs);
QINIU_DLLAPI extern void Qiniu_Client_SetConnectTimeout(Qiniu_Client *self, long connectTimeoutMs);
QINIU_DLLAPI extern void Qiniu_Client_EnableAutoQuery(Qiniu_Client *self, Qiniu_Bool useHttps);
+ QINIU_DLLAPI extern void Qiniu_Client_EnableUploadingAcceleration(Qiniu_Client *self);
+ QINIU_DLLAPI extern void Qiniu_Client_DisableUploadingAcceleration(Qiniu_Client *self);
QINIU_DLLAPI extern void Qiniu_Client_SpecifyRegion(Qiniu_Client *self, Qiniu_Region *region);
QINIU_DLLAPI extern Qiniu_Error Qiniu_Client_Call(Qiniu_Client *self, Qiniu_Json **ret, const char *url);
@@ -166,6 +177,16 @@ extern "C"
QINIU_DLLAPI extern Qiniu_Error Qiniu_Client_CallWithMethod(
Qiniu_Client *self, Qiniu_Json **ret, const char *url,
Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType, const char *httpMethod, const char *md5);
+
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Client_CallWithBinaryAndProgressCallback(
+ Qiniu_Client *self, Qiniu_Json **ret, const char *url,
+ Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType,
+ int (*callback)(void *, double, double, double, double), void *callbackData);
+
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Client_CallWithMethodAndProgressCallback(
+ Qiniu_Client *self, Qiniu_Json **ret, const char *url,
+ Qiniu_Reader body, Qiniu_Int64 bodyLen, const char *mimeType, const char *httpMethod, const char *md5,
+ int (*callback)(void *, double, double, double, double), void *callbackData);
/*============================================================================*/
/* func Qiniu_Client_InitNoAuth/InitMacAuth */
@@ -185,7 +206,9 @@ extern "C"
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/io.c b/qiniu/io.c
index 31ea14e2..5ad35b51 100644
--- a/qiniu/io.c
+++ b/qiniu/io.c
@@ -11,6 +11,7 @@
#include "reader.h"
#include "recorder_utils.h"
#include "private/region.h"
+#include "private/code.h"
#include
/*============================================================================*/
@@ -58,20 +59,27 @@ CURL *Qiniu_Client_reset(Qiniu_Client *self);
Qiniu_Error Qiniu_callex(CURL *curl, Qiniu_Buffer *resp, Qiniu_Json **ret, Qiniu_Bool simpleError, Qiniu_Buffer *resph);
-static Qiniu_Error Get_Qiniu_UpHost(Qiniu_Client *client, const char *accessKey, const char *bucketName, Qiniu_Io_PutExtra *extra, const char **upHost)
+static Qiniu_Error _Qiniu_Get_UpHosts(Qiniu_Client *client, const char *accessKey, const char *bucketName, Qiniu_Io_PutExtra *extra, const char *const **upHosts, size_t *upHostsCount)
{
if (extra && extra->ipCount != 0)
{
Qiniu_Count oldIndex = Qiniu_Count_Inc(&extra->ipIndex);
- *upHost = extra->upIps[abs(oldIndex % extra->ipCount)];
+ *upHosts = &extra->upIps[labs(oldIndex % extra->ipCount)];
+ *upHostsCount = 1;
}
else if (extra && extra->upHost != NULL)
{
- *upHost = extra->upHost;
+ *upHosts = &extra->upHost;
+ *upHostsCount = 1;
+ }
+ else if (extra && extra->upHosts != NULL && extra->upHostsCount != 0)
+ {
+ *upHosts = extra->upHosts;
+ *upHostsCount = extra->upHostsCount;
}
else
{
- Qiniu_Error err = _Qiniu_Region_Get_Up_Host(client, accessKey, bucketName, upHost);
+ Qiniu_Error err = _Qiniu_Region_Get_Up_Hosts(client, accessKey, bucketName, upHosts, upHostsCount);
if (err.code != 200)
{
return err;
@@ -144,54 +152,85 @@ Qiniu_Error Qiniu_Client_config(Qiniu_Client *self)
return err;
}
+static int _Qiniu_Progress_Callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ void (*uploadingProgress)(size_t, size_t) = (void (*)(size_t, size_t))clientp;
+ uploadingProgress((size_t)ultotal, (size_t)ulnow);
+ return 0;
+}
+
static Qiniu_Error Qiniu_Io_call(
Qiniu_Client *self, const char *accessKey, const char *bucketName,
Qiniu_Io_PutRet *ret, struct curl_httppost *formpost, Qiniu_Io_PutExtra *extra)
{
int retCode = 0;
Qiniu_Error err;
- struct curl_slist *headers = NULL;
- const char *upHost = NULL;
+ struct curl_slist *headers;
+ const char *const *upHosts;
+ const char *defaultUpHosts[] = {QINIU_UP_HOST};
+ size_t upHostsCount;
- err = Qiniu_Client_config(self);
+ headers = curl_slist_append(NULL, "Expect:");
+ err = _Qiniu_Get_UpHosts(self, accessKey, bucketName, extra, &upHosts, &upHostsCount);
if (err.code != 200)
{
return err;
}
- err = Get_Qiniu_UpHost(self, accessKey, bucketName, extra, &upHost);
- if (err.code != 200)
+ if (upHostsCount == 0)
{
- return err;
+ upHosts = defaultUpHosts;
+ upHostsCount = 1;
}
- headers = curl_slist_append(NULL, "Expect:");
-
- CURL *curl = Qiniu_Client_reset(self);
- curl_easy_setopt(curl, CURLOPT_URL, upHost);
- curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-
- //// For aborting uploading file.
- if (extra->upAbortCallback)
+ for (size_t retries = 0; retries <= self->hostsRetriesMax; retries++)
{
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, Qiniu_Rd_Reader_Callback);
- } // if
+ CURL *curl = Qiniu_Client_reset(self);
+ err = Qiniu_Client_config(self);
+ if (err.code != 200)
+ {
+ return err;
+ }
- err = Qiniu_callex(curl, &self->b, &self->root, Qiniu_False, &self->respHeader);
- if (err.code == 200 && ret != NULL)
- {
- if (extra->callbackRetParser != NULL)
+ curl_easy_setopt(curl, CURLOPT_URL, upHosts[retries % upHostsCount]);
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ if (extra->uploadingProgress != NULL)
{
- err = (*extra->callbackRetParser)(extra->callbackRet, self->root);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, _Qiniu_Progress_Callback);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, extra->uploadingProgress);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
}
else
{
- ret->hash = Qiniu_Json_GetString(self->root, "hash", NULL);
- ret->key = Qiniu_Json_GetString(self->root, "key", NULL);
- ret->persistentId = Qiniu_Json_GetString(self->root, "persistentId", NULL);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
}
- }
+ //// For aborting uploading file.
+ if (extra->upAbortCallback)
+ {
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, Qiniu_Rd_Reader_Callback);
+ } // if
+
+ err = Qiniu_callex(curl, &self->b, &self->root, Qiniu_False, &self->respHeader);
+ if (err.code == 200)
+ {
+ if (extra->callbackRetParser != NULL)
+ {
+ err = (*extra->callbackRetParser)(extra->callbackRet, self->root);
+ }
+ else if (ret != NULL)
+ {
+ ret->hash = Qiniu_Json_GetString(self->root, "hash", NULL);
+ ret->key = Qiniu_Json_GetString(self->root, "key", NULL);
+ ret->persistentId = Qiniu_Json_GetString(self->root, "persistentId", NULL);
+ }
+ break;
+ }
+ else if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
+ {
+ break;
+ }
+ }
curl_formfree(formpost);
curl_slist_free_all(headers);
return err;
@@ -313,7 +352,6 @@ Qiniu_Error Qiniu_Io_PutBuffer(
CURLFORM_BUFFER, key, CURLFORM_BUFFERPTR, buf, CURLFORM_BUFFERLENGTH, fsize, CURLFORM_END);
err = Qiniu_Io_call(self, accessKey, bucketName, ret, form.formpost, extra);
-error:
Qiniu_Free((void *)accessKey);
Qiniu_Free((void *)bucketName);
return err;
@@ -327,42 +365,67 @@ static Qiniu_Error Qiniu_Io_call_with_callback(
{
int retCode = 0;
Qiniu_Error err;
- struct curl_slist *headers = NULL;
- const char *upHost;
+ struct curl_slist *headers;
+ const char *const *upHosts;
+ const char *defaultUpHosts[] = {QINIU_UP_HOST};
+ size_t upHostsCount;
- CURL *curl = Qiniu_Client_reset(self);
- err = Qiniu_Client_config(self);
+ headers = curl_slist_append(NULL, "Expect:");
+ err = _Qiniu_Get_UpHosts(self, accessKey, bucketName, extra, &upHosts, &upHostsCount);
if (err.code != 200)
{
return err;
}
- err = Get_Qiniu_UpHost(self, accessKey, bucketName, extra, &upHost);
- if (err.code != 200)
+ if (upHostsCount == 0)
{
- return err;
+ upHosts = defaultUpHosts;
+ upHostsCount = 1;
}
- headers = curl_slist_append(NULL, "Expect:");
+ for (size_t retries = 0; retries <= self->hostsRetriesMax; retries++)
+ {
+ CURL *curl = Qiniu_Client_reset(self);
+ err = Qiniu_Client_config(self);
+ if (err.code != 200)
+ {
+ return err;
+ }
- curl_easy_setopt(curl, CURLOPT_URL, upHost);
- curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, rdr);
+ curl_easy_setopt(curl, CURLOPT_URL, upHosts[retries % upHostsCount]);
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, rdr);
- err = Qiniu_callex(curl, &self->b, &self->root, Qiniu_False, &self->respHeader);
- if (err.code == 200 && ret != NULL)
- {
- if (extra->callbackRetParser != NULL)
+ if (extra->uploadingProgress != NULL)
{
- err = (*extra->callbackRetParser)(extra->callbackRet, self->root);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, _Qiniu_Progress_Callback);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, extra->uploadingProgress);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
}
else
{
- ret->hash = Qiniu_Json_GetString(self->root, "hash", NULL);
- ret->key = Qiniu_Json_GetString(self->root, "key", NULL);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
}
- }
+ err = Qiniu_callex(curl, &self->b, &self->root, Qiniu_False, &self->respHeader);
+ if (err.code == 200)
+ {
+ if (extra->callbackRetParser != NULL)
+ {
+ err = (*extra->callbackRetParser)(extra->callbackRet, self->root);
+ }
+ else if (ret != NULL)
+ {
+ ret->hash = Qiniu_Json_GetString(self->root, "hash", NULL);
+ ret->key = Qiniu_Json_GetString(self->root, "key", NULL);
+ }
+ break;
+ }
+ else if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
+ {
+ break;
+ }
+ }
curl_formfree(formpost);
curl_slist_free_all(headers);
return err;
diff --git a/qiniu/io.h b/qiniu/io.h
index 2329bcaa..e5eb036b 100644
--- a/qiniu/io.h
+++ b/qiniu/io.h
@@ -3,7 +3,7 @@
Name : io.h
Author : Qiniu.com
Copyright : 2012(c) Shanghai Qiniu Information Technologies Co., Ltd.
- Description :
+ Description :
============================================================================
*/
@@ -13,7 +13,9 @@
#include "http.h"
#include "reader.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -50,10 +52,18 @@ extern "C"
void *upAbortUserData;
Qiniu_Rd_FnAbort upAbortCallback;
+ // Deprecated fields, prefer upHosts.
const char *upHost;
const char **upIps;
Qiniu_Count ipCount;
Qiniu_Count ipIndex;
+
+ // Specify multiple upHosts
+ const char *const *upHosts;
+ size_t upHostsCount;
+
+ // Uploading file progress
+ void (*uploadingProgress)(size_t ultotal, size_t ulnow);
} Qiniu_Io_PutExtra;
/*============================================================================*/
@@ -98,7 +108,9 @@ extern "C"
void *self, Qiniu_Header **header, const char *url, const char *addition, size_t addlen);
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/multipart_upload.c b/qiniu/multipart_upload.c
index cea290bd..d217b08b 100644
--- a/qiniu/multipart_upload.c
+++ b/qiniu/multipart_upload.c
@@ -16,6 +16,7 @@
#include "../cJSON/cJSON.h"
#include "tm.h"
#include "private/region.h"
+#include "private/code.h"
/*============================================================================*/
#if defined(_WIN32)
@@ -49,6 +50,12 @@ typedef struct
int totalPartNum;
} Qiniu_UploadParts_Ret;
+struct _Qiniu_Progress_Callback_Data
+{
+ size_t base, totalSize, previousUlNow;
+ void (*callback)(size_t, size_t);
+};
+
static Qiniu_Error readMedium(struct Qiniu_Record_Medium *medium, char **uploadId, Qiniu_Uint64 *expireAt, Qiniu_UploadPartResp *ret);
static Qiniu_Error writeMedium(struct Qiniu_Record_Medium *medium, const Qiniu_InitPart_Ret *, const Qiniu_UploadPartResp *);
static Qiniu_Error initializeRecorder(Qiniu_Multipart_PutExtra *param, const char *uptoken, const char *key, const char *fileName, Qiniu_FileInfo *fi, Qiniu_Record_Medium *medium, Qiniu_Multipart_Recorder *recorder);
@@ -124,67 +131,116 @@ getTotalPartNum(Qiniu_Int64 fileSize, Qiniu_Int64 partSize, int *totalPartNum)
static Qiniu_Error init_upload(Qiniu_Client *client, const char *bucket, const char *encodedKey, Qiniu_Multipart_PutExtra *extraParam, Qiniu_InitPart_Ret *initPartRet)
{
Qiniu_Error err = Qiniu_OK;
- const char *uphost = extraParam->upHost;
- char *reqUrl = Qiniu_String_Concat(uphost, "/buckets/", bucket, "/objects/", encodedKey, "/uploads", NULL);
+ const char *const *upHosts;
+ const char *upHost, *reqUrl;
+ size_t upHostsCount;
+ Qiniu_Json *callRet;
- for (int i = 0; i < extraParam->tryTimes; i++)
+ if (extraParam->upHost != NULL)
{
- Qiniu_Json *callRet = NULL; // don't cJSON_Delete(callRet), it will be automatically freed on next http request by Qiniu_Client_Call.
+ upHosts = &extraParam->upHost;
+ upHostsCount = 1;
+ }
+ else
+ {
+ upHosts = extraParam->upHosts;
+ upHostsCount = extraParam->upHostsCount;
+ }
+
+ for (int i = 0; i < extraParam->tryTimes && i <= client->hostsRetriesMax; i++)
+ {
+ upHost = upHosts[i % upHostsCount];
+ reqUrl = Qiniu_String_Concat(upHost, "/buckets/", bucket, "/objects/", encodedKey, "/uploads", NULL);
+
+ callRet = NULL; // don't cJSON_Delete(callRet), it will be automatically freed on next http request by Qiniu_Client_Call.
err = Qiniu_Client_Call(client, &callRet, reqUrl);
- if ((err.code / 100 == 5) || (callRet == NULL)) // 5xx , net error will retry
+ Qiniu_Free((void *)reqUrl);
+ if (err.code == 200)
{
- Qiniu_Log_Error("init_upload retry: %E", err);
- continue;
+ if (initPartRet != NULL)
+ {
+ initPartRet->uploadId = Qiniu_String_Dup(Qiniu_Json_GetString(callRet, "uploadId", NULL));
+ initPartRet->expireAt = Qiniu_Json_GetUInt64(callRet, "expireAt", 0);
+ }
+ break;
}
- else if (err.code != 200)
+ else if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
{
- Qiniu_Log_Error("init_upload: %E", err);
break;
}
-
- initPartRet->uploadId = Qiniu_String_Dup(Qiniu_Json_GetString(callRet, "uploadId", NULL));
- initPartRet->expireAt = Qiniu_Json_GetUInt64(callRet, "expireAt", 0);
- break;
}
- Qiniu_Free(reqUrl);
return err;
}
-Qiniu_Error upload_one_part(Qiniu_Client *client, Qiniu_Multipart_PutExtra *extraParam, const char *reqUrl, int partNum, Qiniu_ReaderAt reader, Qiniu_Int64 partOffset, Qiniu_Int64 partSize, const char *md5str, Qiniu_UploadPartResp *ret)
+static int _Qiniu_Progress_Callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ struct _Qiniu_Progress_Callback_Data *data = (struct _Qiniu_Progress_Callback_Data *)clientp;
+ if (data->previousUlNow != (size_t)ulnow)
+ {
+ data->callback((size_t)data->totalSize, data->base + (size_t)ulnow);
+ data->previousUlNow = (size_t)ulnow;
+ }
+ return 0;
+}
+
+Qiniu_Error upload_one_part(Qiniu_Client *client, Qiniu_Multipart_PutExtra *extraParam, const char *path,
+ int partNum, Qiniu_ReaderAt reader, Qiniu_Int64 partOffset, Qiniu_Int64 partSize, const char *md5str,
+ Qiniu_UploadPartResp *ret, struct _Qiniu_Progress_Callback_Data *progressCallback)
{
Qiniu_Error err = Qiniu_OK;
- for (int try = 0; try < extraParam->tryTimes; try++)
+ const char *const *upHosts;
+ size_t upHostsCount;
+ int (*callback)(void *, double, double, double, double) = NULL;
+ void *callbackData = NULL;
+ if (progressCallback != NULL && progressCallback->callback != NULL)
+ {
+ callback = _Qiniu_Progress_Callback;
+ callbackData = (void *)progressCallback;
+ }
+
+ if (extraParam->upHost != NULL)
+ {
+ upHosts = &extraParam->upHost;
+ upHostsCount = 1;
+ }
+ else
+ {
+ upHosts = extraParam->upHosts;
+ upHostsCount = extraParam->upHostsCount;
+ }
+ for (int tries = 0; tries < extraParam->tryTimes && tries <= client->hostsRetriesMax; tries++)
{
Qiniu_Section section;
Qiniu_Zero(section);
Qiniu_Reader thisPartBody = Qiniu_SectionReader(§ion, reader, (Qiniu_Off_T)partOffset, partSize);
Qiniu_Json *callRet = NULL; // don't cJSON_Delete(callRet), it will be automatically freed on next http request by Qiniu_Client_Call.
- err = Qiniu_Client_CallWithMethod(client, &callRet, reqUrl, thisPartBody, partSize, NULL, "PUT", md5str);
- if ((err.code / 100 == 5) || (callRet == NULL)) // 5xx,net error will retry
+ const char *upHost = upHosts[tries % upHostsCount];
+ const char *reqUrl = Qiniu_String_Concat(upHost, path, NULL);
+ err = Qiniu_Client_CallWithMethodAndProgressCallback(client, &callRet, reqUrl, thisPartBody, partSize, NULL, "PUT", md5str, callback, callbackData);
+ Qiniu_Free((void *)reqUrl);
+ if (err.code == 200)
{
- Qiniu_Log_Error("upload_part retry: partNum:%d, %E", partNum, err);
- continue;
+ if (ret != NULL)
+ {
+ const char *md5 = Qiniu_Json_GetString(callRet, "md5", NULL);
+ const char *etag = Qiniu_Json_GetString(callRet, "etag", NULL);
+ ret->etag = Qiniu_String_Dup(etag);
+ ret->md5 = Qiniu_String_Dup(md5);
+ ret->partNum = partNum;
+ }
+ break;
}
- else if (err.code != 200)
+ else if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
{
- Qiniu_Log_Error("upload_part: partNum:%d, %E", partNum, err);
break;
}
-
- const char *md5 = Qiniu_Json_GetString(callRet, "md5", NULL);
- const char *etag = Qiniu_Json_GetString(callRet, "etag", NULL);
- ret->etag = Qiniu_String_Dup(etag);
- ret->md5 = Qiniu_String_Dup(md5);
- ret->partNum = partNum;
- // Qiniu_Log_Debug("partNum:%d,remote md5:%s ", partNum, md5);
- break;
}
// notify callback
- if ((err.code == 200) && extraParam->notify)
+ if (err.code == 200 && extraParam->notify)
{
extraParam->notify(ret);
}
- if ((err.code != 200) && extraParam->notifyErr)
+ else if (err.code != 200 && extraParam->notifyErr)
{
extraParam->notifyErr(partNum, err);
}
@@ -195,22 +251,19 @@ Qiniu_Error upload_one_part(Qiniu_Client *client, Qiniu_Multipart_PutExtra *extr
static Qiniu_Error upload_parts(Qiniu_Client *client, const char *bucket, const char *encodedKey, const Qiniu_InitPart_Ret *initParts, Qiniu_ReaderAt *reader, Qiniu_Int64 fsize, int totalPartNum, Qiniu_Multipart_PutExtra *extraParam, Qiniu_Multipart_Recorder *recorder, Qiniu_UploadParts_Ret *uploadPartsRet)
{
Qiniu_Error err = Qiniu_OK;
- const char *uphost = extraParam->upHost;
-
Qiniu_Int64 partSize = extraParam->partSize;
- int lastPart = totalPartNum - 1;
- for (int partNum = 0; partNum < totalPartNum; partNum++)
- {
- if ((uploadPartsRet->PartsRet + partNum)->etag != NULL)
- {
- continue;
- }
+ const int lastPart = totalPartNum - 1;
+ struct _Qiniu_Progress_Callback_Data progressCallbackData;
+ Qiniu_Zero(progressCallbackData);
- int partNumInReq = partNum + 1; // partNum start from 1
- char partNumStr[10]; // valid partNum ={"1"~"10000"}
- snprintf(partNumStr, 10, "%d", partNumInReq);
- char *reqUrl = Qiniu_String_Concat(uphost, "/buckets/", bucket, "/objects/", encodedKey, "/uploads/", initParts->uploadId, "/", partNumStr, NULL);
+ if (extraParam->uploadingProgress != NULL)
+ {
+ progressCallbackData.callback = extraParam->uploadingProgress;
+ progressCallbackData.totalSize = (size_t)fsize;
+ }
+ for (int partNum = 0; partNum < totalPartNum; partNum++)
+ {
Qiniu_Int64 thisPartOffset = partNum * partSize;
Qiniu_Int64 thisPartSize = partSize;
if (partNum == lastPart)
@@ -218,29 +271,36 @@ static Qiniu_Error upload_parts(Qiniu_Client *client, const char *bucket, const
thisPartSize = fsize - (totalPartNum - 1) * partSize;
}
- const char *md5str = NULL;
- if (extraParam->enableContentMd5)
+ if ((uploadPartsRet->PartsRet + partNum)->etag == NULL)
{
- md5str = caculatePartMd5(*reader, thisPartOffset, thisPartSize);
- // Qiniu_Log_Debug("partNum:%d, local Md5:%s ", partNumInReq, md5str);
- }
- err = upload_one_part(client, extraParam, reqUrl, partNumInReq, *reader, thisPartOffset, thisPartSize, md5str, &uploadPartsRet->PartsRet[partNum]);
-
- Qiniu_Multi_Free(2, (void *)reqUrl, (void *)md5str);
+ const int partNumInReq = partNum + 1; // partNum start from 1
+ char partNumStr[10]; // valid partNum ={"1"~"10000"}
+ snprintf(partNumStr, 10, "%d", partNumInReq);
+ const char *path = Qiniu_String_Concat("/buckets/", bucket, "/objects/", encodedKey, "/uploads/", initParts->uploadId, "/", partNumStr, NULL);
- if (err.code != 200)
- {
- return err;
- }
+ const char *md5str = NULL;
+ if (extraParam->enableContentMd5)
+ {
+ md5str = caculatePartMd5(*reader, thisPartOffset, thisPartSize);
+ // Qiniu_Log_Debug("partNum:%d, local Md5:%s ", partNumInReq, md5str);
+ }
+ err = upload_one_part(client, extraParam, path, partNumInReq, *reader, thisPartOffset, thisPartSize, md5str, &uploadPartsRet->PartsRet[partNum], &progressCallbackData);
+ Qiniu_Multi_Free(2, (void *)path, (void *)md5str);
- if (recorder != NULL && recorder->recorderMedium != NULL)
- {
- err = writeMedium(recorder->recorderMedium, initParts, &uploadPartsRet->PartsRet[partNum]);
if (err.code != 200)
{
return err;
}
+ if (recorder != NULL && recorder->recorderMedium != NULL)
+ {
+ err = writeMedium(recorder->recorderMedium, initParts, &uploadPartsRet->PartsRet[partNum]);
+ if (err.code != 200)
+ {
+ return err;
+ }
+ }
}
+ progressCallbackData.base += thisPartSize;
}
return err;
}
@@ -283,35 +343,54 @@ static Qiniu_Error complete_upload(
// step2:send req
Qiniu_Error err = Qiniu_OK;
- char *reqUrl = Qiniu_String_Concat(extraParam->upHost, "/buckets/", bucket, "/objects/", encodedKey, "/uploads/", initPartsRet->uploadId, NULL);
+ const char *const *upHosts;
+ size_t upHostsCount;
- for (int try = 0; try < extraParam->tryTimes; try++)
+ if (extraParam->upHost != NULL)
{
+ upHosts = &extraParam->upHost;
+ upHostsCount = 1;
+ }
+ else
+ {
+ upHosts = extraParam->upHosts;
+ upHostsCount = extraParam->upHostsCount;
+ }
+
+ for (int tries = 0; tries < extraParam->tryTimes && tries <= client->hostsRetriesMax; tries++)
+ {
+ const char *upHost = upHosts[tries % upHostsCount];
+ const char *reqUrl = Qiniu_String_Concat(upHost, "/buckets/", bucket, "/objects/", encodedKey, "/uploads/", initPartsRet->uploadId, NULL);
Qiniu_Json *callRet = NULL; // don't cJSON_Delete(callRet), it will be automatically freed on next http request by Qiniu_Client_Call.
err = Qiniu_Client_CallWithBuffer(client, &callRet, reqUrl, body, strlen(body), "application/json");
- if ((err.code / 100 == 5) || (callRet == NULL)) // 5xx,net error will retry
- {
- Qiniu_Log_Error("cupload: retry uploadId:%s, %E", initPartsRet->uploadId, err);
- continue;
- }
- if (err.code / 100 != 4 && extraParam->recorder != NULL && recorder != NULL && recorder->recorderKey != NULL)
+ Qiniu_Free((void *)reqUrl);
+
+ if (err.code == 200)
{
- extraParam->recorder->remove(extraParam->recorder, recorder->recorderKey);
+ if (extraParam->recorder != NULL && recorder != NULL && recorder->recorderKey != NULL)
+ {
+ extraParam->recorder->remove(extraParam->recorder, recorder->recorderKey);
+ }
+ if (callRet != NULL)
+ {
+ const char *hash = Qiniu_Json_GetString(callRet, "hash", NULL); // don't free(hash) since no malloc happen.
+ const char *key = Qiniu_Json_GetString(callRet, "key", NULL);
+ completeRet->hash = Qiniu_String_Dup(hash);
+ completeRet->key = Qiniu_String_Dup(key);
+ }
+ break;
}
- if (err.code != 200)
+ else if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
{
- Qiniu_Log_Error("cupload: uploadId:%s, %E", initPartsRet->uploadId, err);
+ if (err.code / 100 != 4 && extraParam->recorder != NULL && recorder != NULL && recorder->recorderKey != NULL)
+ {
+ extraParam->recorder->remove(extraParam->recorder, recorder->recorderKey);
+ }
break;
}
-
- const char *hash = Qiniu_Json_GetString(callRet, "hash", NULL); // don't free(hash) since no malloc happen.
- const char *key = Qiniu_Json_GetString(callRet, "key", NULL);
- completeRet->hash = Qiniu_String_Dup(hash);
- completeRet->key = Qiniu_String_Dup(key);
- break;
}
// Qiniu_Log_Debug("Upload result: uploadid:%s, hash:%s, key:%s ", uploadId, completeRet->hash, completeRet->key);
- Qiniu_Multi_Free(2, (void *)reqUrl, (void *)body);
+ Qiniu_Free((void *)body);
return err;
}
@@ -506,9 +585,9 @@ Qiniu_Error verifyParam(Qiniu_Client *client, const char *accessKey, const char
{
param->tryTimes = 3;
}
- if (param->upHost == NULL)
+ if (param->upHost == NULL && (param->upHosts == NULL || param->upHostsCount == 0))
{
- err = _Qiniu_Region_Get_Up_Host(client, accessKey, bucketName, ¶m->upHost);
+ err = _Qiniu_Region_Get_Up_Hosts(client, accessKey, bucketName, ¶m->upHosts, ¶m->upHostsCount);
}
return err;
diff --git a/qiniu/multipart_upload.h b/qiniu/multipart_upload.h
index 3d265ca7..4c536d7e 100644
--- a/qiniu/multipart_upload.h
+++ b/qiniu/multipart_upload.h
@@ -17,7 +17,9 @@
#include "stdio.h"
#include "recorder.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -37,7 +39,7 @@ extern "C"
typedef struct _Qiniu_Multipart_PutExtra
{
- const char *upHost; //if not set explicitly ,will use global QINIU_UP_HOST;
+ const char *upHost; // if not set explicitly, will use global upHosts;
Qiniu_Int64 partSize; //size for each part
const char *mimeType;
int tryTimes; //at least 1, default=3
@@ -58,6 +60,12 @@ extern "C"
Qiniu_Recorder *recorder;
+ // Specify multiple upHosts, if not set explicitly, will global QINIU_UP_HOST
+ const char *const *upHosts;
+ size_t upHostsCount;
+
+ // Uploading file progress
+ void (*uploadingProgress)(size_t ultotal, size_t ulnow);
} Qiniu_Multipart_PutExtra;
typedef struct
diff --git a/qiniu/private/code.h b/qiniu/private/code.h
new file mode 100644
index 00000000..492bc735
--- /dev/null
+++ b/qiniu/private/code.h
@@ -0,0 +1,21 @@
+#ifndef QINIU_CODE_H
+#define QINIU_CODE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef enum
+ {
+ QINIU_DONT_RETRY,
+ QINIU_TRY_NEXT_DOMAIN
+ } Qiniu_Retry_Decision;
+
+ Qiniu_Retry_Decision _Qiniu_Should_Retry(int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // QINIU_CODE_H
diff --git a/qiniu/private/region.h b/qiniu/private/region.h
index 8a5cc465..1dc9f0ea 100644
--- a/qiniu/private/region.h
+++ b/qiniu/private/region.h
@@ -1,26 +1,17 @@
#ifndef QINIU_PRIVATE_REGION_H
#define QINIU_PRIVATE_REGION_H
#include
-#include "../base.h"
+#include "../http.h"
#ifdef __cplusplus
extern "C"
{
#endif
- struct _Qiniu_Client;
- typedef struct _Qiniu_Client Qiniu_Client;
-
- struct _Qiniu_Region_Hosts
- {
- const char *const *hosts;
- size_t hostsCount;
- };
-
struct _Qiniu_Region_Service
{
- struct _Qiniu_Region_Hosts *preferredHosts;
- struct _Qiniu_Region_Hosts *alternativeHosts;
+ const char *const *hosts;
+ size_t acceleratedHostsCount, preferredHostsCount, alternativeHostsCount;
};
struct _Qiniu_Region
@@ -37,20 +28,31 @@ extern "C"
struct _Qiniu_Region_Service *apiService;
};
- static struct _Qiniu_Region_Hosts *_Qiniu_Region_Hosts_New(const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
- static struct _Qiniu_Region_Hosts *_Qiniu_Region_Hosts_New_without_dup(const char *const *hosts, size_t hostsCount);
- static void _Qiniu_Region_Hosts_Free(struct _Qiniu_Region_Hosts *hosts);
+ struct _Qiniu_Region_Service *_Qiniu_Region_Service_New(const char *const *acceleratedHosts, size_t acceleratedHostsCount,
+ const char *const *preferredHosts, size_t preferredHostsCount,
+ const char *const *alternativeHosts, size_t alternativeHostsCount,
+ Qiniu_Bool useHttps);
+ void _Qiniu_Region_Service_Free(struct _Qiniu_Region_Service *service);
- static struct _Qiniu_Region_Service *_Qiniu_Region_Service_New(struct _Qiniu_Region_Hosts *preferredHosts, struct _Qiniu_Region_Hosts *alternativeHosts);
- static void _Qiniu_Region_Service_Free(struct _Qiniu_Region_Service *service);
-
- Qiniu_Error _Qiniu_Region_Get_Up_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host);
Qiniu_Error _Qiniu_Region_Get_Io_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host);
Qiniu_Error _Qiniu_Region_Get_Rs_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host);
Qiniu_Error _Qiniu_Region_Get_Rsf_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host);
Qiniu_Error _Qiniu_Region_Get_Api_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host);
+
+ Qiniu_Error _Qiniu_Region_Get_Up_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count);
+ Qiniu_Error _Qiniu_Region_Get_Io_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count);
+ Qiniu_Error _Qiniu_Region_Get_Rs_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count);
+ Qiniu_Error _Qiniu_Region_Get_Rsf_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count);
+ Qiniu_Error _Qiniu_Region_Get_Api_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count);
Qiniu_Error _Qiniu_Region_Query(Qiniu_Client *self, struct _Qiniu_Region **region, const char *accessKey, const char *const bucketName, Qiniu_Bool useHttps);
+ const char *const *_Qiniu_Region_Get_All_Up_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Io_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Io_Src_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Bucket_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Rs_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Rsf_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
+ const char *const *_Qiniu_Region_Get_All_Api_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled);
#ifdef __cplusplus
}
#endif
diff --git a/qiniu/qetag.c b/qiniu/qetag.c
index 9e9bcaa5..5a87823e 100644
--- a/qiniu/qetag.c
+++ b/qiniu/qetag.c
@@ -10,7 +10,9 @@ extern "C"
{
#endif
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#define BLOCK_ELEMENT_MAX_COUNT 16
#define BLOCK_MAX_SIZE (1 << 22)
@@ -60,7 +62,7 @@ static Qiniu_Error Qiniu_Qetag_mergeBlocks(struct _Qiniu_Qetag_Context * ctx) {
}
blk->done = NO;
-
+
ctx->blkBegin += 1;
if (ctx->blkBegin >= ctx->blkElementCount) {
ctx->blkBegin = 0;
@@ -266,7 +268,7 @@ Qiniu_Error Qiniu_Qetag_Final(struct _Qiniu_Qetag_Context * ctx, char ** digest)
Qiniu_Error Qiniu_Qetag_AllocateBlock(struct _Qiniu_Qetag_Context * ctx, struct _Qiniu_Qetag_Block ** blk, size_t * remainderSize)
{
Qiniu_Error err;
-
+
if (ctx->blk) {
*blk = ctx->blk;
ctx->blk = NULL;
@@ -394,7 +396,9 @@ Qiniu_Error Qiniu_Qetag_DigestBuffer(const char * buf, size_t bufSize, char ** d
return err;
} // Qiniu_Qetag_DigestBuffer
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/qetag.h b/qiniu/qetag.h
index 165b8821..ef12ed99 100644
--- a/qiniu/qetag.h
+++ b/qiniu/qetag.h
@@ -3,7 +3,7 @@
Name : qetag.h
Author : Qiniu.com
Copyright : 2012(c) Shanghai Qiniu Information Technologies Co., Ltd.
- Description :
+ Description :
============================================================================
*/
@@ -18,27 +18,31 @@ extern "C"
{
#endif
+#if defined(_WIN32)
#pragma pack(1)
+#endif
-struct _Qiniu_Qetag_Context;
-struct _Qiniu_Qetag_Block;
+ struct _Qiniu_Qetag_Context;
+ struct _Qiniu_Qetag_Block;
-// 底层函数
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_New(struct _Qiniu_Qetag_Context ** ctx, unsigned int concurrency);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Reset(struct _Qiniu_Qetag_Context * ctx);
-QINIU_DLLAPI extern void Qiniu_Qetag_Destroy(struct _Qiniu_Qetag_Context * ctx);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Update(struct _Qiniu_Qetag_Context * ctx, const char * buf, size_t bufSize);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Final(struct _Qiniu_Qetag_Context * ctx, char ** digest);
+ // 底层函数
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_New(struct _Qiniu_Qetag_Context **ctx, unsigned int concurrency);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Reset(struct _Qiniu_Qetag_Context *ctx);
+ QINIU_DLLAPI extern void Qiniu_Qetag_Destroy(struct _Qiniu_Qetag_Context *ctx);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Update(struct _Qiniu_Qetag_Context *ctx, const char *buf, size_t bufSize);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_Final(struct _Qiniu_Qetag_Context *ctx, char **digest);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_AllocateBlock(struct _Qiniu_Qetag_Context * ctx, struct _Qiniu_Qetag_Block ** blk, size_t * blkCapacity);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_UpdateBlock(struct _Qiniu_Qetag_Block * blk, const char * buf, size_t bufSize, size_t * blkCapacity);
-QINIU_DLLAPI extern void Qiniu_Qetag_CommitBlock(struct _Qiniu_Qetag_Context * ctx, struct _Qiniu_Qetag_Block * blk);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_AllocateBlock(struct _Qiniu_Qetag_Context *ctx, struct _Qiniu_Qetag_Block **blk, size_t *blkCapacity);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_UpdateBlock(struct _Qiniu_Qetag_Block *blk, const char *buf, size_t bufSize, size_t *blkCapacity);
+ QINIU_DLLAPI extern void Qiniu_Qetag_CommitBlock(struct _Qiniu_Qetag_Context *ctx, struct _Qiniu_Qetag_Block *blk);
-// 单线程计算 QETAG
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_DigestFile(const char * localFile, char ** digest);
-QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_DigestBuffer(const char * buf, size_t fsize, char ** digest);
+ // 单线程计算 QETAG
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_DigestFile(const char *localFile, char **digest);
+ QINIU_DLLAPI extern Qiniu_Error Qiniu_Qetag_DigestBuffer(const char *buf, size_t fsize, char **digest);
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/reader.h b/qiniu/reader.h
index 8515475f..2a07f5ca 100644
--- a/qiniu/reader.h
+++ b/qiniu/reader.h
@@ -3,7 +3,7 @@
Name : reader.h
Author : Qiniu.com
Copyright : 2012-2016(c) Shanghai Qiniu Information Technologies Co., Ltd.
- Description :
+ Description :
============================================================================
*/
@@ -12,7 +12,9 @@
#include "base.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -47,7 +49,9 @@ QINIU_DLLAPI extern void Qiniu_Rd_Reader_Close(Qiniu_Rd_Reader * rdr);
QINIU_DLLAPI extern size_t Qiniu_Rd_Reader_Callback(char * buffer, size_t size, size_t nitems, void * rdr);
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/recorder.c b/qiniu/recorder.c
index 3afc31dc..d786c32d 100644
--- a/qiniu/recorder.c
+++ b/qiniu/recorder.c
@@ -157,7 +157,6 @@ Qiniu_Error _Qiniu_FileSystem_RecordMedium_Read_Entry(const struct Qiniu_Record_
Qiniu_Error _Qiniu_FileSystem_RecordMedium_Has_Next_Entry(const struct Qiniu_Record_Medium *medium, Qiniu_Bool *has)
{
struct _FileSystem_Recorder_Data *data = (struct _FileSystem_Recorder_Data *)medium->data;
- size_t i;
Qiniu_Bool hasEntry = Qiniu_True;
if (data->buf == NULL || data->bufOffset >= data->bufSize)
diff --git a/qiniu/recorder.h b/qiniu/recorder.h
index 08ae4fe0..49698b32 100644
--- a/qiniu/recorder.h
+++ b/qiniu/recorder.h
@@ -14,7 +14,9 @@
#define Qiniu_Recorder_Read_Error 9801
#define Qiniu_Recorder_Write_Error 9802
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -45,7 +47,9 @@ extern "C"
QINIU_DLLAPI extern Qiniu_Error Qiniu_FileSystem_Recorder_New(const char *rootPath, struct Qiniu_Recorder *recorder);
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/recorder_key.h b/qiniu/recorder_key.h
index a7f56602..c5d10e55 100644
--- a/qiniu/recorder_key.h
+++ b/qiniu/recorder_key.h
@@ -13,7 +13,9 @@
#include "base.h"
#include "private/crypto.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -36,7 +38,9 @@ QINIU_DLLAPI extern void Qiniu_Recorder_Key_Generator_Free(Qiniu_Recorder_Key_Ge
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/recorder_utils.h b/qiniu/recorder_utils.h
index a8623805..4b717f46 100644
--- a/qiniu/recorder_utils.h
+++ b/qiniu/recorder_utils.h
@@ -13,7 +13,9 @@
#include "base.h"
#include "recorder.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -29,7 +31,9 @@ extern "C"
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/region.c b/qiniu/region.c
index 821a69b5..7d3d7871 100644
--- a/qiniu/region.c
+++ b/qiniu/region.c
@@ -11,6 +11,7 @@
#include "region.h"
#include "tm.h"
#include "../cJSON/cJSON.h"
+#include "../hashmap/hashmap.h"
/*============================================================================*/
@@ -18,8 +19,7 @@
#include
#endif
-static const char *
-duplicate_host(const char *host, Qiniu_Bool useHttps)
+static const char *_Qiniu_Duplicate_Host(const char *host, Qiniu_Bool useHttps)
{
if (host == NULL)
{
@@ -39,83 +39,82 @@ duplicate_host(const char *host, Qiniu_Bool useHttps)
}
}
-struct _Qiniu_Region_Hosts *_Qiniu_Region_Hosts_New(const char *const *const hosts, size_t hostsCount, Qiniu_Bool useHttps)
+static Qiniu_Bool _Qiniu_Region_Service_Copy_Hosts(const char **targetHosts, size_t copyOffset, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
{
- const char **newHosts = malloc(sizeof(const char *) * hostsCount);
- if (newHosts == NULL)
- {
- return NULL;
- }
-
- struct _Qiniu_Region_Hosts *newRegionHosts = malloc(sizeof(struct _Qiniu_Region_Hosts));
- if (newRegionHosts == NULL)
- {
- free((void *)newHosts);
- return NULL;
- }
- newRegionHosts->hosts = (const char *const *)newHosts;
-
- for (size_t i = 0; i < hostsCount; i++)
+ size_t i, j;
+ for (i = 0; i < hostsCount; i++)
{
if (hosts[i] == NULL)
{
break;
}
- newHosts[i] = duplicate_host(hosts[i], useHttps);
- if (newHosts[i] == NULL)
+ targetHosts[copyOffset + i] = _Qiniu_Duplicate_Host(hosts[i], useHttps);
+ if (targetHosts[copyOffset + i] == NULL)
{
- for (size_t j = 0; j < i; j++)
- {
- free((void *)newHosts[j]);
- }
- free((void *)newHosts);
- free((void *)newRegionHosts);
- return NULL;
+ goto handleErr;
}
- newRegionHosts->hostsCount = i + 1;
}
- return newRegionHosts;
-}
-
-struct _Qiniu_Region_Hosts *_Qiniu_Region_Hosts_New_without_dup(const char *const *hosts, size_t hostsCount)
-{
- struct _Qiniu_Region_Hosts *newRegionHosts = malloc(sizeof(struct _Qiniu_Region_Hosts));
- if (newRegionHosts == NULL)
+ return Qiniu_True;
+handleErr:
+ for (j = 0; j < i; j++)
{
- return NULL;
+ free((void *)targetHosts[copyOffset + j]);
}
- newRegionHosts->hosts = hosts;
- newRegionHosts->hostsCount = hostsCount;
- return newRegionHosts;
+ return Qiniu_False;
}
-void _Qiniu_Region_Hosts_Free(struct _Qiniu_Region_Hosts *hosts)
+struct _Qiniu_Region_Service *_Qiniu_Region_Service_New(
+ const char *const *acceleratedHosts, size_t acceleratedHostsCount,
+ const char *const *preferredHosts, size_t preferredHostsCount,
+ const char *const *alternativeHosts, size_t alternativeHostsCount,
+ Qiniu_Bool useHttps)
{
- if (hosts == NULL)
+ struct _Qiniu_Region_Service *newService = malloc(sizeof(struct _Qiniu_Region_Service));
+ if (newService == NULL)
{
- return;
+ goto handleErr;
}
- for (int i = 0; i < hosts->hostsCount; i++)
+ size_t allHostsCount = acceleratedHostsCount + preferredHostsCount + alternativeHostsCount;
+ const char **newHosts = malloc(sizeof(const char *) * allHostsCount);
+ if (newHosts == NULL)
{
- free((void *)hosts->hosts[i]);
+ goto handleErr;
}
- free((void *)hosts->hosts);
- hosts->hosts = NULL;
- hosts->hostsCount = 0;
- free((void *)hosts);
-}
-
-struct _Qiniu_Region_Service *_Qiniu_Region_Service_New(struct _Qiniu_Region_Hosts *preferredHosts, struct _Qiniu_Region_Hosts *alternativeHosts)
-{
- struct _Qiniu_Region_Service *newService = malloc(sizeof(struct _Qiniu_Region_Service));
- if (newService == NULL)
+ size_t offset = 0;
+ if (_Qiniu_Region_Service_Copy_Hosts(newHosts, offset, acceleratedHosts, acceleratedHostsCount, useHttps) == Qiniu_False)
{
- return NULL;
+ goto handleErr;
}
- newService->preferredHosts = preferredHosts;
- newService->alternativeHosts = alternativeHosts;
-
+ offset += acceleratedHostsCount;
+ if (_Qiniu_Region_Service_Copy_Hosts(newHosts, offset, preferredHosts, preferredHostsCount, useHttps) == Qiniu_False)
+ {
+ goto handleErr;
+ }
+ offset += preferredHostsCount;
+ if (_Qiniu_Region_Service_Copy_Hosts(newHosts, offset, alternativeHosts, alternativeHostsCount, useHttps) == Qiniu_False)
+ {
+ goto handleErr;
+ }
+ offset += alternativeHostsCount;
+ newService->hosts = newHosts;
+ newService->acceleratedHostsCount = acceleratedHostsCount;
+ newService->preferredHostsCount = preferredHostsCount;
+ newService->alternativeHostsCount = alternativeHostsCount;
return newService;
+handleErr:
+ if (newHosts != NULL)
+ {
+ for (size_t j = 0; j < offset; j++)
+ {
+ free((void *)newHosts[offset + j]);
+ }
+ free((void *)newHosts);
+ }
+ if (newService != NULL)
+ {
+ free((void *)newService);
+ }
+ return NULL;
}
void _Qiniu_Region_Service_Free(struct _Qiniu_Region_Service *service)
@@ -124,11 +123,13 @@ void _Qiniu_Region_Service_Free(struct _Qiniu_Region_Service *service)
{
return;
}
- _Qiniu_Region_Hosts_Free(service->preferredHosts);
- service->preferredHosts = NULL;
- _Qiniu_Region_Hosts_Free(service->alternativeHosts);
- service->alternativeHosts = NULL;
- free(service);
+ size_t allHostsCount = service->acceleratedHostsCount + service->preferredHostsCount + service->alternativeHostsCount;
+ for (size_t i = 0; i < allHostsCount; i++)
+ {
+ free((void *)service->hosts[i]);
+ }
+ free((void *)service->hosts);
+ free((void *)service);
}
Qiniu_Region *Qiniu_Region_New(const char *regionId, Qiniu_Uint64 ttl)
@@ -182,22 +183,48 @@ void Qiniu_Region_Free(Qiniu_Region *region)
free((void *)region);
}
+void Qiniu_Region_Set_Up_Accelerated_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
+{
+ if (region == NULL)
+ {
+ return;
+ }
+ if (region->upService != NULL)
+ {
+ struct _Qiniu_Region_Service *oldUpService = region->upService;
+ region->upService = _Qiniu_Region_Service_New(
+ hosts, hostsCount,
+ oldUpService->hosts + oldUpService->acceleratedHostsCount, oldUpService->preferredHostsCount,
+ oldUpService->hosts + oldUpService->acceleratedHostsCount + oldUpService->preferredHostsCount, oldUpService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldUpService);
+ }
+ else
+ {
+ region->upService = _Qiniu_Region_Service_New(hosts, hostsCount, NULL, 0, NULL, 0, useHttps);
+ }
+}
+
void Qiniu_Region_Set_Up_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
{
if (region == NULL)
{
return;
}
- if (region->upService == NULL)
+ if (region->upService != NULL)
{
- region->upService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldUpService = region->upService;
+ region->upService = _Qiniu_Region_Service_New(
+ oldUpService->hosts, oldUpService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldUpService->hosts + oldUpService->acceleratedHostsCount + oldUpService->preferredHostsCount, oldUpService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldUpService);
}
- if (region->upService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->upService->preferredHosts);
- region->upService->preferredHosts = NULL;
+ region->upService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->upService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Up_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -206,16 +233,20 @@ void Qiniu_Region_Set_Up_Alternative_Hosts(Qiniu_Region *region, const char *con
{
return;
}
- if (region->upService == NULL)
+ if (region->upService != NULL)
{
- region->upService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldUpService = region->upService;
+ region->upService = _Qiniu_Region_Service_New(
+ oldUpService->hosts, oldUpService->acceleratedHostsCount,
+ oldUpService->hosts + oldUpService->acceleratedHostsCount, oldUpService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldUpService);
}
- if (region->upService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->upService->alternativeHosts);
- region->upService->alternativeHosts = NULL;
+ region->upService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->upService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Io_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -224,16 +255,20 @@ void Qiniu_Region_Set_Io_Preferred_Hosts(Qiniu_Region *region, const char *const
{
return;
}
- if (region->ioService == NULL)
+ if (region->ioService != NULL)
{
- region->ioService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldIoService = region->ioService;
+ region->ioService = _Qiniu_Region_Service_New(
+ oldIoService->hosts, oldIoService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldIoService->hosts + oldIoService->acceleratedHostsCount + oldIoService->preferredHostsCount, oldIoService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldIoService);
}
- if (region->ioService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ioService->preferredHosts);
- region->ioService->preferredHosts = NULL;
+ region->ioService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->ioService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Io_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -242,16 +277,20 @@ void Qiniu_Region_Set_Io_Alternative_Hosts(Qiniu_Region *region, const char *con
{
return;
}
- if (region->ioService == NULL)
+ if (region->ioService != NULL)
{
- region->ioService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldIoService = region->ioService;
+ region->ioService = _Qiniu_Region_Service_New(
+ oldIoService->hosts, oldIoService->acceleratedHostsCount,
+ oldIoService->hosts + oldIoService->acceleratedHostsCount, oldIoService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldIoService);
}
- if (region->ioService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ioService->alternativeHosts);
- region->ioService->alternativeHosts = NULL;
+ region->ioService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->ioService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Io_Src_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -260,16 +299,20 @@ void Qiniu_Region_Set_Io_Src_Preferred_Hosts(Qiniu_Region *region, const char *c
{
return;
}
- if (region->ioSrcService == NULL)
+ if (region->ioSrcService != NULL)
{
- region->ioSrcService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldIoSrcService = region->ioSrcService;
+ region->ioSrcService = _Qiniu_Region_Service_New(
+ oldIoSrcService->hosts, oldIoSrcService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldIoSrcService->hosts + oldIoSrcService->acceleratedHostsCount + oldIoSrcService->preferredHostsCount, oldIoSrcService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldIoSrcService);
}
- if (region->ioSrcService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ioSrcService->preferredHosts);
- region->ioSrcService->preferredHosts = NULL;
+ region->ioSrcService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->ioSrcService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Io_Src_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -278,16 +321,20 @@ void Qiniu_Region_Set_Io_Src_Alternative_Hosts(Qiniu_Region *region, const char
{
return;
}
- if (region->ioSrcService == NULL)
+ if (region->ioSrcService != NULL)
{
- region->ioSrcService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldIoSrcService = region->ioSrcService;
+ region->ioSrcService = _Qiniu_Region_Service_New(
+ oldIoSrcService->hosts, oldIoSrcService->acceleratedHostsCount,
+ oldIoSrcService->hosts + oldIoSrcService->acceleratedHostsCount, oldIoSrcService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldIoSrcService);
}
- if (region->ioSrcService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ioSrcService->alternativeHosts);
- region->ioSrcService->alternativeHosts = NULL;
+ region->ioSrcService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->ioSrcService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Bucket_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -296,16 +343,20 @@ void Qiniu_Region_Set_Bucket_Preferred_Hosts(Qiniu_Region *region, const char *c
{
return;
}
- if (region->ucService == NULL)
+ if (region->ucService != NULL)
{
- region->ucService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldUcService = region->ucService;
+ region->ucService = _Qiniu_Region_Service_New(
+ oldUcService->hosts, oldUcService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldUcService->hosts + oldUcService->acceleratedHostsCount + oldUcService->preferredHostsCount, oldUcService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldUcService);
}
- if (region->ucService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ucService->preferredHosts);
- region->ucService->preferredHosts = NULL;
+ region->ucService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->ucService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Bucket_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -314,16 +365,20 @@ void Qiniu_Region_Set_Bucket_Alternative_Hosts(Qiniu_Region *region, const char
{
return;
}
- if (region->ucService == NULL)
+ if (region->ucService != NULL)
{
- region->ucService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldUcService = region->ucService;
+ region->ucService = _Qiniu_Region_Service_New(
+ oldUcService->hosts, oldUcService->acceleratedHostsCount,
+ oldUcService->hosts + oldUcService->acceleratedHostsCount, oldUcService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldUcService);
}
- if (region->ucService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->ucService->alternativeHosts);
- region->ucService->alternativeHosts = NULL;
+ region->ucService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->ucService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Rs_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -332,16 +387,20 @@ void Qiniu_Region_Set_Rs_Preferred_Hosts(Qiniu_Region *region, const char *const
{
return;
}
- if (region->rsService == NULL)
+ if (region->rsService != NULL)
{
- region->rsService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldRsService = region->rsService;
+ region->rsService = _Qiniu_Region_Service_New(
+ oldRsService->hosts, oldRsService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldRsService->hosts + oldRsService->acceleratedHostsCount + oldRsService->preferredHostsCount, oldRsService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldRsService);
}
- if (region->rsService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->rsService->preferredHosts);
- region->rsService->preferredHosts = NULL;
+ region->rsService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->rsService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Rs_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -350,16 +409,20 @@ void Qiniu_Region_Set_Rs_Alternative_Hosts(Qiniu_Region *region, const char *con
{
return;
}
- if (region->rsService == NULL)
+ if (region->rsService != NULL)
{
- region->rsService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldRsService = region->rsService;
+ region->rsService = _Qiniu_Region_Service_New(
+ oldRsService->hosts, oldRsService->acceleratedHostsCount,
+ oldRsService->hosts + oldRsService->acceleratedHostsCount, oldRsService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldRsService);
}
- if (region->rsService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->rsService->alternativeHosts);
- region->rsService->alternativeHosts = NULL;
+ region->rsService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->rsService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Rsf_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -368,16 +431,20 @@ void Qiniu_Region_Set_Rsf_Preferred_Hosts(Qiniu_Region *region, const char *cons
{
return;
}
- if (region->rsfService == NULL)
+ if (region->rsfService != NULL)
{
- region->rsfService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldRsfService = region->rsfService;
+ region->rsfService = _Qiniu_Region_Service_New(
+ oldRsfService->hosts, oldRsfService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldRsfService->hosts + oldRsfService->acceleratedHostsCount + oldRsfService->preferredHostsCount, oldRsfService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldRsfService);
}
- if (region->rsfService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->rsfService->preferredHosts);
- region->rsfService->preferredHosts = NULL;
+ region->rsfService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->rsfService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Rsf_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -386,16 +453,20 @@ void Qiniu_Region_Set_Rsf_Alternative_Hosts(Qiniu_Region *region, const char *co
{
return;
}
- if (region->rsfService == NULL)
+ if (region->rsfService != NULL)
{
- region->rsfService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldRsfService = region->rsfService;
+ region->rsfService = _Qiniu_Region_Service_New(
+ oldRsfService->hosts, oldRsfService->acceleratedHostsCount,
+ oldRsfService->hosts + oldRsfService->acceleratedHostsCount, oldRsfService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldRsfService);
}
- if (region->rsfService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->rsfService->alternativeHosts);
- region->rsfService->alternativeHosts = NULL;
+ region->rsfService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->rsfService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Api_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -404,16 +475,20 @@ void Qiniu_Region_Set_Api_Preferred_Hosts(Qiniu_Region *region, const char *cons
{
return;
}
- if (region->apiService == NULL)
+ if (region->apiService != NULL)
{
- region->apiService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldApiService = region->apiService;
+ region->apiService = _Qiniu_Region_Service_New(
+ oldApiService->hosts, oldApiService->acceleratedHostsCount,
+ hosts, hostsCount,
+ oldApiService->hosts + oldApiService->acceleratedHostsCount + oldApiService->preferredHostsCount, oldApiService->alternativeHostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldApiService);
}
- if (region->apiService->preferredHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->apiService->preferredHosts);
- region->apiService->preferredHosts = NULL;
+ region->apiService = _Qiniu_Region_Service_New(NULL, 0, hosts, hostsCount, NULL, 0, useHttps);
}
- region->apiService->preferredHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
void Qiniu_Region_Set_Api_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps)
@@ -422,19 +497,23 @@ void Qiniu_Region_Set_Api_Alternative_Hosts(Qiniu_Region *region, const char *co
{
return;
}
- if (region->apiService == NULL)
+ if (region->apiService != NULL)
{
- region->apiService = _Qiniu_Region_Service_New(NULL, NULL);
+ struct _Qiniu_Region_Service *oldApiService = region->apiService;
+ region->apiService = _Qiniu_Region_Service_New(
+ oldApiService->hosts, oldApiService->acceleratedHostsCount,
+ oldApiService->hosts + oldApiService->acceleratedHostsCount, oldApiService->preferredHostsCount,
+ hosts, hostsCount,
+ useHttps);
+ _Qiniu_Region_Service_Free(oldApiService);
}
- if (region->apiService->alternativeHosts != NULL)
+ else
{
- _Qiniu_Region_Hosts_Free(region->apiService->alternativeHosts);
- region->apiService->alternativeHosts = NULL;
+ region->apiService = _Qiniu_Region_Service_New(NULL, 0, NULL, 0, hosts, hostsCount, useHttps);
}
- region->apiService->alternativeHosts = _Qiniu_Region_Hosts_New(hosts, hostsCount, useHttps);
}
-const char *const *Qiniu_Region_Get_Up_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Up_Accelerated_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -448,18 +527,18 @@ const char *const *Qiniu_Region_Get_Up_Preferred_Hosts(Qiniu_Region *region, siz
{
return NULL;
}
- if (region->upService->preferredHosts == NULL)
+ if (region->upService->hosts == NULL || region->upService->acceleratedHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->upService->preferredHosts->hostsCount;
+ *count = region->upService->acceleratedHostsCount;
}
- return region->upService->preferredHosts->hosts;
+ return region->upService->hosts;
}
-const char *const *Qiniu_Region_Get_Up_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Up_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -473,18 +552,18 @@ const char *const *Qiniu_Region_Get_Up_Alternative_Hosts(Qiniu_Region *region, s
{
return NULL;
}
- if (region->upService->alternativeHosts == NULL)
+ if (region->upService->hosts == NULL || region->upService->preferredHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->upService->alternativeHosts->hostsCount;
+ *count = region->upService->preferredHostsCount;
}
- return region->upService->alternativeHosts->hosts;
+ return region->upService->hosts + region->upService->acceleratedHostsCount;
}
-const char *const *Qiniu_Region_Get_Io_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Up_Alternative_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -494,22 +573,22 @@ const char *const *Qiniu_Region_Get_Io_Preferred_Hosts(Qiniu_Region *region, siz
{
return NULL;
}
- if (region->ioService == NULL)
+ if (region->upService == NULL)
{
return NULL;
}
- if (region->ioService->preferredHosts == NULL)
+ if (region->upService->hosts == NULL || region->upService->alternativeHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ioService->preferredHosts->hostsCount;
+ *count = region->upService->alternativeHostsCount;
}
- return region->ioService->preferredHosts->hosts;
+ return region->upService->hosts + region->upService->acceleratedHostsCount + region->upService->preferredHostsCount;
}
-const char *const *Qiniu_Region_Get_Io_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *_Qiniu_Region_Get_All_Up_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
{
if (count != NULL)
{
@@ -519,22 +598,34 @@ const char *const *Qiniu_Region_Get_Io_Alternative_Hosts(Qiniu_Region *region, s
{
return NULL;
}
- if (region->ioService == NULL)
+ if (region->upService == NULL)
{
return NULL;
}
- if (region->ioService->alternativeHosts == NULL)
+ size_t allHostsCount = region->upService->preferredHostsCount + region->upService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->upService->acceleratedHostsCount;
+ }
+ if (region->upService->hosts == NULL || allHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ioService->alternativeHosts->hostsCount;
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->upService->hosts;
+ }
+ else
+ {
+ return region->upService->hosts + region->upService->acceleratedHostsCount;
}
- return region->ioService->alternativeHosts->hosts;
}
-const char *const *Qiniu_Region_Get_Io_Src_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Io_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -544,22 +635,22 @@ const char *const *Qiniu_Region_Get_Io_Src_Preferred_Hosts(Qiniu_Region *region,
{
return NULL;
}
- if (region->ioSrcService == NULL)
+ if (region->ioService == NULL)
{
return NULL;
}
- if (region->ioSrcService->preferredHosts == NULL)
+ if (region->ioService->hosts == NULL || region->ioService->preferredHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ioSrcService->preferredHosts->hostsCount;
+ *count = region->ioService->preferredHostsCount;
}
- return region->ioSrcService->preferredHosts->hosts;
+ return region->ioService->hosts + region->ioService->acceleratedHostsCount;
}
-const char *const *Qiniu_Region_Get_Io_Src_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Io_Alternative_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -569,22 +660,22 @@ const char *const *Qiniu_Region_Get_Io_Src_Alternative_Hosts(Qiniu_Region *regio
{
return NULL;
}
- if (region->ioSrcService == NULL)
+ if (region->ioService == NULL)
{
return NULL;
}
- if (region->ioSrcService->alternativeHosts == NULL)
+ if (region->ioService->hosts == NULL || region->ioService->alternativeHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ioSrcService->alternativeHosts->hostsCount;
+ *count = region->ioService->alternativeHostsCount;
}
- return region->ioSrcService->alternativeHosts->hosts;
+ return region->ioService->hosts + region->ioService->acceleratedHostsCount + region->ioService->preferredHostsCount;
}
-const char *const *Qiniu_Region_Get_Bucket_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *_Qiniu_Region_Get_All_Io_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
{
if (count != NULL)
{
@@ -594,22 +685,34 @@ const char *const *Qiniu_Region_Get_Bucket_Preferred_Hosts(Qiniu_Region *region,
{
return NULL;
}
- if (region->ucService == NULL)
+ if (region->ioService == NULL)
{
return NULL;
}
- if (region->ucService->preferredHosts == NULL)
+ size_t allHostsCount = region->ioService->preferredHostsCount + region->ioService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->ioService->acceleratedHostsCount;
+ }
+ if (region->ioService->hosts == NULL || allHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ucService->preferredHosts->hostsCount;
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->ioService->hosts;
+ }
+ else
+ {
+ return region->ioService->hosts + region->ioService->acceleratedHostsCount;
}
- return region->ucService->preferredHosts->hosts;
}
-const char *const *Qiniu_Region_Get_Bucket_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Io_Src_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -619,22 +722,22 @@ const char *const *Qiniu_Region_Get_Bucket_Alternative_Hosts(Qiniu_Region *regio
{
return NULL;
}
- if (region->ucService == NULL)
+ if (region->ioSrcService == NULL)
{
return NULL;
}
- if (region->ucService->alternativeHosts == NULL)
+ if (region->ioSrcService->hosts == NULL || region->ioSrcService->preferredHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->ucService->alternativeHosts->hostsCount;
+ *count = region->ioSrcService->preferredHostsCount;
}
- return region->ucService->alternativeHosts->hosts;
+ return region->ioSrcService->hosts + region->ioSrcService->acceleratedHostsCount;
}
-const char *const *Qiniu_Region_Get_Rs_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Io_Src_Alternative_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -644,22 +747,22 @@ const char *const *Qiniu_Region_Get_Rs_Preferred_Hosts(Qiniu_Region *region, siz
{
return NULL;
}
- if (region->rsService == NULL)
+ if (region->ioSrcService == NULL)
{
return NULL;
}
- if (region->rsService->preferredHosts == NULL)
+ if (region->ioSrcService->hosts == NULL || region->ioSrcService->alternativeHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->rsService->preferredHosts->hostsCount;
+ *count = region->ioSrcService->alternativeHostsCount;
}
- return region->rsService->preferredHosts->hosts;
+ return region->ioSrcService->hosts + region->ioSrcService->acceleratedHostsCount + region->ioSrcService->preferredHostsCount;
}
-const char *const *Qiniu_Region_Get_Rs_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *_Qiniu_Region_Get_All_Io_Src_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
{
if (count != NULL)
{
@@ -669,22 +772,34 @@ const char *const *Qiniu_Region_Get_Rs_Alternative_Hosts(Qiniu_Region *region, s
{
return NULL;
}
- if (region->rsService == NULL)
+ if (region->ioSrcService == NULL)
{
return NULL;
}
- if (region->rsService->alternativeHosts == NULL)
+ size_t allHostsCount = region->ioSrcService->preferredHostsCount + region->ioSrcService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->ioSrcService->acceleratedHostsCount;
+ }
+ if (region->ioSrcService->hosts == NULL || allHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->rsService->alternativeHosts->hostsCount;
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->ioSrcService->hosts;
+ }
+ else
+ {
+ return region->ioSrcService->hosts + region->ioSrcService->acceleratedHostsCount;
}
- return region->rsService->alternativeHosts->hosts;
}
-const char *const *Qiniu_Region_Get_Rsf_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Bucket_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -694,22 +809,22 @@ const char *const *Qiniu_Region_Get_Rsf_Preferred_Hosts(Qiniu_Region *region, si
{
return NULL;
}
- if (region->rsfService == NULL)
+ if (region->ucService == NULL)
{
return NULL;
}
- if (region->rsfService->preferredHosts == NULL)
+ if (region->ucService->hosts == NULL || region->ucService->preferredHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->rsfService->preferredHosts->hostsCount;
+ *count = region->ucService->preferredHostsCount;
}
- return region->rsfService->preferredHosts->hosts;
+ return region->ucService->hosts + region->ucService->acceleratedHostsCount;
}
-const char *const *Qiniu_Region_Get_Rsf_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Bucket_Alternative_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -719,22 +834,22 @@ const char *const *Qiniu_Region_Get_Rsf_Alternative_Hosts(Qiniu_Region *region,
{
return NULL;
}
- if (region->rsfService == NULL)
+ if (region->ucService == NULL)
{
return NULL;
}
- if (region->rsfService->alternativeHosts == NULL)
+ if (region->ucService->hosts == NULL || region->ucService->alternativeHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->rsfService->alternativeHosts->hostsCount;
+ *count = region->ucService->alternativeHostsCount;
}
- return region->rsfService->alternativeHosts->hosts;
+ return region->ucService->hosts + region->ucService->acceleratedHostsCount + region->ucService->preferredHostsCount;
}
-const char *const *Qiniu_Region_Get_Api_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *_Qiniu_Region_Get_All_Bucket_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
{
if (count != NULL)
{
@@ -744,22 +859,34 @@ const char *const *Qiniu_Region_Get_Api_Preferred_Hosts(Qiniu_Region *region, si
{
return NULL;
}
- if (region->apiService == NULL)
+ if (region->ucService == NULL)
{
return NULL;
}
- if (region->apiService->preferredHosts == NULL)
+ size_t allHostsCount = region->ucService->preferredHostsCount + region->ucService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->ucService->acceleratedHostsCount;
+ }
+ if (region->ucService->hosts == NULL || allHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->apiService->preferredHosts->hostsCount;
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->ucService->hosts;
+ }
+ else
+ {
+ return region->ucService->hosts + region->ucService->acceleratedHostsCount;
}
- return region->apiService->preferredHosts->hosts;
}
-const char *const *Qiniu_Region_Get_Api_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+const char *const *Qiniu_Region_Get_Rs_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
if (count != NULL)
{
@@ -769,153 +896,485 @@ const char *const *Qiniu_Region_Get_Api_Alternative_Hosts(Qiniu_Region *region,
{
return NULL;
}
- if (region->apiService == NULL)
+ if (region->rsService == NULL)
{
return NULL;
}
- if (region->apiService->alternativeHosts == NULL)
+ if (region->rsService->hosts == NULL || region->rsService->preferredHostsCount == 0)
{
return NULL;
}
if (count != NULL)
{
- *count = region->apiService->alternativeHosts->hostsCount;
+ *count = region->rsService->preferredHostsCount;
}
- return region->apiService->alternativeHosts->hosts;
+ return region->rsService->hosts + region->rsService->acceleratedHostsCount;
}
-Qiniu_Region *Qiniu_Use_Region(const char *const regionId, Qiniu_Bool useHttps)
+const char *const *Qiniu_Region_Get_Rs_Alternative_Hosts(Qiniu_Region *region, size_t *count)
{
- Qiniu_Region *region = Qiniu_Region_New(regionId, 0);
+ if (count != NULL)
+ {
+ *count = 0;
+ }
if (region == NULL)
{
return NULL;
}
-
- char buf[32], buf2[32];
- Qiniu_Zero(buf);
- Qiniu_Zero(buf2);
-
- if (strcmp(regionId, "z0") == 0)
+ if (region->rsService == NULL)
{
- Qiniu_Region_Set_Up_Preferred_Hosts(region, (const char *const[]){"upload.qiniup.com", "up.qiniup.com"}, 2, useHttps);
- Qiniu_Region_Set_Up_Alternative_Hosts(region, (const char *const[]){"up.qbox.me"}, 1, useHttps);
- Qiniu_Region_Set_Io_Preferred_Hosts(region, (const char *const[]){"iovip.qiniuio.com"}, 1, useHttps);
- Qiniu_Region_Set_Io_Alternative_Hosts(region, (const char *const[]){"iovip.qbox.me"}, 1, useHttps);
+ return NULL;
}
- else
+ if (region->rsService->hosts == NULL || region->rsService->alternativeHostsCount == 0)
{
- Qiniu_snprintf(buf, sizeof(buf), "upload-%s.qiniup.com", regionId);
- Qiniu_snprintf(buf2, sizeof(buf2), "up-%s.qiniup.com", regionId);
- Qiniu_Region_Set_Up_Preferred_Hosts(region, (const char *const[]){buf, buf2}, 2, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "up-%s.qbox.me", regionId);
- Qiniu_Region_Set_Up_Alternative_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "iovip-%s.qiniuio.com", regionId);
- Qiniu_Region_Set_Io_Preferred_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "iovip-%s.qbox.me", regionId);
- Qiniu_Region_Set_Io_Alternative_Hosts(region, (const char *const[]){buf}, 1, useHttps);
+ return NULL;
}
- Qiniu_snprintf(buf, sizeof(buf), "rs-%s.qiniuapi.com", regionId);
- Qiniu_Region_Set_Rs_Preferred_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "rs-%s.qbox.me", regionId);
- Qiniu_Region_Set_Rs_Alternative_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "rsf-%s.qiniuapi.com", regionId);
- Qiniu_Region_Set_Rsf_Preferred_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "rsf-%s.qbox.me", regionId);
- Qiniu_Region_Set_Rsf_Alternative_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "api-%s.qiniuapi.com", regionId);
- Qiniu_Region_Set_Api_Preferred_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- Qiniu_snprintf(buf, sizeof(buf), "api-%s.qbox.me", regionId);
- Qiniu_Region_Set_Api_Alternative_Hosts(region, (const char *const[]){buf}, 1, useHttps);
- return region;
-}
-
-static Qiniu_Bool isRegionQueryRetryable(Qiniu_Error err)
-{
- switch (err.code)
+ if (count != NULL)
{
- case 501:
- case 509:
- case 573:
- case 579:
- case 608:
- case 612:
- case 614:
- case 616:
- case 618:
- case 630:
- case 631:
- case 632:
- case 640:
- case 701:
- return Qiniu_False;
- default:
- return err.code >= 500 || err.code < 100;
+ *count = region->rsService->alternativeHostsCount;
}
+ return region->rsService->hosts + region->rsService->acceleratedHostsCount + region->rsService->preferredHostsCount;
}
-static Qiniu_Error _Qiniu_Region_Query_call(Qiniu_Client *self, const char *accessKey, const char *const bucketName, cJSON **ret)
+const char *const *_Qiniu_Region_Get_All_Rs_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
{
- char *url;
- Qiniu_Error err;
- Qiniu_Zero(err);
- const char *hosts[3] = {NULL, NULL, NULL};
- int hostsLen = 0;
-
- if (QINIU_UC_HOST != NULL)
+ if (count != NULL)
{
- hosts[hostsLen++] = QINIU_UC_HOST;
+ *count = 0;
}
- if (QINIU_UC_HOST_BACKUP != NULL)
+ if (region == NULL)
{
- hosts[hostsLen++] = QINIU_UC_HOST_BACKUP;
+ return NULL;
}
- if (QINIU_API_HOST != NULL)
+ if (region->rsService == NULL)
{
- hosts[hostsLen++] = QINIU_API_HOST;
+ return NULL;
}
-
- for (int i = 0; i < hostsLen; i++)
+ size_t allHostsCount = region->rsService->preferredHostsCount + region->rsService->alternativeHostsCount;
+ if (accelerationEnabled)
{
- url = Qiniu_String_Concat(hosts[i], "/v4/query?ak=", accessKey, "&bucket=", bucketName, NULL);
- err = Qiniu_Client_Call(self, ret, url);
- Qiniu_Free(url);
- if (err.code == 200)
- {
- cJSON *hostsJson = Qiniu_Json_GetObjectItem(*ret, "hosts", NULL);
- if (hostsJson == NULL)
- {
- continue;
- }
- cJSON *firstHostJson = Qiniu_Json_GetArrayItem(hostsJson, 0, NULL);
- if (firstHostJson == NULL)
- {
- continue;
- }
- return err;
- }
- else if (!isRegionQueryRetryable(err))
- {
- return err;
- }
+ allHostsCount += region->rsService->acceleratedHostsCount;
}
- if (err.code == 0)
+ if (region->rsService->hosts == NULL || allHostsCount == 0)
{
- err.code = 599;
- err.message = "no uc hosts available";
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->rsService->hosts;
+ }
+ else
+ {
+ return region->rsService->hosts + region->rsService->acceleratedHostsCount;
}
- return err;
}
-Qiniu_Error
-_Qiniu_Region_Query(Qiniu_Client *self, Qiniu_Region **pRegion, const char *accessKey, const char *const bucketName, Qiniu_Bool useHttps)
+const char *const *Qiniu_Region_Get_Rsf_Preferred_Hosts(Qiniu_Region *region, size_t *count)
{
- Qiniu_Error err;
- cJSON *root;
-
- if (accessKey == NULL)
+ if (count != NULL)
{
- accessKey = self->auth.itbl->GetAccessKey(self->auth.self);
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->rsfService == NULL)
+ {
+ return NULL;
+ }
+ if (region->rsfService->hosts == NULL || region->rsfService->preferredHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = region->rsfService->preferredHostsCount;
+ }
+ return region->rsfService->hosts + region->rsfService->acceleratedHostsCount;
+}
+
+const char *const *Qiniu_Region_Get_Rsf_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+{
+ if (count != NULL)
+ {
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->rsfService == NULL)
+ {
+ return NULL;
+ }
+ if (region->rsfService->hosts == NULL || region->rsfService->alternativeHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = region->rsfService->alternativeHostsCount;
+ }
+ return region->rsfService->hosts + region->rsfService->acceleratedHostsCount + region->rsfService->preferredHostsCount;
+}
+
+const char *const *_Qiniu_Region_Get_All_Rsf_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
+{
+ if (count != NULL)
+ {
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->rsfService == NULL)
+ {
+ return NULL;
+ }
+ size_t allHostsCount = region->rsfService->preferredHostsCount + region->rsfService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->rsfService->acceleratedHostsCount;
+ }
+ if (region->rsfService->hosts == NULL || allHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->rsfService->hosts;
+ }
+ else
+ {
+ return region->rsfService->hosts + region->rsfService->acceleratedHostsCount;
+ }
+}
+
+const char *const *Qiniu_Region_Get_Api_Preferred_Hosts(Qiniu_Region *region, size_t *count)
+{
+ if (count != NULL)
+ {
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->apiService == NULL)
+ {
+ return NULL;
+ }
+ if (region->apiService->hosts == NULL || region->apiService->preferredHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = region->apiService->preferredHostsCount;
+ }
+ return region->apiService->hosts + region->apiService->acceleratedHostsCount;
+}
+
+const char *const *Qiniu_Region_Get_Api_Alternative_Hosts(Qiniu_Region *region, size_t *count)
+{
+ if (count != NULL)
+ {
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->apiService == NULL)
+ {
+ return NULL;
+ }
+ if (region->apiService->hosts == NULL || region->apiService->alternativeHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = region->apiService->alternativeHostsCount;
+ }
+ return region->apiService->hosts + region->apiService->acceleratedHostsCount + region->apiService->preferredHostsCount;
+}
+
+const char *const *_Qiniu_Region_Get_All_Api_Hosts(Qiniu_Region *region, size_t *count, Qiniu_Bool accelerationEnabled)
+{
+ if (count != NULL)
+ {
+ *count = 0;
+ }
+ if (region == NULL)
+ {
+ return NULL;
+ }
+ if (region->apiService == NULL)
+ {
+ return NULL;
+ }
+ size_t allHostsCount = region->apiService->preferredHostsCount + region->apiService->alternativeHostsCount;
+ if (accelerationEnabled)
+ {
+ allHostsCount += region->apiService->acceleratedHostsCount;
+ }
+ if (region->apiService->hosts == NULL || allHostsCount == 0)
+ {
+ return NULL;
+ }
+ if (count != NULL)
+ {
+ *count = allHostsCount;
+ }
+ if (accelerationEnabled)
+ {
+ return region->apiService->hosts;
+ }
+ else
+ {
+ return region->apiService->hosts + region->apiService->acceleratedHostsCount;
+ }
+}
+
+Qiniu_Region *Qiniu_Use_Region(const char *const regionId, Qiniu_Bool useHttps)
+{
+ Qiniu_Region *region = Qiniu_Region_New(regionId, 0);
+ if (region == NULL)
+ {
+ return NULL;
+ }
+
+ char buf[32], buf2[32];
+ Qiniu_Zero(buf);
+ Qiniu_Zero(buf2);
+
+ if (strcmp(regionId, "z0") == 0)
+ {
+ region->upService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){"upload.qiniup.com", "up.qiniup.com"}, 2, (const char *const[]){"up.qbox.me"}, 1, useHttps);
+ region->ioService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){"iovip.qiniuio.com"}, 1, (const char *const[]){"iovip.qbox.me"}, 1, useHttps);
+ }
+ else
+ {
+ Qiniu_snprintf(buf, sizeof(buf), "upload-%s.qiniup.com", regionId);
+ Qiniu_snprintf(buf2, sizeof(buf2), "up-%s.qiniup.com", regionId);
+ region->upService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){buf, buf2}, 2, NULL, 0, useHttps);
+ Qiniu_snprintf(buf, sizeof(buf), "iovip-%s.qiniuio.com", regionId);
+ region->ioService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){buf}, 1, NULL, 0, useHttps);
+ }
+ Qiniu_snprintf(buf, sizeof(buf), "rs-%s.qiniuapi.com", regionId);
+ region->rsService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){buf}, 1, NULL, 0, useHttps);
+ Qiniu_snprintf(buf, sizeof(buf), "rsf-%s.qiniuapi.com", regionId);
+ region->rsfService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){buf}, 1, NULL, 0, useHttps);
+ Qiniu_snprintf(buf, sizeof(buf), "api-%s.qiniuapi.com", regionId);
+ region->apiService = _Qiniu_Region_Service_New(NULL, 0, (const char *const[]){buf}, 1, NULL, 0, useHttps);
+ return region;
+}
+
+static Qiniu_Bool isRegionQueryRetryable(Qiniu_Error err)
+{
+ switch (err.code)
+ {
+ case 501:
+ case 509:
+ case 573:
+ case 579:
+ case 608:
+ case 612:
+ case 614:
+ case 616:
+ case 618:
+ case 630:
+ case 631:
+ case 632:
+ case 640:
+ case 701:
+ return Qiniu_False;
+ default:
+ return err.code >= 500 || err.code < 100;
+ }
+}
+
+QINIU_DLLAPI extern const char *QINIU_UC_HOST_BACKUP_2;
+
+static const char **_Qiniu_Get_Bucket_Hosts(Qiniu_Client *self, size_t *count, Qiniu_Bool *needToFree)
+{
+ const char **hosts = NULL;
+ *count = 0;
+ *needToFree = Qiniu_False;
+
+ if (self->specifiedRegion == NULL)
+ {
+ hosts = (const char **)malloc(sizeof(const char *) * 3);
+ *needToFree = Qiniu_True;
+ if (QINIU_UC_HOST != NULL)
+ {
+ hosts[(*count)++] = QINIU_UC_HOST;
+ }
+ if (QINIU_UC_HOST_BACKUP != NULL)
+ {
+ hosts[(*count)++] = QINIU_UC_HOST_BACKUP;
+ }
+ if (QINIU_UC_HOST_BACKUP_2 != NULL)
+ {
+ hosts[(*count)++] = QINIU_UC_HOST_BACKUP_2;
+ }
+ }
+ else
+ {
+ hosts = (const char **)_Qiniu_Region_Get_All_Bucket_Hosts(self->specifiedRegion, count, Qiniu_False);
+ }
+ return hosts;
+}
+
+static Qiniu_Error _Qiniu_Region_Query_call(Qiniu_Client *self, const char *accessKey, const char *const bucketName, cJSON **ret)
+{
+ char *url;
+ Qiniu_Error err;
+ Qiniu_Zero(err);
+ const char *const *hosts;
+ size_t hostsLen;
+ Qiniu_Bool needToFreeHosts;
+
+ hosts = _Qiniu_Get_Bucket_Hosts(self, &hostsLen, &needToFreeHosts);
+
+ for (size_t i = 0; i < hostsLen; i++)
+ {
+ url = Qiniu_String_Concat(hosts[i], "/v4/query?ak=", accessKey, "&bucket=", bucketName, NULL);
+ err = Qiniu_Client_Call(self, ret, url);
+ Qiniu_Free(url);
+ if (err.code == 200)
+ {
+ cJSON *hostsJson = Qiniu_Json_GetObjectItem(*ret, "hosts", NULL);
+ if (hostsJson == NULL)
+ {
+ continue;
+ }
+ cJSON *firstHostJson = Qiniu_Json_GetArrayItem(hostsJson, 0, NULL);
+ if (firstHostJson == NULL)
+ {
+ continue;
+ }
+ goto handleErr;
+ }
+ else if (!isRegionQueryRetryable(err))
+ {
+ goto handleErr;
+ }
+ }
+ if (err.code == 0)
+ {
+ err.code = 599;
+ err.message = "no uc hosts available";
+ }
+handleErr:
+ if (needToFreeHosts)
+ {
+ Qiniu_Free((void *)hosts);
+ }
+ return err;
+}
+
+static struct _Qiniu_Region_Service *_Qiniu_Region_New_From_JSON(cJSON *json, Qiniu_Bool useHttps)
+{
+ cJSON *accDomainsJson = Qiniu_Json_GetObjectItem(json, "acc_domains", NULL);
+ cJSON *domainsJson = Qiniu_Json_GetObjectItem(json, "domains", NULL);
+ cJSON *oldJson = Qiniu_Json_GetObjectItem(json, "old", NULL);
+ size_t accDomainsCount = 0, domainsCount = 0, oldCount = 0;
+ if (accDomainsJson != NULL)
+ {
+ accDomainsCount = cJSON_GetArraySize(accDomainsJson);
+ }
+ if (domainsJson != NULL)
+ {
+ domainsCount = cJSON_GetArraySize(domainsJson);
+ }
+ if (oldJson != NULL)
+ {
+ oldCount = cJSON_GetArraySize(oldJson);
+ }
+
+ struct _Qiniu_Region_Service *newService = malloc(sizeof(struct _Qiniu_Region_Service));
+ if (newService == NULL)
+ {
+ goto handleErr;
+ }
+ const char **newHosts = malloc(sizeof(const char *) * (accDomainsCount + domainsCount + oldCount));
+ if (newHosts == NULL)
+ {
+ goto handleErr;
+ }
+ size_t offset = 0, i;
+ const char *h;
+ for (i = 0; i < accDomainsCount; i++)
+ {
+ h = _Qiniu_Duplicate_Host(Qiniu_Json_GetStringAt(accDomainsJson, i, NULL), useHttps);
+ if (h == NULL)
+ {
+ goto handleErr;
+ }
+ newHosts[offset + i] = h;
+ }
+ offset += accDomainsCount;
+ for (i = 0; i < domainsCount; i++)
+ {
+ h = _Qiniu_Duplicate_Host(Qiniu_Json_GetStringAt(domainsJson, i, NULL), useHttps);
+ if (h == NULL)
+ {
+ goto handleErr;
+ }
+ newHosts[offset + i] = h;
+ }
+ offset += domainsCount;
+ for (i = 0; i < oldCount; i++)
+ {
+ h = _Qiniu_Duplicate_Host(Qiniu_Json_GetStringAt(oldJson, i, NULL), useHttps);
+ if (h == NULL)
+ {
+ goto handleErr;
+ }
+ newHosts[offset + i] = h;
+ }
+ newService->hosts = newHosts;
+ newService->acceleratedHostsCount = accDomainsCount;
+ newService->preferredHostsCount = domainsCount;
+ newService->alternativeHostsCount = oldCount;
+ return newService;
+handleErr:
+ if (newHosts != NULL)
+ {
+ for (size_t j = 0; j < offset + i; j++)
+ {
+ free((void *)newHosts[j]);
+ }
+ free((void *)newHosts);
+ }
+ if (newService != NULL)
+ {
+ free((void *)newService);
+ }
+ return NULL;
+}
+
+Qiniu_Error
+_Qiniu_Region_Query(Qiniu_Client *self, Qiniu_Region **pRegion, const char *accessKey, const char *const bucketName, Qiniu_Bool useHttps)
+{
+ Qiniu_Error err;
+ cJSON *root;
+
+ if (accessKey == NULL)
+ {
+ accessKey = self->auth.itbl->GetAccessKey(self->auth.self);
}
err = _Qiniu_Region_Query_call(self, accessKey, bucketName, &root);
if (err.code == 200)
@@ -943,253 +1402,43 @@ _Qiniu_Region_Query(Qiniu_Client *self, Qiniu_Region **pRegion, const char *acce
cJSON *upJson = Qiniu_Json_GetObjectItem(firstHostJson, "up", NULL);
if (upJson != NULL)
{
- struct _Qiniu_Region_Hosts *upPreferredRegionHosts = NULL, *upAlternativeRegionHosts = NULL;
- cJSON *upDomainsJson = Qiniu_Json_GetObjectItem(upJson, "domains", NULL);
- if (upDomainsJson != NULL)
- {
- int upPreferredHostsCount = cJSON_GetArraySize(upDomainsJson);
- if (upPreferredHostsCount > 0)
- {
- const char **upPreferredHosts = (const char **)malloc(sizeof(const char *) * upPreferredHostsCount);
- for (int i = 0; i < upPreferredHostsCount; i++)
- {
- upPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(upDomainsJson, i, NULL), useHttps);
- }
- upPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(upPreferredHosts, upPreferredHostsCount);
- }
- }
- cJSON *upOldJson = Qiniu_Json_GetObjectItem(upJson, "old", NULL);
- if (upOldJson != NULL)
- {
- int upAlternativeHostsCount = cJSON_GetArraySize(upOldJson);
- if (upAlternativeHostsCount > 0)
- {
- const char **upAlternativeHosts = (const char **)malloc(sizeof(const char *) * upAlternativeHostsCount);
- for (int i = 0; i < upAlternativeHostsCount; i++)
- {
- upAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(upOldJson, i, NULL), useHttps);
- }
- upAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(upAlternativeHosts, upAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *upService = _Qiniu_Region_Service_New(upPreferredRegionHosts, upAlternativeRegionHosts);
- region->upService = upService;
+ region->upService = _Qiniu_Region_New_From_JSON(upJson, useHttps);
}
cJSON *ioJson = Qiniu_Json_GetObjectItem(firstHostJson, "io", NULL);
if (ioJson != NULL)
{
- struct _Qiniu_Region_Hosts *ioPreferredRegionHosts = NULL, *ioAlternativeRegionHosts = NULL;
- cJSON *ioDomainsJson = Qiniu_Json_GetObjectItem(ioJson, "domains", NULL);
- if (ioDomainsJson != NULL)
- {
- int ioPreferredHostsCount = cJSON_GetArraySize(ioDomainsJson);
- if (ioPreferredHostsCount > 0)
- {
- const char **ioPreferredHosts = (const char **)malloc(sizeof(const char *) * ioPreferredHostsCount);
- for (int i = 0; i < ioPreferredHostsCount; i++)
- {
- ioPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ioDomainsJson, i, NULL), useHttps);
- }
- ioPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ioPreferredHosts, ioPreferredHostsCount);
- }
- }
- cJSON *ioOldJson = Qiniu_Json_GetObjectItem(ioJson, "old", NULL);
- if (ioOldJson != NULL)
- {
- int ioAlternativeHostsCount = cJSON_GetArraySize(ioOldJson);
- if (ioAlternativeHostsCount > 0)
- {
- const char **ioAlternativeHosts = (const char **)malloc(sizeof(const char *) * ioAlternativeHostsCount);
- for (int i = 0; i < ioAlternativeHostsCount; i++)
- {
- ioAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ioOldJson, i, NULL), useHttps);
- }
- ioAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ioAlternativeHosts, ioAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *ioService = _Qiniu_Region_Service_New(ioPreferredRegionHosts, ioAlternativeRegionHosts);
- region->ioService = ioService;
+ region->ioService = _Qiniu_Region_New_From_JSON(ioJson, useHttps);
}
cJSON *ioSrcJson = Qiniu_Json_GetObjectItem(firstHostJson, "io_src", NULL);
if (ioSrcJson != NULL)
{
- struct _Qiniu_Region_Hosts *ioSrcPreferredRegionHosts = NULL, *ioSrcAlternativeRegionHosts = NULL;
- cJSON *ioSrcDomainsJson = Qiniu_Json_GetObjectItem(ioSrcJson, "domains", NULL);
- if (ioSrcDomainsJson != NULL)
- {
- int ioSrcPreferredHostsCount = cJSON_GetArraySize(ioSrcDomainsJson);
- if (ioSrcPreferredHostsCount > 0)
- {
- const char **ioSrcPreferredHosts = (const char **)malloc(sizeof(const char *) * ioSrcPreferredHostsCount);
- for (int i = 0; i < ioSrcPreferredHostsCount; i++)
- {
- ioSrcPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ioSrcDomainsJson, i, NULL), useHttps);
- }
- ioSrcPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ioSrcPreferredHosts, ioSrcPreferredHostsCount);
- }
- }
- cJSON *ioSrcOldJson = Qiniu_Json_GetObjectItem(ioSrcJson, "old", NULL);
- if (ioSrcOldJson != NULL)
- {
- int ioSrcAlternativeHostsCount = cJSON_GetArraySize(ioSrcOldJson);
- if (ioSrcAlternativeHostsCount > 0)
- {
- const char **ioSrcAlternativeHosts = (const char **)malloc(sizeof(const char *) * ioSrcAlternativeHostsCount);
- for (int i = 0; i < ioSrcAlternativeHostsCount; i++)
- {
- ioSrcAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ioSrcOldJson, i, NULL), useHttps);
- }
- ioSrcAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ioSrcAlternativeHosts, ioSrcAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *ioSrcService = _Qiniu_Region_Service_New(ioSrcPreferredRegionHosts, ioSrcAlternativeRegionHosts);
- region->ioSrcService = ioSrcService;
+ region->ioSrcService = _Qiniu_Region_New_From_JSON(ioSrcJson, useHttps);
}
cJSON *ucJson = Qiniu_Json_GetObjectItem(firstHostJson, "uc", NULL);
if (ucJson != NULL)
{
- struct _Qiniu_Region_Hosts *ucPreferredRegionHosts = NULL, *ucAlternativeRegionHosts = NULL;
- cJSON *ucDomainsJson = Qiniu_Json_GetObjectItem(ucJson, "domains", NULL);
- if (ucDomainsJson != NULL)
- {
- int ucPreferredHostsCount = cJSON_GetArraySize(ucDomainsJson);
- if (ucPreferredHostsCount > 0)
- {
- const char **ucPreferredHosts = (const char **)malloc(sizeof(const char *) * ucPreferredHostsCount);
- for (int i = 0; i < ucPreferredHostsCount; i++)
- {
- ucPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ucDomainsJson, i, NULL), useHttps);
- }
- ucPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ucPreferredHosts, ucPreferredHostsCount);
- }
- }
- cJSON *ucOldJson = Qiniu_Json_GetObjectItem(ucJson, "old", NULL);
- if (ucOldJson != NULL)
- {
- int ucAlternativeHostsCount = cJSON_GetArraySize(ucOldJson);
- if (ucAlternativeHostsCount > 0)
- {
- const char **ucAlternativeHosts = (const char **)malloc(sizeof(const char *) * ucAlternativeHostsCount);
- for (int i = 0; i < ucAlternativeHostsCount; i++)
- {
- ucAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(ucOldJson, i, NULL), useHttps);
- }
- ucAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(ucAlternativeHosts, ucAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *ucService = _Qiniu_Region_Service_New(ucPreferredRegionHosts, ucAlternativeRegionHosts);
- region->ucService = ucService;
+ region->ucService = _Qiniu_Region_New_From_JSON(ucJson, useHttps);
}
cJSON *rsJson = Qiniu_Json_GetObjectItem(firstHostJson, "rs", NULL);
if (rsJson != NULL)
{
- struct _Qiniu_Region_Hosts *rsPreferredRegionHosts = NULL, *rsAlternativeRegionHosts = NULL;
- cJSON *rsDomainsJson = Qiniu_Json_GetObjectItem(rsJson, "domains", NULL);
- if (rsDomainsJson != NULL)
- {
- int rsPreferredHostsCount = cJSON_GetArraySize(rsDomainsJson);
- if (rsPreferredHostsCount > 0)
- {
- const char **rsPreferredHosts = (const char **)malloc(sizeof(const char *) * rsPreferredHostsCount);
- for (int i = 0; i < rsPreferredHostsCount; i++)
- {
- rsPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(rsDomainsJson, i, NULL), useHttps);
- }
- rsPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(rsPreferredHosts, rsPreferredHostsCount);
- }
- }
- cJSON *rsOldJson = Qiniu_Json_GetObjectItem(rsJson, "old", NULL);
- if (rsOldJson != NULL)
- {
- int rsAlternativeHostsCount = cJSON_GetArraySize(rsOldJson);
- if (rsAlternativeHostsCount > 0)
- {
- const char **rsAlternativeHosts = (const char **)malloc(sizeof(const char *) * rsAlternativeHostsCount);
- for (int i = 0; i < rsAlternativeHostsCount; i++)
- {
- rsAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(rsOldJson, i, NULL), useHttps);
- }
- rsAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(rsAlternativeHosts, rsAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *rsService = _Qiniu_Region_Service_New(rsPreferredRegionHosts, rsAlternativeRegionHosts);
- region->rsService = rsService;
+ region->rsService = _Qiniu_Region_New_From_JSON(rsJson, useHttps);
}
cJSON *rsfJson = Qiniu_Json_GetObjectItem(firstHostJson, "rsf", NULL);
if (rsfJson != NULL)
{
- struct _Qiniu_Region_Hosts *rsfPreferredRegionHosts = NULL, *rsfAlternativeRegionHosts = NULL;
- cJSON *rsfDomainsJson = Qiniu_Json_GetObjectItem(rsfJson, "domains", NULL);
- if (rsfDomainsJson != NULL)
- {
- int rsfPreferredHostsCount = cJSON_GetArraySize(rsfDomainsJson);
- if (rsfPreferredHostsCount > 0)
- {
- const char **rsfPreferredHosts = (const char **)malloc(sizeof(const char *) * rsfPreferredHostsCount);
- for (int i = 0; i < rsfPreferredHostsCount; i++)
- {
- rsfPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(rsfDomainsJson, i, NULL), useHttps);
- }
- rsfPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(rsfPreferredHosts, rsfPreferredHostsCount);
- }
- }
- cJSON *rsfOldJson = Qiniu_Json_GetObjectItem(rsfJson, "old", NULL);
- if (rsfOldJson != NULL)
- {
- int rsfAlternativeHostsCount = cJSON_GetArraySize(rsfOldJson);
- if (rsfAlternativeHostsCount > 0)
- {
- const char **rsfAlternativeHosts = (const char **)malloc(sizeof(const char *) * rsfAlternativeHostsCount);
- for (int i = 0; i < rsfAlternativeHostsCount; i++)
- {
- rsfAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(rsfOldJson, i, NULL), useHttps);
- }
- rsfAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(rsfAlternativeHosts, rsfAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *rsfService = _Qiniu_Region_Service_New(rsfPreferredRegionHosts, rsfAlternativeRegionHosts);
- region->rsfService = rsfService;
+ region->rsfService = _Qiniu_Region_New_From_JSON(rsfJson, useHttps);
}
cJSON *apiJson = Qiniu_Json_GetObjectItem(firstHostJson, "api", NULL);
if (apiJson != NULL)
{
- struct _Qiniu_Region_Hosts *apiPreferredRegionHosts = NULL, *apiAlternativeRegionHosts = NULL;
- cJSON *apiDomainsJson = Qiniu_Json_GetObjectItem(apiJson, "domains", NULL);
- if (apiDomainsJson != NULL)
- {
- int apiPreferredHostsCount = cJSON_GetArraySize(apiDomainsJson);
- if (apiPreferredHostsCount > 0)
- {
- const char **apiPreferredHosts = (const char **)malloc(sizeof(const char *) * apiPreferredHostsCount);
- for (int i = 0; i < apiPreferredHostsCount; i++)
- {
- apiPreferredHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(apiDomainsJson, i, NULL), useHttps);
- }
- apiPreferredRegionHosts = _Qiniu_Region_Hosts_New_without_dup(apiPreferredHosts, apiPreferredHostsCount);
- }
- }
- cJSON *apiOldJson = Qiniu_Json_GetObjectItem(apiJson, "old", NULL);
- if (apiOldJson != NULL)
- {
- int apiAlternativeHostsCount = cJSON_GetArraySize(apiOldJson);
- if (apiAlternativeHostsCount > 0)
- {
- const char **apiAlternativeHosts = (const char **)malloc(sizeof(const char *) * apiAlternativeHostsCount);
- for (int i = 0; i < apiAlternativeHostsCount; i++)
- {
- apiAlternativeHosts[i] = duplicate_host(Qiniu_Json_GetStringAt(apiOldJson, i, NULL), useHttps);
- }
- apiAlternativeRegionHosts = _Qiniu_Region_Hosts_New_without_dup(apiAlternativeHosts, apiAlternativeHostsCount);
- }
- }
- struct _Qiniu_Region_Service *apiService = _Qiniu_Region_Service_New(apiPreferredRegionHosts, apiAlternativeRegionHosts);
- region->apiService = apiService;
+ region->apiService = _Qiniu_Region_New_From_JSON(apiJson, useHttps);
}
*pRegion = region;
@@ -1204,99 +1453,237 @@ Qiniu_Error Qiniu_Region_Query(Qiniu_Client *self, Qiniu_Region **pRegion, const
return _Qiniu_Region_Query(self, pRegion, NULL, bucketName, useHttps);
}
-Qiniu_Error _Qiniu_Region_Get_Up_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+struct _Qiniu_Region_Cache
{
- const char *const *hosts;
- size_t count;
- Qiniu_Error err = Qiniu_OK;
- Qiniu_Region **foundRegion = &self->cachedRegion;
+ Qiniu_Region *region;
+ const char *accessKey, *bucketName, *const *bucketHosts;
+ size_t bucketHostsLen;
+ Qiniu_Bool needToFreeBucketHosts, uploadingAccelerationEnabled;
+};
- if (self->specifiedRegion)
+static int _Qiniu_Compare_Str(const char *a, const char *b)
+{
+ if (a == NULL && b == NULL)
{
- foundRegion = &self->specifiedRegion;
- goto foundCache;
+ return 0;
+ }
+ else if (a == NULL)
+ {
+ return -1;
+ }
+ else if (b == NULL)
+ {
+ return 1;
+ }
+ else
+ {
+ return strcmp(a, b);
+ }
+}
+
+static int _Qiniu_Region_Cache_Compare(const void *a, const void *b, void *user_data)
+{
+ int result;
+ struct _Qiniu_Region_Cache *cacheA = (struct _Qiniu_Region_Cache *)a;
+ struct _Qiniu_Region_Cache *cacheB = (struct _Qiniu_Region_Cache *)b;
+ result = _Qiniu_Compare_Str(cacheA->accessKey, cacheB->accessKey);
+ if (!result)
+ {
+ return result;
}
- else if (self->autoQueryRegion)
+ result = _Qiniu_Compare_Str(cacheA->bucketName, cacheB->bucketName);
+ if (!result)
{
- if (self->cachedRegion != NULL)
+ return result;
+ }
+ if (cacheA->bucketHostsLen == cacheB->bucketHostsLen)
+ {
+ for (size_t i = 0; i < cacheA->bucketHostsLen; i++)
{
- if (strcmp(self->cachedRegionBucketName, bucketName) == 0)
- {
- goto foundCache;
- }
- else
+ result = _Qiniu_Compare_Str(cacheA->bucketHosts[i], cacheB->bucketHosts[i]);
+ if (!result)
{
- Qiniu_Region_Free(self->cachedRegion);
- self->cachedRegion = NULL;
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
+ return result;
}
}
- err = _Qiniu_Region_Query(self, &self->cachedRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
- if (err.code != 200)
- {
- goto useDefault;
- }
- self->cachedRegionBucketName = Qiniu_String_Dup(bucketName);
- goto foundCache;
+ return result;
}
-useDefault:
- *host = QINIU_UP_HOST;
- return err;
+ else
+ {
+ return cacheA->bucketHostsLen - cacheB->bucketHostsLen;
+ }
+}
-foundCache:
- hosts = Qiniu_Region_Get_Up_Preferred_Hosts(*foundRegion, &count);
- if (count == 0)
+static void _Qiniu_Region_Cache_Free(void *r)
+{
+ struct _Qiniu_Region_Cache *cache = (struct _Qiniu_Region_Cache *)r;
+ Qiniu_Free((void *)cache->accessKey);
+ Qiniu_Free((void *)cache->bucketName);
+ if (cache->needToFreeBucketHosts)
{
- *host = QINIU_UP_HOST;
+ Qiniu_Free((void *)cache->bucketHosts);
}
- else
+ Qiniu_Region_Free(cache->region);
+ Qiniu_Zero_Ptr(cache);
+}
+
+static uint64_t _Qiniu_Region_Cache_Hash(const void *r, uint64_t seed0, uint64_t seed1)
+{
+ struct _Qiniu_Region_Cache *cache = (struct _Qiniu_Region_Cache *)r;
+ uint64_t hash = 0;
+ if (cache->accessKey != NULL)
{
- *host = hosts[0];
+ hash ^= hashmap_sip(cache->accessKey, strlen(cache->accessKey), seed0, seed1);
}
- return err;
+ if (cache->bucketName != NULL)
+ {
+ hash ^= hashmap_sip(cache->bucketName, strlen(cache->bucketName), seed0, seed1);
+ }
+ hash ^= hashmap_sip(&cache->bucketHostsLen, sizeof(cache->bucketHostsLen), seed0, seed1);
+ for (size_t i = 0; i < cache->bucketHostsLen; i++)
+ {
+ hash ^= hashmap_sip(cache->bucketHosts[i], strlen(cache->bucketHosts[i]), seed0, seed1);
+ }
+ hash ^= hashmap_sip(&cache->uploadingAccelerationEnabled, sizeof(cache->uploadingAccelerationEnabled), seed0, seed1);
+ return hash;
}
-Qiniu_Error _Qiniu_Region_Get_Io_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+static Qiniu_Error _Qiniu_Region_Auto_Query_With_Cache(Qiniu_Client *self, const char *accessKey, const char *bucketName, Qiniu_Region **foundRegion)
{
const char *const *hosts;
- size_t count;
+ size_t hostsLen;
+ Qiniu_Bool needToFreeHosts;
Qiniu_Error err = Qiniu_OK;
- Qiniu_Region **foundRegion = &self->cachedRegion;
- if (self->specifiedRegion)
+ if (accessKey == NULL)
{
- foundRegion = &self->specifiedRegion;
- goto foundCache;
+ accessKey = QINIU_ACCESS_KEY;
}
- else if (self->autoQueryRegion)
+
+ hosts = _Qiniu_Get_Bucket_Hosts(self, &hostsLen, &needToFreeHosts);
+ const struct _Qiniu_Region_Cache cacheKey = {
+ .accessKey = accessKey,
+ .bucketName = bucketName,
+ .bucketHosts = hosts,
+ .bucketHostsLen = hostsLen,
+ .needToFreeBucketHosts = needToFreeHosts,
+ .uploadingAccelerationEnabled = self->enableUploadingAcceleration,
+ };
+ struct _Qiniu_Region_Cache *cache = NULL;
+ if (self->cachedRegions != NULL)
{
- if (self->cachedRegion != NULL)
+ cache = (struct _Qiniu_Region_Cache *)hashmap_get(self->cachedRegions, &cacheKey);
+ if (cache != NULL && !Qiniu_Region_Is_Expired(cache->region))
{
- if (strcmp(self->cachedRegionBucketName, bucketName) == 0 && !Qiniu_Region_Is_Expired(self->cachedRegion))
- {
- goto foundCache;
- }
- else
+ *foundRegion = cache->region;
+ err = Qiniu_OK;
+ goto handleErr;
+ }
+ }
+ err = _Qiniu_Region_Query(self, foundRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
+ if (err.code != 200)
+ {
+ if (cache != NULL)
+ { // 有已经过期的缓存区域可以使用
+ *foundRegion = cache->region;
+ err = Qiniu_OK;
+ }
+ goto handleErr;
+ }
+ if (self->cachedRegions == NULL)
+ {
+ self->cachedRegions = hashmap_new(sizeof(struct _Qiniu_Region_Cache), 0, rand(), rand(), _Qiniu_Region_Cache_Hash, _Qiniu_Region_Cache_Compare, _Qiniu_Region_Cache_Free, NULL);
+ }
+ if (self->cachedRegions != NULL)
+ {
+ if (cache != NULL)
+ { // 复用前面已经过期的缓存的内存
+ Qiniu_Region_Free(cache->region);
+ cache->region = *foundRegion;
+ }
+ else
+ {
+ struct _Qiniu_Region_Cache *newCache = malloc(sizeof(struct _Qiniu_Region_Cache));
+ if (newCache != NULL)
{
- Qiniu_Region_Free(self->cachedRegion);
- self->cachedRegion = NULL;
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
+ *newCache = (struct _Qiniu_Region_Cache){
+ .region = *foundRegion,
+ .accessKey = Qiniu_String_Dup(accessKey),
+ .bucketName = Qiniu_String_Dup(bucketName),
+ .bucketHosts = hosts,
+ .bucketHostsLen = hostsLen,
+ .needToFreeBucketHosts = needToFreeHosts,
+ };
+ hashmap_set(self->cachedRegions, newCache);
+ needToFreeHosts = Qiniu_False;
}
}
- err = _Qiniu_Region_Query(self, &self->cachedRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
- if (err.code != 200)
+ }
+handleErr:
+ if (needToFreeHosts)
+ {
+ Qiniu_Free((void *)hosts);
+ }
+ return err;
+}
+
+Qiniu_Error _Qiniu_Region_Get_Up_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count)
+{
+ Qiniu_Error err = Qiniu_OK;
+ Qiniu_Region *foundRegion = NULL;
+
+ if (self->specifiedRegion)
+ {
+ foundRegion = self->specifiedRegion;
+ goto foundCache;
+ }
+ else if (self->autoQueryRegion && bucketName != NULL)
+ {
+ err = _Qiniu_Region_Auto_Query_With_Cache(self, accessKey, bucketName, &foundRegion);
+ if (err.code == 200)
{
- goto useDefault;
+ goto foundCache;
}
- self->cachedRegionBucketName = Qiniu_String_Dup(bucketName);
- goto foundCache;
+ return err;
}
-useDefault:
- *host = QINIU_IOVIP_HOST;
+foundCache:
+ *hosts = _Qiniu_Region_Get_All_Up_Hosts(foundRegion, count, self->enableUploadingAcceleration);
return err;
+}
+Qiniu_Error _Qiniu_Region_Get_Io_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count)
+{
+ Qiniu_Error err = Qiniu_OK;
+ Qiniu_Region *foundRegion = NULL;
+
+ if (self->specifiedRegion)
+ {
+ foundRegion = self->specifiedRegion;
+ goto foundCache;
+ }
+ else if (self->autoQueryRegion && bucketName != NULL)
+ {
+ err = _Qiniu_Region_Auto_Query_With_Cache(self, accessKey, bucketName, &foundRegion);
+ if (err.code == 200)
+ {
+ goto foundCache;
+ }
+ return err;
+ }
foundCache:
- hosts = Qiniu_Region_Get_Io_Preferred_Hosts(*foundRegion, &count);
+ *hosts = _Qiniu_Region_Get_All_Io_Hosts(foundRegion, count, Qiniu_False);
+ return err;
+}
+
+Qiniu_Error _Qiniu_Region_Get_Io_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+{
+ const char *const *hosts;
+ size_t count;
+ Qiniu_Error err = _Qiniu_Region_Get_Io_Hosts(self, accessKey, bucketName, &hosts, &count);
+ if (err.code != 200)
+ {
+ return err;
+ }
if (count == 0)
{
*host = QINIU_IOVIP_HOST;
@@ -1308,47 +1695,39 @@ Qiniu_Error _Qiniu_Region_Get_Io_Host(Qiniu_Client *self, const char *accessKey,
return err;
}
-Qiniu_Error _Qiniu_Region_Get_Rs_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+Qiniu_Error _Qiniu_Region_Get_Rs_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count)
{
- const char *const *hosts;
- size_t count;
Qiniu_Error err = Qiniu_OK;
- Qiniu_Region **foundRegion = &self->cachedRegion;
+ Qiniu_Region *foundRegion = NULL;
if (self->specifiedRegion)
{
- foundRegion = &self->specifiedRegion;
+ foundRegion = self->specifiedRegion;
goto foundCache;
}
- else if (self->autoQueryRegion)
+ else if (self->autoQueryRegion && bucketName != NULL)
{
- if (self->cachedRegion != NULL)
- {
- if (strcmp(self->cachedRegionBucketName, bucketName) == 0)
- {
- goto foundCache;
- }
- else
- {
- Qiniu_Region_Free(self->cachedRegion);
- self->cachedRegion = NULL;
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
- }
- }
- err = _Qiniu_Region_Query(self, &self->cachedRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
- if (err.code != 200)
+ err = _Qiniu_Region_Auto_Query_With_Cache(self, accessKey, bucketName, &foundRegion);
+ if (err.code == 200)
{
- goto useDefault;
+ goto foundCache;
}
- self->cachedRegionBucketName = Qiniu_String_Dup(bucketName);
- goto foundCache;
+ return err;
}
-useDefault:
- *host = QINIU_RS_HOST;
+foundCache:
+ *hosts = _Qiniu_Region_Get_All_Rs_Hosts(foundRegion, count, Qiniu_False);
return err;
+}
-foundCache:
- hosts = Qiniu_Region_Get_Rs_Preferred_Hosts(*foundRegion, &count);
+Qiniu_Error _Qiniu_Region_Get_Rs_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+{
+ const char *const *hosts;
+ size_t count;
+ Qiniu_Error err = _Qiniu_Region_Get_Rs_Hosts(self, accessKey, bucketName, &hosts, &count);
+ if (err.code != 200)
+ {
+ return err;
+ }
if (count == 0)
{
*host = QINIU_RS_HOST;
@@ -1360,47 +1739,39 @@ Qiniu_Error _Qiniu_Region_Get_Rs_Host(Qiniu_Client *self, const char *accessKey,
return err;
}
-Qiniu_Error _Qiniu_Region_Get_Rsf_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+Qiniu_Error _Qiniu_Region_Get_Rsf_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count)
{
- const char *const *hosts;
- size_t count;
Qiniu_Error err = Qiniu_OK;
- Qiniu_Region **foundRegion = &self->cachedRegion;
+ Qiniu_Region *foundRegion = NULL;
if (self->specifiedRegion)
{
- foundRegion = &self->specifiedRegion;
+ foundRegion = self->specifiedRegion;
goto foundCache;
}
- else if (self->autoQueryRegion)
+ else if (self->autoQueryRegion && bucketName != NULL)
{
- if (self->cachedRegion != NULL)
- {
- if (strcmp(self->cachedRegionBucketName, bucketName) == 0)
- {
- goto foundCache;
- }
- else
- {
- Qiniu_Region_Free(self->cachedRegion);
- self->cachedRegion = NULL;
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
- }
- }
- err = _Qiniu_Region_Query(self, &self->cachedRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
- if (err.code != 200)
+ err = _Qiniu_Region_Auto_Query_With_Cache(self, accessKey, bucketName, &foundRegion);
+ if (err.code == 200)
{
- goto useDefault;
+ goto foundCache;
}
- self->cachedRegionBucketName = Qiniu_String_Dup(bucketName);
- goto foundCache;
+ return err;
}
-useDefault:
- *host = QINIU_RSF_HOST;
+foundCache:
+ *hosts = _Qiniu_Region_Get_All_Rsf_Hosts(foundRegion, count, Qiniu_False);
return err;
+}
-foundCache:
- hosts = Qiniu_Region_Get_Rsf_Preferred_Hosts(*foundRegion, &count);
+Qiniu_Error _Qiniu_Region_Get_Rsf_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+{
+ const char *const *hosts;
+ size_t count;
+ Qiniu_Error err = _Qiniu_Region_Get_Rsf_Hosts(self, accessKey, bucketName, &hosts, &count);
+ if (err.code != 200)
+ {
+ return err;
+ }
if (count == 0)
{
*host = QINIU_RSF_HOST;
@@ -1412,47 +1783,39 @@ Qiniu_Error _Qiniu_Region_Get_Rsf_Host(Qiniu_Client *self, const char *accessKey
return err;
}
-Qiniu_Error _Qiniu_Region_Get_Api_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+Qiniu_Error _Qiniu_Region_Get_Api_Hosts(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char *const **hosts, size_t *count)
{
- const char *const *hosts;
- size_t count;
Qiniu_Error err = Qiniu_OK;
- Qiniu_Region **foundRegion = &self->cachedRegion;
+ Qiniu_Region *foundRegion = NULL;
if (self->specifiedRegion)
{
- foundRegion = &self->specifiedRegion;
+ foundRegion = self->specifiedRegion;
goto foundCache;
}
- else if (self->autoQueryRegion)
+ else if (self->autoQueryRegion && bucketName != NULL)
{
- if (self->cachedRegion != NULL)
- {
- if (strcmp(self->cachedRegionBucketName, bucketName) == 0)
- {
- goto foundCache;
- }
- else
- {
- Qiniu_Region_Free(self->cachedRegion);
- self->cachedRegion = NULL;
- Qiniu_FreeV2((void **)&self->cachedRegionBucketName);
- }
- }
- err = _Qiniu_Region_Query(self, &self->cachedRegion, accessKey, bucketName, self->autoQueryHttpsRegion);
- if (err.code != 200)
+ err = _Qiniu_Region_Auto_Query_With_Cache(self, accessKey, bucketName, &foundRegion);
+ if (err.code == 200)
{
- goto useDefault;
+ goto foundCache;
}
- self->cachedRegionBucketName = Qiniu_String_Dup(bucketName);
- goto foundCache;
+ return err;
}
-useDefault:
- *host = QINIU_API_HOST;
+foundCache:
+ *hosts = _Qiniu_Region_Get_All_Api_Hosts(foundRegion, count, Qiniu_False);
return err;
+}
-foundCache:
- hosts = Qiniu_Region_Get_Api_Preferred_Hosts(*foundRegion, &count);
+Qiniu_Error _Qiniu_Region_Get_Api_Host(Qiniu_Client *self, const char *accessKey, const char *bucketName, const char **host)
+{
+ const char *const *hosts;
+ size_t count;
+ Qiniu_Error err = _Qiniu_Region_Get_Api_Hosts(self, accessKey, bucketName, &hosts, &count);
+ if (err.code != 200)
+ {
+ return err;
+ }
if (count == 0)
{
*host = QINIU_API_HOST;
diff --git a/qiniu/region.h b/qiniu/region.h
index 9d3d0194..b9d54b23 100644
--- a/qiniu/region.h
+++ b/qiniu/region.h
@@ -11,19 +11,18 @@
#define QINIU_REGION_H
#include "http.h"
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
#ifdef __cplusplus
extern "C"
{
#endif
- struct _Qiniu_Client;
- typedef struct _Qiniu_Client Qiniu_Client;
-
- struct _Qiniu_Region;
- typedef struct _Qiniu_Region Qiniu_Region;
-
QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Up_Preferred_Hosts(Qiniu_Region *region, size_t *count);
QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Up_Alternative_Hosts(Qiniu_Region *region, size_t *count);
+ QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Up_Accelerated_Hosts(Qiniu_Region *region, size_t *count);
QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Io_Preferred_Hosts(Qiniu_Region *region, size_t *count);
QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Io_Alternative_Hosts(Qiniu_Region *region, size_t *count);
QINIU_DLLAPI extern const char *const *Qiniu_Region_Get_Io_Src_Preferred_Hosts(Qiniu_Region *region, size_t *count);
@@ -44,6 +43,7 @@ extern "C"
QINIU_DLLAPI extern void Qiniu_Region_Set_Up_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
QINIU_DLLAPI extern void Qiniu_Region_Set_Up_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
+ QINIU_DLLAPI extern void Qiniu_Region_Set_Up_Accelerated_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
QINIU_DLLAPI extern void Qiniu_Region_Set_Io_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
QINIU_DLLAPI extern void Qiniu_Region_Set_Io_Alternative_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
QINIU_DLLAPI extern void Qiniu_Region_Set_Io_Src_Preferred_Hosts(Qiniu_Region *region, const char *const *hosts, size_t hostsCount, Qiniu_Bool useHttps);
@@ -60,6 +60,10 @@ extern "C"
QINIU_DLLAPI extern Qiniu_Region *Qiniu_Use_Region(const char *const regionId, Qiniu_Bool useHttps);
QINIU_DLLAPI extern Qiniu_Error Qiniu_Region_Query(Qiniu_Client *self, Qiniu_Region **region, const char *const bucketName, Qiniu_Bool useHttps);
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/qiniu/resumable_io.c b/qiniu/resumable_io.c
index e916c458..11315547 100644
--- a/qiniu/resumable_io.c
+++ b/qiniu/resumable_io.c
@@ -16,7 +16,9 @@
#include "recorder_key.h"
#include "recorder_utils.h"
#include "../cJSON/cJSON.h"
+#include "../hashmap/hashmap.h"
#include "private/region.h"
+#include "private/code.h"
#define blockBits 22
#define blockMask ((1 << blockBits) - 1)
@@ -277,7 +279,7 @@ static Qiniu_Error Qiniu_Rio_PutExtra_Init(
}
else
{
- memset(self, 0, sizeof(Qiniu_Rio_PutExtra));
+ Qiniu_Zero_Ptr(self);
}
cbprog = sizeof(Qiniu_Rio_BlkputRet) * blockCnt;
@@ -349,19 +351,133 @@ static void Qiniu_Io_PutExtra_initFrom(Qiniu_Io_PutExtra *self, Qiniu_Rio_PutExt
}
else
{
- memset(self, 0, sizeof(*self));
+ Qiniu_Zero_Ptr(self);
}
}
/*============================================================================*/
-static Qiniu_Error Qiniu_Rio_bput(
- Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, Qiniu_Reader body, int bodyLength, const char *url)
+struct _Qiniu_Uploading_Parts_Progress
+{
+ size_t uploaded;
+ struct hashmap *uploading;
+ Qiniu_Mutex mutex;
+};
+
+struct _Qiniu_Uploading_Parts_Progress_Pair
+{
+ int blkIdx;
+ size_t uploaded;
+};
+
+static uint64_t _Qiniu_Uploading_Parts_Progress_Hash(const void *item, uint64_t seed0, uint64_t seed1)
+{
+ const struct _Qiniu_Uploading_Parts_Progress_Pair *pair = (const struct _Qiniu_Uploading_Parts_Progress_Pair *)item;
+ return hashmap_sip(&pair->blkIdx, sizeof(pair->blkIdx), seed0, seed1);
+}
+
+static int _Qiniu_Uploading_Parts_Progress_Compare(const void *a, const void *b, void *udata)
+{
+ const struct _Qiniu_Uploading_Parts_Progress_Pair *left = (const struct _Qiniu_Uploading_Parts_Progress_Pair *)a;
+ const struct _Qiniu_Uploading_Parts_Progress_Pair *right = (const struct _Qiniu_Uploading_Parts_Progress_Pair *)b;
+ return left->blkIdx - right->blkIdx;
+}
+
+static struct _Qiniu_Uploading_Parts_Progress *_Qiniu_Uploading_Parts_Progress_New()
+{
+ struct _Qiniu_Uploading_Parts_Progress *data = (struct _Qiniu_Uploading_Parts_Progress *)malloc(sizeof(struct _Qiniu_Uploading_Parts_Progress));
+ Qiniu_Zero_Ptr(data);
+ Qiniu_Mutex_Init(&data->mutex);
+ data->uploading = hashmap_new(
+ sizeof(struct _Qiniu_Uploading_Parts_Progress_Pair), 0, rand(), rand(),
+ _Qiniu_Uploading_Parts_Progress_Hash, _Qiniu_Uploading_Parts_Progress_Compare,
+ free, NULL);
+ return data;
+}
+
+static void _Qiniu_Uploading_Parts_Progress_Free(struct _Qiniu_Uploading_Parts_Progress *data)
+{
+ hashmap_free(data->uploading);
+ Qiniu_Mutex_Cleanup(&data->mutex);
+ free((void *)data);
+}
+
+static void _Qiniu_Uploading_Parts_Progress_Set_Progress(struct _Qiniu_Uploading_Parts_Progress *progress, int blkIdx, size_t uploaded)
+{
+ Qiniu_Mutex_Lock(&progress->mutex);
+ const struct _Qiniu_Uploading_Parts_Progress_Pair pair = {.blkIdx = blkIdx};
+ struct _Qiniu_Uploading_Parts_Progress_Pair *pPair = (struct _Qiniu_Uploading_Parts_Progress_Pair *)hashmap_get(progress->uploading, &pair);
+ if (pPair == NULL)
+ {
+ pPair = (struct _Qiniu_Uploading_Parts_Progress_Pair *)malloc(sizeof(struct _Qiniu_Uploading_Parts_Progress_Pair));
+ pPair->blkIdx = blkIdx;
+ pPair->uploaded = uploaded;
+ hashmap_set(progress->uploading, pPair);
+ }
+ else
+ {
+ pPair->uploaded = uploaded;
+ }
+
+ Qiniu_Mutex_Unlock(&progress->mutex);
+}
+
+static void _Qiniu_Uploading_Parts_Progress_Part_Uploaded(struct _Qiniu_Uploading_Parts_Progress *progress, int blkIdx, size_t partSize)
+{
+ Qiniu_Mutex_Lock(&progress->mutex);
+ const struct _Qiniu_Uploading_Parts_Progress_Pair pair = {.blkIdx = blkIdx};
+ hashmap_delete(progress->uploading, (const void *)&pair);
+ progress->uploaded += partSize;
+ Qiniu_Mutex_Unlock(&progress->mutex);
+}
+
+static size_t _Qiniu_Uploading_Parts_Progress_Get_Total_Size(struct _Qiniu_Uploading_Parts_Progress *progress)
+{
+ Qiniu_Mutex_Lock(&progress->mutex);
+ size_t total = progress->uploaded, iter = 0;
+ void *iter_val;
+ while (hashmap_iter(progress->uploading, &iter, &iter_val))
+ {
+ struct _Qiniu_Uploading_Parts_Progress_Pair *pair = (struct _Qiniu_Uploading_Parts_Progress_Pair *)iter_val;
+ total += pair->uploaded;
+ }
+ Qiniu_Mutex_Unlock(&progress->mutex);
+ return total;
+}
+
+struct _Qiniu_Progress_Callback_Data
+{
+ size_t totalSize, previousUlNow;
+ int blkIdx;
+ struct _Qiniu_Uploading_Parts_Progress *progress;
+ void (*callback)(size_t, size_t);
+};
+
+static int _Qiniu_Progress_Callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ struct _Qiniu_Progress_Callback_Data *data = (struct _Qiniu_Progress_Callback_Data *)clientp;
+ if (data->previousUlNow != (size_t)ulnow)
+ {
+ _Qiniu_Uploading_Parts_Progress_Set_Progress(data->progress, data->blkIdx, (size_t)ulnow);
+ data->callback((size_t)data->totalSize, _Qiniu_Uploading_Parts_Progress_Get_Total_Size(data->progress));
+ data->previousUlNow = (size_t)ulnow;
+ }
+ return 0;
+}
+
+static Qiniu_Error Qiniu_Rio_bput(Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, Qiniu_Reader body, int bodyLength, const char *url, struct _Qiniu_Progress_Callback_Data *progressCallback)
{
Qiniu_Rio_BlkputRet retFromResp;
Qiniu_Json *root;
+ int (*callback)(void *, double, double, double, double) = NULL;
+ void *callbackData = NULL;
+ if (progressCallback != NULL && progressCallback->callback != NULL)
+ {
+ callback = _Qiniu_Progress_Callback;
+ callbackData = (void *)progressCallback;
+ }
- Qiniu_Error err = Qiniu_Client_CallWithBinary(self, &root, url, body, bodyLength, NULL);
+ Qiniu_Error err = Qiniu_Client_CallWithBinaryAndProgressCallback(self, &root, url, body, bodyLength, NULL, callback, callbackData);
if (err.code == 200)
{
retFromResp.ctx = Qiniu_Json_GetString(root, "ctx", NULL);
@@ -385,22 +501,21 @@ static Qiniu_Error Qiniu_Rio_bput(
}
static Qiniu_Error Qiniu_Rio_Mkblock(
- Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, int blkSize, Qiniu_Reader body, int bodyLength,
- Qiniu_Rio_PutExtra *extra)
+ Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, int blkSize, const char *upHost, Qiniu_Reader body, int bodyLength, struct _Qiniu_Progress_Callback_Data *progressCallback)
{
- char *url = Qiniu_String_Format(128, "%s/mkblk/%d", extra->upHost, blkSize);
- Qiniu_Error err = Qiniu_Rio_bput(self, ret, body, bodyLength, url);
- Qiniu_Free(url);
+ const char *url = Qiniu_String_Format(128, "%s/mkblk/%d", upHost, blkSize);
+ Qiniu_Error err = Qiniu_Rio_bput(self, ret, body, bodyLength, url, progressCallback);
+ Qiniu_Free((void *)url);
return err;
}
static Qiniu_Error Qiniu_Rio_Blockput(
- Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, Qiniu_Reader body, int bodyLength)
+ Qiniu_Client *self, Qiniu_Rio_BlkputRet *ret, const char *upHost, Qiniu_Reader body, int bodyLength, struct _Qiniu_Progress_Callback_Data *progressCallback)
{
- char *url = Qiniu_String_Format(1024, "%s/bput/%s/%d", ret->host, ret->ctx, (int)ret->offset);
- Qiniu_Error err = Qiniu_Rio_bput(self, ret, body, bodyLength, url);
- Qiniu_Free(url);
+ const char *url = Qiniu_String_Format(1024, "%s/bput/%s/%d", upHost, ret->ctx, (int)ret->offset);
+ Qiniu_Error err = Qiniu_Rio_bput(self, ret, body, bodyLength, url, progressCallback);
+ Qiniu_Free((void *)url);
return err;
}
@@ -409,19 +524,18 @@ static Qiniu_Error Qiniu_Rio_Blockput(
static Qiniu_Error ErrUnmatchedChecksum = {
Qiniu_Rio_UnmatchedChecksum, "unmatched checksum"};
-static int Qiniu_TemporaryError(int code)
-{
- return code / 100 != 4;
-}
-
-static Qiniu_Error Qiniu_Rio_ResumableBlockput(
- Qiniu_Client *c, Qiniu_Rio_BlkputRet *ret, Qiniu_ReaderAt f, int blkIdx, int blkSize,
- Qiniu_Rio_PutExtra *extra, size_t *chunksUploaded)
+static Qiniu_Error
+Qiniu_Rio_ResumableBlockput(
+ Qiniu_Client *c, Qiniu_Rio_BlkputRet *ret, Qiniu_ReaderAt f, int blkIdx, int blkSize, int fsize,
+ struct _Qiniu_Uploading_Parts_Progress *uploadingPartsProgress, Qiniu_Rio_PutExtra *extra, size_t *pChunksUploaded)
{
Qiniu_Error err = Qiniu_OK;
Qiniu_Tee tee;
Qiniu_Section section;
Qiniu_Reader body, body1;
+ const char *const *upHosts;
+ const char *upHost;
+ size_t upHostsCount;
Qiniu_Crc32 crc32;
Qiniu_Writer h = Qiniu_Crc32Writer(&crc32, 0);
@@ -431,11 +545,32 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
int bodyLength;
int tryTimes;
int notifyRet = 0;
- size_t httpCalled = 0;
+ size_t chunksUploaded = 0;
- if (ret->ctx == NULL)
+ struct _Qiniu_Progress_Callback_Data progressCallbackData;
+ Qiniu_Zero(progressCallbackData);
+
+ if (extra->uploadingProgress != NULL)
{
+ progressCallbackData.callback = extra->uploadingProgress;
+ progressCallbackData.totalSize = (size_t)fsize;
+ progressCallbackData.progress = uploadingPartsProgress;
+ progressCallbackData.blkIdx = blkIdx;
+ }
+ if (extra->upHost != NULL)
+ {
+ upHosts = &extra->upHost;
+ upHostsCount = 1;
+ }
+ else
+ {
+ upHosts = extra->upHosts;
+ upHostsCount = extra->upHostsCount;
+ }
+
+ if (ret->ctx == NULL)
+ {
if (chunkSize < blkSize)
{
bodyLength = chunkSize;
@@ -445,19 +580,42 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
bodyLength = blkSize;
}
- body1 = Qiniu_SectionReader(§ion, f, (Qiniu_Off_T)offbase, bodyLength);
- body = Qiniu_TeeReader(&tee, body1, h);
+ for (int tries = 0; tries < extra->tryTimes && tries <= c->hostsRetriesMax; tries++)
+ {
+ body1 = Qiniu_SectionReader(§ion, f, (Qiniu_Off_T)offbase, bodyLength);
+ body = Qiniu_TeeReader(&tee, body1, h);
- err = Qiniu_Rio_Mkblock(c, ret, blkSize, body, bodyLength, extra);
+ upHost = upHosts[tries % upHostsCount];
+ err = Qiniu_Rio_Mkblock(c, ret, blkSize, upHost, body, bodyLength, &progressCallbackData);
+ if (err.code == 200)
+ {
+ if (extra->uploadingProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Part_Uploaded(uploadingPartsProgress, blkIdx, bodyLength);
+ }
+ break;
+ }
+ else
+ {
+ if (extra->uploadingProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Set_Progress(uploadingPartsProgress, blkIdx, 0);
+ }
+ if (_Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
+ {
+ goto handleErr;
+ }
+ }
+ }
if (err.code != 200)
{
- return err;
+ goto handleErr;
}
-
- httpCalled++;
+ chunksUploaded++;
if (ret->crc32 != crc32.val || (int)(ret->offset) != bodyLength)
{
- return ErrUnmatchedChecksum;
+ err = ErrUnmatchedChecksum;
+ goto handleErr;
}
notifyRet = extra->notify(extra->notifyRecvr, blkIdx, blkSize, ret);
if (notifyRet == QINIU_RIO_NOTIFY_EXIT)
@@ -465,9 +623,13 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
// Terminate the upload process if the caller requests
err.code = Qiniu_Rio_PutInterrupted;
err.message = "Interrupted by the caller";
- return err;
+ goto handleErr;
}
}
+ else if (extra->uploadingProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Part_Uploaded(uploadingPartsProgress, blkIdx, ret->offset);
+ }
while ((int)(ret->offset) < blkSize)
{
@@ -482,16 +644,21 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
}
tryTimes = extra->tryTimes;
+ if (tryTimes > c->hostsRetriesMax + 1)
+ {
+ tryTimes = c->hostsRetriesMax + 1;
+ }
lzRetry:
crc32.val = 0;
body1 = Qiniu_SectionReader(§ion, f, (Qiniu_Off_T)offbase + (ret->offset), bodyLength);
body = Qiniu_TeeReader(&tee, body1, h);
+ upHost = upHosts[tryTimes % upHostsCount];
- err = Qiniu_Rio_Blockput(c, ret, body, bodyLength);
+ err = Qiniu_Rio_Blockput(c, ret, upHost, body, bodyLength, &progressCallbackData);
if (err.code == 200)
{
- httpCalled++;
+ chunksUploaded++;
if (ret->crc32 == crc32.val)
{
notifyRet = extra->notify(extra->notifyRecvr, blkIdx, blkSize, ret);
@@ -500,25 +667,39 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
// Terminate the upload process if the caller requests
err.code = Qiniu_Rio_PutInterrupted;
err.message = "Interrupted by the caller";
- return err;
+ goto handleErr;
+ }
+ if (extra->uploadingProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Part_Uploaded(uploadingPartsProgress, blkIdx, bodyLength);
}
-
continue;
}
- Qiniu_Log_Warn("ResumableBlockput: invalid checksum, retry");
- err = ErrUnmatchedChecksum;
+ else
+ {
+ Qiniu_Log_Warn("ResumableBlockput: invalid checksum, retry");
+ err = ErrUnmatchedChecksum;
+ if (extra->uploadingProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Set_Progress(uploadingPartsProgress, blkIdx, 0);
+ }
+ }
+ }
+ else if (err.code == Qiniu_Rio_InvalidCtx)
+ {
+ Qiniu_Rio_BlkputRet_Cleanup(ret); // reset
+ Qiniu_Log_Warn("ResumableBlockput: invalid ctx, please retry");
+ goto handleErr;
}
else
{
- if (err.code == Qiniu_Rio_InvalidCtx)
+ Qiniu_Log_Warn("ResumableBlockput %d off:%d failed - %E", blkIdx, (int)ret->offset, err);
+ if (extra->uploadingProgress != NULL)
{
- Qiniu_Rio_BlkputRet_Cleanup(ret); // reset
- Qiniu_Log_Warn("ResumableBlockput: invalid ctx, please retry");
- return err;
+ _Qiniu_Uploading_Parts_Progress_Set_Progress(uploadingPartsProgress, blkIdx, 0);
}
- Qiniu_Log_Warn("ResumableBlockput %d off:%d failed - %E", blkIdx, (int)ret->offset, err);
}
- if (tryTimes > 1 && Qiniu_TemporaryError(err.code))
+ if (tryTimes > 1 && _Qiniu_Should_Retry(err.code) != QINIU_DONT_RETRY)
{
tryTimes--;
Qiniu_Log_Info("ResumableBlockput %E, retrying ...", err);
@@ -527,9 +708,10 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
break;
}
- if (chunksUploaded != NULL)
+handleErr:
+ if (pChunksUploaded != NULL)
{
- *chunksUploaded = httpCalled;
+ *pChunksUploaded = chunksUploaded;
}
return err;
@@ -538,7 +720,7 @@ static Qiniu_Error Qiniu_Rio_ResumableBlockput(
/*============================================================================*/
static Qiniu_Error Qiniu_Rio_Mkfile(
- Qiniu_Client *c, Qiniu_Rio_PutRet *ret, const char *key, Qiniu_Int64 fsize, Qiniu_Rio_PutExtra *extra)
+ Qiniu_Client *c, Qiniu_Rio_PutRet *ret, const char *upHost, const char *key, Qiniu_Int64 fsize, Qiniu_Rio_PutExtra *extra)
{
size_t i, blkCount = extra->blockCnt;
Qiniu_Json *root;
@@ -547,7 +729,7 @@ static Qiniu_Error Qiniu_Rio_Mkfile(
int j = 0;
Qiniu_Buffer_Init(&url, 2048);
- Qiniu_Buffer_AppendFormat(&url, "%s/mkfile/%D", extra->upHost, fsize);
+ Qiniu_Buffer_AppendFormat(&url, "%s/mkfile/%D", upHost, fsize);
if (key != NULL)
{
@@ -674,6 +856,8 @@ typedef struct _Qiniu_Rio_task
Qiniu_Count *ninterrupts;
int blkIdx;
int blkSize1;
+ int fsize;
+ struct _Qiniu_Uploading_Parts_Progress *progress;
} Qiniu_Rio_task;
static void Qiniu_Rio_doTask(void *params)
@@ -701,7 +885,7 @@ static void Qiniu_Rio_doTask(void *params)
lzRetry:
Qiniu_Rio_BlkputRet_Assign(&ret, &extra->progresses[blkIdx]);
- Qiniu_Error err = Qiniu_Rio_ResumableBlockput(c, &ret, task->f, blkIdx, task->blkSize1, extra, &chunksUploaded);
+ Qiniu_Error err = Qiniu_Rio_ResumableBlockput(c, &ret, task->f, blkIdx, task->blkSize1, task->fsize, task->progress, extra, &chunksUploaded);
if (err.code != 200)
{
if (err.code == Qiniu_Rio_PutInterrupted)
@@ -714,7 +898,7 @@ static void Qiniu_Rio_doTask(void *params)
return;
}
- if (tryTimes > 1 && Qiniu_TemporaryError(err.code))
+ if (tryTimes > 1 && _Qiniu_Should_Retry(err.code) != QINIU_DONT_RETRY)
{
tryTimes--;
Qiniu_Log_Info("resumable.Put %E, retrying ...", err);
@@ -740,10 +924,8 @@ static void Qiniu_Rio_doTask(void *params)
/*============================================================================*/
/* func Qiniu_Rio_PutXXX */
-static Qiniu_Error ErrPutFailed = {
- Qiniu_Rio_PutFailed, "resumable put failed"};
-static Qiniu_Error ErrPutInterrupted = {
- Qiniu_Rio_PutInterrupted, "resumable put interrupted"};
+static Qiniu_Error ErrPutFailed;
+static Qiniu_Error ErrPutInterrupted;
static Qiniu_Error Qiniu_Rio_loadProgresses(Qiniu_Rio_PutRet *ret, Qiniu_Rio_PutExtra *extra, Qiniu_Rio_Recorder *recorder)
{
@@ -807,12 +989,13 @@ static Qiniu_Error _Qiniu_Rio_Put(
int nfails;
int retCode;
Qiniu_Count ninterrupts;
+ struct _Qiniu_Uploading_Parts_Progress *uploadingPartProgress = NULL;
Qiniu_Error err = Qiniu_Rio_PutExtra_Init(&extra, fsize, extra1);
if (err.code != 200)
{
return err;
}
- if (extra.upHost == NULL)
+ if (extra.upHost == NULL && (extra.upHosts == NULL || extra.upHostsCount == 0))
{
if (!Qiniu_Utils_Extract_Bucket(uptoken, &accessKey, &bucketName))
{
@@ -820,7 +1003,7 @@ static Qiniu_Error _Qiniu_Rio_Put(
err.message = "parse uptoken failed";
return err;
}
- err = _Qiniu_Region_Get_Up_Host(self, accessKey, bucketName, &extra.upHost);
+ err = _Qiniu_Region_Get_Up_Hosts(self, accessKey, bucketName, &extra.upHosts, &extra.upHostsCount);
if (err.code != 200)
{
Qiniu_Free((void *)accessKey);
@@ -828,6 +1011,10 @@ static Qiniu_Error _Qiniu_Rio_Put(
return err;
}
}
+ if (extra.uploadingProgress != NULL)
+ {
+ uploadingPartProgress = _Qiniu_Uploading_Parts_Progress_New();
+ }
tm = extra.threadModel;
wg = tm.itbl->WaitGroup(tm.self);
@@ -853,6 +1040,8 @@ static Qiniu_Error _Qiniu_Rio_Put(
task->ninterrupts = &ninterrupts;
task->blkIdx = i;
task->blkSize1 = blkSize;
+ task->fsize = fsize;
+ task->progress = uploadingPartProgress;
if (i == last)
{
offbase = (Qiniu_Int64)(i) << blockBits;
@@ -865,7 +1054,7 @@ static Qiniu_Error _Qiniu_Rio_Put(
{
wg.itbl->Done(wg.self);
Qiniu_Count_Inc(&ninterrupts);
- free(task);
+ Qiniu_Free((void *)task);
}
if (ninterrupts > 0)
@@ -885,19 +1074,45 @@ static Qiniu_Error _Qiniu_Rio_Put(
}
else
{
- err = Qiniu_Rio_Mkfile(self, ret, key, fsize, &extra);
- if (err.code == Qiniu_Rio_InvalidCtx && recorder != NULL && recorder->toLoadProgresses == Qiniu_True && fi != NULL)
+ const char *const *upHosts;
+ size_t upHostsCount;
+
+ if (extra.upHost != NULL)
{
- Qiniu_Rio_PutExtra_Clear(&extra);
- reinitializeRecorder(&extra, fi, recorder);
- goto reinit;
+ upHosts = &extra.upHost;
+ upHostsCount = 1;
}
- if (err.code / 100 == 2 || err.code / 100 == 4 || err.code == Qiniu_Rio_InvalidCtx)
+ else
{
- Qiniu_Rio_clearProgresses(&extra, recorder);
+ upHosts = extra.upHosts;
+ upHostsCount = extra.upHostsCount;
+ }
+
+ for (int tries = 0; tries < extra.tryTimes && tries <= self->hostsRetriesMax; tries++)
+ {
+ const char *upHost = upHosts[tries % upHostsCount];
+ err = Qiniu_Rio_Mkfile(self, ret, upHost, key, fsize, &extra);
+ if (err.code == Qiniu_Rio_InvalidCtx && recorder != NULL && recorder->toLoadProgresses == Qiniu_True && fi != NULL)
+ {
+ Qiniu_Rio_PutExtra_Clear(&extra);
+ reinitializeRecorder(&extra, fi, recorder);
+ goto reinit;
+ }
+ else if (err.code == 200 || _Qiniu_Should_Retry(err.code) == QINIU_DONT_RETRY)
+ {
+ if (err.code / 100 != 4 || err.code == Qiniu_Rio_InvalidCtx)
+ {
+ Qiniu_Rio_clearProgresses(&extra, recorder);
+ }
+ break;
+ }
}
}
+ if (uploadingPartProgress != NULL)
+ {
+ _Qiniu_Uploading_Parts_Progress_Free(uploadingPartProgress);
+ }
Qiniu_Rio_PutExtra_Cleanup(&extra);
Qiniu_Free((void *)accessKey);
Qiniu_Free((void *)bucketName);
diff --git a/qiniu/resumable_io.h b/qiniu/resumable_io.h
index a1efacf7..091da120 100644
--- a/qiniu/resumable_io.h
+++ b/qiniu/resumable_io.h
@@ -14,7 +14,9 @@
#include "io.h"
#include "recorder.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -137,6 +139,13 @@ extern "C"
// For those who want to send request to specific host.
const char *upHost;
+
+ // Specify multiple upHosts, if not set explicitly, will global QINIU_UP_HOST
+ const char *const *upHosts;
+ size_t upHostsCount;
+
+ // Uploading file progress
+ void (*uploadingProgress)(size_t ultotal, size_t ulnow);
} Qiniu_Rio_PutExtra;
/*============================================================================*/
@@ -166,7 +175,9 @@ extern "C"
/*============================================================================*/
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/rfc3339.c b/qiniu/rfc3339.c
new file mode 100644
index 00000000..bc329655
--- /dev/null
+++ b/qiniu/rfc3339.c
@@ -0,0 +1,247 @@
+#include "rfc3339.h"
+
+static void _Strip_Spaces(char *source)
+{
+ char *i = source;
+
+ while (*source != 0)
+ {
+ *i = *source++;
+ if (*i != ' ')
+ i++;
+ }
+
+ *i = 0;
+}
+
+static void _Qiniu_Parse_Date(char *date_string, Qiniu_Date *d)
+{
+ char *const tokens = strdup(date_string);
+ _Strip_Spaces(tokens);
+ d->ok = 0;
+
+ if (strlen(tokens) < 10)
+ {
+ return;
+ }
+
+ int status = sscanf(
+ tokens, "%04d-%02d-%02d", &(d->year), &(d->month), &(d->day));
+ free((char *)tokens);
+
+ if (status != 3)
+ {
+ return;
+ }
+ if (d->year < 1 || d->year > 9999)
+ {
+ return;
+ }
+ if (d->month < 1 || d->month > 12)
+ {
+ return;
+ }
+ if (d->day < 1 || d->day > 31)
+ {
+ return;
+ }
+
+ const int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ if (d->month == 2)
+ {
+ int leap = (d->year % 4 == 0 && d->year % 100 != 0) || (d->year % 400 == 0);
+ if (d->day > (days_in_month[2 - 1] + leap))
+ {
+ return;
+ }
+ }
+ else if (d->day > days_in_month[d->month - 1])
+ {
+ return;
+ }
+
+ d->ok = 1;
+}
+
+static void _Qiniu_Parse_Time(char *time_string, Qiniu_Time *t)
+{
+ char *tokens = strdup(time_string);
+ char *token_ptr = tokens;
+
+ _Strip_Spaces(tokens);
+
+ t->ok = 0;
+
+ if (strlen(tokens) < 8)
+ {
+ goto cleanup;
+ }
+
+ if ((strlen(tokens) > 11) && ((*(tokens + 10) == 'T') || (*(tokens + 10) == 't')))
+ {
+ tokens += 11;
+ }
+
+ int status = sscanf(
+ tokens, "%02d:%02d:%02d", &(t->hour), &(t->minute), &(t->second));
+
+ if (status != 3)
+ {
+ goto cleanup;
+ }
+ if (t->hour < 0 || t->hour > 23)
+ {
+ goto cleanup;
+ }
+ if (t->minute < 0 || t->minute > 59)
+ {
+ goto cleanup;
+ }
+ if (t->second < 0 || t->second > 59)
+ {
+ goto cleanup;
+ }
+
+ if (strlen(tokens) == 8)
+ {
+ t->offset = 0;
+ t->ok = 1;
+ goto cleanup;
+ }
+ else
+ {
+ tokens += 8;
+ }
+
+ if (*tokens == '.')
+ {
+ tokens++;
+ char fractions[7] = {0};
+
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ if ((*(tokens + i) >= 48) && (*(tokens + i) <= 57))
+ {
+ fractions[i] = *(tokens + i);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ status = sscanf(fractions, "%d", &(t->fraction));
+
+ if (strlen(fractions) < 6 && strlen(fractions) > 0)
+ {
+ t->fraction = t->fraction * pow(10, 6 - strlen(fractions)); // convert msec to usec
+ }
+ else if (strlen(fractions) == 6)
+ {
+ // all fine, already in usec
+ }
+ else
+ {
+ // Invalid fractions must be msec or usec
+ goto cleanup;
+ }
+
+ if (status != 1)
+ {
+ goto cleanup;
+ }
+ if (t->fraction < 0 || t->fraction > 999999)
+ {
+ goto cleanup;
+ }
+
+ tokens += strlen(fractions);
+
+ if (strlen(tokens) == 0)
+ {
+ t->offset = 0;
+ t->ok = 1;
+ goto cleanup;
+ }
+ }
+
+ if ((*tokens == 'Z') || (*tokens == 'z'))
+ {
+ t->offset = 0;
+
+ tokens++;
+ if (strlen(tokens) == 0)
+ {
+ t->ok = 1;
+ }
+ else
+ {
+ t->ok = 0;
+ }
+ goto cleanup;
+ }
+ else if ((*tokens == '+') || (*tokens == '-'))
+ {
+ unsigned int tz_hour, tz_minute;
+
+ status = sscanf(tokens + 1, "%02d:%02d", &tz_hour, &tz_minute);
+
+ if (status != 2)
+ {
+ goto cleanup;
+ }
+ if ((tz_hour < 0) || (tz_hour > 23))
+ {
+ goto cleanup;
+ }
+ if ((tz_minute < 0) || (tz_minute > 59))
+ {
+ goto cleanup;
+ }
+
+ int tz_offset = (tz_hour * HOUR_IN_MINS) + tz_minute;
+
+ if (*tokens == '-')
+ {
+ tz_offset = tz_offset * -1;
+ }
+
+ t->offset = tz_offset;
+
+ tokens = tokens + 6;
+ if (strlen(tokens) == 0)
+ {
+ t->ok = 1;
+ }
+ else
+ {
+ t->ok = 0;
+ }
+ }
+
+cleanup:
+ free(token_ptr);
+ tokens = NULL;
+ token_ptr = NULL;
+ return;
+}
+
+void _Qiniu_Parse_Date_Time(char *datetime_string, Qiniu_DateTime *dt)
+{
+ dt->ok = 0;
+
+ _Qiniu_Parse_Date(datetime_string, &(dt->date));
+ if (dt->date.ok == 0)
+ {
+ return;
+ }
+
+ _Qiniu_Parse_Time(datetime_string, &(dt->time));
+ if (dt->time.ok == 0)
+ {
+ return;
+ }
+
+ dt->ok = 1;
+}
diff --git a/qiniu/rfc3339.h b/qiniu/rfc3339.h
new file mode 100644
index 00000000..1add59c5
--- /dev/null
+++ b/qiniu/rfc3339.h
@@ -0,0 +1,56 @@
+#ifndef QINIU_RFC3339_H
+#define QINIU_RFC3339_H
+
+#include
+#include
+#include
+#include
+
+#define DAY_IN_SECS 86400
+#define HOUR_IN_SECS 3600
+#define MINUTE_IN_SECS 60
+#define HOUR_IN_MINS 60
+
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct _Qiniu_Date
+ {
+ unsigned int year;
+ unsigned int month;
+ unsigned int day;
+ char ok;
+ } Qiniu_Date;
+
+ typedef struct _Qiniu_Time
+ {
+ unsigned int hour;
+ unsigned int minute;
+ unsigned int second;
+ unsigned int fraction;
+ int offset; // UTC offset in minutes
+ char ok;
+ } Qiniu_Time;
+
+ typedef struct _Qiniu_DateTime
+ {
+ Qiniu_Date date;
+ Qiniu_Time time;
+ char ok;
+ } Qiniu_DateTime;
+
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // QINIU_RFC3339_H
diff --git a/qiniu/rs.c b/qiniu/rs.c
index a6da08c9..1b0c4e99 100644
--- a/qiniu/rs.c
+++ b/qiniu/rs.c
@@ -108,6 +108,10 @@ char *Qiniu_RS_PutPolicy_Token(Qiniu_RS_PutPolicy *auth, Qiniu_Mac *mac)
{
cJSON_AddNumberToObject(root, "fileType", auth->fileType);
}
+ if (auth->persistentType)
+ {
+ cJSON_AddNumberToObject(root, "persistentType", auth->persistentType);
+ }
if (auth->expires)
{
diff --git a/qiniu/rs.h b/qiniu/rs.h
index 474144af..ee0da67d 100644
--- a/qiniu/rs.h
+++ b/qiniu/rs.h
@@ -12,7 +12,9 @@
#include "http.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -55,6 +57,7 @@ typedef struct _Qiniu_RS_PutPolicy {
Qiniu_Uint32 expires;
Qiniu_Uint32 deleteAfterDays;
Qiniu_Uint32 fileType;
+ Qiniu_Uint32 persistentType;
} Qiniu_RS_PutPolicy;
/* @endgist */
@@ -245,7 +248,9 @@ QINIU_DLLAPI extern Qiniu_Error Qiniu_RS_BatchRestoreArchive(Qiniu_Client *self,
Qiniu_RS_EntryRestoreArchive *entries,
Qiniu_ItemCount entryCount);
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/rsf.h b/qiniu/rsf.h
index 66acbb86..f0723f15 100644
--- a/qiniu/rsf.h
+++ b/qiniu/rsf.h
@@ -7,7 +7,9 @@
#include "http.h"
+#if defined(_WIN32)
#pragma pack(1)
+#endif
#ifdef __cplusplus
extern "C"
@@ -37,7 +39,9 @@ extern "C"
const char *prefix, const char *delimiter, const char *marker,
int limit);
QINIU_DLLAPI extern void Qiniu_RSF_ListRet_Cleanup(Qiniu_RSF_ListRet *self);
+#if defined(_WIN32)
#pragma pack()
+#endif
#ifdef __cplusplus
}
diff --git a/qiniu/tm.h b/qiniu/tm.h
index 6f0db071..7d1fbe11 100644
--- a/qiniu/tm.h
+++ b/qiniu/tm.h
@@ -12,6 +12,10 @@
#include "base.h"
+#if defined(_WIN32)
+#pragma pack(1)
+#endif
+
#ifdef __cplusplus
extern "C"
{
@@ -22,6 +26,10 @@ extern "C"
QINIU_DLLAPI extern const char *Qiniu_MD5_HexStr(const char *src);
QINIU_DLLAPI extern const char *Qiniu_MD5_HexStr_From_Reader(Qiniu_Reader r);
+#if defined(_WIN32)
+#pragma pack()
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/vcpkg.json b/vcpkg.json
index 845d5be2..e70e89d3 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -1,6 +1,6 @@
{
"name": "qiniu-c-sdk",
- "version-string": "7.7.0",
+ "version-string": "7.8.0",
"dependencies": [
"curl",
"openssl"