IPv6 search and docs is ready

This commit is contained in:
lion 2025-09-18 15:53:48 +08:00
parent 73c8b20d3a
commit 8fbdf82797
7 changed files with 491 additions and 337 deletions

View File

@ -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 <stdio.h>
#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 文件相同的源文件。

View File

@ -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));

Binary file not shown.

View File

@ -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)

View File

@ -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 *);

View File

@ -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;
}

View File

@ -7,6 +7,7 @@
// @Date 2022/06/27
#include "xdb_api.h"
#include <ctype.h>
// 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;
}