ip2region_ext 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 29c808556500f049f3e21908c35fbb809b992874f21f9cf81478c862d541f0f0
4
+ data.tar.gz: e40d4f499e75a22adea750e529dbaee7be7ad25b0549ba9b4c0d703596593c60
5
+ SHA512:
6
+ metadata.gz: '048a8fdd7e4942b348ce08f1d3d962c2bdb3d64535b86471200b569ef427bb82be6a9913f6aa83462247acc6af78c8a475d8bba7ae0d15fe4492a93b3d80293c'
7
+ data.tar.gz: c89112c8ea4797e9e5288ad8c906042dd532a1e1440937ed04dba94a84877cc3c073fb78c4c0597744c5a46b5b59edfe5268b8ab936cf44543fc1c9b0657fd01
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+
2
+ # Ip2regionExt
3
+
4
+ ## 介绍
5
+
6
+ 这是一个ip2region库c客户端的ruby ffi扩展, 基于 [lionsoul2014/ip2region](https://github.com/lionsoul2014/ip2region) 最新版本开发
7
+
8
+ 直接调用c原生api,充分发挥IO操作的性能优势
9
+
10
+ ## 安装
11
+ `gem install ip2region_ext`
12
+ ## 使用
13
+
14
+ ```ruby
15
+ require 'ip2region_ext'
16
+
17
+ # 查询
18
+ #db_type = :file|:index|:cache
19
+ #Ip2regionExt.connect({db_type: :index, db_path: "/var/ip2region.xdb"})
20
+ Ip2regionExt.connect({db_type: :index})
21
+ p Ip2regionExt.search('114.114.114.114')
22
+ Ip2regionExt.close
23
+ # => "中国|0|辽宁省|丹东市|联通"
24
+
25
+ ```
26
+
27
+ ## 如何下载外挂 xdb 文件
28
+
29
+ 下载这个文件 [https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb)
30
+
31
+
32
+ ## 协议
33
+
34
+ MIT 协议
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
data/db/ip2region.xdb ADDED
Binary file
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'mkmf'
3
+
4
+ extension_name = 'libip2region_ext'
5
+ dir_config(extension_name)
6
+
7
+ create_makefile(extension_name)
8
+
@@ -0,0 +1,428 @@
1
+ // Copyright 2022 The Ip2Region Authors. All rights reserved.
2
+ // Use of this source code is governed by a Apache2.0-style
3
+ // license that can be found in the LICENSE file.
4
+
5
+ // ---
6
+ // @Author Lion <chenxin619315@gmail.com>
7
+ // @Date 2022/06/27
8
+
9
+ #include "xdb_searcher.h"
10
+
11
+ // for Linux
12
+ #ifdef XDB_LINUX
13
+ #include "sys/time.h"
14
+ #endif
15
+
16
+ // @Note: since 2023/10/13 to compatible with the windows system
17
+ #ifdef XDB_WINDOWS
18
+ #include <windows.h>
19
+ XDB_PRIVATE(int) gettimeofday(struct timeval* tp, void* tzp) {
20
+ FILETIME ft;
21
+ ULARGE_INTEGER uint64;
22
+ long long ns100 = 116444736000000000LL;
23
+
24
+ GetSystemTimeAsFileTime(&ft);
25
+ uint64.LowPart = ft.dwLowDateTime;
26
+ uint64.HighPart = ft.dwHighDateTime;
27
+ uint64.QuadPart -= ns100;
28
+ uint64.QuadPart /= 10;
29
+
30
+ tp->tv_sec = uint64.QuadPart / 1000000;
31
+ tp->tv_usec = uint64.QuadPart % 1000000;
32
+
33
+ return 0;
34
+ }
35
+ #endif
36
+
37
+
38
+ // internal function prototype define
39
+ XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length);
40
+
41
+ XDB_PRIVATE(int) xdb_new_base(xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index, const xdb_content_t *c_buffer) {
42
+ memset(xdb, 0x00, sizeof(xdb_searcher_t));
43
+
44
+ // check the content buffer first
45
+ if (c_buffer != NULL) {
46
+ xdb->v_index = NULL;
47
+ xdb->content = c_buffer;
48
+ return 0;
49
+ }
50
+
51
+ // open the xdb binary file
52
+ FILE *handle = fopen(db_path, "rb");
53
+ if (handle == NULL) {
54
+ return 1;
55
+ }
56
+
57
+ xdb->handle = handle;
58
+ xdb->v_index = v_index;
59
+
60
+ return 0;
61
+ }
62
+
63
+ // xdb searcher new api define
64
+ XDB_PUBLIC(int) xdb_new_with_file_only(xdb_searcher_t *xdb, const char *db_path) {
65
+ return xdb_new_base(xdb, db_path, NULL, NULL);
66
+ }
67
+
68
+ XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index) {
69
+ return xdb_new_base(xdb, db_path, v_index, NULL);
70
+ }
71
+
72
+ XDB_PUBLIC(int) xdb_new_with_buffer(xdb_searcher_t *xdb, const xdb_content_t *c_buffer) {
73
+ return xdb_new_base(xdb, NULL, NULL, c_buffer);
74
+ }
75
+
76
+ XDB_PUBLIC(void) xdb_close(void *ptr) {
77
+ xdb_searcher_t *xdb = (xdb_searcher_t *) ptr;
78
+ if (xdb->handle != NULL) {
79
+ fclose(xdb->handle);
80
+ xdb->handle = NULL;
81
+ }
82
+ }
83
+
84
+ // --- xdb searcher search api define
85
+
86
+ XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const char *str_ip, char *region_buffer, size_t length) {
87
+ unsigned int ip = 0;
88
+ int errcode = xdb_check_ip(str_ip, &ip);
89
+ if (errcode != 0) {
90
+ return 10 + errcode;
91
+ } else {
92
+ return xdb_search(xdb, ip, region_buffer, length);
93
+ }
94
+ }
95
+
96
+ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, unsigned int ip, char *region_buffer, size_t length) {
97
+ int il0, il1, idx, err, l, h, m, data_len;
98
+ unsigned int s_ptr, e_ptr, p, sip, eip, data_ptr;
99
+ char vector_buffer[xdb_vector_index_size], segment_buffer[xdb_segment_index_size];
100
+
101
+ // reset the io counter
102
+ xdb->io_count = 0;
103
+
104
+ // locate the segment index block based on the vector index
105
+ il0 = ((int) (ip >> 24)) & 0xFF;
106
+ il1 = ((int) (ip >> 16)) & 0xFF;
107
+ idx = il0 * xdb_vector_index_cols * xdb_vector_index_size + il1 * xdb_vector_index_size;
108
+ if (xdb->v_index != NULL) {
109
+ s_ptr = xdb_get_uint(xdb->v_index->buffer, idx);
110
+ e_ptr = xdb_get_uint(xdb->v_index->buffer, idx + 4);
111
+ } else if (xdb->content != NULL) {
112
+ s_ptr = xdb_get_uint(xdb->content->buffer, xdb_header_info_length + idx);
113
+ e_ptr = xdb_get_uint(xdb->content->buffer, xdb_header_info_length + idx + 4);
114
+ } else {
115
+ err = read(xdb, xdb_header_info_length + idx, vector_buffer, sizeof(vector_buffer));
116
+ if (err != 0) {
117
+ return 10 + err;
118
+ }
119
+
120
+ s_ptr = xdb_get_uint(vector_buffer, 0);
121
+ e_ptr = xdb_get_uint(vector_buffer, 4);
122
+ }
123
+
124
+ // printf("s_ptr=%u, e_ptr=%u\n", s_ptr, e_ptr);
125
+ // binary search to get the final region info
126
+ data_len = 0, data_ptr = 0;
127
+ l = 0, h = ((int) (e_ptr - s_ptr)) / xdb_segment_index_size;
128
+ while (l <= h) {
129
+ m = (l + h) >> 1;
130
+ p = s_ptr + m * xdb_segment_index_size;
131
+
132
+ // read the segment index item
133
+ err = read(xdb, p, segment_buffer, sizeof(segment_buffer));
134
+ if (err != 0) {
135
+ return 20 + err;
136
+ }
137
+
138
+ // decode the data fields as needed
139
+ sip = xdb_get_uint(segment_buffer, 0);
140
+ if (ip < sip) {
141
+ h = m - 1;
142
+ } else {
143
+ eip = xdb_get_uint(segment_buffer, 4);
144
+ if (ip > eip) {
145
+ l = m + 1;
146
+ } else {
147
+ data_len = xdb_get_ushort(segment_buffer, 8);
148
+ data_ptr = xdb_get_uint(segment_buffer, 10);
149
+ break;
150
+ }
151
+ }
152
+ }
153
+
154
+ // printf("data_len=%u, data_ptr=%u\n", data_len, data_ptr);
155
+ if (data_len == 0) {
156
+ region_buffer[0] = '\0';
157
+ return 0;
158
+ }
159
+
160
+ // buffer length checking
161
+ if (data_len >= (int) length) {
162
+ return 1;
163
+ }
164
+
165
+ err = read(xdb, data_ptr, region_buffer, data_len);
166
+ if (err != 0) {
167
+ return 30 + err;
168
+ }
169
+
170
+ // auto append a NULL-end
171
+ region_buffer[data_len] = '\0';
172
+ return 0;
173
+ }
174
+
175
+ XDB_PRIVATE(int) read(xdb_searcher_t *xdb, long offset, char *buffer, size_t length) {
176
+ // check the xdb content cache first
177
+ if (xdb->content != NULL) {
178
+ memcpy(buffer, xdb->content->buffer + offset, length);
179
+ return 0;
180
+ }
181
+
182
+ // seek to the offset
183
+ if (fseek(xdb->handle, offset, SEEK_SET) == -1) {
184
+ return 1;
185
+ }
186
+
187
+ xdb->io_count++;
188
+ if (fread(buffer, 1, length, xdb->handle) != length) {
189
+ return 2;
190
+ }
191
+
192
+ return 0;
193
+ }
194
+
195
+ XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *xdb) {
196
+ return xdb->io_count;
197
+ }
198
+
199
+
200
+ // --- buffer load util functions
201
+
202
+ XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *handle) {
203
+ xdb_header_t *header;
204
+ unsigned int size = xdb_header_info_length;
205
+
206
+ // entry alloc
207
+ header = (xdb_header_t *) xdb_malloc(sizeof(xdb_header_t));
208
+ if (header == NULL) {
209
+ return NULL;
210
+ }
211
+
212
+ if (fseek(handle, 0, SEEK_SET) == -1) {
213
+ xdb_free(header);
214
+ return NULL;
215
+ }
216
+
217
+ if (fread(header->buffer, 1,size, handle) != size) {
218
+ xdb_free(header);
219
+ return NULL;
220
+ }
221
+
222
+ // fill the fields
223
+ header->length = size;
224
+ header->version = (unsigned short) xdb_get_ushort(header->buffer, 0);
225
+ header->index_policy = (unsigned short) xdb_get_ushort(header->buffer, 2);
226
+ header->created_at = xdb_get_uint(header->buffer, 4);
227
+ header->start_index_ptr = xdb_get_uint(header->buffer, 8);
228
+ header->end_index_ptr = xdb_get_uint(header->buffer,12);
229
+
230
+ return header;
231
+ }
232
+
233
+ XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *db_path) {
234
+ xdb_header_t *header;
235
+ FILE *handle = fopen(db_path, "rb");
236
+ if (handle == NULL) {
237
+ return NULL;
238
+ }
239
+
240
+ header = xdb_load_header(handle);
241
+ fclose(handle);
242
+ return header;
243
+ }
244
+
245
+ XDB_PUBLIC(void) xdb_close_header(void *ptr) {
246
+ xdb_header_t *header = (xdb_header_t *) ptr;
247
+ if (header->length > 0) {
248
+ header->length = 0;
249
+ xdb_free(header);
250
+ }
251
+ }
252
+
253
+ // --- vector index
254
+
255
+ XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *handle) {
256
+ xdb_vector_index_t *v_index;
257
+ unsigned int size = xdb_vector_index_length;
258
+
259
+ // seek to the vector index offset
260
+ if (fseek(handle, xdb_header_info_length, SEEK_SET) == -1) {
261
+ return NULL;
262
+ }
263
+
264
+ // do the buffer read
265
+ v_index = (xdb_vector_index_t *) xdb_malloc(sizeof(xdb_vector_index_t));
266
+ if (v_index == NULL) {
267
+ return NULL;
268
+ }
269
+
270
+ v_index->length = size;
271
+ if (fread(v_index->buffer, 1, size, handle) != size) {
272
+ xdb_free(v_index);
273
+ return NULL;
274
+ }
275
+
276
+ return v_index;
277
+ }
278
+
279
+ XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *db_path) {
280
+ xdb_vector_index_t *v_index;
281
+ FILE *handle = fopen(db_path, "rb");
282
+ if (handle == NULL) {
283
+ return NULL;
284
+ }
285
+
286
+ v_index = xdb_load_vector_index(handle);
287
+ fclose(handle);
288
+ return v_index;
289
+ }
290
+
291
+ XDB_PUBLIC(void) xdb_close_vector_index(void *ptr) {
292
+ xdb_vector_index_t *v_index = (xdb_vector_index_t *) ptr;
293
+ if (v_index->length > 0) {
294
+ v_index->length = 0;
295
+ xdb_free(v_index);
296
+ }
297
+ }
298
+
299
+ // --- content buffer
300
+
301
+ XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *handle) {
302
+ unsigned int size;
303
+ xdb_content_t *content;
304
+
305
+ // determine the file size
306
+ if (fseek(handle, 0, SEEK_END) == -1) {
307
+ return NULL;
308
+ }
309
+
310
+ size = (unsigned int) ftell(handle);
311
+ if (fseek(handle, 0, SEEK_SET) == -1) {
312
+ return NULL;
313
+ }
314
+
315
+ // do the file read
316
+ content = (xdb_content_t *) xdb_malloc(sizeof(xdb_content_t));
317
+ if (content == NULL) {
318
+ return NULL;
319
+ }
320
+
321
+ // do the buffer alloc
322
+ content->buffer = (char *) xdb_malloc(size);
323
+ if (content->buffer == NULL) {
324
+ xdb_free(content);
325
+ return NULL;
326
+ }
327
+
328
+ // read the content into the buffer
329
+ content->length = size;
330
+ if (fread(content->buffer, 1, size, handle) != size) {
331
+ xdb_free(content);
332
+ return NULL;
333
+ }
334
+
335
+ return content;
336
+ }
337
+
338
+ XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *db_path) {
339
+ xdb_content_t *content;
340
+ FILE *handle = fopen(db_path, "rb");
341
+ if (handle == NULL) {
342
+ return NULL;
343
+ }
344
+
345
+ content = xdb_load_content(handle);
346
+ fclose(handle);
347
+ return content;
348
+ }
349
+
350
+ XDB_PUBLIC(void) xdb_close_content(void *ptr) {
351
+ xdb_content_t *content = (xdb_content_t *) ptr;
352
+ if (content->length > 0) {
353
+ content->length = 0;
354
+ xdb_free(content->buffer);
355
+ content->buffer = NULL;
356
+ xdb_free(content);
357
+ }
358
+ }
359
+
360
+ // --- End
361
+
362
+ // get unsigned long (4bytes) from a specified buffer start from the specified offset
363
+ XDB_PUBLIC(unsigned int) xdb_get_uint(const char *buffer, int offset) {
364
+ return (
365
+ ((buffer[offset ]) & 0x000000FF) |
366
+ ((buffer[offset+1] << 8) & 0x0000FF00) |
367
+ ((buffer[offset+2] << 16) & 0x00FF0000) |
368
+ ((buffer[offset+3] << 24) & 0xFF000000)
369
+ );
370
+ }
371
+
372
+ // get unsigned short (2bytes) from a specified buffer start from the specified offset
373
+ XDB_PUBLIC(int) xdb_get_ushort(const char *buffer, int offset) {
374
+ return (
375
+ ((buffer[offset ]) & 0x000000FF) |
376
+ ((buffer[offset+1] << 8) & 0x0000FF00)
377
+ );
378
+ }
379
+
380
+ // string ip to unsigned int
381
+ static int shiftIndex[4] = {24, 16, 8, 0};
382
+ XDB_PUBLIC(int) xdb_check_ip(const char *src_ip, unsigned int *dst_ip) {
383
+ char c;
384
+ int i, n, ip = 0;
385
+ const char *ptr = src_ip;
386
+ for (i = 0; i < 4; i++) {
387
+ n = 0;
388
+ while (1) {
389
+ c = *ptr;
390
+ ptr++;
391
+ if (c >= '0' && c <= '9') {
392
+ n *= 10;
393
+ n += c - '0';
394
+ } else if ((i < 3 && c == '.') || i == 3) {
395
+ // stopping at the '.' but ignore the tailing chars
396
+ // after the 3rd one (auto clean the tailing none-integer ?).
397
+ break;
398
+ } else {
399
+ return 1;
400
+ }
401
+ }
402
+
403
+ if (n > 0xFF) {
404
+ return 2;
405
+ }
406
+
407
+ ip |= (n << shiftIndex[i]);
408
+ }
409
+
410
+ *dst_ip = ip;
411
+ return 0;
412
+ }
413
+
414
+ // unsigned int ip to string ip
415
+ XDB_PUBLIC(void) xdb_long2ip(unsigned int ip, char *buffer) {
416
+ sprintf(buffer, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
417
+ }
418
+
419
+ // get the middle ip of a and b
420
+ XDB_PUBLIC(unsigned int) xdb_mip(unsigned long a, unsigned long b) {
421
+ return (unsigned int) ((a + b) >> 1);
422
+ }
423
+
424
+ XDB_PUBLIC(long) xdb_now() {
425
+ struct timeval c_time;
426
+ gettimeofday(&c_time, NULL);
427
+ return c_time.tv_sec * (int)1e6 + c_time.tv_usec;
428
+ }
@@ -0,0 +1,149 @@
1
+ // Copyright 2022 The Ip2Region Authors. All rights reserved.
2
+ // Use of this source code is governed by a Apache2.0-style
3
+ // license that can be found in the LICENSE file.
4
+
5
+ // ---
6
+ // @Author Lion <chenxin619315@gmail.com>
7
+ // @Date 2022/06/27
8
+
9
+ #ifndef C_XDB_SEARCHER_H
10
+ #define C_XDB_SEARCHER_H
11
+
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+
16
+ #if ( defined(WIN32) || defined(_WIN32) || defined(__WINDOWS_) || defined(WINNT) )
17
+ # define XDB_PUBLIC(type) extern __declspec(dllexport) type
18
+ # define XDB_PRIVATE(type) static type
19
+ # define XDB_WINDOWS
20
+ #elif ( defined(linux) || defined(_UNIX) || defined(__APPLE__) )
21
+ # define XDB_PUBLIC(type) extern type
22
+ # define XDB_PRIVATE(type) static inline type
23
+ # define XDB_LINUX
24
+ #endif
25
+
26
+ #define xdb_calloc( _blocks, _bytes ) calloc( _blocks, _bytes )
27
+ #define xdb_malloc( _bytes ) malloc( _bytes )
28
+ #define xdb_free( _ptr ) free( _ptr )
29
+
30
+ // public constants define
31
+ #define xdb_header_info_length 256
32
+ #define xdb_vector_index_rows 256
33
+ #define xdb_vector_index_cols 256
34
+ #define xdb_vector_index_size 8
35
+ #define xdb_segment_index_size 14
36
+
37
+ // cache of vector_index_row × vector_index_rows × vector_index_size
38
+ #define xdb_vector_index_length 524288
39
+
40
+
41
+ // --- buffer load util functions
42
+
43
+ // use the following buffer struct to wrap the binary buffer data
44
+ // since the buffer data could not be operated with the string API.
45
+ struct xdb_header {
46
+ unsigned short version;
47
+ unsigned short index_policy;
48
+ unsigned int created_at;
49
+ unsigned int start_index_ptr;
50
+ unsigned int end_index_ptr;
51
+
52
+ // the original buffer
53
+ unsigned int length;
54
+ char buffer[xdb_header_info_length];
55
+ };
56
+ typedef struct xdb_header xdb_header_t;
57
+
58
+ XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *);
59
+
60
+ XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *);
61
+
62
+ XDB_PUBLIC(void) xdb_close_header(void *);
63
+
64
+
65
+ // --- vector index buffer
66
+ struct xdb_vector_index {
67
+ unsigned int length;
68
+ char buffer[xdb_vector_index_length];
69
+ };
70
+ typedef struct xdb_vector_index xdb_vector_index_t;
71
+
72
+ XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *);
73
+
74
+ XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *);
75
+
76
+ XDB_PUBLIC(void) xdb_close_vector_index(void *);
77
+
78
+
79
+ // --- content buffer
80
+ struct xdb_content {
81
+ unsigned int length;
82
+ char *buffer;
83
+ };
84
+ typedef struct xdb_content xdb_content_t;
85
+
86
+ XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *);
87
+
88
+ XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *);
89
+
90
+ XDB_PUBLIC(void) xdb_close_content(void *);
91
+
92
+ // --- End buffer load
93
+
94
+ // xdb searcher structure
95
+ struct xdb_searcher_entry {
96
+ FILE *handle;
97
+
98
+ // header info
99
+ const char *header;
100
+ int io_count;
101
+
102
+ // vector index buffer cache.
103
+ // preload the vector index will reduce the number of IO operations
104
+ // thus speedup the search process.
105
+ const xdb_vector_index_t *v_index;
106
+
107
+ // content buffer.
108
+ // cache the whole xdb content.
109
+ const xdb_content_t *content;
110
+ };
111
+ typedef struct xdb_searcher_entry xdb_searcher_t;
112
+
113
+ // xdb searcher new api define
114
+ XDB_PUBLIC(int) xdb_new_with_file_only(xdb_searcher_t *, const char *);
115
+
116
+ XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_searcher_t *, const char *, const xdb_vector_index_t *);
117
+
118
+ XDB_PUBLIC(int) xdb_new_with_buffer(xdb_searcher_t *, const xdb_content_t *);
119
+
120
+ XDB_PUBLIC(void) xdb_close(void *);
121
+
122
+ // xdb searcher search api define
123
+ XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const char *, char *, size_t);
124
+
125
+ XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, unsigned int, char *, size_t);
126
+
127
+ XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *);
128
+
129
+
130
+ // get unsigned long (4bytes) from a specified buffer start from the specified offset with little-endian
131
+ XDB_PUBLIC(unsigned int) xdb_get_uint(const char *, int);
132
+
133
+ // get unsigned short (2bytes) from a specified buffer start from the specified offset with little-endian
134
+ XDB_PUBLIC(int) xdb_get_ushort(const char *, int);
135
+
136
+ // check the specified string ip and convert it to an unsigned int
137
+ XDB_PUBLIC(int) xdb_check_ip(const char *, unsigned int *);
138
+
139
+ // unsigned int ip to string ip
140
+ XDB_PUBLIC(void) xdb_long2ip(unsigned int, char *);
141
+
142
+ // get the middle ip of a and b
143
+ XDB_PUBLIC(unsigned int) xdb_mip(unsigned long, unsigned long);
144
+
145
+ // get the current time in microseconds
146
+ XDB_PUBLIC(long) xdb_now();
147
+
148
+
149
+ #endif //C_XDB_SEARCHER_H
@@ -0,0 +1,53 @@
1
+ require 'ffi'
2
+
3
+ module Ip2regionExt
4
+ extend FFI::Library
5
+ ffi_lib File.join(__dir__, '../','libip2region_ext.so')
6
+
7
+ XDB_HEADER_INFO_LENGTH = 256
8
+ XDB_VECTOR_INDEX_LENGTH = 524288
9
+
10
+ class XdbHeaderT < FFI::Struct
11
+ layout :version, :ushort,
12
+ :index_policy, :ushort,
13
+ :created_at, :uint,
14
+ :start_index_ptr, :uint,
15
+ :end_index_ptr, :uint,
16
+ :length, :uint,
17
+ :buffer, [:char, XDB_HEADER_INFO_LENGTH] # xdb_header_info_length 需要替换为实际的值
18
+ end
19
+
20
+ class XdbVectorIndexT < FFI::Struct
21
+ layout :length, :uint,
22
+ :buffer, [:char, XDB_VECTOR_INDEX_LENGTH]
23
+ end
24
+
25
+ class XdbContentT < FFI::Struct
26
+ layout :length, :uint,
27
+ :buffer, :pointer
28
+ end
29
+
30
+ class XdbSearcherT < FFI::Struct
31
+ layout :handle, :pointer,
32
+ :header, :string,
33
+ :io_count, :int,
34
+ :v_index, :pointer, # 指向 xdb_vector_index_t 结构体的指针
35
+ :content, :pointer # 指向 xdb_content_t 结构体的指针
36
+ end
37
+
38
+ #attach_function :xdb_load_content_from_file, [:string], XdbContentT.ptr
39
+ attach_function :xdb_new_with_file_only, [:pointer, :string], :int
40
+ attach_function :xdb_check_ip, [:string, :pointer], :int
41
+ attach_function :xdb_search_by_string, [ :pointer, :string, :pointer, :size_t ], :int
42
+ #attach_function :xdb_search, [:pointer, :uint, :string, :size_t], :int
43
+ attach_function :xdb_close, [:pointer], :void
44
+ attach_function :xdb_load_vector_index_from_file, [:string], XdbVectorIndexT.ptr
45
+ attach_function :xdb_new_with_vector_index, [:pointer, :string, :pointer], :int
46
+ attach_function :xdb_close_vector_index, [:pointer], :void
47
+ attach_function :xdb_load_content_from_file, [:string], :pointer
48
+ attach_function :xdb_new_with_buffer, [:pointer, :pointer], :int
49
+ attach_function :xdb_close_content, [:pointer], :void
50
+
51
+
52
+ end
53
+
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ip2regionExt
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ require 'singleton'
3
+ module Ip2regionExt
4
+ class Xdb
5
+ include Singleton
6
+
7
+ def connect(opt = {})
8
+ @option = {db_path: nil, db_type: :file}.merge(opt)
9
+ @searcher = XdbSearcherT.new()
10
+ #@vector_index = XdbVectorIndexT.new()
11
+ #@content = XdbContentT.new()
12
+
13
+ case @option[:db_type]
14
+ when :file
15
+ load_by_file
16
+ when :index
17
+ load_by_index
18
+ when :cache
19
+ load_by_cache
20
+ else
21
+ load_by_file
22
+ end
23
+ end
24
+
25
+ def load_by_file
26
+ err = Ip2regionExt.xdb_new_with_file_only(@searcher, @option[:db_path])
27
+ raise Error.new("failed to create xdb searcher from `#{@option[:db_path]}` with errno=#{err}\n") if err != 0
28
+ end
29
+
30
+ def load_by_index
31
+ @vector_index = Ip2regionExt.xdb_load_vector_index_from_file(@option[:db_path])
32
+ raise Error.new("failed to load vector index from `#{@option[:db_path]}`\n") unless @vector_index
33
+
34
+ err = Ip2regionExt.xdb_new_with_vector_index(@searcher.pointer, @option[:db_path], @vector_index)
35
+ raise Error.new("failed to create vector index cached searcher with errno=#{err}\n") if err != 0
36
+ end
37
+
38
+ def load_by_cache
39
+ @content = Ip2regionExt.xdb_load_content_from_file(@option[:db_path])
40
+ raise Error.new("failed to load xdb content from `#{}`\n") unless @content
41
+
42
+ err = Ip2regionExt.xdb_new_with_buffer(@searcher, @content)
43
+ raise Error.new("failed to create content cached searcher with errno=#{err}\n") if err != 0
44
+ end
45
+
46
+ def query(ip_address)
47
+ region_buffer = FFI::MemoryPointer.new(:char, 256)
48
+ err = Ip2regionExt.xdb_search_by_string(@searcher, ip_address, region_buffer, region_buffer.size)
49
+ raise Error.new("failed search(#{ip_address}) with errno=#{err}\n") if err != 0
50
+ region_buffer.read_string.force_encoding('UTF-8')
51
+ end
52
+
53
+ def close
54
+ Ip2regionExt.xdb_close(@searcher)
55
+ Ip2regionExt.xdb_close_vector_index(@vector_index) if @option[:db_type] == :index
56
+ Ip2regionExt.xdb_close_content(@content) if @option[:db_type] == :cache
57
+ end
58
+
59
+ def check_ip(ip_address)
60
+ result = FFI::MemoryPointer.new(:int)
61
+ err = Ip2regionExt.xdb_check_ip(ip_address, result)
62
+ raise Error.new("failed check ip(#{ip_address}) with errno=#{err}\n") if err != 0
63
+ result.get_int(0)
64
+ end
65
+
66
+
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ip2region_ext/version"
4
+ require_relative 'ip2region_ext/ip2region_ffi'
5
+ require_relative 'ip2region_ext/xdb'
6
+
7
+ module Ip2regionExt
8
+ class Error < StandardError; end
9
+
10
+ @@xdb_path = File.join(__dir__, '../db','ip2region.xdb')
11
+
12
+ def self.connect(option = {})
13
+ option[:db_path] ||= @@xdb_path
14
+ Xdb.instance.connect(option)
15
+ end
16
+
17
+ def self.search(ip_address)
18
+ Xdb.instance.query(ip_address)
19
+ end
20
+
21
+ def self.close
22
+ Xdb.instance.close
23
+ end
24
+
25
+
26
+ end
data/sig/ip2region.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Ip2region
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ip2region_ext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - songgz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: 直接调用c原生api,充分发挥IO操作的性能优势
28
+ email:
29
+ - sgzhe@163.com
30
+ executables: []
31
+ extensions:
32
+ - ext/ip2region/extconf.rb
33
+ extra_rdoc_files: []
34
+ files:
35
+ - README.md
36
+ - Rakefile
37
+ - db/ip2region.xdb
38
+ - ext/ip2region/extconf.rb
39
+ - ext/ip2region/xdb_searcher.c
40
+ - ext/ip2region/xdb_searcher.h
41
+ - lib/ip2region_ext.rb
42
+ - lib/ip2region_ext/ip2region_ffi.rb
43
+ - lib/ip2region_ext/version.rb
44
+ - lib/ip2region_ext/xdb.rb
45
+ - sig/ip2region.rbs
46
+ homepage: https://github.com/songgz/ip2region_ext
47
+ licenses: []
48
+ metadata:
49
+ allowed_push_host: https://rubygems.org
50
+ homepage_uri: https://github.com/songgz/ip2region_ext
51
+ source_code_uri: https://github.com/songgz/ip2region_ext
52
+ changelog_uri: https://github.com/songgz/ip2region_ext
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubygems_version: 3.5.10
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: 这是一个ip2region库c客户端的ruby ffi扩展
72
+ test_files: []