From 48c2b395bc0de3f68dd6c00e57b1ef4b1aa3952c Mon Sep 17 00:00:00 2001 From: lion Date: Fri, 19 Sep 2025 13:08:47 +0800 Subject: [PATCH] c binding is ready for IPv6 --- binding/c/ReadMe.md | 83 +++++++++++++++++++++++++++++------ binding/c/main.c | 34 +++++++++----- binding/c/xdb_api.h | 24 +++++++++- binding/c/xdb_searcher.c | 95 ++++++++++++++++++++++++++++++---------- 4 files changed, 186 insertions(+), 50 deletions(-) diff --git a/binding/c/ReadMe.md b/binding/c/ReadMe.md index f385a87..6fbf7ee 100644 --- a/binding/c/ReadMe.md +++ b/binding/c/ReadMe.md @@ -46,9 +46,17 @@ if ($err != 0) { int main(int argc, char *argv[]) { xdb_searcher_t searcher; char region_buffer[512] = {'\0'}; + xdb_region_buffer_t region; + + // 使用栈空间的 region_buffer 初始化 region_buffer_t + int err = xdb_region_buffer_init(®ion, region_buffer, sizeof(region_buffer)); + if (err != 0) { + printf("failed to init the region buffer with errcode=%d\n", err); + return 1; + } // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 - int err = xdb_init_winsock(); + err = xdb_init_winsock(); if (err != 0) { printf("failed to init the winsock with errno=%d\n", err); return 1; @@ -63,19 +71,21 @@ int main(int argc, char *argv[]) { } // 2、调用 search API 查询,IPv4 和 IPv6 都支持. - // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 const char *ip_string = "1.2.3.4"; // ip_string = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, region_buffer, sizeof(region_buffer)); + err = xdb_search_by_string(&searcher, ip_string, ®ion); cost_time = (int) (xdb_now() - s_time); if (err != 0) { printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, cost_time); + printf("{region: %s, took: %d μs}", region.value, cost_time); } + // 清理 region 信息的内存资源,每次 search 之后都得调用 + xdb_region_buffer_free(®ion); + // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 3、关闭 xdb 查询器 @@ -95,10 +105,17 @@ int main(int argc, char *argv[]) { int main(int argc, char *argv[]) { xdb_vector_index_t *v_index; xdb_searcher_t searcher; - char region_buffer[512]; + xdb_region_buffer_t region; + + // 使用 NULL 初始化 region_buffer,让其自动管理内存的分配 + int err = xdb_region_buffer_init(®ion, NULL, 0); + if (err != 0) { + printf("failed to init the region buffer with errcode=%d\n", err); + return 0; + } // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 - int err = xdb_init_winsock(); + err = xdb_init_winsock(); if (err != 0) { printf("failed to init the winsock with errno=%d\n", err); return 1; @@ -123,19 +140,22 @@ int main(int argc, char *argv[]) { // 3、调用 search API 查询,IPv4 和 IPv6 都支持 - // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 const char *ip_string = "1.2.3.4"; // ip_string = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, region_buffer, sizeof(region_buffer)); + err = xdb_search_by_string(&searcher, ip_string, ®ion); cost_time = (int) (xdb_now() - s_time); if (err != 0) { printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, cost_time); + printf("{region: %s, took: %d μs}", region.value, cost_time); } + // 清理 region 信息的内存资源,每次 search 之后都得调用 + xdb_region_buffer_free(®ion); + + // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 4、关闭 xdb 查询器,如果是要关闭服务,也需要释放 v_index 的内存。 @@ -156,11 +176,18 @@ int main(int argc, char *argv[]) { int main(int argc, char *argv[]) { xdb_content_t *c_buffer; xdb_searcher_t searcher; - char region_buffer[512] = {'\0'}; + xdb_region_buffer_t region; + + // 使用 NULL 初始化 region_buffer,让其自动管理内存的分配 + int err = xdb_region_buffer_init(®ion, NULL, 0); + if (err != 0) { + printf("failed to init the region buffer with errcode=%d\n", err); + return 0; + } // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 - int err = xdb_init_winsock(); + err = xdb_init_winsock(); if (err != 0) { printf("failed to init the winsock with errno=%d\n", err); return 1; @@ -182,19 +209,22 @@ int main(int argc, char *argv[]) { } // 3、调用 search API 查询,IPv4 和 IPv6 都支持 - // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 const char *ip_string = "1.2.3.4"; // ip_string = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, region_buffer, sizeof(region_buffer)); + err = xdb_search_by_string(&searcher, ip_string, ®ion); cost_time = (int) (xdb_now() - s_time); if (err != 0) { printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, cost_time); + printf("{region: %s, took: %d μs}", region.value, cost_time); } + // 清理 region 信息的内存资源,每次 search 之后都得调用 + xdb_region_buffer_free(®ion); + + // 备注:并发使用,使用这种方式创建的 xdb 查询对象可以安全用于并发。 // 建议在服务启动的时候创建好,然后一直安全并发使用,直到服务关闭。 @@ -206,6 +236,31 @@ int main(int argc, char *argv[]) { } ``` +### 关于定位信息的存储 +在旧版本的实现中,search相关的函数都是依靠指定一个 `region_buffer` 内存来用于存储地域信息,这种方式还是有很大的局限性。 +新的实现提供了一个 `xdb_region_buffer_t` 对象来管理这些内存的分配,你依然可以指定一个固定的 `region_buffer` 来创建 region 的内存管理,这个情况适合当你的地域信息的最大长度是可知的,这种方式可以减少运行过程中内存的碎片堆积。如果地域信息的长度不确定或者你的程序不适合提前分配一块内存来管理,你可以通过指定 NULL 的方式来初始化 `xdb_region_buffer_t`,这样对象会自动管理内存的分配,也适合任意长度的地域信息的存储,不过这种方式在长期的运行过程中肯定会增加内存碎片的堆积。 +```c +// 1, 通过指定一块内存来创建 region_buffer +char buffer[512]; +xdb_region_buffer_t region; +int err = xdb_region_buffer_init(®ion, buffer, sizeof(buffer)); +if (err != 0) { + // 初始化失败 +} + +// 2,通过指定 NULL 来创建 region_buffer,让其自动按需分配内存 +xdb_region_buffer_t region; +int err = xdb_region_buffer_init(®ion, NULL, 0); +if (err != 0) { + // 初始化失败 +} + + +// 备注:在每次调用 search 完成 IP 定位信息的查询后,你需要手动调用函数来释放内存 . +// search 函数使用未经清理的 region 信息会报错。 +xdb_region_buffer_free(®ion); +``` + # 测试程序编译 diff --git a/binding/c/main.c b/binding/c/main.c index 1280666..1a7f39b 100644 --- a/binding/c/main.c +++ b/binding/c/main.c @@ -13,6 +13,10 @@ struct searcher_test_entry { xdb_searcher_t searcher; xdb_vector_index_t *v_index; xdb_content_t *c_buffer; + + // xdb region buffer + char region_buffer[256]; + xdb_region_buffer_t region; }; typedef struct searcher_test_entry searcher_test_t; @@ -90,6 +94,14 @@ int init_searcher_test(searcher_test_t *test, char *db_path, char *cache_policy) goto defer; } + // init the region buffer + err = xdb_region_buffer_init(&test->region, test->region_buffer, sizeof(test->region_buffer)); + if (err != 0) { + printf("failed to init the region buffer with err=%d\n", err); + errcode = 7; + goto defer; + } + defer: if (header != NULL) { xdb_free_header(header); @@ -152,7 +164,6 @@ void test_search(int argc, char *argv[]) { long s_time, c_time; unsigned int ip; char line[512] = {'\0'}, region[512] = {'\0'}; - char *region_buffer = NULL; // ip parse xdb_version_t *version; @@ -236,17 +247,16 @@ void test_search(int argc, char *argv[]) { } s_time = xdb_now(); - err = xdb_search(&test.searcher, ip_bytes, version->bytes, ®ion_buffer, sizeof(region)); + err = xdb_search(&test.searcher, ip_bytes, version->bytes, &test.region); if (err != 0) { printf("{err: %d, io_count: %d}\n", err, xdb_get_io_count(&test.searcher)); } else { c_time = xdb_now() - s_time; - printf("{region: %s, io_count: %d, took: %ld μs}\n", region_buffer, xdb_get_io_count(&test.searcher), c_time); + printf("{region: %s, io_count: %d, took: %ld μs}\n", test.region.value, xdb_get_io_count(&test.searcher), c_time); } - // free the region_buffer - xdb_free(region_buffer); - region_buffer = NULL; + // free the region + xdb_region_buffer_free(&test.region); } destroy_searcher_test(&test); @@ -261,8 +271,7 @@ void test_bench(int argc, char *argv[]) { FILE *handle; char line[1024] = {'\0'}, sip_str[INET6_ADDRSTRLEN+1] = {'\0'}, eip_str[INET6_ADDRSTRLEN+1] = {'\0'}; - char src_region[512] = {'\0'}, region[512] = {'\0'}; - char *region_buffer = region; + char src_region[512] = {'\0'}; int count = 0, took; long s_time, t_time, c_time = 0; @@ -370,7 +379,7 @@ void test_bench(int argc, char *argv[]) { ip_list[1] = eip_bytes; for (i = 0; i < 2; i++) { t_time = xdb_now(); - err = xdb_search(&test.searcher, ip_list[i], s_version->bytes, ®ion_buffer, sizeof(region)); + err = xdb_search(&test.searcher, ip_list[i], s_version->bytes, &test.region); c_time += xdb_now() - t_time; if (err != 0) { xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string)); @@ -379,12 +388,15 @@ void test_bench(int argc, char *argv[]) { } // check the region info - if (strcmp(region_buffer, src_region) != 0) { + if (strcmp(test.region.value, src_region) != 0) { xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string)); - printf("failed to search(%s) with (%s != %s)\n", ip_string, region_buffer, src_region); + printf("failed to search(%s) with (%s != %s)\n", ip_string, test.region.value, src_region); return; } + // free the region buffer + xdb_region_buffer_free(&test.region); + count++; } }; diff --git a/binding/c/xdb_api.h b/binding/c/xdb_api.h index d044bff..2db43cf 100644 --- a/binding/c/xdb_api.h +++ b/binding/c/xdb_api.h @@ -202,6 +202,26 @@ XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *, int, const char *, int); // --- xdb searcher api +// xdb region info structure +#define xdb_region_buffer_wrapper 1 +#define xdb_region_buffer_auto 2 +struct xdb_region_buffer_entry { + int type; // buffer type + char *value; // region value + size_t length; // buffer length +}; +typedef struct xdb_region_buffer_entry xdb_region_buffer_t; + +// wrapper the region from a local stack buffer. +// returns: 0 for succeed or failed +XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *, char *, size_t); + +// do the buffer alloc. +// returns: 0 for ok or failed +XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *, int); + +XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *); + // xdb searcher structure struct xdb_searcher_entry { // ip version @@ -235,9 +255,9 @@ XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *, xdb_searcher_t *, const xdb XDB_PUBLIC(void) xdb_close(void *); // xdb searcher search api define -XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, char **, size_t); +XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *); -XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, char **, size_t); +XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *); XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *); diff --git a/binding/c/xdb_searcher.c b/binding/c/xdb_searcher.c index 38a950e..8eba674 100644 --- a/binding/c/xdb_searcher.c +++ b/binding/c/xdb_searcher.c @@ -8,6 +8,64 @@ #include "xdb_api.h" +// --- region buffer +XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *region, char *buffer, size_t length) { + if (buffer == NULL) { + region->type = xdb_region_buffer_auto; + region->length = 0; + } else if (length <= 0) { + return 1; + } else { + region->type = xdb_region_buffer_wrapper; + region->length = length; + memset(buffer, 0x00, length); // zero-fill the buffer + } + + region->value = buffer; + return 0; +} + +XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *region, int length) { + if (length <= 0) { + return 1; + } + + // no allocation supports for the buffer wapper + if (region->type == xdb_region_buffer_wrapper) { + if (length >= region->length) { + return 2; + } + + region->value[length] = '\0'; + return 0; + } + + // ensure that the value were freed + // by calling #xdb_region_buffer_free + if (region->value != NULL) { + return 3; + } + + char *ptr = (char *) xdb_malloc(length + 1); + if (ptr == NULL) { + return 4; + } + + ptr[length] = '\0'; // NULL-end + region->value = ptr; + region->length = length; + return 0; +} + +XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *region) { + if (region->type == xdb_region_buffer_auto) { + xdb_free(region->value); + region->value = NULL; + } +} + +// --- END region buffer + // internal function prototype define XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length); @@ -59,17 +117,17 @@ XDB_PUBLIC(void) xdb_close(void *ptr) { // --- xdb searcher search api define -XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_ip_t *ip_string, char **region_buffer, size_t length) { +XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_ip_t *ip_string, xdb_region_buffer_t *region) { bytes_ip_t ip_bytes[16] = {'\0'}; xdb_version_t *version = xdb_parse_ip(ip_string, ip_bytes, sizeof(ip_bytes)); if (version == NULL) { return 10; } else { - return xdb_search(xdb, ip_bytes, version->bytes, region_buffer, length); + return xdb_search(xdb, ip_bytes, version->bytes, region); } } -XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int ip_len, char **region_buffer, size_t length) { +XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int ip_len, xdb_region_buffer_t *region) { int il0, il1, idx, err, bytes, d_bytes; register int seg_index_size, l, h, m, p; unsigned int s_ptr, e_ptr, data_ptr, data_len; @@ -126,7 +184,7 @@ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int err = read(xdb, p, segment_buffer, seg_index_size); if (err != 0) { err += 20; - goto done; + goto defer; } // decode the data fields as needed @@ -143,33 +201,24 @@ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int // printf("data_len=%u, data_ptr=%u\n", data_len, data_ptr); if (data_len == 0) { - goto done; + err = 100; + goto defer; } - // buffer length checking - if (length > 0 && data_len >= (int) length) { - err = 1; - goto done; + // buffer alloc checking + err = xdb_region_buffer_alloc(region, data_len); + if (err != 0) { + err += 100; + goto defer; } - // @Note: since 2025/09/18 with IPv6 supporting. - // if the region_buffer is NULL or the length is 0 we will create a buffer - // to hold the region info and the caller should call xdb_free to free the region info after finished use it. - char *r_buffer = *region_buffer; - if (r_buffer == NULL || length == 0) { - r_buffer = (char *) xdb_malloc(data_len + 1); - *region_buffer = r_buffer; - } - - // auto append a NULL-end - r_buffer[data_len] = '\0'; - err = read(xdb, data_ptr, r_buffer, data_len); + err = read(xdb, data_ptr, region->value, data_len); if (err != 0) { err += 30; - goto done; + goto defer; } -done: +defer: // checn and free the segment buffer if (segment_buffer != NULL) { xdb_free(segment_buffer);