diff --git a/binding/c/ReadMe.md b/binding/c/ReadMe.md index b684f5a..f385a87 100644 --- a/binding/c/ReadMe.md +++ b/binding/c/ReadMe.md @@ -3,6 +3,40 @@ # 使用方式 +### 关于 IPv4 和 IPv6 +该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下: +```c +#include "xdb_api.h"; + +// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 IPv4 +const char *db_path = "../../data/ip2region_v4.xdb"; // 或者你的 ipv4 xdb 的路径 +xdb_version_t *version = XDB_IPv4; + +// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 IPv6 +const char *db_path = "../../data/ip2region_v6.xdb"; // 或者你的 ipv6 xdb 路径 +xdb_version_t *version = XDB_IPv6; + +// db_path 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错 +// 备注:以下演示直接使用 db_path 和 version 变量 +``` + +### XDB 文件验证 +建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。 +```c +#include "xdb_api.h"; + +int errcode = xdb_verify(db_path); +if ($err != 0) { + // 适用性验证失败!!! + // 当前查询客户端实现不适用于 db_path 指定的 xdb 文件的查询. + // 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 db_path 的 Searcher 实现。 + printf("failed to verify xdb file `%s`, errcode: %d\n", db_path, errcode); + return; +} + +// 验证通过,当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作 +``` + ### 完全基于文件的查询 ```c @@ -10,32 +44,43 @@ #include "xdb_api.h" int main(int argc, char *argv[]) { - char *db_path = "ip2region.xdb file path"; xdb_searcher_t searcher; - char region_buffer[256], ip_buffer[16], *ip = "1.2.3.4"; - long s_time; + char region_buffer[512] = {'\0'}; - // 1、从 db_path 初始化 xdb 查询对象 - int err = xdb_new_with_file_only(&searcher, db_path); + // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 + int err = xdb_init_winsock(); + if (err != 0) { + printf("failed to init the winsock with errno=%d\n", err); + return 1; + } + + // 1、从 db_path 初始化 xdb 查询对象. + // @Note: 使用顶部描述的 db_path 和 version 来创建 searcher + err = xdb_new_with_file_only(version, &searcher, db_path); if (err != 0) { printf("failed to create xdb searcher from `%s` with errno=%d\n", db_path, err); return 1; } - // 2、调用 search API 查询 + // 2、调用 search API 查询,IPv4 和 IPv6 都支持. // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 - s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip, region_buffer, sizeof(region_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)); + cost_time = (int) (xdb_now() - s_time); if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip, err); + printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, (int)(xdb_now() - s_time)); + printf("{region: %s, took: %d μs}", region_buffer, cost_time); } - // 备注:并发使用,没一个线程需要单独定义并且初始化一个 searcher 查询对象。 + // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 3、关闭 xdb 查询器 xdb_close(&searcher); + xdb_clean_winsock(); // windows 下调用 return 0; } ``` @@ -48,13 +93,18 @@ int main(int argc, char *argv[]) { #include "xdb_api.h" int main(int argc, char *argv[]) { - char *db_path = "ip2region.xdb file path"; xdb_vector_index_t *v_index; xdb_searcher_t searcher; - char region_buffer[256], ip_buffer[16], *ip = "1.2.3.4"; - long s_time; + char region_buffer[512]; - // 1、从 db_path 加载 VectorIndex 索引。 + // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 + int err = xdb_init_winsock(); + if (err != 0) { + printf("failed to init the winsock with errno=%d\n", err); + return 1; + } + + // 1、从顶部描述的 db_path 加载 VectorIndex 索引。 // 得到 v_index 做成全局缓存,便于后续反复使用。 // 注意:v_index 不需要每次都加载,建议在服务启动的时候加载一次,然后做成全局资源。 v_index = xdb_load_vector_index_from_file(db_path); @@ -63,68 +113,86 @@ int main(int argc, char *argv[]) { return 1; } - // 2、使用全局的 VectorIndex 变量创建带 VectorIndex 缓存的 xdb 查询对象 - int err = xdb_new_with_vector_index(&searcher, db_path, v_index); + // 2、使用全局的 VectorIndex 变量创建带 VectorIndex 缓存的 xdb 查询对象. + // @Note: 使用顶部描述的 db_path 和 version 来创建 searcher + err = xdb_new_with_vector_index(version, &searcher, db_path, v_index); if (err != 0) { printf("failed to create vector index cached searcher with errcode=%d\n", err); return 2; } - // 3、调用 search API 查询 + + // 3、调用 search API 查询,IPv4 和 IPv6 都支持 // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 - s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip, region_buffer, sizeof(region_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)); + cost_time = (int) (xdb_now() - s_time); if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip, err); + printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, (int)(xdb_now() - s_time)); + printf("{region: %s, took: %d μs}", region_buffer, cost_time); } - // 备注:并发使用,没一个线程需要单独定义并且初始化一个 searcher 查询对象。 + // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。 // 4、关闭 xdb 查询器,如果是要关闭服务,也需要释放 v_index 的内存。 xdb_close(&searcher); xdb_close_vector_index(v_index); + xdb_clean_winsock(); return 0; } ``` ### 缓存整个 `xdb` 数据 -我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。 +我们也可以预先加载整个 xdb 文件到内存,然后基于这个数据创建查询对象来实现完全基于内存的查询,类似之前的 memory search。 ```c #include #include "xdb_api.h" int main(int argc, char *argv[]) { - char *db_path = "ip2region.xdb file path"; xdb_content_t *c_buffer; xdb_searcher_t searcher; - char region_buffer[256], ip_buffer[16], *ip = "1.2.3.4"; - long s_time; + char region_buffer[512] = {'\0'}; - // 1、从 db_path 加载整个 xdb 的数据。 + + // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 + int err = xdb_init_winsock(); + if (err != 0) { + printf("failed to init the winsock with errno=%d\n", err); + return 1; + } + + // 1、从 顶部描述的 db_path 加载整个 xdb 的数据。 c_buffer = xdb_load_content_from_file(db_path); if (v_index == NULL) { printf("failed to load xdb content from `%s`\n", db_path); return 1; } - // 2、使用全局的 c_buffer 变量创建一个完全基于内存的 xdb 查询对象 - err = xdb_new_with_buffer(&searcher, c_buffer); + // 2、使用全局的 c_buffer 变量创建一个完全基于内存的 xdb 查询对象. + // @Note: 使用顶部描述的 version 来创建 searcher. + err = xdb_new_with_buffer(version, &searcher, c_buffer); if (err != 0) { printf("failed to create content cached searcher with errcode=%d\n", err); return 2; } - // 3、调用 search API 查询 + // 3、调用 search API 查询,IPv4 和 IPv6 都支持 // 得到的 region 信息会存储到 region_buffer 里面,如果你自定义了数据,请确保给足 buffer 的空间。 - s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip, region_buffer, sizeof(region_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)); + cost_time = (int) (xdb_now() - s_time); if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip, err); + printf("failed search(%s) with errno=%d\n", ip_string, err); } else { - printf("{region: %s, took: %d μs}", region_buffer, (int)(xdb_now() - s_time)); + printf("{region: %s, took: %d μs}", region_buffer, cost_time); } // 备注:并发使用,使用这种方式创建的 xdb 查询对象可以安全用于并发。 @@ -133,6 +201,7 @@ int main(int argc, char *argv[]) { // 4、关闭 xdb 查询器,关闭服务的时候需要释放 c_buffer 的内存。 xdb_close(&searcher); xdb_close_content(c_buffer); + xdb_clean_winsock(); return 0; } ``` @@ -143,29 +212,43 @@ int main(int argc, char *argv[]) { 通过如下方式编译得到 xdb_searcher 可执行程序: ```bash # cd 到 c binding 根目录 -make +➜ c git:(fr_c_ipv6) ✗ make +gcc -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher +gcc -O2 -I./ xdb_util.c test_util.c -o test_util ``` # 查询测试 -通过 `xdb_searcher search` 命令来测试对 ip2region.xdb 的查询: +通过 `xdb_searcher search` 命令来测试对 xdb 的查询: ```bash -➜ c git:(c_binding) ✗ ./xdb_searcher search +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search ./xdb_searcher search [command options] options: --db string ip2region binary xdb file path --cache-policy string cache policy: file/vectorIndex/content ``` -例如:使用默认的 data/ip2region.xdb 进行查询测试: +例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 查询测试: ```bash -➜ c git:(c_binding) ✗ ./xdb_searcher search --db=../../data/ip2region.xdb --cache-policy=vectorIndex -ip2region xdb searcher test program, cache_policy: vectorIndex +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v4.xdb +ip2region xdb searcher test program +source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex) type 'quit' to exit -ip2region>> 1.2.3.4 -{region: 美国|0|华盛顿|0|谷歌, io_count: 7, took: 13 μs} -ip2region>> +ip2region>> 120.229.45.2 +{region: 中国|广东省|深圳市|移动, io_count: 3, took: 29 μs} +``` + +例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 查询测试: +```bash +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v6.xdb +ip2region xdb searcher test program +source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) +type 'quit' to exit +ip2region>> :: +{region: |||, io_count: 2, took: 38 μs} +ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff +{region: 中国|广东省|深圳市|数据中心, io_count: 13, took: 77 μs} ``` 输入 ip 即可进行查询,输入 quit 即可退出测试程序。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同的缓存实现的效率。 @@ -176,7 +259,7 @@ ip2region>> 通过 `xdb_searcher bench` 命令来进行 bench 测试,一方面确保查询程序和 `xdb` 文件没有错误,另一方面可以通过大量的查询得到评价的查询性能: ```bash -➜ c git:(c_binding) ✗ ./xdb_searcher bench +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench ./xdb_searcher bench [command options] options: --db string ip2region binary xdb file path @@ -184,10 +267,16 @@ options: --cache-policy string cache policy: file/vectorIndex/content ``` -例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 来进行 bench 测试: +例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 来进行 IPv4 的 bench 测试: ```bash -➜ c git:(c_binding) ✗ ./xdb_searcher bench --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=vectorIndex -Bench finished, {cache_policy: vectorIndex, total: 3417955, took: 4.233s, cost: 1 μs/op} +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt +Bench finished, {cache_policy: vectorIndex, total: 1367686, took: 7.640s, cost: 5 μs/op} +``` + +例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 来进行 IPv6 的 bench 测试: +```bash +➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt +Bench finished, {cache_policy: vectorIndex, total: 34159862, took: 857.750s, cost: 24 μs/op} ``` 可以设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 不同缓存实现机制的效率。 @Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件相同的源文件。 diff --git a/binding/c/main.c b/binding/c/main.c index a209da3..eef96a8 100644 --- a/binding/c/main.c +++ b/binding/c/main.c @@ -18,7 +18,22 @@ typedef struct searcher_test_entry searcher_test_t; int init_searcher_test(searcher_test_t *test, char *db_path, char *cache_policy) { int err; - xdb_ip_version_t *version = XDB_IPv4; + + // auto detect the version from the xdb header + xdb_header_t *header = xdb_load_header_from_file(db_path); + if (header == NULL) { + printf("failed to load header from `%s`\n", db_path); + return 1; + } + + xdb_version_t *version = xdb_version_from_header(header); + if (version == NULL) { + printf("failed to load version from header\n"); + xdb_free_header(header); + return 1; + } + + xdb_free_header(header); test->v_index = NULL; test->c_buffer = NULL; @@ -112,7 +127,7 @@ void test_search(int argc, char *argv[]) { char line[512] = {'\0'}, region[512] = {'\0'}; // ip parse - xdb_ip_version_t *version; + xdb_version_t *version; bytes_ip_t ip_bytes[16] = {'\0'}; searcher_test_t test; @@ -158,6 +173,13 @@ void test_search(int argc, char *argv[]) { return; } + // init the win sock + err = xdb_init_winsock(); + if (err != 0) { + printf("failed to init the winsock with errno=%d\n", err); + return; + } + // printf("db_file=%s, cache_policy=%s\n", db_file, cache_policy); err = init_searcher_test(&test, db_file, cache_policy); if (err != 0) { @@ -165,8 +187,9 @@ void test_search(int argc, char *argv[]) { return; } - printf("ip2region xdb searcher test program, " - "cache_policy: %s\ntype 'quit' to exit\n", cache_policy); + printf("ip2region xdb searcher test program\n" + "source xdb: %s (%s, %s)\n" + "type 'quit' to exit\n", db_file, xdb_get_version(&test.searcher)->name, cache_policy); while ( 1 ) { printf("ip2region>> "); get_line(stdin, line); @@ -195,6 +218,7 @@ void test_search(int argc, char *argv[]) { } destroy_searcher_test(&test); + xdb_clean_winsock(); printf("searcher test program exited, thanks for trying\n"); } @@ -204,19 +228,18 @@ void test_bench(int argc, char *argv[]) { char db_file[256] = {'\0'}, src_file[256] = {'\0'}, cache_policy[16] = {"vectorIndex"}; FILE *handle; - char line[1024] = {'\0'}, sip_str[16] = {'\0'}, eip_str[16] = {'\0'}; + char line[1024] = {'\0'}, sip_str[INET6_ADDRSTRLEN+1] = {'\0'}, eip_str[INET6_ADDRSTRLEN+1] = {'\0'}; char src_region[512] = {'\0'}, region_buffer[512] = {'\0'}; int count = 0, took; long s_time, t_time, c_time = 0; // ip parse - xdb_ip_version_t *s_version, *e_version; + xdb_version_t *s_version, *e_version; bytes_ip_t sip_bytes[16] = {'\0'}, eip_bytes[16] = {'\0'}; string_ip_t ip_string[INET6_ADDRSTRLEN] = {'\0'}; bytes_ip_t *ip_list[2]; searcher_test_t test; - for (i = 2; i < argc; i++) { r = argv[i]; if (strlen(r) < 5) { @@ -259,6 +282,13 @@ void test_bench(int argc, char *argv[]) { return; } + // init the win sock + err = xdb_init_winsock(); + if (err != 0) { + printf("failed to init the winsock with errno=%d\n", err); + return; + } + // printf("db_file=%s, src_file=%s, cache_policy=%s\n", db_file, src_file, cache_policy); s_time = xdb_now(); err = init_searcher_test(&test, db_file, cache_policy); @@ -275,7 +305,7 @@ void test_bench(int argc, char *argv[]) { } while(fgets(line, sizeof(line), handle) != NULL) { - n = sscanf(line, "%15[^|]|%15[^|]|%511[^\n]", sip_str, eip_str, src_region); + n = sscanf(line, "%46[^|]|%46[^|]|%511[^\n]", sip_str, eip_str, src_region); if (n != 3) { printf("invalid ip segment line `%s`\n", line); return; @@ -328,6 +358,7 @@ void test_bench(int argc, char *argv[]) { took = xdb_now() - s_time; destroy_searcher_test(&test); + xdb_clean_winsock(); fclose(handle); printf("Bench finished, {cache_policy: %s, total: %d, took: %.3fs, cost: %d μs/op}\n", cache_policy, count, took/1e6, count == 0 ? 0 : (int)(c_time/count)); diff --git a/binding/c/test_util b/binding/c/test_util index 8ae3a2e..91a4af0 100755 Binary files a/binding/c/test_util and b/binding/c/test_util differ diff --git a/binding/c/test_util.c b/binding/c/test_util.c index ea39248..de41057 100644 --- a/binding/c/test_util.c +++ b/binding/c/test_util.c @@ -27,10 +27,13 @@ void test_load_header() { " created_at: %u, \n" " start_index_ptr: %d, \n" " end_index_ptr: %d\n" + " ip_version: %d\n" + " runtime_ptr_bytes: %d\n" " length: %d\n" "}\n", header->version, header->index_policy, header->created_at, - header->start_index_ptr, header->end_index_ptr, header->length + header->start_index_ptr, header->end_index_ptr, + header->ip_version, header->runtime_ptr_bytes, header->length ); } @@ -68,7 +71,7 @@ void test_parse_ip() { }; int errcode; - xdb_ip_version_t *version; + xdb_version_t *version; bytes_ip_t ip_bytes[16] = {'\0'}; string_ip_t ip_string[INET6_ADDRSTRLEN] = {'\0'}; @@ -117,7 +120,7 @@ void test_ip_compare() { struct ip_pair *pair_ptr = NULL; bytes_ip_t sip_bytes[16] = {'\0'}; bytes_ip_t eip_bytes[16] = {'\0'}; - xdb_ip_version_t *s_version, *e_version; + xdb_version_t *s_version, *e_version; int bytes, errcode; // init the sock env (for windows) diff --git a/binding/c/xdb_api.h b/binding/c/xdb_api.h index d0f7338..2e01e11 100644 --- a/binding/c/xdb_api.h +++ b/binding/c/xdb_api.h @@ -35,6 +35,8 @@ #define xdb_free( _ptr ) free( _ptr ) // public constants define +#define xdb_structure_20 2 +#define xdb_structure_30 3 #define xdb_header_info_length 256 #define xdb_vector_index_rows 256 #define xdb_vector_index_cols 256 @@ -42,93 +44,13 @@ #define xdb_segment_index_size 14 // --- ip version info -#define xdb_ipv4_version_no 4 -#define xdb_ipv6_version_no 6 +#define xdb_ipv4_id 4 +#define xdb_ipv6_id 6 #define xdb_ipv4_bytes 4 #define xdb_ipv6_bytes 16 // cache of vector_index_row × vector_index_rows × vector_index_size #define xdb_vector_index_length 524288 -// types type define -typedef char string_ip_t; -typedef unsigned char bytes_ip_t; - -// --- ip version -#define XDB_IPv4 (xdb_version_ipv4()) -#define XDB_IPv6 (xdb_version_ipv6()) -typedef int (* ip_compare_fn_t) (const bytes_ip_t *, int, const char *, int); -struct xdb_ip_version_entry { - int id; // version id - char *name; // version name - int bytes; // ip bytes number - int segment_index_size; // segment index size in bytes - - // function to compare two ips - ip_compare_fn_t ip_compare; -}; -typedef struct xdb_ip_version_entry xdb_ip_version_t; - -XDB_PUBLIC(xdb_ip_version_t *) xdb_version_ipv4(); -XDB_PUBLIC(xdb_ip_version_t *) xdb_version_ipv6(); - -XDB_PUBLIC(int) xdb_ip_version_is_v4(const xdb_ip_version_t *); -XDB_PUBLIC(int) xdb_ip_version_is_v6(const xdb_ip_version_t *); - -// --- END ip version - -// --- xdb util functions - -// to compatiable with the windows -// returns: 0 for ok and -1 for failed -XDB_PUBLIC(int) xdb_init_winsock(); -XDB_PUBLIC(void) xdb_clean_winsock(); - -// get the current time in microseconds -XDB_PUBLIC(long) xdb_now(); - -// get unsigned long (4bytes) from a specified buffer start from the specified offset with little-endian -XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *, int); - -// get unsigned short (2bytes) from a specified buffer start from the specified offset with little-endian -XDB_PUBLIC(int) xdb_le_get_uint16(const char *, int); - -// check the specified string ip and convert it to an unsigned int -XDB_PUBLIC(int) xdb_check_ip(const char *, unsigned int *); - -// unsigned int ip to string ip -XDB_PUBLIC(void) xdb_long2ip(unsigned int, char *); - - -// parse the specified IP address to byte array. -// returns: xdb_ip_version_t for valid ipv4 / ipv6, or NULL for failed -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_ip(const string_ip_t *, bytes_ip_t *, size_t); - -// parse the specified IPv4 address to byte array -// returns: xdb_ip_version_t for valid ipv4, or NULL for failed -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_v4_ip(const string_ip_t *, bytes_ip_t *, size_t); - -// parse the specified IPv6 address to byte array -// returns: xdb_ip_version_t for valid ipv6, or NULL for failed -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_v6_ip(const string_ip_t *, bytes_ip_t *, size_t); - -// convert a specified ip bytes to humen-readable string. -// returns: 0 for success or -1 for failed. -XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *, int, char *, size_t); - -// ipv4 bytes to string -XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *, char *, size_t); - -// ipv6 bytes to string -XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *, char *, size_t); - -// compare the specified ip bytes with another ip bytes in the specified buff from offset. -// ip args must be the return value from #xdb_parse_ip. -// returns: -1 if ip1 < ip2, 1 if ip1 > ip2 or 0 -XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *, int, const char *, int); - -// --- END xdb utils - - // --- xdb buffer functions // use the following buffer struct to wrap the binary buffer data @@ -186,12 +108,90 @@ XDB_PUBLIC(void) xdb_free_content(void *); // --- End xdb buffer + +// types type define +typedef char string_ip_t; +typedef unsigned char bytes_ip_t; + +// --- ip version +#define XDB_IPv4 (xdb_version_v4()) +#define XDB_IPv6 (xdb_version_v6()) +typedef int (* ip_compare_fn_t) (const bytes_ip_t *, int, const char *, int); +struct xdb_ip_version_entry { + int id; // version id + char *name; // version name + int bytes; // ip bytes number + int segment_index_size; // segment index size in bytes + + // function to compare two ips + ip_compare_fn_t ip_compare; +}; +typedef struct xdb_ip_version_entry xdb_version_t; + +XDB_PUBLIC(xdb_version_t *) xdb_version_v4(); +XDB_PUBLIC(xdb_version_t *) xdb_version_v6(); + +XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *); +XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *); + +XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *); +XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *); + +// --- END ip version + +// --- xdb util functions + +// to compatiable with the windows +// returns: 0 for ok and -1 for failed +XDB_PUBLIC(int) xdb_init_winsock(); +XDB_PUBLIC(void) xdb_clean_winsock(); + +// get the current time in microseconds +XDB_PUBLIC(long) xdb_now(); + +// get unsigned long (4bytes) from a specified buffer start from the specified offset with little-endian +XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *, int); + +// get unsigned short (2bytes) from a specified buffer start from the specified offset with little-endian +XDB_PUBLIC(int) xdb_le_get_uint16(const char *, int); + + +// parse the specified IP address to byte array. +// returns: xdb_version_t for valid ipv4 / ipv6, or NULL for failed +XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *, bytes_ip_t *, size_t); + +// parse the specified IPv4 address to byte array +// returns: xdb_version_t for valid ipv4, or NULL for failed +XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *, bytes_ip_t *, size_t); + +// parse the specified IPv6 address to byte array +// returns: xdb_version_t for valid ipv6, or NULL for failed +XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *, bytes_ip_t *, size_t); + +// convert a specified ip bytes to humen-readable string. +// returns: 0 for success or -1 for failed. +XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *, int, char *, size_t); + +// ipv4 bytes to string +XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *, char *, size_t); + +// ipv6 bytes to string +XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *, char *, size_t); + +// compare the specified ip bytes with another ip bytes in the specified buff from offset. +// ip args must be the return value from #xdb_parse_ip. +// returns: -1 if ip1 < ip2, 1 if ip1 > ip2 or 0 +XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *, int, const char *, int); + +// --- END xdb utils + + // --- xdb searcher api // xdb searcher structure struct xdb_searcher_entry { // ip version - xdb_ip_version_t *version; + xdb_version_t *version; // xdb file handle FILE *handle; @@ -212,11 +212,11 @@ struct xdb_searcher_entry { typedef struct xdb_searcher_entry xdb_searcher_t; // xdb searcher new api define -XDB_PUBLIC(int) xdb_new_with_file_only(xdb_ip_version_t *, xdb_searcher_t *, const char *); +XDB_PUBLIC(int) xdb_new_with_file_only(xdb_version_t *, xdb_searcher_t *, const char *); -XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_ip_version_t *, xdb_searcher_t *, const char *, const xdb_vector_index_t *); +XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_version_t *, xdb_searcher_t *, const char *, const xdb_vector_index_t *); -XDB_PUBLIC(int) xdb_new_with_buffer(xdb_ip_version_t *, xdb_searcher_t *, const xdb_content_t *); +XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *, xdb_searcher_t *, const xdb_content_t *); XDB_PUBLIC(void) xdb_close(void *); @@ -225,7 +225,7 @@ XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, char XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, char *, size_t); -XDB_PUBLIC(xdb_ip_version_t *) xdb_get_ip_version(xdb_searcher_t *); +XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *); XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *); diff --git a/binding/c/xdb_searcher.c b/binding/c/xdb_searcher.c index 275c30a..0beeb5f 100644 --- a/binding/c/xdb_searcher.c +++ b/binding/c/xdb_searcher.c @@ -11,7 +11,7 @@ // internal function prototype define XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length); -XDB_PRIVATE(int) xdb_new_base(xdb_ip_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index, const xdb_content_t *c_buffer) { +XDB_PRIVATE(int) xdb_new_base(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index, const xdb_content_t *c_buffer) { memset(xdb, 0x00, sizeof(xdb_searcher_t)); // set the version @@ -37,15 +37,15 @@ XDB_PRIVATE(int) xdb_new_base(xdb_ip_version_t *version, xdb_searcher_t *xdb, co } // xdb searcher new api define -XDB_PUBLIC(int) xdb_new_with_file_only(xdb_ip_version_t *version, xdb_searcher_t *xdb, const char *db_path) { +XDB_PUBLIC(int) xdb_new_with_file_only(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path) { return xdb_new_base(version, xdb, db_path, NULL, NULL); } -XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_ip_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index) { +XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index) { return xdb_new_base(version, xdb, db_path, v_index, NULL); } -XDB_PUBLIC(int) xdb_new_with_buffer(xdb_ip_version_t *version, xdb_searcher_t *xdb, const xdb_content_t *c_buffer) { +XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *version, xdb_searcher_t *xdb, const xdb_content_t *c_buffer) { return xdb_new_base(version, xdb, NULL, NULL, c_buffer); } @@ -61,7 +61,7 @@ XDB_PUBLIC(void) xdb_close(void *ptr) { XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_ip_t *ip_string, char *region_buffer, size_t length) { bytes_ip_t ip_bytes[16] = {'\0'}; - xdb_ip_version_t *version = xdb_parse_ip(ip_string, ip_bytes, sizeof(ip_bytes)); + xdb_version_t *version = xdb_parse_ip(ip_string, ip_bytes, sizeof(ip_bytes)); if (version == NULL) { return 10; } else { @@ -75,7 +75,6 @@ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int unsigned int s_ptr, e_ptr, data_ptr, data_len; char vector_buffer[xdb_vector_index_size]; char *segment_buffer = NULL; - string_ip_t sip_string[INET6_ADDRSTRLEN] = {'\0'}, eip_string[INET6_ADDRSTRLEN] = {'\0'}; // ip version check if (ip_len != xdb->version->bytes) { @@ -109,7 +108,7 @@ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int e_ptr = xdb_le_get_uint32(vector_buffer, 4); } - printf("s_ptr=%u, e_ptr=%u\n", s_ptr, e_ptr); + // printf("s_ptr=%u, e_ptr=%u\n", s_ptr, e_ptr); // binary search to get the final region info seg_index_size = xdb->version->segment_index_size; segment_buffer = xdb_malloc(seg_index_size); @@ -124,16 +123,12 @@ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int p = s_ptr + m * seg_index_size; // read the segment index item - err = read(xdb, p, segment_buffer, sizeof(segment_buffer)); + err = read(xdb, p, segment_buffer, seg_index_size); if (err != 0) { err += 20; goto done; } - xdb_ip_to_string(segment_buffer, bytes, sip_string, sizeof(sip_string)); - xdb_ip_to_string(segment_buffer + bytes, bytes, eip_string, sizeof(eip_string)); - printf("l=%d, h=%d, p=%d, sip: %s, eip: %s\n", l, h, p, sip_string, eip_string); - // decode the data fields as needed if (xdb->version->ip_compare(ip_bytes, bytes, segment_buffer, 0) < 0) { h = m - 1; @@ -146,7 +141,7 @@ 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); + // printf("data_len=%u, data_ptr=%u\n", data_len, data_ptr); if (data_len == 0) { goto done; } @@ -195,7 +190,7 @@ XDB_PRIVATE(int) read(xdb_searcher_t *xdb, long offset, char *buffer, size_t len return 0; } -XDB_PUBLIC(xdb_ip_version_t *) xdb_get_ip_version(xdb_searcher_t *xdb) { +XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *xdb) { return xdb->version; } diff --git a/binding/c/xdb_util.c b/binding/c/xdb_util.c index 6699bc0..29e40b9 100644 --- a/binding/c/xdb_util.c +++ b/binding/c/xdb_util.c @@ -7,6 +7,7 @@ // @Date 2022/06/27 #include "xdb_api.h" +#include // for Linux #ifdef XDB_LINUX @@ -58,182 +59,6 @@ XDB_PUBLIC(int) xdb_init_winsock() {return 0;} XDB_PUBLIC(void) xdb_clean_winsock() {} #endif -// --- ip version - -// ip compare for IPv4 -// ip1 - with Big endian byte order parsed from an input -// ip2 - with Little endian byte order read from the xdb index. -// to compatiable with the Little Endian encoded IPv4 on xdb 2.0. -XDB_PRIVATE(int) _ipv4_sub_compare(const bytes_ip_t *ip_bytes, int bytes, const char *buffer, int offset) { - register int i0, i1; - for (int i = 0, j = offset + bytes - 1; i < bytes; i++, j--) { - i0 = ip_bytes[i]; - i1 = buffer[j] & 0xFF; - if (i0 > i1) { - return 1; - } else if (i0 < i1) { - return -1; - } - } - return 0; -} - -static xdb_ip_version_t _ip_version_list[] = { - // 14 = 4 + 4 + 2 + 4 - {xdb_ipv4_version_no, "IPv4", xdb_ipv4_bytes, 14, _ipv4_sub_compare}, - - // 38 = 16 + 16 + 2 + 4 - {xdb_ipv6_version_no, "IPv6", xdb_ipv6_bytes, 38, xdb_ip_sub_compare}, - - // END - {0, NULL, 0, 0, NULL} -}; - -XDB_PUBLIC(xdb_ip_version_t *) xdb_version_ipv4() { - return &_ip_version_list[0]; -} - -XDB_PUBLIC(xdb_ip_version_t *) xdb_version_ipv6() { - return &_ip_version_list[1]; -} - -XDB_PUBLIC(int) xdb_ip_version_is_v4(const xdb_ip_version_t *version) { - return version->id == xdb_ipv4_version_no; -} - -XDB_PUBLIC(int) xdb_ip_version_is_v6(const xdb_ip_version_t *version) { - return version->id == xdb_ipv6_version_no; -} - -// --- END ip version - -XDB_PUBLIC(long) xdb_now() { - struct timeval c_time; - gettimeofday(&c_time, NULL); - return c_time.tv_sec * (int)1e6 + c_time.tv_usec; -} - -XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *buffer, int offset) { - return ( - ((buffer[offset ]) & 0x000000FF) | - ((buffer[offset+1] << 8) & 0x0000FF00) | - ((buffer[offset+2] << 16) & 0x00FF0000) | - ((buffer[offset+3] << 24) & 0xFF000000) - ); -} - -XDB_PUBLIC(int) xdb_le_get_uint16(const char *buffer, int offset) { - return ( - ((buffer[offset ]) & 0x000000FF) | - ((buffer[offset+1] << 8) & 0x0000FF00) - ); -} - -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - // version check - if (strchr(ip_string, '.') != NULL && strchr(ip_string, ':') == NULL) { - return xdb_parse_v4_ip(ip_string, buffer, length); - } else if (strchr(ip_string, ':') != NULL) { - return xdb_parse_v6_ip(ip_string, buffer, length); - } - - return NULL; -} - -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_v4_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - struct in_addr addr; - - // buffer length checking - if (length < xdb_ipv4_bytes) { - return NULL; - } - - if (inet_pton(AF_INET, ip_string, &addr) != 1) { - return NULL; - } - - // encode the address to buffer with big endian byte bufffer. - buffer[0] = (addr.s_addr) & 0xFF; - buffer[1] = (addr.s_addr >> 8) & 0xFF; - buffer[2] = (addr.s_addr >> 16) & 0xFF; - buffer[3] = (addr.s_addr >> 24) & 0xFF; - return XDB_IPv4; -} - -XDB_PUBLIC(xdb_ip_version_t *) xdb_parse_v6_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - struct in6_addr addr; - - // buffer length checking - if (length < xdb_ipv6_bytes) { - return NULL; - } - - if (inet_pton(AF_INET6, ip_string, &addr) != 1) { - return NULL; - } - - memcpy(buffer, addr.s6_addr, xdb_ipv6_bytes); - return XDB_IPv6; -} - -XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *ip_bytes, int bytes, char *ip_string, size_t length) { - if (bytes == xdb_ipv4_bytes) { - return xdb_v4_ip_to_string(ip_bytes, ip_string, length); - } else if (bytes == xdb_ipv6_bytes) { - return xdb_v6_ip_to_string(ip_bytes, ip_string, length); - } - - return -1; -} - -XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { - if (!ip_bytes || !ip_string || length == 0) { - return -1; - } - - // buffer length checking - if (length < INET_ADDRSTRLEN) { - return -1; - } - - if (inet_ntop(AF_INET, ip_bytes, ip_string, length) == NULL) { - return -1; - } - - return 0; -} - -XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { - if (!ip_bytes || !ip_string || length == 0) { - return -1; - } - - if (length < INET6_ADDRSTRLEN) { - return -1; - } - - if (inet_ntop(AF_INET6, ip_bytes, ip_string, length) == NULL) { - return -1; - } - - return 0; -} - -XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *ip1, int bytes, const char *buffer, int offset) { - register int i, i1, i2; - for (i = 0; i < bytes; i++) { - i1 = ip1[i]; - i2 = buffer[offset + i] & 0xFF; - if (i1 > i2) { - return 1; - } else if (i1 < i2) { - return -1; - } - } - return 0; -} - - // --- xdb buffer function implementations XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *handle) { @@ -398,4 +223,215 @@ XDB_PUBLIC(void) xdb_free_content(void *ptr) { } } -// --- End +// --- End content buffer + + +// --- ip version + +// ip compare for IPv4 +// ip1 - with Big endian byte order parsed from an input +// ip2 - with Little endian byte order read from the xdb index. +// to compatiable with the Little Endian encoded IPv4 on xdb 2.0. +XDB_PRIVATE(int) _ipv4_sub_compare(const bytes_ip_t *ip_bytes, int bytes, const char *buffer, int offset) { + register int i0, i1; + for (int i = 0, j = offset + bytes - 1; i < bytes; i++, j--) { + i0 = ip_bytes[i]; + i1 = buffer[j] & 0xFF; + if (i0 > i1) { + return 1; + } else if (i0 < i1) { + return -1; + } + } + return 0; +} + +static xdb_version_t _ip_version_list[] = { + // 14 = 4 + 4 + 2 + 4 + {xdb_ipv4_id, "IPv4", xdb_ipv4_bytes, 14, _ipv4_sub_compare}, + + // 38 = 16 + 16 + 2 + 4 + {xdb_ipv6_id, "IPv6", xdb_ipv6_bytes, 38, xdb_ip_sub_compare}, + + // END + {0, NULL, 0, 0, NULL} +}; + +XDB_PUBLIC(xdb_version_t *) xdb_version_v4() { + return &_ip_version_list[0]; +} + +XDB_PUBLIC(xdb_version_t *) xdb_version_v6() { + return &_ip_version_list[1]; +} + +XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *version) { + return version->id == xdb_ipv4_id; +} + +XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *version) { + return version->id == xdb_ipv6_id; +} + +XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *name) { + // to upper case the name + for (int i = 0; name[i] != '\0'; i++) { + name[i] = toupper((unsigned char) name[i]); + } + + if (strcmp(name, "V4") == 0 || strcmp(name, "IPV4") == 0) { + return xdb_version_v4(); + } else if (strcmp(name, "V6") == 0 || strcmp(name, "IPV6") == 0) { + return xdb_version_v6(); + } else { + return NULL; + } +} + +XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *header) { + // Old structure with ONLY IPv4 supports + if (header->version == xdb_structure_20) { + return xdb_version_v4(); + } + + // structure 3.0 with IPv6 supporting + if (header->version != xdb_structure_30) { + return NULL; + } + + if (header->ip_version == xdb_ipv4_id) { + return xdb_version_v4(); + } else if (header->ip_version == xdb_ipv6_id) { + return xdb_version_v6(); + } else { + return NULL; + } +} + +// --- END ip version + +XDB_PUBLIC(long) xdb_now() { + struct timeval c_time; + gettimeofday(&c_time, NULL); + return c_time.tv_sec * (int)1e6 + c_time.tv_usec; +} + +XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *buffer, int offset) { + return ( + ((buffer[offset ]) & 0x000000FF) | + ((buffer[offset+1] << 8) & 0x0000FF00) | + ((buffer[offset+2] << 16) & 0x00FF0000) | + ((buffer[offset+3] << 24) & 0xFF000000) + ); +} + +XDB_PUBLIC(int) xdb_le_get_uint16(const char *buffer, int offset) { + return ( + ((buffer[offset ]) & 0x000000FF) | + ((buffer[offset+1] << 8) & 0x0000FF00) + ); +} + +XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { + // version check + if (strchr(ip_string, '.') != NULL && strchr(ip_string, ':') == NULL) { + return xdb_parse_v4_ip(ip_string, buffer, length); + } else if (strchr(ip_string, ':') != NULL) { + return xdb_parse_v6_ip(ip_string, buffer, length); + } + + return NULL; +} + +XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { + struct in_addr addr; + + // buffer length checking + if (length < xdb_ipv4_bytes) { + return NULL; + } + + if (inet_pton(AF_INET, ip_string, &addr) != 1) { + return NULL; + } + + // encode the address to buffer with big endian byte bufffer. + buffer[0] = (addr.s_addr) & 0xFF; + buffer[1] = (addr.s_addr >> 8) & 0xFF; + buffer[2] = (addr.s_addr >> 16) & 0xFF; + buffer[3] = (addr.s_addr >> 24) & 0xFF; + return XDB_IPv4; +} + +XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { + struct in6_addr addr; + + // buffer length checking + if (length < xdb_ipv6_bytes) { + return NULL; + } + + if (inet_pton(AF_INET6, ip_string, &addr) != 1) { + return NULL; + } + + memcpy(buffer, addr.s6_addr, xdb_ipv6_bytes); + return XDB_IPv6; +} + +XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *ip_bytes, int bytes, char *ip_string, size_t length) { + if (bytes == xdb_ipv4_bytes) { + return xdb_v4_ip_to_string(ip_bytes, ip_string, length); + } else if (bytes == xdb_ipv6_bytes) { + return xdb_v6_ip_to_string(ip_bytes, ip_string, length); + } + + return -1; +} + +XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { + if (!ip_bytes || !ip_string || length == 0) { + return -1; + } + + // buffer length checking + if (length < INET_ADDRSTRLEN) { + return -1; + } + + if (inet_ntop(AF_INET, ip_bytes, ip_string, length) == NULL) { + return -1; + } + + return 0; +} + +XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { + if (!ip_bytes || !ip_string || length == 0) { + return -1; + } + + if (length < INET6_ADDRSTRLEN) { + return -1; + } + + if (inet_ntop(AF_INET6, ip_bytes, ip_string, length) == NULL) { + return -1; + } + + return 0; +} + +XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *ip1, int bytes, const char *buffer, int offset) { + register int i, i1, i2; + for (i = 0; i < bytes; i++) { + i1 = ip1[i]; + i2 = buffer[offset + i] & 0xFF; + if (i1 > i2) { + return 1; + } else if (i1 < i2) { + return -1; + } + } + return 0; +}