Compare commits

...

1225 Commits

Author SHA1 Message Date
Leon / 狮子的魂 2955431edb
Merge pull request #499 from lionsoul2014/fix_golang_searcher
Fix golang searcher
2026-08-01 08:53:04 +08:00
lionsoul2014 c0d85cc71d extend the check for the runtime ptr size 2026-08-01 08:51:32 +08:00
lionsoul2014 d5e3616d8e close the handle 2026-08-01 08:50:56 +08:00
Leon / 狮子的魂 cd40e3a1d5
Merge pull request #497 from lionsoul2014/data_with_geofeed_merge_20260709
merge the geofeeds of global top providers on 20260709
2026-07-10 00:02:42 +08:00
lionsoul2014 ee19ac95ca merge the geofeed of global top provider on 20260709 2026-07-09 23:54:54 +08:00
Leon / 狮子的魂 a99989343e
Merge pull request #495 from Alice39s/fix/js-license-metadata
fix: correct license metadata and Python packaging typo
2026-06-30 22:01:18 +08:00
Alice39s 324bfdf35d
fix: correct Python MANIFEST.in typo 2026-06-30 19:22:01 +09:00
Alice39s f4d81dd3dd
fix: ensure root SPDX identifier and correct npm license metadata 2026-06-30 19:22:01 +09:00
lionsoul2014 ba96deaecf Erlang IPv6 supports 2026-06-28 10:12:48 +08:00
Leon / 狮子的魂 c4eb1a14ce
Merge pull request #492 from Alice39s/feature/erlang-ipv6
feat(binding/erlang): implement IPv6 dual-stack support for Erlang binding
2026-06-28 10:09:08 +08:00
Alice39s dbef88e337
chore: fix trailing whitespace on worktree entries in .gitignore 2026-06-28 05:18:11 +09:00
Alice39s 30ed0cf028
docs(erlang): update README test counts to 63 2026-06-28 05:18:08 +09:00
Alice39s d06690f651
test(erlang): add shared test helper and DRY app setup 2026-06-28 05:18:03 +09:00
Alice39s daf6749c34
fix(erlang): pass expected version to worker and fail fast on xdb mismatch 2026-06-28 05:17:59 +09:00
Alice39s e50ead2cba
fix(erlang): validate tuple segment ranges in ip2region_util 2026-06-28 05:17:55 +09:00
Alice39s b1eb00e25e
docs(erlang): update READMEs with IPv6 examples and benchmark output 2026-06-28 05:10:10 +09:00
Alice39s fac3f73193
test(erlang): expand xdb header and table helper coverage 2026-06-28 05:10:07 +09:00
Alice39s d1dda93abd
fix(erlang): guard xdb:search against unconfigured pool 2026-06-28 05:10:04 +09:00
Alice39s 44013251b6
test(erlang): fix integer ip_version assertion and add invalid format cases 2026-06-28 05:09:38 +09:00
Alice39s 546ce589a9
docs(erlang): update README examples for IPv6 and current test output 2026-06-28 04:58:45 +09:00
Alice39s 507b7bddc1
fix(erlang): post-review cleanups — pread, self-contained tests, poolboy app dep, dead docs 2026-06-28 04:54:08 +09:00
Alice39s 25513cb246
fix(erlang): unify v4 pool name and stop caching errors 2026-06-28 04:12:44 +09:00
Alice39s 445eb4cb8e
fix(erlang): integer guard, cache dedup, dead macro cleanup 2026-06-28 04:12:43 +09:00
Alice39s a1667d1182
refactor(erlang): simplify benchmark output to ms/QPS and add CPU clock info 2026-06-28 01:04:00 +09:00
Alice39s d189535bb8
chore(erlang): add Makefile bench targets, IPv6-capable benchmark script and docs 2026-06-28 00:51:53 +09:00
Alice39s 06d54d8084
docs(erlang): add dual-stack IPv4/IPv6 setup and usage guide 2026-06-28 00:31:17 +09:00
Alice39s 8849cc9130
feat(erlang): auto-route search by IPv4/IPv6 to correct worker pool 2026-06-28 00:07:22 +09:00
Alice39s c911ca1290
feat(erlang): create version-specific worker pools from db config 2026-06-28 00:07:09 +09:00
Alice39s 241831a6f0
feat(erlang): make worker xdb version-aware for IPv4/IPv6 2026-06-28 00:01:09 +09:00
Alice39s 2f0dc5f33f
feat(erlang): add dual-stack IP parsing utilities 2026-06-27 23:52:40 +09:00
Alice39s c1db56c369
feat(erlang): add xdb header parser helper module 2026-06-27 23:51:07 +09:00
Alice39s 211c374ee2
feat(erlang): add xdb header and dual-stack table/pool macros 2026-06-27 23:50:19 +09:00
Alice39s a3b8d817ee
chore: ignore .worktrees directory 2026-06-27 21:27:36 +09:00
lionsoul2014 b7bb22589f fix the name to fit the geo standard 2026-06-26 16:20:14 +08:00
Leon / 狮子的魂 1068bf05a3
Merge pull request #491 from lionsoul2014/ft_golang_segments_iterate_with_full_options
more options for segments iterate
2026-06-23 10:09:23 +08:00
lionsoul2014 fc46254325 more options for segments iterate 2026-06-23 10:07:48 +08:00
lionsoul2014 7b320846c9 update the test assert and optimize the bench tests 2026-06-11 16:37:31 +08:00
Leon / 狮子的魂 e360f3d031
Merge pull request #488 from isslower/codex/python-test-args
Fix Python test helpers to use function arguments
2026-06-09 13:19:39 +08:00
isslower e7c240a8d9 Fix Python test helper arguments 2026-06-09 12:51:13 +08:00
lionsoul2014 d638015592 region.fields with sep 2026-05-30 12:22:44 +08:00
lionsoul2014 3eb65a2657 sep args for segment parse 2026-05-30 11:02:41 +08:00
lionsoul2014 fac3d0376a PushBack for empty match 2026-05-24 20:14:29 +08:00
lionsoul2014 a8b9d542c6 srcReader is nil check 2026-05-24 20:13:47 +08:00
Leon / 狮子的魂 82a73fa60d
Merge pull request #487 from lionsoul2014/fix_golang_cidr_to_range
fix Index overflow for the cidr parse with bits equals to IP max bits
2026-05-19 19:49:56 +08:00
lionsoul2014 8b79b69524 fix Index overflow for the cidr parse with bits equals to IP max bits 2026-05-19 19:43:51 +08:00
Leon / 狮子的魂 c5fb71facb
Merge pull request #486 from lionsoul2014/fr_go_binding_with_xdb_stream_input
xdb io.Reader based stream input supports
2026-05-19 11:46:59 +08:00
lionsoul2014 62e1f98497 xdb io.Reader based stream input supports 2026-05-19 11:45:11 +08:00
Leon / 狮子的魂 2af5ee176c
Merge pull request #485 from lionsoul2014/fr_golang_maker_io_stream
add io.Reader based xdb input supports
2026-05-19 11:43:14 +08:00
lionsoul2014 0ce74b8873 add io.Reader based xdb input supports 2026-05-19 11:42:03 +08:00
Leon / 狮子的魂 f4fcd5e900
Merge pull request #484 from lionsoul2014/fr_golang_cidr_to_range
Fr golang CIDR supports
2026-05-16 11:06:38 +08:00
lionsoul2014 072b6fed24 segments iterate with CIDR supports 2026-05-16 10:58:49 +08:00
lionsoul2014 f939363a19 SegmentFrom parser is ready for CIDR and IP range 2026-05-16 10:14:20 +08:00
lionsoul2014 353524325a CIDR to ip range supports 2026-05-16 00:07:15 +08:00
lionsoul2014 59cf547107 re-use the Header 2026-05-09 19:52:13 +08:00
Leon / 狮子的魂 3147144a7f
Merge pull request #481 from lionsoul2014/data_opt_merge_issues_480
merge the data reported by issues/480
2026-05-08 10:23:36 +08:00
lionsoul2014 d4299a1d1b merge the data reported by issues/480 2026-05-08 10:21:28 +08:00
lionsoul2014 18ecd7ae85 add Cangjie query client 2026-05-06 20:58:41 +08:00
狮子的魂 5fbdef93cd
!24 Add Cangjie binding with service layer, tests, and docs
Merge pull request !24 from lewist4cj/master
2026-05-06 12:48:54 +00:00
lewist4cj a90191e8d8 Add Cangjie binding with service layer, tests, and docs
Cangjie ip2region xdb searcher binding with:
- Low-level Searcher API (content buff / vector index / file only)
- Multi-threaded service layer (Config, SearcherPool, Ip2Region)
- 41 unit tests covering xdb and service packages
- Example project with 4 usage demos + performance benchmark
- Full documentation in README.md
2026-05-05 10:59:51 +08:00
Leon / 狮子的魂 593e028677
Merge pull request #478 from lionsoul2014/golang_maker_region_mgr
Golang maker region struct and cache impl
2026-04-29 17:41:08 +08:00
lionsoul2014 72c657c378 export the Region API for callbacks 2026-04-29 17:36:30 +08:00
lionsoul2014 19a8816827 clean the region cache 2026-04-29 16:04:40 +08:00
lionsoul2014 787bd7ae07 use the Region cache 2026-04-29 15:52:40 +08:00
lionsoul2014 5b10989ac2 Thread-safe impl for CacheRegion 2026-04-29 13:44:14 +08:00
lionsoul2014 90e827633a Use the Region struct to manager the region info instead of old string 2026-04-29 12:23:51 +08:00
Leon / 狮子的魂 4dd0a1e006
Merge pull request #477 from lionsoul2014/data_merge_reports_on_20260429
merge reports before 2026/04/29
2026-04-29 12:17:36 +08:00
lionsoul2014 77134c4315 merge reports before 2026/04/29 2026-04-29 12:15:39 +08:00
Leon / 狮子的魂 fedf5aaf03
Merge pull request #476 from lionsoul2014/data_merge_reports_on_20260420
merge reports from community before 20260420
2026-04-28 19:00:50 +08:00
lionsoul2014 d28a10b0c1 merge reports from community before 20260420 2026-04-28 18:58:34 +08:00
lionsoul2014 aab908c923 create if file not exists 2026-04-24 16:14:45 +08:00
lionsoul2014 c314ef54ca add SaveToFile(string) api 2026-04-24 09:41:02 +08:00
lionsoul2014 69d267d85d fix the bug of convert eList to segment list 2026-04-23 17:33:23 +08:00
Leon / 狮子的魂 a603a75ab7
Merge pull request #475 from lionsoul2014/opt_golang_editor
editor with put callback and auto segments merge
2026-04-22 16:29:55 +08:00
lionsoul2014 d730db816a editor with put callback and auto segments merge 2026-04-22 16:28:11 +08:00
lionsoul2014 d36edc6e15 empty cli args value & help flag support 2026-04-22 15:34:45 +08:00
lionsoul2014 1d9ffabfeb add the LISENCE to the golang package root 2026-04-19 10:23:13 +08:00
Leon / 狮子的魂 03cc433717
Merge pull request #474 from lionsoul2014/opt_v6_stdlize_cn_region_name
stdlize the cn region name for v6 data
2026-04-17 16:30:04 +08:00
lionsoul2014 3562b7af8e stdlize the cn region name for v6 data 2026-04-17 16:27:03 +08:00
Leon / 狮子的魂 165746ba38
Merge pull request #473 from lionsoul2014/opt_data_v4_stdlize_name_of_autonomous_prefecture
use the short name for all the cn autonomous prefecture
2026-04-16 22:46:57 +08:00
lionsoul2014 79243eb0ba use the short name for all the cn autonomous prefecture 2026-04-16 22:45:19 +08:00
lionsoul2014 be71b92f20 stdlize the city name of cn with suffix zhou 2026-04-16 16:30:47 +08:00
Leon / 狮子的魂 d380827871
Merge pull request #472 from lionsoul2014/opt_v4_data_field_name_std
stdlize all the province and city names for CN ipv4 segments
2026-04-16 16:07:02 +08:00
lionsoul2014 1a08f5e568 stdlize all the province and city names for CN ip segments 2026-04-16 16:00:26 +08:00
lionsoul2014 36c9de10bb use the sCmd args for sub-command 2026-04-09 21:06:08 +08:00
Leon / 狮子的魂 61ef15cff0
Merge pull request #468 from lionsoul2014/data_updates_on_20260408
merge some updates reported by community
2026-04-08 22:07:59 +08:00
lionsoul2014 d6444e740f merge some updates reported by community 2026-04-08 22:04:29 +08:00
lionsoul2014 e3094c8ef2 export the cmd util funcs 2026-04-08 10:56:37 +08:00
lionsoul2014 5244ebad08 update doc desc about setFairLock(boolean) 2026-04-05 22:04:48 +08:00
Leon / 狮子的魂 4d391acac2
Merge pull request #466 from lionsoul2014/opt_golang_search_api
Opt golang search api
2026-04-04 18:43:38 +08:00
lionsoul2014 5cb17713a7 input ip type changed to any to supports string or bytes ip 2026-04-04 18:42:12 +08:00
lionsoul2014 e1f0d5126a go1.18+ 2026-04-04 18:39:52 +08:00
lionsoul2014 d9fc17727e add bytes ip sub and middle util func 2026-04-04 18:39:31 +08:00
Leon / 狮子的魂 dabe7798f6
Merge pull request #465 from lionsoul2014/opt_java_service_fair_lock
add ConfigBuilder.setFairLock(boolean) to config whether ReentrantLoc…
2026-04-04 17:56:18 +08:00
lionsoul2014 8f841e6237 add ConfigBuilder.setFairLock(boolean) to config whether ReentrantLock uses a fair lock 2026-04-04 17:54:05 +08:00
Leon / 狮子的魂 d7b60f41e4
Merge pull request #464 from lionsoul2014/data_update_report_by_issue_461
update the data reported by issues/461
2026-04-04 17:15:03 +08:00
lionsoul2014 83d6fafaa0 update the data reported by issues/461 2026-04-04 17:12:37 +08:00
Leon / 狮子的魂 02f6b31894
Merge pull request #463 from lionsoul2014/data_update_on_20260403
merge the reports from chengdu china mobile on 2026/04/03
2026-04-03 12:20:24 +08:00
lionsoul2014 97e647234a merge the reports from chengdu china mobile on 2026/04/03 2026-04-03 12:18:27 +08:00
Leon / 狮子的魂 1209b72452
Merge pull request #462 from lionsoul2014/opt_golang_ip_util
optimize the golang ip utils
2026-04-02 11:28:09 +08:00
lionsoul2014 62aa42e05e remove the old rules 2026-04-02 11:27:25 +08:00
lionsoul2014 e76f995ceb add autoMege flag and return total/merged count for segments iterate 2026-04-02 11:24:56 +08:00
lionsoul2014 edb29aea3b add ip sub/half/middle functions 2026-04-02 11:00:19 +08:00
lionsoul2014 8a7b088216 IPv6 supports for nginx module 2026-03-23 20:55:19 +08:00
Leon / 狮子的魂 897ee5c0a9
Merge pull request #460 from fa1seut0pia/feature-ipv6
IPv6 supports for nginx module
2026-03-23 20:53:33 +08:00
fa1seut0pia 1eba8ece3b 容器化 golang xdb_maker 2026-03-22 09:50:24 +08:00
fa1seut0pia 007cb0808a nginx 模块支持IPv6 2026-03-22 09:35:54 +08:00
fa1seut0pia 7d6ea00303 nginx 模块支持IPv6 2026-03-22 07:06:28 +08:00
fa1seut0pia e183607da5 ngx_module_libs 与 bingding/c/Makefile 生成的 libxdb.a 保持一致 2026-03-22 07:06:28 +08:00
fa1seut0pia b0466348da 避免 shell 通配符展开 2026-03-22 07:06:28 +08:00
lionsoul2014 04e51e2182 mv all the tests files to the ./sample 2026-03-13 09:36:24 +08:00
lionsoul2014 c63b1dc5db empty source and auto append supports 2026-03-12 11:23:43 +08:00
Leon / 狮子的魂 1a29562c2d
Merge pull request #459 from lionsoul2014/fr_java_non_contiguous_range
maker non-contiguous and disorder ip segments supports
2026-03-07 11:30:39 +08:00
lionsoul2014 29c7048f93 optimize the disorder segments check 2026-03-07 11:28:30 +08:00
lionsoul2014 fa16a4a45d non-contiguous and disorder ip range supports 2026-03-06 14:28:07 +08:00
Leon / 狮子的魂 8adec5a3c8
Merge pull request #457 from lionsoul2014/fr_go_editor_non_contiguous_range
discontinuous or unordered data segments edit supports
2026-03-05 14:24:25 +08:00
lionsoul2014 51e194a105 discontinuous or unordered data segments edit supports 2026-03-05 14:22:03 +08:00
lionsoul2014 b965ab8be9 non-contiguous segments load 2026-03-04 14:39:50 +08:00
Leon / 狮子的魂 a720267842
Merge pull request #456 from lionsoul2014/fr_go_non_contiguous_range
non-contiguous ip ranges supports
2026-03-04 14:07:49 +08:00
lionsoul2014 112dfb99b5 non-contiguous ip ranges supports 2026-03-04 12:44:05 +08:00
lionsoul2014 ca665cfd2e update the usage doc since v6 for nginx moudle is not ready yet 2026-03-03 13:53:54 +08:00
Leon / 狮子的魂 0ad8031754
Merge pull request #455 from lionsoul2014/opt_missing_search_return
optimize the ptr checking and return empty string for missing data
2026-03-03 13:45:13 +08:00
lionsoul2014 fb0efface4 optimize the ptr checking and return empty string for missing data 2026-03-03 13:40:54 +08:00
lionsoul2014 e373f92312 maven version to 3.3.5 2026-02-28 15:26:06 +08:00
Leon / 狮子的魂 3d21a8247c
Merge pull request #454 from lionsoul2014/fr_java_config_builder_assert
assert the builder data source args to avoid errors like reported on…
2026-02-28 15:24:24 +08:00
lionsoul2014 d5e10e1383 assert the builder data source args to ignore errors like reported on issues/453 2026-02-28 15:22:54 +08:00
Leon / 狮子的魂 1a8d01d967
Merge pull request #452 from lionsoul2014/i18n_csharp_maker_readme
add english readme for c# maker
2026-02-26 14:26:15 +08:00
lionsoul2014 e87f580510 add english readme for c# maker 2026-02-26 14:24:40 +08:00
lionsoul2014 962335954a use xdb binary 2026-02-26 14:22:51 +08:00
Leon / 狮子的魂 2c01e01d2c
Merge pull request #451 from lionsoul2014/i18n_python_maker_readme
I18n python maker readme
2026-02-26 14:13:04 +08:00
lionsoul2014 79886f77e7 update the command title 2026-02-26 14:11:16 +08:00
lionsoul2014 132221b494 add english readme for python maker 2026-02-26 14:08:10 +08:00
lionsoul2014 aef4f91348 i18n readme for source ip editor 2026-02-26 13:55:34 +08:00
Leon / 狮子的魂 9d4b165409
Merge pull request #450 from lionsoul2014/i18n_cpp_maker_readme
add english readme for cpp maker
2026-02-25 23:19:51 +08:00
Leon / 狮子的魂 4b55d14fca
Merge pull request #449 from lionsoul2014/i18n_rust_maker_readme
add english readme for rust maker
2026-02-25 23:19:14 +08:00
lionsoul2014 b030661865 add english readme for cpp maker 2026-02-25 23:17:50 +08:00
lionsoul2014 537e6f9306 add english readme for rust maker 2026-02-25 23:13:29 +08:00
Leon / 狮子的魂 aaad26244d
Merge pull request #448 from lionsoul2014/i18n_java_maker_readme
add english readme for java maker
2026-02-25 23:05:52 +08:00
lionsoul2014 a31a966546 add english readme for java maker 2026-02-25 23:01:17 +08:00
Leon / 狮子的魂 832e3fd77d
Merge pull request #447 from lionsoul2014/i18n_golang_maker_readme
I18n golang maker readme
2026-02-25 22:46:43 +08:00
lionsoul2014 ef51b901ee link to the local lang readme 2026-02-25 22:45:44 +08:00
lionsoul2014 e8242a24ea add english readme for golang maker 2026-02-25 22:41:35 +08:00
Leon / 狮子的魂 30e5f2732d
Merge pull request #446 from lionsoul2014/i18n_cpp_readme
add english readme for c++ binding
2026-02-25 22:24:19 +08:00
lionsoul2014 1c6d299efa add english readme for c++ binding 2026-02-25 22:21:31 +08:00
Leon / 狮子的魂 dce0bb582f
Merge pull request #445 from lionsoul2014/i18n_erlang_readme
I18n erlang readme
2026-02-25 20:13:13 +08:00
lionsoul2014 b3115ba7aa adjust the layout 2026-02-25 20:12:14 +08:00
lionsoul2014 81885a16cf add english readme for erlang binding 2026-02-25 20:07:35 +08:00
lionsoul2014 2b61de20d6 package version to 3.1.7 with cn/en doc 2026-02-25 18:49:22 +08:00
lionsoul2014 7a1970729a python 3.0.3 with cn/en README doc 2026-02-25 14:12:16 +08:00
Leon / 狮子的魂 5a53add68b
Merge pull request #444 from lionsoul2014/i18n_javascript_readme
add english readme for javascript binding
2026-02-25 13:11:03 +08:00
lionsoul2014 c9e8acaffc add english readme for javascript binding 2026-02-25 13:07:05 +08:00
Leon / 狮子的魂 ce92aabc11
Merge pull request #443 from lionsoul2014/i18n_python_readme
add english readme for python binding
2026-02-24 16:12:13 +08:00
lionsoul2014 3f0480be1f add english readme for python binding 2026-02-24 16:09:57 +08:00
lionsoul2014 a694cfa8d2 add global emoji for supported lang list 2026-02-24 15:56:58 +08:00
Leon / 狮子的魂 8a44c98f99
Merge pull request #442 from lionsoul2014/i18n_rust_readme
add english readme for rust binding
2026-02-24 15:50:00 +08:00
lionsoul2014 cab8fbc4ab add english readme for rust binding 2026-02-24 15:47:33 +08:00
Leon / 狮子的魂 7d11be2ce7
Merge pull request #441 from lionsoul2014/i18n_lua_readme
I18n lua readme
2026-02-24 15:38:05 +08:00
lionsoul2014 8302525ae8 remove the lang label 2026-02-24 15:34:08 +08:00
lionsoul2014 e926501591 use the gloabl emoji only 2026-02-24 15:31:22 +08:00
lionsoul2014 87418d879a add english readme for pure lua binding 2026-02-24 15:29:41 +08:00
Leon / 狮子的魂 16fa5572f2
Merge pull request #440 from lionsoul2014/i18n_lua_c_readme
add english readme for lua c extension binding
2026-02-24 14:48:03 +08:00
lionsoul2014 39c297ad04 add english readme for lua c extension binding 2026-02-24 14:45:34 +08:00
Leon / 狮子的魂 6081603bd8
Merge pull request #439 from lionsoul2014/i18n_c_readme
add english readme for c binding
2026-02-24 13:43:14 +08:00
lionsoul2014 2861abcb83 add english readme for c binding 2026-02-24 13:40:09 +08:00
Leon / 狮子的魂 daf54bf877
Merge pull request #438 from lionsoul2014/i18n_java_readme
add english readme for java binding
2026-02-24 12:28:14 +08:00
lionsoul2014 6c98b8faf9 add english readme for java binding 2026-02-24 12:22:27 +08:00
Leon / 狮子的魂 1dc0003661
Merge pull request #437 from lionsoul2014/i18n_php_readme
I18n php readme
2026-02-24 11:58:00 +08:00
lionsoul2014 c4a3a2e1f8 add en README for php binding 2026-02-24 11:54:18 +08:00
lionsoul2014 0e828bba0d lang navi 2026-02-24 11:54:06 +08:00
lionsoul2014 b0ab8a0b2f update the README title 2026-02-23 21:23:23 +08:00
Leon / 狮子的魂 9caef15329
Merge pull request #436 from lionsoul2014/i18n_golang_readme
I18n golang readme
2026-02-23 19:32:27 +08:00
lionsoul2014 572fda5910 remove the tailing empty line 2026-02-23 19:28:06 +08:00
lionsoul2014 67d709ab39 add en README and golang binding 2026-02-23 19:22:48 +08:00
lionsoul2014 076f3918fa Optimize the layout and headings 2026-02-22 21:31:22 +08:00
lionsoul2014 9411b7f8c2 stdlize the en table header 2026-02-22 21:24:04 +08:00
lionsoul2014 75c375d146 update the paragraph title 2026-02-22 21:19:36 +08:00
lionsoul2014 5037816f41 set en README as default 2026-02-22 21:16:47 +08:00
Leon / 狮子的魂 aafee317c8
Merge pull request #435 from lionsoul2014/i18n_readme
I18n supporting with zh and en README
2026-02-22 19:13:21 +08:00
lionsoul2014 839d5f59a5 active the en README 2026-02-22 19:11:00 +08:00
lionsoul2014 d4180c0f57 zh as the default lang 2026-02-22 19:09:08 +08:00
lionsoul2014 8da3f55092 take en as the default desc lang 2026-02-22 19:03:42 +08:00
lionsoul2014 7ff7e5f61f add en and zh readme 2026-02-22 18:53:33 +08:00
Leon / 狮子的魂 44fd27f027
Merge pull request #434 from ArgoZhang/test-ip
test: update tests and make all of them passed
2026-02-12 20:11:50 +08:00
Argo Zhang 3539f5e4e3 chore: bump version 3.0.1 2026-02-12 13:32:57 +08:00
Argo Zhang 7bb61ec59c test: 更新单元测试 2026-02-12 11:04:18 +08:00
lionsoul2014 89852a70f0 special chars filter sample 2026-02-10 15:13:48 +08:00
lionsoul2014 d427b4dcd8 fix the doc desc errors 2026-02-02 19:02:55 +08:00
lionsoul2014 ad4253dbb0 update the demo tests result with the latest data 2026-01-28 22:48:03 +08:00
lionsoul2014 e6e3087144 source data clear based supports 2026-01-28 20:02:58 +08:00
lionsoul2014 ad0cfd6bd9 pack the ftell & fseek for later large file supports 2026-01-27 18:56:04 +08:00
lionsoul2014 0cf4cf8e26 fix the typo 2026-01-27 15:55:20 +08:00
Leon / 狮子的魂 ba2c9ec119
Merge pull request #430 from lionsoul2014/reserved_region_stdlize
stdlize the Reserved IP region info
2026-01-25 19:28:42 +08:00
lionsoul2014 91f281d53c stdlize the Reserved IP region info 2026-01-25 19:17:19 +08:00
lionsoul2014 05158e0a2e search tests result updated with the latest data 2026-01-25 18:56:46 +08:00
lionsoul2014 384567f184 Merge branch 'master' of github.com:lionsoul2014/ip2region 2026-01-25 14:49:41 +08:00
lionsoul2014 937c0772ec exp the iso-3166-alpha2-code 2026-01-25 14:48:25 +08:00
Leon / 狮子的魂 ebc6253dfe
Merge pull request #429 from lionsoul2014/update_readme_demo_tests_result
Update readme demo tests result
2026-01-25 14:46:14 +08:00
lionsoul2014 969a5f0e03 tests with output in Chinese and English 2026-01-25 14:44:31 +08:00
lionsoul2014 c2bb78aecd update the demo tests result with the latest data 2026-01-25 14:37:50 +08:00
lionsoul2014 e139731d9e update the demo tests to the latest result 2026-01-25 14:23:09 +08:00
lionsoul2014 96717ab155 update to the latest region format country|province|city|ISP|iso-3166-alpha2-code 2026-01-25 13:24:02 +08:00
Leon / 狮子的魂 0520f299ee
Merge pull request #428 from lionsoul2014/v6_updates_on_202601
V6 updates on 2026/01 with overseas region info updated and published in English ...
2026-01-25 13:14:10 +08:00
Leon / 狮子的魂 bbe2dddecf
Merge pull request #427 from lionsoul2014/v4_updates_on_202601
ipv4 data update on 2026/01 (overseas region update and with english,…
2026-01-25 13:13:46 +08:00
lionsoul2014 14753ffc29 re-gen the ipv6 xdb binary 2026-01-25 13:12:30 +08:00
lionsoul2014 3127ac0cdc ipv6 data updates on 2026/01 with overseas region info update and published in English 2026-01-25 13:08:36 +08:00
lionsoul2014 108ef78a51 ipv4 data update on 2026/01 (overseas region update and with english, add ISO-3166 alpha2 code field) 2026-01-25 12:55:54 +08:00
Leon / 狮子的魂 f2c988287c
Merge pull request #424 from lionsoul2014/merge_updates_20260121
merge the update reports from the communitiy
2026-01-21 16:14:38 +08:00
lionsoul2014 7bab822a49 merge the update reports from the communitiy 2026-01-21 16:11:33 +08:00
Leon / 狮子的魂 1df8b55789
Merge pull request #423 from lionsoul2014/fix_cn_special_administrative_region
fix the name of the CN special administrative region
2026-01-21 15:50:44 +08:00
lionsoul2014 68cd1053d2 fix the name of the cnc special administrative region 2026-01-21 15:46:11 +08:00
lionsoul2014 398149f17e update the desc for ip2region.close and builder.setCacheSliceBytes 2026-01-09 11:30:43 +08:00
Leon / 狮子的魂 4c569581b8
Merge pull request #419 from lionsoul2014/ipv4_region_fix_20260108
updated 908 segments based on the reports from community
2026-01-08 12:23:14 +08:00
Leon / 狮子的魂 3d876f895e
Merge pull request #418 from lionsoul2014/fix_ipv6_municipality_name
stdlize the municipality name
2026-01-08 12:21:59 +08:00
lionsoul2014 0863e172eb update 908 segments based on the reports from community 2026-01-08 12:19:42 +08:00
lionsoul2014 437a2c424a stdlize the municipality name 2026-01-08 11:28:58 +08:00
Leon / 狮子的魂 2cb7863112
Merge pull request #417 from lionsoul2014/ipv6_source_isp_stdlize
remove the scene and replace it with stdlized isp for ipv6 data
2026-01-07 16:00:14 +08:00
Leon / 狮子的魂 249ea17178
Merge pull request #416 from lionsoul2014/ipv4_source_isp_stdlize
ipv4 source isp name stdlize
2026-01-07 15:57:50 +08:00
lionsoul2014 697eabc33b remove the scene and replace it with stdlized isp 2026-01-07 15:50:24 +08:00
lionsoul2014 930924bb34 stdlize the isp name 2026-01-07 15:44:21 +08:00
lionsoul2014 7839a335ed update the region for 103.151.173.0-255 2026-01-07 11:50:02 +08:00
Leon / 狮子的魂 91816b108b
Merge pull request #415 from lionsoul2014/fix_ipv4_city_260107
update the city name with the latest administrative info
2026-01-07 11:39:55 +08:00
lionsoul2014 31dabf8c95 update the city name for some lines with the latest administrative info 2026-01-07 11:36:06 +08:00
lionsoul2014 7628ba32c2 update the POM verison to 3.3.4 2026-01-05 22:00:02 +08:00
lionsoul2014 4577e9a17b update the default sliceBytes 2026-01-05 21:59:38 +08:00
lionsoul2014 f302c68012 to README 2025-12-31 22:04:07 +08:00
Leon / 狮子的魂 fe2dfc1088
Merge pull request #413 from 4ba-Co/fix/412-peak-memory-usage
fix: Correct abnormal memory spikes
2025-12-31 21:53:00 +08:00
vccddd 93f7652594 fix: Correct abnormal memory spikes
ContentCacheStrategy now implements ICacheStrategy only and load data from files directly
2025-12-31 13:34:09 +08:00
lionsoul2014 0d152d91a5 usually we don't need to call setCacheSliceBytes 2025-12-29 19:42:00 +08:00
Leon / 狮子的魂 351be412e6
Merge pull request #411 from lionsoul2014/fr_java_fix_slice_bytes
Fr java fixed slice bytes
2025-12-29 19:39:32 +08:00
lionsoul2014 bd4a2d6bd1 pom verison 3.3.3 with fixed slice bytes supports 2025-12-29 19:36:19 +08:00
lionsoul2014 bf76adad4e content loads with fixed slice bytes tests 2025-12-29 19:36:02 +08:00
lionsoul2014 02e10db478 take the ip parse for costs stats 2025-12-29 19:35:43 +08:00
lionsoul2014 ad08637d21 use the fixed slice bytes for all content loads 2025-12-29 19:34:48 +08:00
lionsoul2014 245bbaa366 fixed sliceBytes supports 2025-12-29 19:34:12 +08:00
lionsoul2014 56606de2f6 update the fields list doc link 2025-12-26 12:19:27 +08:00
lionsoul2014 de6089e321 pom version upgrade to 3.3.2 2025-12-24 13:04:32 +08:00
lionsoul2014 37f5e4475c doc about sliceBytes setting 2025-12-24 13:04:14 +08:00
lionsoul2014 1f850dcde4 add #setCacheSliceBytes and tests 2025-12-24 13:01:52 +08:00
lionsoul2014 7f9e6b8586 loadContentXXX methods with sliceBytes supports 2025-12-24 13:01:07 +08:00
lionsoul2014 b0de60f6cb update the doc for concurrency safety search 2025-12-23 12:43:37 +08:00
lionsoul2014 f12b9765b3 Buffer tests for loaded with InputStream 2025-12-15 17:40:00 +08:00
lionsoul2014 8c20994b12 add #size() to return the internal buffs size 2025-12-15 17:39:30 +08:00
lionsoul2014 897af4532e update the usage desc for setXdbInputStream 2025-12-12 15:14:58 +08:00
Leon / 狮子的魂 9669585216
Merge pull request #408 from lionsoul2014/fr_java_easier_to_build
Ip2Region Config build from xdb File Object or xdb InputStream
2025-12-12 14:55:12 +08:00
lionsoul2014 e84ea900d4 update pom version to 3.3.1 2025-12-12 14:49:26 +08:00
lionsoul2014 450fce589c Config build tests from XdbFile and XdbInputStream 2025-12-12 14:49:05 +08:00
lionsoul2014 9b0bedc045 remove the space 2025-12-12 14:48:20 +08:00
lionsoul2014 6059d63504 build from InputStream supports 2025-12-12 14:29:09 +08:00
lionsoul2014 e11a390afc content/vectorIndex/verify from Buffer 2025-12-12 14:28:50 +08:00
lionsoul2014 ff75ddbcfd verify from xdb File/Path 2025-12-12 12:16:59 +08:00
lionsoul2014 ea297041dd load from xdb File and xdb Path 2025-12-12 12:15:21 +08:00
lionsoul2014 c68cc0097c xdbFile is null check 2025-12-12 11:58:03 +08:00
lionsoul2014 c2a0a55005 config build from xdb path and File supports 2025-12-12 11:50:05 +08:00
lionsoul2014 b52555c5ec xdbPath to xdbFile and with path and File create supports 2025-12-12 11:49:10 +08:00
lionsoul2014 cb9ed02bc6 adjust the data to std format 2025-12-10 10:59:11 +08:00
lionsoul2014 d4b8f9f841 go is ready for concurrent safe search 2025-12-07 19:51:01 +08:00
lionsoul2014 7f454276a1 remove the err declare 2025-12-07 00:45:01 +08:00
Leon / 狮子的魂 f80830f666
Merge pull request #407 from lionsoul2014/golang_test_with_ip2region_service
Golang search test driven by `Ip2Region` service
2025-12-07 00:43:08 +08:00
lionsoul2014 2fbf899140 title the bench blocks 2025-12-07 00:41:28 +08:00
lionsoul2014 72912301ee bold the service use tips 2025-12-07 00:39:45 +08:00
lionsoul2014 6531f3970f search test driven by ip2region service 2025-12-07 00:38:14 +08:00
lionsoul2014 b44654230f add cache policy parse func 2025-12-07 00:38:00 +08:00
Leon / 狮子的魂 1b9fb0c95c
Merge pull request #406 from lionsoul2014/java_app_with_ip2region_service
Java search test driven by ip2region service
2025-12-06 12:42:27 +08:00
lionsoul2014 242bff6208 pom version set to 2025-12-06 12:38:37 +08:00
lionsoul2014 a1c536ba98 Update the Doc for search tests drived by Ip2Region service 2025-12-06 12:38:02 +08:00
lionsoul2014 2454568203 search test driver change to Ip2Region service 2025-12-06 12:24:23 +08:00
lionsoul2014 48dce3545e parse cache policy from name 2025-12-06 12:23:52 +08:00
Leon / 狮子的魂 dde56752af
Merge pull request #404 from lionsoul2014/fr_java_service
Package all the classes into the service package.
2025-12-05 23:50:51 +08:00
lionsoul2014 d9ce5da85f track the total searches and display the avg took 2025-12-05 23:45:38 +08:00
lionsoul2014 dae949b8ce pom version to 3.2.2 2025-12-05 23:37:10 +08:00
lionsoul2014 54628265b3 pack all the service class to service package 2025-12-05 23:33:23 +08:00
lionsoul2014 9d67e3fcad service.NewIp2Region 2025-12-05 23:18:06 +08:00
lionsoul2014 bbccbfa451 add Usage doc for Ip2Region service 2025-12-05 23:15:29 +08:00
lionsoul2014 1f8bf73a0f ip2region search service & tests 2025-12-05 18:51:44 +08:00
lionsoul2014 620646e8e0 stdlize the code style 2025-12-05 18:51:23 +08:00
lionsoul2014 1adc5643aa Add IP Add Sub funcs 2025-12-05 18:50:46 +08:00
lionsoul2014 316355a72e remove the Stat 2025-12-05 12:49:14 +08:00
lionsoul2014 e73321a860 searcher pool is ready 2025-12-05 12:42:45 +08:00
lionsoul2014 ab22a5e03c config is ready 2025-12-04 14:15:48 +08:00
lionsoul2014 ace0c7f300 update String format 2025-12-04 14:14:40 +08:00
lionsoul2014 2a8e3444d4 searcher pool impl 2025-12-04 13:14:03 +08:00
lionsoul2014 4c9ef35a9a Add string method 2025-12-04 13:13:49 +08:00
lionsoul2014 540831e6a9 add String method 2025-12-04 13:13:10 +08:00
lionsoul2014 a220db2345 export the NewSearcher 2025-12-04 13:12:50 +08:00
lionsoul2014 619b6645a5 maven pom version 3.2.1 2025-12-04 11:11:41 +08:00
lionsoul2014 40c6c2c1bd maven pom version 3.2.1 2025-12-04 11:10:10 +08:00
lionsoul2014 89bb8cb709 Align the comments 2025-12-04 10:59:47 +08:00
狮子的魂 e64864aa40
!21 chore: fix the typo to (Seacher to Searcher)
Merge pull request !21 from 酒饮三酌/master
2025-12-04 02:54:43 +00:00
lionsoul2014 c78ba4827d service config 2025-12-03 13:36:49 +08:00
libo a3d1ba5d30 chore: 调整拼写错误单词Seacher->Searcher 2025-12-03 11:51:44 +08:00
lionsoul2014 000e19d419 disassemble the header info 2025-12-03 11:47:32 +08:00
lionsoul2014 b7f2ffa285 update the concurrent test with more tests and assert check 2025-12-02 10:37:13 +08:00
lionsoul2014 12c94cf0b6 Doc update 2025-12-01 13:53:52 +08:00
lionsoul2014 3f84cd41c1 Merge branch 'master' of gitee.com:lionsoul/ip2region 2025-12-01 13:51:54 +08:00
lionsoul2014 9669237830 update the Concurrency Safety Desc 2025-12-01 13:51:06 +08:00
lionsoul2014 8cecf2f3aa update the Update Concurrency Safety Desc 2025-12-01 13:41:37 +08:00
lionsoul2014 ab16071797 update the remarks for ip2region service 2025-12-01 13:06:34 +08:00
lionsoul2014 4a4c8de636 Merge branch 'fr_java_service' 2025-12-01 13:03:50 +08:00
lionsoul2014 942f3d9dbd java ip2region search service is ready 2025-12-01 13:03:11 +08:00
Leon / 狮子的魂 a383e4dd17
Merge pull request #403 from liuyunbin/cpp-ipv6
refactor and support ipv6 for binding maker and editor use C++
2025-12-01 11:18:09 +08:00
lionsoul2014 b5ae94b36d ip2region service with thread safe and auto ip version detect supports 2025-11-28 16:34:48 +08:00
lionsoul2014 efbbb576ee SearcherApp to SearcherTest 2025-11-28 16:34:06 +08:00
lionsoul2014 5c5c0ab160 add ipAddOne and ipSubOne impls 2025-11-28 16:33:47 +08:00
lionsoul2014 7f2d1ae789 SearcherApp to SearcherTest 2025-11-28 16:33:25 +08:00
Yunbin Liu a91c8e9747 refactor and support ipv6 for binding maker and editor use C++ 2025-11-28 08:19:56 +00:00
lionsoul2014 f8a154aeda searcher pool and tests 2025-11-27 15:11:37 +08:00
lionsoul2014 a8087e06b7 searcher pool init 2025-11-25 15:10:21 +08:00
lionsoul2014 317a900077 add vIndex & cBuffer fields and auto build 2025-11-25 15:03:31 +08:00
Leon / 狮子的魂 096d68ea77
Merge pull request #401 from ArgoZhang/dev-net
fix(NET): refactor GetData method
2025-11-24 16:07:01 +08:00
Argo Zhang 23ae923cff doc: 更新 CHANGELOG 文件 2025-11-22 18:49:57 +08:00
Argo Zhang c0f0efdb95 chore: 更新版本号为 3.0.0 2025-11-22 18:49:40 +08:00
Argo Zhang b3c2d40588 doc: 压力测试结果 2025-11-22 13:10:24 +08:00
Argo Zhang 7612e679fb feat: 增加 IDispose 接口保证释放文件句柄 2025-11-22 13:04:38 +08:00
Argo Zhang 5b8b47df5e perf: 增加预热方法 2025-11-22 12:53:22 +08:00
Argo Zhang 0b3635b3c3 refactor: 使用接口代替虚类 2025-11-22 12:53:02 +08:00
Argo Zhang 33f8bf766d test: 更新单元测试 2025-11-22 09:14:34 +08:00
Argo Zhang 0b3d794738 refactor: Search(uint) 方法标记过期 2025-11-22 09:14:28 +08:00
lionsoul2014 613501fc00 simplify the js desc 2025-11-21 22:47:38 +08:00
lionsoul2014 e95452c1c8 Merge branch 'master' of github.com:lionsoul2014/ip2region 2025-11-21 22:44:41 +08:00
lionsoul2014 3ad925406b c# is ready for IPv6 2025-11-21 22:42:49 +08:00
Leon / 狮子的魂 6e4af36dc9
Merge pull request #399 from ArgoZhang/feat-ipV6
IPv6 supports for C# binding
2025-11-21 22:37:39 +08:00
Argo Zhang d1151accde doc: 增加支持框架列表文档增加 NET10 支持 2025-11-21 17:09:31 +08:00
Argo Zhang 2c9ba9c56c doc: 增加基准测试结果 2025-11-21 17:05:22 +08:00
Argo Zhang 85144e1c40 refactor: 代码重构 2025-11-21 17:05:13 +08:00
Argo Zhang 426038b3fe refactor: 重构代码 2025-11-21 15:46:09 +08:00
lionsoul2014 2c7b170637 still working on the solutions 2025-11-21 14:49:31 +08:00
Argo Zhang 14d2332483 doc: 更新头文件 2025-11-21 13:14:42 +08:00
Argo Zhang ad84da7d3c test: 更新单元测试 2025-11-21 12:38:10 +08:00
Argo Zhang 9ecca28fc2 refactor: 更新时差问题 2025-11-21 12:37:47 +08:00
Argo Zhang 4ea2e7360d chore: 增加压力测试 2025-11-21 12:25:24 +08:00
Argo Zhang edc2559489 test: 更新单元测试 2025-11-21 12:25:13 +08:00
Argo Zhang 7bd81fecbf chore: 增加 net10 支持 2025-11-21 12:24:49 +08:00
Argo Zhang 6684d0a928 perf: 优化缓存策略 2025-11-21 12:24:32 +08:00
Argo Zhang 30fa54a83f perf: 更新基准测试 2025-11-21 11:27:51 +08:00
Argo Zhang ca7fc93953 test: 增加单元测试提高代码覆盖率 2025-11-21 11:18:37 +08:00
Argo Zhang db50f7b0da test: 增加单元测试覆盖向量缓存逻辑 2025-11-21 11:15:46 +08:00
Argo Zhang 5cfd5f8242 perf: 增加向量缓存逻辑提高性能 2025-11-21 11:15:31 +08:00
Argo Zhang 87b69e6b5f perf: 提高 Content 缓存模式性能 2025-11-21 09:42:59 +08:00
Argo Zhang 002c482db3 doc: 文档统一格式化 2025-11-21 09:03:37 +08:00
Argo Zhang 75d944eaf7 doc: 增加类注释 2025-11-21 08:52:17 +08:00
Argo Zhang 92cc3fd7cb chore: 增加 editorconfig 配置文件 2025-11-21 08:52:07 +08:00
Argo Zhang f1d0e1bee0 chore: 更新 BenchMark 工程 2025-11-21 08:15:23 +08:00
Argo Zhang d7d06e2185 chore: 单元测试框架更改为 net10 2025-11-20 17:52:48 +08:00
Argo Zhang 55a493fd8e chore: bump version 3.0.0 2025-11-20 17:51:13 +08:00
Argo Zhang f03861162f test: 增加全数据验证单元测试 2025-11-20 17:50:33 +08:00
Argo Zhang cbf8f9bfa8 test: 增加单元测试 2025-11-20 17:49:57 +08:00
Argo Zhang 3aa732d1ce test: 增加单元测试 2025-11-20 17:31:21 +08:00
Argo Zhang 9ef1fc34ac test: 增加单元测试提高代码覆盖率 2025-11-20 17:31:13 +08:00
Argo Zhang 62ab160572 refactor: 精简代码 2025-11-20 17:30:57 +08:00
Argo Zhang 87666aa353 test: 更新单元测试 2025-11-20 16:25:38 +08:00
Argo Zhang a2778f9b98 refactor: 增加边界数据支持 2025-11-20 16:25:25 +08:00
Argo Zhang af82b7e18e feat: 增加 XdbVersion 结构体与读取方法 2025-11-20 15:45:39 +08:00
lionsoul2014 a6cb5161ff config and config builder is ready 2025-11-20 15:40:54 +08:00
Argo Zhang d4d211c50b test: 更新单元测试 2025-11-20 15:26:30 +08:00
Argo Zhang 31cd078455 refactor: 兼容 netstander 框架 2025-11-20 15:26:12 +08:00
Argo Zhang 499e091761 feat: 增加 IPv6 算法支持 2025-11-20 15:22:21 +08:00
Argo Zhang 4f39707beb refactor: 更改为 XdbVersion 2025-11-20 15:22:04 +08:00
Argo Zhang 7fab19c46f chore: 更新逻辑 2025-11-19 18:01:38 +08:00
lionsoul2014 bd30b77d54 fix the typo 2025-11-13 09:39:23 +08:00
lionsoul2014 cd78ff2797 add min version supports desc 2025-11-05 12:23:43 +08:00
lionsoul2014 8692309c5b fix the typo to util.load_xxx 2025-11-05 10:59:12 +08:00
lionsoul2014 aa5437c637 3.0.2 with python >= 3.7 supports 2025-11-05 10:35:53 +08:00
Argo Zhang 8573f2f04e feat: 增加 Version 适配 ipV6 2025-11-04 15:45:38 +08:00
lionsoul2014 6eee95967b add tips for compile & usage verson match 2025-11-04 15:02:35 +08:00
Argo Zhang ef7ac3c088 refactor: 更新单元测试 2025-11-04 14:58:04 +08:00
lionsoul2014 4b7172766c lua 5.1/5.2/5.3/5.4 supports 2025-11-04 14:36:20 +08:00
lionsoul2014 dc64c27abd c99 std supports 2025-11-02 23:03:07 +08:00
lionsoul2014 1da81ffc02 API define optimize for C99 std supports 2025-11-02 22:54:44 +08:00
lionsoul2014 ae94b2dac3 version 3.0.1 with License classifier removed 2025-11-02 22:18:36 +08:00
lionsoul2014 f8f01ecd13 use μs 2025-11-02 22:17:47 +08:00
lionsoul2014 db95055a56 μs for elapsed 2025-11-02 21:43:54 +08:00
lionsoul2014 32a755ac3f use the py-ip2region as the pypi package name 2025-11-02 18:34:21 +08:00
lionsoul2014 784f336646 add keywords 2025-11-01 23:02:49 +08:00
lionsoul2014 732d4eb945 ip2region 3.0.0 and pip install desc 2025-10-31 23:41:35 +08:00
lionsoul2014 e753437afc include the bench search test script 2025-10-31 23:41:00 +08:00
lionsoul2014 ab0c39b7cd add ip2region pip install desc 2025-10-31 23:40:38 +08:00
lionsoul2014 db3b9b22ae ip2region package for pypi 2025-10-31 16:12:36 +08:00
lionsoul2014 91e542de38 python is ready for IPv6 2025-10-31 11:58:47 +08:00
lionsoul2014 bec072cd04 add ReadMe & python is ready for IPv6 2025-10-31 11:56:29 +08:00
lionsoul2014 bf0ded1171 search with ip_string for 3-kinds-of cache-policy searcher 2025-10-31 11:52:17 +08:00
lionsoul2014 c4113e5eae xdb bench script and bench test passed 2025-10-30 23:20:03 +08:00
lionsoul2014 731269ebad .3f format the took 2025-10-30 23:19:38 +08:00
lionsoul2014 9797b595ea strip the input 2025-10-30 23:18:03 +08:00
lionsoul2014 3ea717043c fix the bug of buffer read 2025-10-30 23:17:43 +08:00
lionsoul2014 42b9cebb9d call the ip_compare args func 2025-10-30 22:23:57 +08:00
lionsoul2014 9e2ca6bd98 ignore __pycache__ 2025-10-30 22:17:41 +08:00
lionsoul2014 0422ed3f4f to run_search_test 2025-10-30 15:34:33 +08:00
lionsoul2014 7f5844ca94 xdb searcher and searcher test script 2025-10-30 15:33:26 +08:00
lionsoul2014 cc97bb80a2 fix the bug of ip_sub_compare call 2025-10-30 15:33:02 +08:00
lionsoul2014 d4d5525392 utils is ready and with unit tests passed 2025-10-30 11:37:19 +08:00
lionsoul2014 6c653f85cd multi-line the str format 2025-10-29 22:59:05 +08:00
lionsoul2014 47067dbe73 utils keep going and unit tests 2025-10-29 22:55:10 +08:00
lionsoul2014 f6213aaaa4 remove the old implmentation 2025-10-29 22:53:04 +08:00
lionsoul2014 3a3abdd01c python ipv6 supporting 2025-10-29 16:03:19 +08:00
lionsoul2014 bbf5df5601 fix the typo 2025-10-27 21:48:25 +08:00
lionsoul2014 0f2260d116 warnings fixed 2025-10-27 16:00:37 +08:00
lion 572aa7b76a update the default xdb path 2025-10-24 12:49:00 +08:00
lion 580d1bb431 update the default xdb path to the latest xdb 3.0 version 2025-10-24 12:47:34 +08:00
lion 029a5578aa remove the un-used stats sub-cmd 2025-10-23 14:26:52 +08:00
lion 501c3c086d seperate all the sub commands impl 2025-10-23 14:02:51 +08:00
狮子的魂 / Leon 08d3ef69b0
Merge pull request #392 from gongzhengyang/master
Fix: rust bind return empty string for not found IP
2025-10-21 14:47:55 +08:00
gongzhengyang 4ebfa61179 Fix: rust bind return empty string for not found IP 2025-10-21 14:35:18 +08:00
lion 8a7a899c64 fix the sample region info 2025-10-21 13:31:01 +08:00
lion f7ad4309f8 search api prototyp desc 2025-10-21 13:12:59 +08:00
lion d56230518a search api desc 2025-10-21 12:57:25 +08:00
lion 2a77acf1c0 return empty string for empty match 2025-10-21 12:57:14 +08:00
lion d5d0c15c8d syntax the api code with php 2025-10-21 11:20:44 +08:00
lion b3498197a8 return empty string for empty match 2025-10-21 11:14:58 +08:00
lion d10c57b598 search api desc 2025-10-21 11:14:22 +08:00
lion 7050da1a68 search api desc 2025-10-21 10:55:42 +08:00
lion bb3c845c81 search api desc & return empty string for empty match 2025-10-21 10:48:25 +08:00
lion ca6c0506e6 search api desc and return empty string for empty match 2025-10-21 10:40:34 +08:00
lion 79cc6c1b0d putInt2 to putUint16 and getInt2 to getUint16 2025-10-21 10:34:53 +08:00
lion bd69a255fd return empty string for empty match 2025-10-21 10:28:56 +08:00
lion 8f6effd8eb verify api and verify the xdb before search or bench test 2025-10-20 22:26:15 +08:00
lion d503206e14 3.1.5 release verison with third party package lists 2025-10-20 19:10:21 +08:00
lion b3383680f9 code format stdlize 2025-10-20 19:09:37 +08:00
lion 567224ca60 pure lua script is ready for IPv6 2025-10-20 19:01:30 +08:00
lion 541420b8df pure lua script is ready for IPv6 2025-10-20 18:59:39 +08:00
lion 6cb1da2794 search process forward 2025-10-20 15:37:51 +08:00
lion ec7cb6d3ed Merge branch 'master' into fr_lua_ipv6 2025-10-19 23:08:31 +08:00
lion a30d922225 lines merge 2025-10-19 23:07:00 +08:00
lion a1b2c21767 optimize the ipv6 bytes to string 2025-10-19 23:05:15 +08:00
lion 1e31b4b143 ip util functions is ready 2025-10-19 22:57:06 +08:00
lion baa9f62d6b Merge branch 'master' into fr_lua_ipv6 2025-10-19 18:42:52 +08:00
狮子的魂 / Leon 3b4a229918
Merge pull request #390 from ArgoZhang/feat-services
feat(CSharp): Add injection service extension method
2025-10-19 18:41:48 +08:00
Argo Zhang d362231ae8 test: 更新依赖到最新 2025-10-19 17:37:18 +08:00
Argo Zhang 1ad01ff98b doc: 更新 readme 文档 2025-10-19 17:37:09 +08:00
Argo Zhang 5deb6d6553 test: 更新依赖文件名 2025-10-19 17:36:57 +08:00
Argo Zhang 444b2036d1 chore: 更新基准测试文件名 2025-10-19 17:36:37 +08:00
Argo Zhang 35e505bd74 test: 更新单元测试依赖文件 2025-10-19 17:36:03 +08:00
Argo Zhang 2fe8032d8b chore: 更改项目框架为 net9 2025-10-19 17:35:37 +08:00
lion 69eb94dcd8 funcs to parse ip address (IPv4 and IPv6) 2025-10-19 17:26:17 +08:00
Argo Zhang 0ad3172f2e doc: 增加注释 2025-10-19 15:30:34 +08:00
Argo Zhang 2ac7655f62 chore: 更改依赖 2025-10-19 15:30:26 +08:00
Argo Zhang 6aab24fb88 refactor: 更改为 TryAddSingleton 防止重复注册 2025-10-19 15:24:39 +08:00
Argo Zhang b94123fee3 refactor: 重构代码 2025-10-19 15:19:00 +08:00
Argo Zhang 0caff12306 chore: 更新依赖包版本号为具体版本号 2025-10-19 15:16:39 +08:00
Argo Zhang 6ad1e8e2e2 test: 更新单元测试 xdb 文件路径 2025-10-19 15:16:23 +08:00
Argo Zhang 9e979efd7b refactor: 更新 link 路径 2025-10-19 15:11:58 +08:00
Argo Zhang f594838784 refactor: 更改扩展类名称 2025-10-19 15:09:06 +08:00
Argo Zhang 24dcb24e24 refactor: 增加 Keyed 逻辑 2025-10-19 15:08:16 +08:00
Argo Zhang ddb2b4ff1d Merge remote-tracking branch 'github.com/master' into feat-services 2025-10-19 14:57:44 +08:00
Argo Zhang 2662fb05c2 feat: 增加注入服务支持 2025-10-19 14:55:37 +08:00
Argo Zhang 7e4d37b1db refactor: 增加更多框架支持 2025-10-19 14:55:21 +08:00
Argo Zhang d1715c75f6 refactor: 更新 xdb 文件名 2025-10-19 14:54:49 +08:00
Argo Zhang c82d2e6117 chore: 增加 slnx 解决方案工程 2025-10-19 14:54:31 +08:00
lion ffb632c3d0 add third party js binding impl 2025-10-19 10:19:40 +08:00
lion 11f890f3a4 xdb base tech links 2025-10-19 09:55:47 +08:00
lion 87b6cd5247 update the social media desc 2025-10-19 09:53:24 +08:00
lion 61dc2af93d track the latest optimized ipv6 source and xdb 2025-10-17 12:43:54 +08:00
lion 413ccd32ce version 3.1.3 with empty match interception 2025-10-17 12:42:52 +08:00
lion e3b87326d2 empty match interception with null return 2025-10-17 12:35:10 +08:00
lion 5a51b75411 optimize the doc 2025-10-15 21:33:54 +08:00
lion a2b76d38a9 fix the duration stats 2025-10-15 13:39:18 +08:00
lion 8c7c83cf7f deprecated the old nodejs and typescript binding 2025-10-15 11:36:17 +08:00
lion fbadc7cca0 new javascript binding is Ready with IPv4 and IPv6 supported 2025-10-15 11:27:10 +08:00
lion f03d33e5fe publish the ip2region.js package and update the doc for it 2025-10-15 11:10:47 +08:00
lion c809c072bc add search test demo docs 2025-10-15 10:18:45 +08:00
lion 5c1522fce4 search test app is ready 2025-10-15 10:13:10 +08:00
lion a3eedc5438 rm unnecessary brackets 2025-10-15 10:12:52 +08:00
lion 4f8cdb05c9 remove the unused code 2025-10-15 09:30:32 +08:00
lion 008bbbe4d7 bench script is ready and bench test passed 2025-10-14 21:44:07 +08:00
lion d35a7b9120 bench script 2025-10-14 14:20:19 +08:00
lion 4d71b47e05 merge the tests to util or searcher 2025-10-14 11:25:12 +08:00
lion 40ea8a267f remove the debug checks 2025-10-13 22:35:51 +08:00
lion 2dcccf22a3 verify impl and verify the xdb files 2025-10-13 22:21:30 +08:00
lion e073e26f52 fix the bug of ipToString with tailing double colon 2025-10-13 18:42:20 +08:00
lion ed42fd0fba adjust the export order 2025-10-13 18:13:55 +08:00
lion 9959117d92 ReadMe for basic usage 2025-10-13 18:06:54 +08:00
lion 3cab1bae2e add ts type define and api exports 2025-10-13 15:28:30 +08:00
lion 5a8d7c4152 use the ESM standard 2025-10-13 14:35:45 +08:00
lion 4507c2f706 javascript is Ready for IPv6 2025-10-13 12:05:20 +08:00
lion ad78dbe547 searcher with ipv4 tests passed 2025-10-12 13:48:26 +08:00
lion e924a7c17d version ip compare is ready 2025-10-12 11:07:22 +08:00
lion 6c463282d0 merge the header/version to util.js 2025-10-11 23:57:55 +08:00
lion bb19c7110a mi = max-index 2025-10-11 23:45:05 +08:00
lion 5fce4eaa01 ipv6 parse and toString beta version 2025-10-11 23:43:37 +08:00
lion 01bd749d4a ipv6 parse and to string alpha version 2025-10-11 19:34:24 +08:00
lion 3693eb47a8 Merge branch 'master' into fr_javascript_ipv6 2025-10-11 15:53:47 +08:00
lion e39ac2d6d1 optimize the type define 2025-10-11 15:53:09 +08:00
lion d97967d565 ip parse and to string util implementing 2025-10-11 15:49:24 +08:00
lion 8650273a85 Merge branch 'master' into fr_javascript_ipv6 2025-10-11 14:45:17 +08:00
lion ccf7f323c2 header/vector/content load util funcs is ready 2025-10-11 13:28:20 +08:00
lion a930a17e39 fix the typo 2025-10-10 16:14:23 +08:00
lion 0a456283e6 list the ts-ip2region addon repo 2025-10-10 16:12:35 +08:00
lion d6345bc015 ignore the package-lock.json 2025-10-10 16:07:45 +08:00
lion 086250c666 javascript binding with IPv4 and IPv6 supports 2025-10-10 16:03:33 +08:00
lion 54a160e230 ignore the test_util binary 2025-10-10 10:31:22 +08:00
lion 2d71241a3b for msvc 2025-09-30 09:36:52 +08:00
lion 8a254e3e83 rust maker is ready for IPv6 2025-09-28 22:43:45 +08:00
狮子的魂 95f5fcfcdb
Merge pull request #383 from gongzhengyang/master
Add rust maker (with IPv4 and IPv6 supported)
2025-09-28 22:39:03 +08:00
gongzhengyang 2cd59657e1 Improve rust maker log output 2025-09-28 16:44:40 +08:00
gongzhengyang 1f1ad5aaf6 Style: fmt binding rust code 2025-09-28 15:51:54 +08:00
gongzhengyang e887c01615 Fix: remove maker rust unused deps 2025-09-28 15:46:37 +08:00
gongzhengyang df6004b42e Feat: add rust maker for ipv4 and ipv6 2025-09-28 15:27:11 +08:00
gongzhengyang 05c7c17af0 Docs: add bench result for rust binding in readme 2025-09-28 15:27:11 +08:00
lion a6c0a66a36 update the doc links 2025-09-28 14:14:47 +08:00
lion a137813509 enable all standard warnings & fix all the warnings 2025-09-25 22:24:01 +08:00
lion 0179edc3a8 auto manager the region buffer & remove the unused vars 2025-09-25 22:11:52 +08:00
lion 4ff67f168c optimize the region filtering to achieve higher consecutive segments merging rate 2025-09-25 11:17:09 +08:00
lion cb3329390a Allow empty region info 2025-09-24 15:56:40 +08:00
lion 93c5088591 rust is ready for IPv6 2025-09-24 15:05:48 +08:00
狮子的魂 1ac2113afc
Merge pull request #382 from gongzhengyang/master
Rust binding supports multi cache policies and IPv6
2025-09-24 15:03:32 +08:00
gongzhengyang 139972dfb8 Fix: rust code fmt 2025-09-24 14:55:08 +08:00
gongzhengyang 379ab6ce73 Fix: rust binding bench count error 2025-09-24 14:50:09 +08:00
gongzhengyang d3592eb7cd Feat: improve bench query code in rust example, add usage at Readme 2025-09-24 14:35:42 +08:00
gongzhengyang b6410e16d0 Docs: improve rust binding usage in Readme 2025-09-24 10:42:12 +08:00
gongzhengyang d7f4ef5a07 Feat: limit bench IPv6 range 2025-09-24 10:41:47 +08:00
gongzhengyang 7a4bd4ed2b Fix: binary search ip unneccessary repeat call 2025-09-24 10:40:53 +08:00
gongzhengyang a6931ce31b Docs: improve binding rust readme docs, basic usage 2025-09-23 19:51:56 +08:00
gongzhengyang 32e005e1e6 Feat: rust binding support IPv6 search 2025-09-23 19:51:51 +08:00
gongzhengyang b5b00aeb87 Feat: improve rust binding example read file 2025-09-23 19:51:44 +08:00
gongzhengyang b16fe21506 Feat: add file and vector index cache search 2025-09-23 19:51:34 +08:00
lion 931fb9e67b use the local stack region_buffer instead of heap allocation 2025-09-23 18:15:46 +08:00
lion 7576daa6cc empty input ignore 2025-09-23 11:09:23 +08:00
lion 75893f0331 include time.h for windows struture tm 2025-09-22 23:47:38 +08:00
lion 355c0d8673 default lua version to 5.4 2025-09-21 13:06:25 +08:00
lion 815b1fb54b lua c module is ready for IPv6 2025-09-21 13:02:42 +08:00
lion 29fa8e25ab lua c ext is ready for IPv6 2025-09-21 12:56:42 +08:00
lion 13324d1575 ignore the .vscode 2025-09-21 11:30:43 +08:00
lion 46f56f0eca remove the unused defines 2025-09-21 00:51:19 +08:00
lion 2fe0d1f59e Ipv6 search is ready 2025-09-21 00:45:45 +08:00
lion b01253e6b3 ip parse and binary ip bytes use 2025-09-20 17:29:11 +08:00
lion 6712ee9504 calling the latest xdb api 2025-09-20 14:37:22 +08:00
lion 5fa69300ad lua_c ipv6 supporting 2025-09-20 14:03:09 +08:00
lion 7e599e05a0 print the error tip if region_buffer init failed 2025-09-19 16:16:19 +08:00
lion a36a85558f c is ready for IPv6 search 2025-09-19 13:30:37 +08:00
lion 48c2b395bc c binding is ready for IPv6 2025-09-19 13:08:47 +08:00
lion dff3084b93 C binding is ready for IPv6 2025-09-18 23:09:19 +08:00
lion 8fbdf82797 IPv6 search and docs is ready 2025-09-18 15:53:48 +08:00
lion 73c8b20d3a apply the bytes ip to the whole search process 2025-09-17 23:10:42 +08:00
lion 69b9f062e4 directly compare the ip from the buffer 2025-09-17 14:45:03 +08:00
lion 1575e581cd try to apply the bytes_ip_t for the search 2025-09-17 14:43:28 +08:00
lion 4cff9eb6ee ip parse / to_string / compare util function is ready 2025-09-17 13:07:35 +08:00
lion 0fd0bc59d0 bytes ip utils prototype 2025-09-16 22:09:49 +08:00
lion c64837db67 ip util function update for IPv6 2025-09-16 15:29:55 +08:00
lion eb80b89c0e ipv6 supporting for clang 2025-09-16 14:46:34 +08:00
lion 9a14e32736 add xdb file verify util and call it before the search/bench test 2025-09-16 12:35:22 +08:00
lion 4c1b0362c8 fix the typo 2025-09-16 12:34:50 +08:00
lion 5d11d6c0fb php ipv6 supports is ready 2025-09-15 15:27:43 +08:00
lion 7bbe7d681d PHP binding IPv6 supports is ready 2025-09-15 15:22:49 +08:00
lion 95c129c7d1 PHP IPv6 supporting 2025-09-14 23:14:42 +08:00
lion 716326f4bc list the source files 2025-09-13 23:30:14 +08:00
lion d1e9b6e3bc Docs for the java maker with IPv6 supported 2025-09-13 00:37:51 +08:00
lion ae344d20aa Java maker and binding IPv6 supports is ready 2025-09-13 00:24:51 +08:00
lion 537077279a Java IPv6 support is ready 2025-09-13 00:21:25 +08:00
lion a91fdd38bc updatet the pom desc 2025-09-12 15:48:20 +08:00
lion 9af9c927fe IPv6 impl for java binding is ready 2025-09-12 15:43:37 +08:00
lion f226ef1f79 update the comments 2025-09-12 15:31:12 +08:00
lion e8b6049584 resource defer close and some optimizations 2025-09-12 12:52:15 +08:00
lion 6db9654c7d IPv6 supporting for Java searcher 2025-09-12 12:41:00 +08:00
lion c6c0ee954f fix the bug of region filtering 2025-09-12 09:44:34 +08:00
lion 4911bdcd41 maker is ready and with consecutive segments check and merge 2025-09-11 21:08:51 +08:00
lion 23b2446736 segment contains and max ptr confirm 2025-09-11 15:18:43 +08:00
lion 6727987960 java maker 3.0 with IPv6 supported 2025-09-11 15:06:10 +08:00
lion 629f509c6f use the unit test instead 2025-09-10 22:54:34 +08:00
lion 23b3c3bc9b fix the func desc 2025-09-10 22:53:53 +08:00
lion 1cf2e419e1 remove the unused imports 2025-09-10 22:48:44 +08:00
lion 697b46bb2e ip util functions and its unit test 2025-09-10 22:46:14 +08:00
lion 83fd149c59 ipv6 utils and testing 2025-09-10 15:38:15 +08:00
lion 87d53b5018 some optimizations 2025-09-09 22:34:13 +08:00
lion f0a9462041 JRE-1.8 at least 2025-09-09 15:58:35 +08:00
lion 6a729e40a5 update the desc about region format 2025-09-08 12:21:15 +08:00
lion fdec88605f update the cli search demo 2025-09-08 11:52:34 +08:00
lion 6194a4c106 IPv6 supports status 2025-09-08 11:29:13 +08:00
lion f7f5d851bb update the status emoji 2025-09-08 11:26:42 +08:00
lion 0623fee57a update the ReadMe for IPv6 supports 2025-09-08 11:13:09 +08:00
lion beb0e452ce xdb verify desc and docs 2025-09-07 18:09:57 +08:00
lion 7cc7d4d9cf add verify func and call it on the test program to make sure the searcher would work properly 2025-09-07 10:47:01 +08:00
lion a8a17b0f98 show the normalized ipv4 region 2025-09-06 10:35:57 +08:00
lion 538ea4194c ipv4 data region normalized to country|province|city|isp 2025-09-06 10:35:31 +08:00
lion 03eef4a75e IPv4 and IPv6 search test and bench demo 2025-09-06 10:14:23 +08:00
lion 6b2e18e551 update the Doc for the latest API call (IPv4 & IPv6) 2025-09-06 10:07:13 +08:00
lion 0307a3df14 IPv6 RC verison with IPv4 compatible with the old implementation 2025-09-06 00:36:10 +08:00
lion ae69123857 add IPv6 supports for golang binding 2025-09-05 17:15:01 +08:00
lion 037663e355 track the latest v4 xdb 2025-09-05 17:10:34 +08:00
lion d3ef2dcc28 stdlize the IP version global vars to IPvx 2025-09-05 14:12:54 +08:00
lion aade8810ba track the latest ipv6 source and xdb file 2025-09-05 13:00:00 +08:00
lion 35eef70083 add LFS config 2025-09-05 11:17:41 +08:00
lion 6092e6d076 update the doc for the latest maker with IPv6 supported 2025-09-05 11:07:27 +08:00
lion 04a4c1f1ce default to v4 2025-09-05 11:05:22 +08:00
lion 9cbcca588a add version flag for source ip editor 2025-09-05 09:51:18 +08:00
lion 33beaf96c9 add version flag and use it for the searcher create 2025-09-04 15:42:38 +08:00
lion 34e89d3f50 apply the field-list filter 2025-09-04 15:22:24 +08:00
lion 2735819b4f source ip data processor 2025-09-04 14:59:20 +08:00
lion 980c9cfdd4 make the region filtering an util func 2025-09-04 14:58:53 +08:00
lion cdd2a2fc85 update the comments for the latest changes 2025-09-04 12:33:58 +08:00
lion c0058781c9 version expected for xdb gen and auto version detect for search test 2025-09-04 11:14:58 +08:00
lion b830361c52 version flag 2025-09-03 21:40:43 +08:00
lion ac46b7359e write the IP version NO and runtime ptr bytes in the header 2025-09-03 21:06:25 +08:00
lion b73e190905 xdb ipv6 supporting (bytes operation is working) 2025-09-03 17:16:05 +08:00
lion 4996c0ff6a add in-memory search supports when the xdb file size exceeds 2^31 - 1 2025-08-22 19:10:51 +08:00
lion f9402614f6 track the offline data link 2025-06-30 16:03:45 +08:00
lion 73e4e6047f add batch test script 2025-06-19 15:08:52 +08:00
lion 7a9a53a058 update the doc links 2025-06-19 11:16:33 +08:00
lion c0fda2e211 update the desc about community 2025-06-17 21:53:03 +08:00
lion e569ef2aad update the intro 2025-06-17 21:48:02 +08:00
lion 1795f2ad36 desc the community vision 2025-06-17 21:38:41 +08:00
lion d8ace3508b update the intro 2025-06-17 17:33:17 +08:00
lion 3931fea9a5 update the intro 2025-06-17 17:31:14 +08:00
lion 4529f9819c add ip2region offical community 2025-06-17 17:30:09 +08:00
lion bbd811e1be update the docs to ip2region offical community 2025-06-17 17:07:20 +08:00
lion b41a805cff range reverse check 2025-06-16 23:47:03 +08:00
lion 78c2fab10a add field-list support self-define region fields 2025-06-16 23:24:19 +08:00
lion c1808255e7 log the start 2025-06-16 19:31:50 +08:00
lion 36f5f129a4 adjust the log print level 2025-06-16 19:20:56 +08:00
lion 95bcc92f11 log-level flag and debug/info/warn/error value supports and apply 2025-06-16 19:13:52 +08:00
lion 6551e8e854 field list range specified supported 2025-06-15 16:04:14 +08:00
lion 47d4f1e213 field-list flag to support self-define fields for the region 2025-06-14 23:37:11 +08:00
lion da47234738 java binding 2.8.0 with vectorIndex/file policy search support xdb size exceeds INT.max bytes 2025-06-10 23:42:21 +08:00
lion 1d32d5e6aa a Temporary solution for xdb size exceeds the INT.max which will cause nagetive array index 2025-06-10 23:37:40 +08:00
lion bff6b5661c add --log-level=debug/info/warn/error flag, so we can close the log print during db gen 2025-06-10 22:55:52 +08:00
lion ed57fa5c52 fix the erlang auther info 2025-05-08 12:39:14 +08:00
lion ec1ae9504e fix the bug of undefined:io 2025-05-05 19:46:13 +08:00
狮子的魂 8952539b0f
Merge pull request #369 from williammmmmmmmm/master
Add a function LoadContentFromFS() to load xdb files from an embedded file system (embed.FS).
2025-05-05 10:45:52 +08:00
Jellyfish 39a806695e 增加LoadContentFromFS函数,提供了一个通用的方法来从 embed.FS 中加载文件内容。 2025-04-21 10:07:38 +08:00
lion 06eb5c6e82 mark the typescript xdb binding impl 2024-12-20 23:29:42 +08:00
狮子的魂 9eabc9fcdc
Merge pull request #365 from malus2077/master
typescript node binding impl
2024-12-20 23:26:45 +08:00
Alan e49a076dee update: add ts-ip2region client 2024-12-17 16:36:29 +08:00
Alan c376ccd72b v0.1.1 2024-12-17 16:34:30 +08:00
Alan 37dc606a59 update: remove @types/ip2region 2024-12-17 16:24:32 +08:00
Alan 1a11c91061 docs: update readme 2024-12-17 15:26:13 +08:00
Alan e481fea2b9 feat: add typescript client 2024-12-17 13:59:20 +08:00
Alan 3a2d02923e feat: initialize TypeScript library project 2024-12-16 14:40:46 +08:00
狮子的魂 89e20ab7b6
Merge pull request #349 from eliauk-coding/master
Update ReadMe.md
2024-05-10 13:56:07 +08:00
eliauk-code 40f2cdfcd9
Update ReadMe.md
spelling mistake
2024-05-09 14:24:57 +08:00
lion d2b12ef0c8 update the Doc about Concurrent query 2024-04-19 21:08:13 +08:00
狮子的魂 40c7f53fc9
Merge pull request #344 from liuyunbin/fix-python-search
fix python search caused by getInt2 impl
2024-03-22 16:14:24 +08:00
Yunbin Liu 3b7cc3d477 fix python search 2024-03-22 07:23:55 +00:00
狮子的魂 22d2e2ed01
Merge pull request #343 from liuyunbin/add-cpp
add binding & maker and source editor for C++
2024-03-22 08:44:31 +08:00
Yunbin Liu 19c2aa8199 add binding maker and editor for C++ 2024-03-21 06:59:50 +00:00
狮子的魂 c82399f3b9
Merge pull request #333 from KevinWL/master
feat: Implement the generation tool of Ip2region in the Rust .
2024-03-14 20:54:57 +08:00
狮子的魂 5826ba610a
Merge pull request #339 from kaifulee/master
chore: fix typo
2024-03-14 20:53:07 +08:00
kaifulee 6943aae2d0 chore: fix typo
Signed-off-by: kaifulee <cuishuang@outlook.com>
2024-03-13 20:38:52 +08:00
KevinWang f793a48b82 Update README. 2024-03-07 08:34:39 +00:00
lion f8f5f687e6 Merge branch 'master' of gitee.com:lionsoul/ip2region 2024-03-07 10:08:53 +08:00
lion b3c9dff603 disable wx group 2024-03-07 10:08:42 +08:00
Lion 27b5efde55 regen the xdb and remove the -g for c binding default compile flag 2024-02-28 10:35:24 +08:00
KevinWang 01902a6792 feat: Implement the generation tool of Ip2region in the Rust . 2024-02-23 06:33:21 +00:00
Lion 3066d243cd Compatible with windows system 2023-10-13 11:07:45 +08:00
Lion aa834af535 use the old charset.forName instead of standart to avoid JDK level set 2023-10-13 10:49:43 +08:00
Lion 86553fba20 remove the level setting 2023-10-13 09:21:34 +08:00
Lion 7256345713 force the reader default charset to UTF_8 to compita 2023-10-13 09:15:57 +08:00
Lion 3ef87ff727 tab replace with space for the structure doc comments 2023-10-08 17:37:06 +08:00
Lion 75cddb1f2e Merge branch 'master' of gitee.com:lionsoul/ip2region 2023-10-08 17:34:21 +08:00
Lion b66471f0ba tab to space 2023-10-08 17:33:59 +08:00
狮子的魂 aad2d5d306
Merge pull request #323 from malus2077/master
fix:issue #320
2023-10-04 14:27:15 +08:00
Alan 4c87a3b71e fix:issue #320 2023-10-02 21:02:26 +08:00
Lion 0c68fd1ed8 wxgh instead of personal wx group 2023-09-18 11:52:19 +08:00
狮子的魂 6ed8bf0118
Merge pull request #311 from malus2077/master
c# client Support netstandard2.0
2023-07-31 14:04:29 +08:00
Alan b8fc536521 docs:support netstandard2.0 2023-07-30 17:54:14 +08:00
Alan dd18282102 Merge remote-tracking branch 'origin/master' 2023-07-30 17:52:26 +08:00
Alan 769f708b16 feat: support netstandard2.0 2023-07-30 17:52:08 +08:00
狮子的魂 a72a6b8282
Merge pull request #310 from malus2077/master
c# client support VectorIndex, File concurrent queries
2023-07-27 11:13:57 +08:00
Alan edaca47635 docs:add file query instructions 2023-07-26 21:35:17 +08:00
Alan df1252345b test:concurrent query ip 2023-07-26 21:30:34 +08:00
Alan 7f5ec337d0 feat:support for concurrent file-based queries 2023-07-26 21:25:34 +08:00
lion c8446edaab update the NAT ip range reported at https://gitee.com/lionsoul/ip2region/issues/I7K36N 2023-07-17 12:06:33 +08:00
lion 5fe0151907 update the features 2023-07-12 20:54:43 +08:00
lion f24ed31469 update the hints 2023-07-12 20:49:54 +08:00
lion 068dc2aee8 Update the ReadMe 2023-07-12 16:47:35 +08:00
lion da40a4ad17 update the third-party links 2023-04-15 12:24:40 +08:00
lion 77b70409c6 add third-party repo tools 2023-04-15 12:22:07 +08:00
lion a5b42e32fe convert length type to compitatible with the old std 2023-04-03 09:50:23 +08:00
lion 7599cb1dfc update the nginx module status 2023-04-03 09:49:21 +08:00
狮子的魂 5412c3e365 Merge pull request #296 from wujjpp/nginx-impl
ip2region http module for nginx
2023-04-03 09:43:46 +08:00
Wu Jian Ping 4697640fbc cleanup 2023-03-31 18:22:54 +08:00
Wu Jian Ping 4e588db3de 移除LICENSE 2023-03-31 18:19:25 +08:00
Wu Jian Ping a406f0e636 1. c binding 支持MAC构建 2. c binding 支持构建静态库(nginx模块需要用到) 2023-03-31 18:14:24 +08:00
Wu Jian Ping 4ddd785bf0 cleanup 2023-03-31 00:32:05 +08:00
Wu Jian Ping fed6788fe4 enhance readme 2023-03-31 00:28:13 +08:00
Wu Jian Ping 09b05a6026 . 2023-03-30 22:19:42 +08:00
Wu Jian Ping 885c4d815f . 2023-03-30 22:16:56 +08:00
Wu Jian Ping 6607c9e61c . 2023-03-30 22:15:41 +08:00
Wu Jian Ping 0b5148be76 优化文档 2023-03-30 22:13:03 +08:00
Wu Jian Ping e8595bf525 优化文档 2023-03-30 22:12:37 +08:00
Wu Jian Ping a8571d4a99 优化文档 2023-03-30 22:11:55 +08:00
Wu Jian Ping 0bc0d9ea87 优化文档 2023-03-30 22:08:49 +08:00
Wu Jian Ping d96d54bbaa 优化代码 2023-03-30 21:58:40 +08:00
Wu Jian Ping 9578720cd1 优化代码 2023-03-30 21:34:06 +08:00
Wu Jian Ping 1d09953acb nginx implementation first cut 2023-03-30 21:03:53 +08:00
Wu Jian Ping b04e477b99 nginx-impl init 2023-03-30 17:46:38 +08:00
lion 138eea8849 upgrade maven version to 2.7.0 2023-02-05 19:05:31 +08:00
lion 3fef2a2fad fix the bug of getInt2 reported by https://github.com/lionsoul2014/ip2region/issues/287 2023-02-05 18:52:40 +08:00
lion a4b39f5315 data for issue 287 testing 2023-02-05 18:52:13 +08:00
lion d75b5929cd Merge branch 'master' of github.com:lionsoul2014/ip2region 2023-02-01 14:52:37 +08:00
狮子的魂 2a579d8db6 Merge pull request #286 from leolin49/master
raise the error for python maker
2023-02-01 14:51:25 +08:00
leolin49 a8cb6b9c31 Exception handling optimization 2023-02-01 00:37:29 +08:00
lion 874a0fbb06 add erlang xdb binding 2023-01-29 20:22:34 +08:00
狮子的魂 decee31d93 Merge pull request #282 from leihua996/master
add xdb erlang implementation
2023-01-29 20:19:58 +08:00
LeiHua f053e97b23 1.查询接口模块名为xdb;2.查询接口返回字符串;3.增加 apache 授权信息;4.调整基准测试数据来源 2023-01-17 11:57:45 +08:00
LeiHua 0a4d16b434 add priv directory 2023-01-14 10:40:12 +08:00
LeiHua 4803a7ecfb add priv directory 2023-01-14 10:37:53 +08:00
LeiHua ab9d5a443a rename util module to ip2region_util 2023-01-14 10:32:54 +08:00
LeiHua 6ae8e496a3 rename util module to ip2region_util 2023-01-14 10:31:47 +08:00
LeiHua 2f647b955c rename util module to ip2region_util 2023-01-14 10:17:21 +08:00
LeiHua de4f5c0c22 增加单元测试及基准测试 2023-01-13 21:41:43 +08:00
LeiHua 4efa1b36b2 增加erlang语言xdb查询客户端 2023-01-13 14:10:59 +08:00
LeiHua 18abb9d999 增加erlang语言xdb查询客户端 2023-01-13 13:42:44 +08:00
lion 1e88ed8163 add rust binding 2022-12-29 12:03:30 +08:00
狮子的魂 8dbbd05060 Merge pull request #278 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/json5-2.2.2
Bump json5 from 2.2.1 to 2.2.2 in /v1.0/binding/nodejs
2022-12-29 11:56:30 +08:00
dependabot[bot] 02dc2b4739 Bump json5 from 2.2.1 to 2.2.2 in /v1.0/binding/nodejs
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-29 03:53:39 +00:00
狮子的魂 c0811c8989 Merge pull request #277 from gongzhengyang/master
add rust binding
2022-12-29 11:53:00 +08:00
gongzhengyang 32438b5443 Merge pull request #2 from gongzhengyang/feature-2xdb
feat: change the ip2region2 -> xdb
2022-12-28 14:32:01 +08:00
gongzhengyang 3c88e7a12f feat: change the ip2region2 -> xdb 2022-12-28 14:20:35 +08:00
gongzhengyang 42a3370e65 docs: add some infos 2022-12-24 14:21:32 +08:00
gongzhengyang c4b722f74b docs: fix docs error 2022-12-24 13:34:21 +08:00
gongzhengyang 02530f3759 Merge pull request #1 from gongzhengyang/feature-rust-binding
Feature rust binding
2022-12-24 13:19:53 +08:00
gongzhengyang 8d606affff docs: finish the binding/rust Readme.md 2022-12-24 13:17:29 +08:00
gongzhengyang 6c9fdc5d26 feat: add bench and update tracing version 2022-12-24 10:39:39 +08:00
gongzhengyang f10b8971e1 feat: add example subcommands 2022-12-23 18:39:58 +08:00
gongzhengyang 654080b1d0 feat: change searcher fn 2022-12-23 17:06:14 +08:00
gongzhengyang 7e7a612437 feat: 调整search的整体接口 2022-12-23 12:02:12 +08:00
gongzhengyang 5702ca9259 feat: change package name 2022-12-22 18:27:01 +08:00
gongzhengyang 76345e67b0 perf(add more benchmark and change function calls): 2022-12-21 16:38:37 +08:00
gongzhengyang e57adde491 feat: finish all jobs 2022-12-20 14:58:10 +08:00
gongzhengyang 102b128c0d feat: add loop test ip 2022-12-20 13:55:52 +08:00
gongzhengyang 95b822ac38 refactor: change searcher to global 2022-12-20 12:01:43 +08:00
gongzhengyang c48150216e feat: add miss depens 2022-12-20 09:58:43 +08:00
gongzhengyang a5f0837bb5 feat: add ToUIntIP for types ip value 2022-12-20 09:56:51 +08:00
gongzhengyang 09c05be3f2 feat: 完成IP搜索 2022-12-19 15:49:13 +08:00
gongzhengyang cb84133da8 feat: 初始化增加rust查询 2022-12-19 14:36:09 +08:00
lion 80ee44a259 add xdb data upgrade links 2022-12-09 10:35:50 +08:00
lion 1355506ff2 Update the data with the data/fix/ data from ip2region community 2022-12-07 11:25:53 +08:00
lion b3f775a196 ignore empty lines 2022-12-07 11:24:58 +08:00
lion 83e15427a8 ignore the comment line which start with # 2022-12-07 10:29:19 +08:00
lion d686979718 add link anchor to the xdb editor 2022-12-07 10:14:25 +08:00
lion db3269eec6 add editor usage doc 2022-12-07 10:05:44 +08:00
lion cbfd6408ad update the docs 2022-12-07 09:16:40 +08:00
lion 750b8768be Update the docs 2022-12-06 21:29:20 +08:00
root 32631fc9ad Update docs 2022-12-06 05:22:20 -08:00
lion aef51bede0 Add xdb update tool and docs 2022-12-06 21:15:52 +08:00
Lion 61788530fd some optimizations 2022-12-06 20:16:44 +08:00
Lion 8b3a039edc segments editor RC version 2022-12-06 20:09:14 +08:00
Lion f489d2a39a task segments.test as the common data 2022-12-05 21:04:33 +08:00
Lion 0c960c4122 use the final 2022-12-05 21:04:21 +08:00
Lion 2c46a386cc trim the input 2022-12-05 20:42:31 +08:00
Lion e085caf148 apply the need save and put segment 2022-12-05 20:40:22 +08:00
Lion 8e3c440ada trim the space to input segment 2022-12-05 20:38:59 +08:00
Lion 7ff7772e14 impl save and put_file 2022-12-05 14:19:49 +08:00
Lion 0dcea0f995 update the data to make sure the automatic consecutive segments merge is working 2022-12-04 17:36:05 +08:00
Lion d7f9fb6882 fix the conflicts 2022-12-04 17:27:09 +08:00
Lion bce4c1e188 optimize the continuity checking and automatic merging the consecutive segments 2022-12-04 17:22:05 +08:00
Lion 3e6440f1dd load segments while editor init 2022-12-02 18:29:39 +08:00
Lion d2b81ac9a0 new Editor 2022-12-01 18:11:40 +08:00
Lion ae5ed74601 editor fields define 2022-12-01 18:10:54 +08:00
Lion d6e8813fef source ip editor 2022-12-01 17:36:51 +08:00
狮子的魂 b4a525a737 Merge pull request #274 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/yargs-parser-and-jest-21.1.1
Bump yargs-parser and jest in /v1.0/binding/nodejs
2022-11-18 09:05:01 +08:00
狮子的魂 dcd3e94802 Merge pull request #275 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/node-notifier-and-jest--removed
Bump node-notifier and jest in /v1.0/binding/nodejs
2022-11-18 09:03:51 +08:00
dependabot[bot] 90bbf5184f Bump node-notifier and jest in /v1.0/binding/nodejs
Removes [node-notifier](https://github.com/mikaelbr/node-notifier). It's no longer used after updating ancestor dependency [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest). These dependencies need to be updated together.


Removes `node-notifier`

Updates `jest` from 19.0.2 to 29.3.1
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.3.1/packages/jest)

---
updated-dependencies:
- dependency-name: node-notifier
  dependency-type: indirect
- dependency-name: jest
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:51:05 +00:00
dependabot[bot] 3db3f4eb52 Bump yargs-parser and jest in /v1.0/binding/nodejs
Bumps [yargs-parser](https://github.com/yargs/yargs-parser) to 21.1.1 and updates ancestor dependency [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest). These dependencies need to be updated together.


Updates `yargs-parser` from 4.2.1 to 21.1.1
- [Release notes](https://github.com/yargs/yargs-parser/releases)
- [Changelog](https://github.com/yargs/yargs-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs-parser/compare/v4.2.1...yargs-parser-v21.1.1)

Updates `jest` from 19.0.2 to 29.3.1
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.3.1/packages/jest)

---
updated-dependencies:
- dependency-name: yargs-parser
  dependency-type: indirect
- dependency-name: jest
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:47:16 +00:00
狮子的魂 6a70f93442 Merge pull request #267 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/minimist-and-mkdirp-1.2.7
Bump minimist and mkdirp in /v1.0/binding/nodejs
2022-11-18 08:46:33 +08:00
狮子的魂 7a03c21dfc Merge pull request #272 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/ajv-and-request-6.12.6
Bump ajv and request in /v1.0/binding/nodejs
2022-11-18 08:46:04 +08:00
dependabot[bot] 82d64d8aca Bump ajv and request in /v1.0/binding/nodejs
Bumps [ajv](https://github.com/ajv-validator/ajv) and [request](https://github.com/request/request). These dependencies needed to be updated together.

Updates `ajv` from 4.11.6 to 6.12.6
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/4.11.6...v6.12.6)

Updates `request` from 2.87.0 to 2.88.2
- [Release notes](https://github.com/request/request/releases)
- [Changelog](https://github.com/request/request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/request/request/commits)

---
updated-dependencies:
- dependency-name: ajv
  dependency-type: indirect
- dependency-name: request
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:45:44 +00:00
dependabot[bot] 9837f68d42 Bump minimist and mkdirp in /v1.0/binding/nodejs
Bumps [minimist](https://github.com/minimistjs/minimist) and [mkdirp](https://github.com/isaacs/node-mkdirp). These dependencies needed to be updated together.

Updates `minimist` from 1.2.5 to 1.2.7
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.7)

Updates `mkdirp` from 0.5.1 to 0.5.6
- [Release notes](https://github.com/isaacs/node-mkdirp/releases)
- [Changelog](https://github.com/isaacs/node-mkdirp/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-mkdirp/compare/0.5.1...v0.5.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
- dependency-name: mkdirp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:45:06 +00:00
狮子的魂 31d5bb6ebc Merge pull request #269 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/json-schema-and-jsprim-0.4.0
Bump json-schema and jsprim in /v1.0/binding/nodejs
2022-11-18 08:44:00 +08:00
狮子的魂 e3f2e572ae Merge pull request #268 from lionsoul2014/dependabot/npm_and_yarn/v1.0/binding/nodejs/minimatch-3.1.2
Bump minimatch from 3.0.3 to 3.1.2 in /v1.0/binding/nodejs
2022-11-18 08:43:33 +08:00
lion 27612f871a update the maven version to 2.6.6 2022-11-18 08:38:53 +08:00
dependabot[bot] 1d80f3f00d Bump json-schema and jsprim in /v1.0/binding/nodejs
Bumps [json-schema](https://github.com/kriszyp/json-schema) and [jsprim](https://github.com/joyent/node-jsprim). These dependencies needed to be updated together.

Updates `json-schema` from 0.2.3 to 0.4.0
- [Release notes](https://github.com/kriszyp/json-schema/releases)
- [Commits](https://github.com/kriszyp/json-schema/compare/v0.2.3...v0.4.0)

Updates `jsprim` from 1.4.1 to 1.4.2
- [Release notes](https://github.com/joyent/node-jsprim/releases)
- [Changelog](https://github.com/TritonDataCenter/node-jsprim/blob/v1.4.2/CHANGES.md)
- [Commits](https://github.com/joyent/node-jsprim/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: json-schema
  dependency-type: indirect
- dependency-name: jsprim
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:38:22 +00:00
dependabot[bot] 4cff6d9a33 Bump minimatch from 3.0.3 to 3.1.2 in /v1.0/binding/nodejs
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.3 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.3...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 00:37:41 +00:00
lion 2084878935 update the version number to 2.6.6 for maven upgrade 2022-11-18 08:36:07 +08:00
lion fe0605224e shoule be VectorIndexSize 2022-11-18 08:33:37 +08:00
狮子的魂 52d25ef0ef !14 移除 Java Binding 无用的导包
Merge pull request !14 from lisb/master
2022-10-17 06:39:54 +00:00
lisb 9843c53a6f java:移除无用导包 2022-10-16 11:38:48 +00:00
lisb 8cb9aeb29e java:移除无用导包
Signed-off-by: lisb <shangshili@hotmail.com>
2022-10-16 11:36:56 +00:00
狮子的魂 3316fce5f5 Merge pull request #263 from sir051223/master
add default callback for fs.close to compitable with the higher version
2022-10-13 22:06:21 +08:00
cenguoping b071019afd fs.close不写回调函数时,在14.x及以上版本中报错 2022-10-13 14:04:04 +08:00
lion 76b494db73 update the csharp contributor info 2022-09-07 14:08:42 +08:00
狮子的魂 7d1101d910 Merge pull request #257 from malus2077/master
feat(csharp): csharp 2.0 xdb client implementation
2022-09-07 14:06:47 +08:00
Alan bdab0d8c6d feat(csharp): 2.0 xdb csharp client implementation 2022-09-06 18:21:23 +08:00
Alan 7d22a7d6ae feat(csharp): csharp 2.0 xdb client implementation 2022-09-06 18:18:43 +08:00
Lion 03efce4f24 Add xdb maker desc docs 2022-08-27 13:39:31 +08:00
Lion b299e9eaf1 fix the doc erors 2022-08-27 13:37:39 +08:00
lion cffdc3d169 fix the doc errors 2022-08-14 17:56:15 +08:00
lion 3641d130d0 use rb mode reported at https://github.com/lionsoul2014/ip2region/issues/255 2022-08-13 11:51:30 +08:00
lion e2aebce0a7 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2022-08-13 11:49:40 +08:00
lion 67eb391a5a remove the unused ptr reported at https://github.com/lionsoul2014/ip2region/issues/254 2022-08-13 11:49:18 +08:00
Lion 2af4766874 add csharp xdb maker 2022-08-09 09:40:37 +08:00
Lion eb6547ee61 Merge pull request #252 from malus2077/master
feat:csharp 2.0 xdb maker
2022-08-09 09:34:12 +08:00
Alan Lee a77ba7f5be feat:csharp 2.0 xdb maker 2022-08-08 21:52:16 +08:00
Lion 294695be48 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2022-08-07 16:53:08 +08:00
Lion c85d8682b8 add the sample bench cmd 2022-08-07 16:52:57 +08:00
lion 239604ca91 remove the unused import 2022-08-04 11:08:57 +08:00
lion de956578f8 nodejs xdb searcher is ready 2022-07-25 13:25:44 +08:00
Lion c80d2ffb82 Merge pull request #245 from wujjpp/v2.0-for-nodejs
xdb searcher for nodejs
2022-07-25 13:23:52 +08:00
Wu Jian Ping f2a7485a56 移除主代码中的关于逻辑说明注释 2022-07-25 12:37:18 +08:00
Wu Jian Ping e434554864 修改ReadMe.md的一处错误 2022-07-25 12:33:53 +08:00
Wu Jian Ping a1879c3d29 移除package-lock.json 2022-07-25 12:32:30 +08:00
Lion 3163e9300f Merge pull request #246 from weideguo/master
fix errors for xdbSearcher.py
2022-07-25 11:42:03 +08:00
weideguo e8bbe27e42 fix error for xdbSearcher.py 2022-07-25 11:33:21 +08:00
Wu Jian Ping f3df96ef30 . 2022-07-22 16:29:18 +08:00
Wu Jian Ping 8c92ed33f4 . 2022-07-22 15:45:45 +08:00
Wu Jian Ping 1a2e327e22 更新readme 2022-07-22 15:10:44 +08:00
Wu Jian Ping 5110cefc47 优化bench程序 2022-07-22 14:57:38 +08:00
Wu Jian Ping 6af5789fc4 优化bench程序 2022-07-22 14:28:14 +08:00
Wu Jian Ping bd084c0a5d bench程序增加对sip、eip合法性检测 2022-07-22 14:05:04 +08:00
Wu Jian Ping 609e7749ee bench程序: 增加单个IP段,检查5个IP,与其它语言保持一致 2022-07-22 13:57:31 +08:00
Wu Jian Ping f6bf9baf1e bench程序: 增加单个IP段,检查5个IP,与其它语言保持一致 2022-07-22 13:56:56 +08:00
Wu Jian Ping e57db724d3 更新bench app,增加比对结果 2022-07-22 13:44:23 +08:00
Wu Jian Ping 142ded8db5 update readme 2022-07-22 13:26:19 +08:00
Wu Jian Ping b601b19d91 增加bench测试程序 2022-07-22 13:24:10 +08:00
Wu Jian Ping 6d42c45c12 增加bench测试程序 2022-07-22 13:22:18 +08:00
Wu Jian Ping 607fbcdde7 add test app 2022-07-22 11:03:10 +08:00
Wu Jian Ping 159ec582f6 增加nodejs相关的忽略文件 2022-07-21 22:36:37 +08:00
Wu Jian Ping e761381d0e update readme 2022-07-21 22:35:46 +08:00
Wu Jian Ping 4fde2363fa 查询返回对象增加took(us)和ioCount 2022-07-21 22:31:44 +08:00
Wu Jian Ping 743bbd204b . 2022-07-21 18:12:36 +08:00
Wu Jian Ping 0ffa58b592 . 2022-07-21 18:09:08 +08:00
Wu Jian Ping 12aee0894d 集成单元测试&覆盖率报告 2022-07-21 17:33:47 +08:00
Wu Jian Ping 7f6cac9e09 nodejs实现 2022-07-21 16:27:04 +08:00
Lion fd0a6e09f4 auto close the handle after buffer loaded 2022-07-20 16:38:43 +08:00
Lion c5fad74d95 auto close the handle after buffer loaded 2022-07-20 16:24:21 +08:00
Lion 680624db65 auto close the handle after buffer loaded 2022-07-20 16:15:42 +08:00
Lion c15be8c7b5 auto close the handle after loaded the buffer 2022-07-20 16:06:37 +08:00
Lion 69a085f8cf auto close the handle for all buffer load functions 2022-07-20 16:04:00 +08:00
Lion 91d0c39f11 add another php composer addr 2022-07-20 14:18:29 +08:00
Lion 36df29a301 add xdb structure and searcher analysis links 2022-07-20 14:15:59 +08:00
lion 05b2a55608 csharp xdb searcher is ready 2022-07-20 09:32:23 +08:00
lion db60303b75 csharp xdb searcher is ready 2022-07-19 15:31:25 +08:00
狮子的魂 4c464fac2e add c# xdb searcher
Merge pull request !12 from Soar360/master
2022-07-19 07:28:40 +00:00
Soar360 a5b759c756 完善文档 2022-07-18 10:24:10 +08:00
Soar360 a66ed6aa1f IP2Region.sln 2022-07-18 10:02:45 +08:00
Soar360 7a6a6fc515 BenchTest 2022-07-18 10:01:17 +08:00
Soar360 b44a71592d SearchTest 2022-07-18 09:47:22 +08:00
lion 3434635509 fix the bug of vector index end ptr set 2022-07-16 12:14:31 +08:00
lion e638421ae2 use the []byte instead of the vector index and segment index encode/decode 2022-07-16 12:12:07 +08:00
lion 53fcdcfdbc Update the xdb searcher lang list 2022-07-15 20:36:57 +08:00
lion 4a88ab4c60 use the relative link 2022-07-15 20:34:05 +08:00
lion d3fa2061f9 java xdb maker is ready 2022-07-15 20:30:14 +08:00
lion 1f1d3b1786 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2022-07-15 20:29:20 +08:00
lion e773ed819f Merge branch 'java_xdb_maker' 2022-07-15 20:29:04 +08:00
lion 35968fafaa Add ReadMe Doc 2022-07-15 20:26:47 +08:00
Lion 061f2f8c91 maker and maker test implemented 2022-07-15 19:05:42 +08:00
Lion 8f421f554d fix the bug of segment splits and tested 2022-07-15 11:50:07 +08:00
Lion 8352b4f9e8 python maker is ready and java maker in progress 2022-07-15 10:46:17 +08:00
Lion c7a05a14b1 python maker is ready and java maker is undergols 2022-07-15 10:43:54 +08:00
Lion 35dd3895a6 Merge pull request #238 from leolin49/master
add python maker
2022-07-15 10:33:58 +08:00
linyufeng 00e53e3af7 Add date in comment 2022-07-14 17:17:35 +08:00
linyufeng a5887f7f54 Revert util_test.go change 2022-07-14 17:12:27 +08:00
lion d5ceb4cd17 maker segment and index policy manager 2022-07-14 16:38:26 +08:00
linyufeng f300f7eaca Standardized code and add comments 2022-07-14 16:16:51 +08:00
leolin49 50a852828a Merge branch 'lionsoul2014:master' into master 2022-07-14 13:46:18 +08:00
lion 811ffa1dce add global copyright and governed agreement 2022-07-14 10:37:55 +08:00
linyufeng a3015ce8c5 Add python xdb maker 2022-07-13 20:23:26 +08:00
lion 3488d10d2b maker constants define 2022-07-13 17:00:24 +08:00
lion 696d7dd647 stdlize the maven project 2022-07-13 16:47:31 +08:00
Lion 97b837bc81 Update the labels 2022-07-12 17:53:55 +08:00
Lion 33030a5235 ready status 2022-07-12 17:50:36 +08:00
Lion 429087e811 add ready status for searcher and maker list 2022-07-12 17:49:37 +08:00
Lion 7f231a4a10 add check for maker/searcher impl status 2022-07-12 17:44:31 +08:00
Lion eb185eb33a java xdb maker 2022-07-12 12:07:02 +08:00
Lion ae4224754e python3 xdb searcher is ready 2022-07-12 10:45:46 +08:00
Lion 88462e9983 stdlize the result print 2022-07-12 10:40:17 +08:00
Lion a27bc6abe9 Merge pull request #231 from luckydog6132/python_dev
add python xdb searcher impl
2022-07-12 10:32:46 +08:00
厉害的花花 b8b3cde805 1.增加bench_test
2.增加search_test
3.修改ip2Region.py及类名,改为xdbSearcher

Signed-off-by: 厉害的花花 <117415792@qq.com>
2022-07-12 09:46:44 +08:00
厉害的花花 b9ed424125 Merge branch 'lionsoul2014:master' into python_dev 2022-07-12 09:35:15 +08:00
Lion 563a6c6f2f Merge pull request #237 from leolin49/patch-1
fix the hint error
2022-07-11 15:43:43 +08:00
leolin49 e68c497936 Update maker.go
correct spelling of comments
2022-07-11 11:08:58 +08:00
厉害的花花 938b0e063f Merge branch 'lionsoul2014:master' into python_dev 2022-07-08 16:10:57 +08:00
Lion 78f4794926 add third composer repo for php 2022-07-08 11:15:40 +08:00
Lion 7d66d96525 optimize the checkIP and Long2IP impl 2022-07-07 10:26:27 +08:00
Lion 0e0664bf36 Merge pull request #235 from leolin49/master
optimize the impl of ip check and convert
2022-07-07 09:40:20 +08:00
linyufeng c1c2336a82 新增接口测试 2022-07-06 20:08:45 +08:00
linyufeng ef755075ec util.go 接口优化 2022-07-06 20:07:44 +08:00
Soar360 0bd2c15c56 C# 版 2022-07-06 18:00:09 +08:00
Lion 791e81efe4 update the version number to 2.6.4 2022-07-06 16:55:49 +08:00
Lion cbf79d4682 rename searchByStr to search and default the region encode to utf-8 2022-07-06 16:55:35 +08:00
Lion 4a2fae35e6 lua xdb searcher is ready 2022-07-06 13:16:18 +08:00
Lion 0b8146fa5f return err for buffer functions 2022-07-06 13:14:39 +08:00
Lion f87bee3f20 Merge branch 'master' of github.com:lionsoul2014/ip2region 2022-07-06 12:08:17 +08:00
Lion 08e11220a5 add lua xdb searcher 2022-07-06 12:01:16 +08:00
Lion e980386c1f util test passed 2022-07-06 11:07:59 +08:00
Lion 1aed3ecc46 Merge branch 'master' into lua_binding 2022-07-06 10:05:40 +08:00
Lion 818777d48a search api is ready 2022-07-05 17:25:23 +08:00
Lion d7f9213cf1 Merge pull request #234 from CodingOX/master
fix the typo errors
2022-07-05 16:20:50 +08:00
alistar fb29feebe0 fix:垮线程 --> 跨线程 2022-07-05 14:58:03 +08:00
alistar cc8bdb9cf1 fix:携程-->协程 2022-07-05 14:57:22 +08:00
Lion 5b52484cf8 xdb lua searcher framework 2022-07-05 14:06:01 +08:00
Lion e58757ada5 require the local xdb_searcher.so and fix the bug for err check and print 2022-07-05 13:47:47 +08:00
Lion 4b19c716ff lua_c xdb searcher is ready 2022-07-04 10:36:00 +08:00
Lion 100493ec11 update the version number to 2.6.3 2022-07-02 18:25:13 +08:00
Lion 14fc2c5de3 keep the original byte for self-define header 2022-07-02 18:15:54 +08:00
Lion e09c9b57b9 update the docs for vectorIndex/content buffer type and resource close 2022-07-02 18:07:17 +08:00
Lion fa978b6b4c Merge branch 'master' of github.com:lionsoul2014/ip2region 2022-07-02 18:00:14 +08:00
Lion fcc503c8d7 Add docs for compile/install/api and tests 2022-07-02 17:59:10 +08:00
Lion c1616fbca3 err checking the searcher create 2022-07-02 17:58:15 +08:00
Lion 59e5545d70 return the undefined the option 2022-07-02 17:35:01 +08:00
Lion d286c15e34 Merge pull request #233 from leolin49/master
Create "make.bat" for windows
2022-07-02 17:13:54 +08:00
Lion f97e72c23e bench test script ready and bench test passed 2022-07-02 17:10:02 +08:00
leolin49 5a5e727811 Create "make.bat" in windows for golang maker
新增golang maker在windows下的编译文件
2022-07-02 15:30:00 +08:00
Lion 089948ae02 search test is ready 2022-07-02 13:40:16 +08:00
Lion 4fa7aad302 optimize the api error return 2022-07-02 13:39:27 +08:00
Lion 90d80ed622 add type method and make the close duplicate call support 2022-07-02 11:50:44 +08:00
Lion a5eb2faf5b basic tests passed 2022-07-01 15:54:18 +08:00
Lion b949574af5 void * for all the close functions 2022-07-01 15:42:39 +08:00
厉害的花花 d827dbd472 Merge branch 'lionsoul2014:master' into python_dev 2022-07-01 13:36:02 +08:00
Lion 91f9efa107 wrap the header/vector/content buffer 2022-07-01 11:57:45 +08:00
Lion 8aa2ef71b3 Merge branch 'master' into lua_binding 2022-06-30 22:13:40 +08:00
Lion fa42e387a3 fix the doc errors reported by https://github.com/lionsoul2014/ip2region/pull/232 2022-06-30 22:10:24 +08:00
Lion dbb8e2b407 stdlize the print of Header stringify 2022-06-30 22:08:16 +08:00
Lion d819ff2047 optimize the args checking 2022-06-30 18:35:59 +08:00
Lion b8710c17d8 safely duplicate close 2022-06-30 18:34:14 +08:00
Lion 8ee41f4e1d impl lua c xdb searcher 2022-06-30 18:26:57 +08:00
厉害的花花 894fdc3924 增加 python 查询实现
Signed-off-by: 厉害的花花 <117415792@qq.com>
2022-06-30 14:33:48 +08:00
Lion 7325306a56 make all the db_path and vIndex/cBuffer args const 2022-06-30 11:55:41 +08:00
Lion 5cf09bb3b1 update the docs 2022-06-30 11:34:54 +08:00
Lion fea3503f7b update the maven version to 2.6.2 and docs 2022-06-29 18:46:44 +08:00
Lion 905590aff5 optimize the var define 2022-06-29 18:44:50 +08:00
Lion 7942816fad update the docs 2022-06-29 17:52:43 +08:00
Lion fdedfcc0d8 some optimizations 2022-06-29 17:50:25 +08:00
Lion 68486ea679 some optimizations 2022-06-29 17:49:59 +08:00
Lion 6ecff85a3e xdb c binding is ready 2022-06-29 17:40:48 +08:00
Lion c5b45e0502 add api usage demo docs 2022-06-29 17:35:19 +08:00
Lion 7f9a9fd3a7 add search/bench test ReadMe docs 2022-06-29 15:28:14 +08:00
Lion 257c391912 add Makefile 2022-06-29 15:16:16 +08:00
Lion bcf12a41db xdb clang binding is ready with search/bench test passed 2022-06-29 15:03:32 +08:00
Lion 015a15f2e0 safe the type convertion 2022-06-29 09:47:09 +08:00
lion 417636d93c newline the error info print 2022-06-28 18:37:16 +08:00
lion cb03009ac4 fix the bug of content based read offset 2022-06-28 18:37:02 +08:00
lion 34c410998f xdb searcher and search test program are ready 2022-06-28 18:30:34 +08:00
lion e17add2d47 xdb search and bench test program 2022-06-28 17:48:19 +08:00
lion f642c54848 c style db path 2022-06-28 17:07:24 +08:00
lion f047a3d360 buffer load functions are ready 2022-06-28 16:28:16 +08:00
lion 2772ccb0ab search logical impl 2022-06-28 12:15:09 +08:00
lion 1810fe74d6 impl the search by string 2022-06-27 18:37:31 +08:00
lion bd065347b3 add the clang xdb searcher binding 2022-06-27 18:32:40 +08:00
Lion 73b8aa3789 bash comments 2022-06-24 15:50:35 +08:00
Lion 2790f818c7 xdb java searcher is ready 2022-06-24 15:28:12 +08:00
Lion 010674506e update the pom for maven release 2022-06-24 15:23:04 +08:00
Lion 3c8569152f optimize the php docs 2022-06-24 12:43:33 +08:00
Lion 3403b74e31 add doc for java binding 2022-06-24 12:41:35 +08:00
Lion 00b3b4c76d update the marks for xdb cached search 2022-06-24 12:24:16 +08:00
Lion deeb947ba1 update the version number to 2.6.0 2022-06-24 11:56:28 +08:00
Lion a0efcf0456 impl the bench test 2022-06-24 11:52:33 +08:00
Lion 5a8a22a01c count maybe 0 2022-06-24 11:52:06 +08:00
lion aca88673d6 store the costs first 2022-06-23 17:59:14 +08:00
lion 3b96003ca1 use the raw []byte as vectorIndex to reduce memory alloc 2022-06-23 17:56:28 +08:00
lion 670cf1dcb3 add java xdb searcher impl 2022-06-23 17:26:17 +08:00
lion 734ac597ea it should be the cols 2022-06-23 17:03:25 +08:00
lion 06d81c581e mark the php xdb search binding as finsihed 2022-06-23 15:04:51 +08:00
lion 11df874b5d reduce the data copy for vectorIndex search with content cached 2022-06-23 15:03:18 +08:00
lion 5b058d2a8a optimize the bench average costs stats 2022-06-23 14:57:02 +08:00
lion 9659a920e9 content buffer directly as vectorIndex 2022-06-23 14:52:39 +08:00
lion ca6b26c53a remove the vectorIndex autoload for buffer based searcher to reduce memory usage 2022-06-23 11:42:28 +08:00
Lion a835d15d0a update docs 2022-06-22 19:23:03 +08:00
Lion ee1c22f932 add docs for php binding 2022-06-22 19:21:08 +08:00
Lion 7b7171c233 update the docs for cache-policy 2022-06-22 19:20:59 +08:00
Lion d482335ca2 add bench test script 2022-06-22 18:49:02 +08:00
Lion 431e42209c print the cache policy for bench 2022-06-22 18:48:06 +08:00
Lion fc843205d5 util test script to test all the utils functions 2022-06-22 18:06:50 +08:00
Lion bf0f413a22 xdb searcher util and tests 2022-06-22 17:36:01 +08:00
Lion b08e798e27 print the dataLen and dataPtr for debug 2022-06-22 15:59:29 +08:00
Lion 10d3287b4d record the start and end segment index ptr 2022-06-22 10:22:40 +08:00
lion 6149b68a84 add php xdb searcher impl 2022-06-21 18:02:54 +08:00
lion 8e88caecab break for the undefine option 2022-06-21 18:02:40 +08:00
lion 59434d03c9 use the Header struct 2022-06-21 11:34:47 +08:00
lion 14badde79e add header mgr struct 2022-06-21 10:57:47 +08:00
lion 9751227b97 fix the bug of header write and regen the xdb file 2022-06-21 10:47:28 +08:00
lion 7eb4a5ba5e add copy right statement and author contact info 2022-06-21 10:21:37 +08:00
lion 08a4ad245d add cache-policy option desc 2022-06-20 22:12:21 +08:00
lion e59d71e316 add docs for cache searcher create 2022-06-20 21:45:59 +08:00
Lion 08b1656e02 add vectorIndex/Content cache policy impl 2022-06-20 15:14:33 +08:00
Lion 4b4984fce0 add Makefile and docs for golang xdb maker and binding 2022-06-19 19:07:03 +08:00
Lion 9cb3bbcb07 add load header func 2022-06-18 19:36:45 +08:00
Lion 059446caa7 add vector index and xdb content load util func 2022-06-18 19:29:24 +08:00
lion db4aa26547 update the command docs 2022-06-17 15:48:48 +08:00
lion 16720b7b26 xdb_maker and usage docs 2022-06-17 15:35:58 +08:00
lion ffb5488988 package the source lib to xdb 2022-06-17 15:23:02 +08:00
lion be472cbbf7 Update the docs 2022-06-17 15:06:34 +08:00
lion 0b36cb947b rename pacakge to xdb and add SearchByStr impl 2022-06-17 14:45:16 +08:00
lion 985dabd51c add golang searcher binding 2022-06-17 14:18:44 +08:00
lion c6a7652d9e Update the doc 2022-06-17 10:10:42 +08:00
lion 6254a598fc add search and bench doc for golang maker 2022-06-16 16:58:08 +08:00
lion 27a8b249ae track the first xdb file 2022-06-16 16:45:44 +08:00
lion 602dc9203b Add golang xdb maker implemetation 2022-06-16 16:44:02 +08:00
lion d746d5d4b2 init the v2.0 framework with v1.0 source kept 2022-06-16 16:27:56 +08:00
lion 1e2544cc57 archive the v1.0 - old db structure 2022-06-16 16:25:31 +08:00
Lion bfd70f95dd add english README 2022-06-02 12:41:17 +08:00
Lion 2965980a53 Merge pull request #223 from Morsmalleo/master
English README.md
2022-06-02 12:13:21 +08:00
Morsmalleo 802c1bb305 Update README.md 2022-06-01 10:13:24 +08:00
Morsmalleo 4e3847455b Update README.md 2022-06-01 10:12:06 +08:00
Morsmalleo de023c538f Rename README-EN.md to README.md 2022-06-01 09:44:50 +08:00
Morsmalleo 4cc87ef6fc Update and rename README.md to README-EN.md 2022-06-01 09:44:09 +08:00
lion 95ee8edef1 add db design and impl video link 2022-05-21 21:56:27 +08:00
lion feb9198470 add data upgrade impl video links 2022-05-21 21:52:51 +08:00
lion f41b8080e2 add video link 2022-05-21 21:50:19 +08:00
lion 238557ea78 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2022-05-21 18:18:28 +08:00
lion 3d1fabb9bd Add data upgrade based on region detect doc 2022-05-21 18:16:52 +08:00
Lion 21c1c5ca11 Merge pull request #220 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/async-2.6.4
Bump async from 2.3.0 to 2.6.4 in /binding/nodejs
2022-04-29 10:32:02 +08:00
dependabot[bot] 1b8d8598b1 Bump async from 2.3.0 to 2.6.4 in /binding/nodejs
Bumps [async](https://github.com/caolan/async) from 2.3.0 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.3.0...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-28 15:55:54 +00:00
Lion f6882b47aa Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2021-10-25 16:38:38 +08:00
Lion 5f9bf8d8d3 Fix the region name and re-generate the binary file 2021-10-25 16:38:23 +08:00
Lion 5056f08b88 Merge pull request #203 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/path-parse-1.0.7
Bump path-parse from 1.0.5 to 1.0.7 in /binding/nodejs
2021-10-24 09:48:41 +08:00
Lion 34e82bc618 Merge pull request #206 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5 in /binding/nodejs
2021-10-24 09:48:18 +08:00
dependabot[bot] 7fee31c645 Bump tmpl from 1.0.4 to 1.0.5 in /binding/nodejs
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 23:38:34 +00:00
dependabot[bot] 2a221eae28 Bump path-parse from 1.0.5 to 1.0.7 in /binding/nodejs
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.5 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-10 19:27:14 +00:00
Lion ea1e38c314 Merge pull request #193 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/handlebars-4.7.7
Bump handlebars from 4.5.3 to 4.7.7 in /binding/nodejs
2021-05-08 09:40:43 +08:00
Lion 58d6fccfdc Merge pull request #187 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/y18n-3.2.2
Bump y18n from 3.2.1 to 3.2.2 in /binding/nodejs
2021-05-08 09:40:30 +08:00
Lion 6ab2b6099c Merge pull request #195 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/hosted-git-info-2.8.9
Bump hosted-git-info from 2.4.2 to 2.8.9 in /binding/nodejs
2021-05-08 09:40:14 +08:00
dependabot[bot] 0369816675 Bump hosted-git-info from 2.4.2 to 2.8.9 in /binding/nodejs
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.4.2 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.4.2...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-08 01:38:48 +00:00
Lion 30c60d19d4 Merge pull request #194 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/lodash-4.17.21
Bump lodash from 4.17.19 to 4.17.21 in /binding/nodejs
2021-05-08 09:36:45 +08:00
dependabot[bot] 2b690b7f09 Bump lodash from 4.17.19 to 4.17.21 in /binding/nodejs
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 16:28:27 +00:00
dependabot[bot] 377b4e6029 Bump handlebars from 4.5.3 to 4.7.7 in /binding/nodejs
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.5.3 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 21:46:24 +00:00
dependabot[bot] 7d69b3aed2 Bump y18n from 3.2.1 to 3.2.2 in /binding/nodejs
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-30 14:25:51 +00:00
Lion 0db9672c7b Upgrade the origin and binary db data to the latest 2020/03/06 version 2021-03-08 23:06:45 +08:00
Lion 70b12eaae2 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2021-03-08 22:27:16 +08:00
Lion 6705ed7cdc Upgrade the origin and binary db data to the latest 2020/03/06 version 2021-03-08 22:26:52 +08:00
狮子的魂 4467b8403f !7 新版 C# 数据库生成器
Merge pull request !7 from Soar360/master
2020-12-12 20:06:10 +08:00
Soar360 8dddff0e2b 新版 C# 数据库生成器 2020-12-03 13:34:39 +08:00
Lion 76c8e8c298 Merge branch 'master' of https://github.com/lionsoul2014/ip2region 2020-10-31 12:49:02 +08:00
Lion 5b24a33768 Upgrade the origin ip and binary data to the latest 2020/10/31 release version 2020-10-31 12:41:59 +08:00
Lion e2ea56f586 Merge pull request #174 from lionsoul2014/dependabot/maven/binding/java/junit-junit-4.13.1
Bump junit from 3.8.1 to 4.13.1 in /binding/java
2020-10-13 17:10:40 +08:00
dependabot[bot] f65eb173b6 Bump junit from 3.8.1 to 4.13.1 in /binding/java
Bumps [junit](https://github.com/junit-team/junit4) from 3.8.1 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/commits/r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-12 22:56:44 +00:00
Lion 5acd1bcc47 Merge pull request #171 from liyang1009/master
fix bug about stack corrupt for example  ip address is spceial  ip 172.16.107.7, Proxy:False, 210.3.213.69
2020-10-11 17:16:28 +08:00
Lion fc39a114c1 Merge pull request #159 from coderbusy/master
add C# maker
2020-10-10 15:27:42 +08:00
liyang ea5bfb52e0 fix bug about stack corrupt for example ip address is spceial "172.16.107.7, Proxy:False, 210.3.213.69" 2020-10-06 21:55:21 +08:00
lionsoul e17286e324 Merge pull request #168 from zzl88824/master
ip2region LabVIEW库
2020-09-18 18:13:48 +08:00
Zhao Zilong bafd7741a2 Add files via upload 2020-09-18 18:03:30 +08:00
Zhao Zilong f4aee793a8 Update readme.md 2020-09-18 17:59:13 +08:00
Zhao Zilong e5e31005ac Update readme.md 2020-09-18 17:58:31 +08:00
Zhao Zilong 5a5ae36b01 Add files via upload 2020-09-18 17:55:35 +08:00
Zhao Zilong b87e6029d6 Create readme.md 2020-09-18 17:54:17 +08:00
lionsoul 4415551681 Merge pull request #163 from Wuxiaodonggit/python_fixbug
Fix multi instance bugs, use instance variable instead of class varia…
2020-09-03 11:08:56 +08:00
wuxiaodong a1ee7fd5e7 Fix multi instance bugs, use instance variable instead of class variable. 2020-09-02 17:26:01 +08:00
lionsoul 5f8a42789d Merge pull request #160 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/lodash-4.17.19
Bump lodash from 4.17.13 to 4.17.19 in /binding/nodejs
2020-07-20 20:08:54 +08:00
dependabot[bot] d58119db4b Bump lodash from 4.17.13 to 4.17.19 in /binding/nodejs
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.13 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.13...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-16 08:58:29 +00:00
Soar360 9c70364080 add CSharp Macker 2020-07-09 14:01:07 +08:00
lionsoul 4e3c0c087b Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2020-06-18 08:53:41 +08:00
lionsoul 5d29fe2f2a Upgrade the minimist version inspired by the github alerts 2020-06-18 08:53:20 +08:00
lionsoul cb1926911c Upgrade the source and binary data to the latest 2020/06/10 version 2020-06-13 22:06:57 +08:00
Soar360 c18377d4c1 Util.cs -> ip2long 2020-06-11 23:58:32 +08:00
Soar360 d9012947de add DbMaker.cs 2020-06-11 23:47:31 +08:00
Soar360 55a0c6993d add sln file 2020-06-11 22:12:00 +08:00
Soar360 9843232542 rename to Java style. 2020-06-11 22:10:56 +08:00
Killer 37c6349f10 clean code 2020-06-10 18:13:42 +08:00
Killer 1bb1f7cd50 add DbSearcher 2020-06-10 18:09:57 +08:00
Killer f03dc7be00 add HeaderBlock.cs
add IndexBlock.cs
add Util.cs
2020-06-10 17:23:01 +08:00
Killer 788dffa9fa add DataBlock
add DbConfig
2020-06-10 16:59:32 +08:00
lionsoul b9b1d5a8c7 upgrade the acorn to version 5.7.4 2020-04-02 09:42:44 +08:00
lionsoul 068a7f48ed Upgrade the minimist to 1.2.2 2020-04-02 09:41:14 +08:00
lionsoul b46b168842 Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2020-04-02 09:38:27 +08:00
lionsoul 247c09376b Upgrade the kind-of to version 6.0.3 2020-04-02 09:38:10 +08:00
lionsoul 73043957d3 Merge pull request #147 from phpstatic/master
fix may coming memory leak when failed to initialize ip2region and symbol duplicate for php extension binding
2020-03-22 16:13:18 +08:00
PHP Static 5ea194bb22 fix php5 symbol duplicate 2020-03-22 11:43:46 +08:00
PHP Static fc80149d82 fix memory leak and symbol duplicate 2020-03-22 11:38:21 +08:00
dongyado ce11dc76d9 update db structure intro file link 2020-03-16 14:28:17 +08:00
lionsoul ceb31be88b Update the link for the php ext 2020-02-21 13:56:56 +08:00
lionsoul 89b80ef310 Optimize the installation DOC and rename the php ext binding name 2020-02-21 13:56:26 +08:00
lionsoul b546335bd1 Optimize the installation DOC 2020-02-21 13:50:55 +08:00
lionsoul f0d1140bba Optimize the README DOC 2020-02-21 13:49:25 +08:00
lionsoul c4f3ebfef8 Optimize the DOC 2020-02-21 13:45:42 +08:00
lionsoul dbc05223fd Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2020-02-21 13:36:35 +08:00
lionsoul ac86979872 Optimize the README DOC 2020-02-21 13:36:21 +08:00
dongyado ff535a79f4 update readme add tested node version 2020-02-20 16:57:50 +08:00
dongyado e33004f319 update nodejs client and use node-ip2region for npm package name 2020-02-20 12:58:56 +08:00
lionsoul 03c3ff6264 Merge pull request #140 from lionsoul2014/dependabot/npm_and_yarn/binding/nodejs/handlebars-4.5.3
Bump handlebars from 4.1.2 to 4.5.3 in /binding/nodejs
2019-12-28 09:01:06 +08:00
dependabot[bot] e7bbeb7919 Bump handlebars from 4.1.2 to 4.5.3 in /binding/nodejs
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.5.3.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-27 11:39:13 +00:00
lionsoul 728c04d633 Merge pull request #138 from RocherKong/master
add data result availability checking
2019-12-11 17:20:05 +08:00
rocher 23609e81f5 test result data right 2019-12-11 16:59:44 +08:00
lionsoul d60515dad6 Update the data to the latest version 2019-11-23 11:21:45 +08:00
lionsoul 29ea6bf2b1 Upgrade the DOC for upgrade desc 2019-10-28 08:00:25 +08:00
lionsoul d628ad5452 Upgrade the data to the version of 2019/10/27 2019-10-28 07:55:13 +08:00
lionsoul f7f6d6282c Update the README 2019-10-28 07:46:58 +08:00
lionsoul b23cb7fdf6 Upgrade mixin-deep for version 1.3.2 and set-value to version 2.0.1 2019-09-07 10:36:36 +08:00
lionsoul 3f4f8a4051 add nginx module support 2019-08-22 10:35:26 +08:00
lionsoul 208a3baab6 Merge pull request #125 from Cherisher/master
add support for nginx
2019-08-22 10:16:59 +08:00
Cherisher 7bdea3d171 add support for nginx 2019-08-22 09:02:09 +08:00
lionsoul 6b50224fd7 Security Alerts fixing 2019-07-21 17:51:07 +08:00
lionsoul 4f52d8c4ef Upgrade some the dependencies version to the one followed by the github alerts 2019-07-07 11:33:31 +08:00
lionsoul 7e81847d01 Upgrade some the dependencies version to the one followed by the github alerts 2019-07-07 11:18:21 +08:00
lionsoul 3ca573869c Merge branch 'master' of https://gitee.com/lionsoul/ip2region 2019-07-07 11:10:00 +08:00
lionsoul ebea2a1467 fix the bug of raf=null in the close interface of DbSearcher 2019-07-07 11:09:48 +08:00
lionsoul 42c22079b4 Optimize the data source list display 2019-05-05 17:25:05 +08:00
lionsoul 2913c82ac8 Add source data platform or API url 2019-05-05 17:24:09 +08:00
lionsoul c377430a43 Add source data platform or API url 2019-05-05 17:23:18 +08:00
lionsoul 5f68d284e1 Optimize the source data list display 2019-05-05 17:21:08 +08:00
lionsoul 8b10e111f0 Optimize the source data list display 2019-05-05 17:19:17 +08:00
lionsoul cdaaa15444 Add source data platform or API url 2019-05-05 17:17:48 +08:00
lionsoul 8436adb45f Optimize the maker doc 2019-05-02 20:02:51 +08:00
lionsoul bcb34f5191 Add the java implementation for ip2region.db maker 2019-05-02 19:51:29 +08:00
lionsoul 2a9d53c2c4 Upgrade the ip source data and db data to the latest version 2019/05/02 2019-05-02 19:30:46 +08:00
lionsoul 008d82eee0 Upgrade the lodash base version to 4.17.11 2019-02-12 17:39:11 +08:00
lionsoul f2f7baabea Update the CHANGES for maven address update and lua comments 2018-12-31 13:32:05 +08:00
lionsoul d936085154 Track the CHANGES for version 1.8.0 2018-12-31 13:24:03 +08:00
lionsoul 8358ccc2f7 ip source text and binary data upgrade to version 2018/12/31 2018-12-31 13:03:50 +08:00
lionsoul 8688ae3ecb Update the merge to version 1.2.1 2018-11-03 11:05:48 +08:00
lionsoul 0809f9eda8 Merge pull request #86 from heweizhen0822/patch-1
Update README.md
2018-10-18 20:59:29 +08:00
lionsoul dd9fe6d218 Merge pull request #85 from heweizhen0822/patch-2
Update main.go
2018-10-18 20:59:09 +08:00
heweizhen0822 87734a614f Update main.go
github.com/mohong122/ip2region/binding/golang/ip2region 并没有子包ip2region 需要改成 github.com/lionsoul2014/ip2region/binding/golang/ip2region
2018-10-18 10:34:33 +08:00
heweizhen0822 94594542f7 Update README.md
github.com/mohong122/ip2region/binding/golang/ip2region  需要改成 github.com/lionsoul2014/ip2region/binding/golang/ip2region
2018-10-18 10:32:32 +08:00
lionsoul d3b65df599 Optimize the lua_c module 2018-10-10 11:54:26 +08:00
lionsoul 7e32a84d2c Add automatically lib dir make 2018-10-07 20:58:54 +08:00
lionsoul d796a76400 Optimize the ip2long function with trim logic applied 2018-10-07 10:23:17 +08:00
lionsoul 835d9c1b90 Update the README doc for lua_c module 2018-10-06 15:12:24 +08:00
lionsoul 6fefefa9ae Add README doc for lua_c module 2018-10-06 11:18:01 +08:00
lionsoul 08eea15172 lua ip2region c module forward with release version published 2018-10-06 11:05:01 +08:00
lionsoul a589b79e6c lua binding module interface stdlize with new and ip2long method access by . support 2018-10-05 21:46:21 +08:00
lionsoul 01ab0a0373 lua_c module forward with basic framework finished(tested) 2018-10-05 21:39:35 +08:00
lionsoul 57f4fb4383 lua_c binding basic framework(Not tested yet...) 2018-10-05 14:03:16 +08:00
lionsoul 6a8d641a07 Optimize the lua module by add the __index and the __tostring meta-method 2018-10-05 10:55:24 +08:00
lionsoul 597b769311 Optimize the benchmark script 2018-10-05 09:49:32 +08:00
lionsoul 0d0cd486c9 Merge pull request #81 from KomaBeyond/master
重构python客户端,提升查询性能,兼容python2.x,3.x
2018-10-05 09:40:15 +08:00
Koma a584ef9caf 重构python客户端,提升查询性能,兼容python2.x,3.x 2018-10-04 11:39:49 +08:00
lionsoul 1701f49e45 Update the lua interface and add setDbFile interface 2018-10-04 10:22:01 +08:00
lionsoul b1463a23a0 Update the Lua binding 2018-10-03 20:12:30 +08:00
lionsoul cbba946ff7 Update README for lua binding 2018-10-03 20:10:02 +08:00
lionsoul 3d69968290 Lua binding completed(tested) 2018-10-03 20:05:14 +08:00
lionsoul 92684b1183 lua binding forward with binarySearch and btreeSearch interface implemented 2018-10-03 13:12:05 +08:00
lionsoul 932a5d80b4 lua binding basic framework with memory search completed(tested) 2018-10-02 22:36:57 +08:00
lionsoul 64adcf122e Update the maven version number to 1.7.2 2018-10-02 11:24:58 +08:00
lionsoul 8494e1075d Add DbSearcher(dbConfig, dbBinStr) construct method specified by Wendal at https://gitee.com/lionsoul/ip2region/issues/IILFL 2018-10-02 11:14:31 +08:00
lionsoul 6027ba7146 Add some notices 2018-10-02 10:39:54 +08:00
lionsoul b029d58aa3 Merge pull request #76 from a631807682/master
[nodejs] publish package on npm
2018-10-02 10:19:43 +08:00
lionsoul 73b988bae5 Merge branch 'master' of https://github.com/lionsoul2014/ip2region 2018-09-05 20:45:30 +08:00
lionsoul 5fe7629fe8 Update the maven version to 1.7 2018-09-05 20:44:37 +08:00
lionsoul 0bb8cb9553 Merge pull request #72 from stulzq/master
Add use stream method for C#
2018-09-01 20:59:52 +08:00
陈睿 ad219e3e07 [nodejs] publish package on npm 2018-08-30 18:20:49 +08:00
lionsoul c95bed6f58 Merge pull request #69 from Maledong/master
[Feature] Add 'memorySearch' and 'memorySearchSync' for Nodejs
2018-08-25 10:59:30 +08:00
stulzq d83e20c99c add use stream method 2018-08-19 16:47:01 +08:00
MaleDong b9e9fcfd80 [Feature] Add 'memorySearch' and 'memorySearchSync' for Nodejs
1) Add features.
2) Add unit tests.
3) Add benchmark.
2018-07-31 16:12:56 +08:00
lionsoul 90f0e86597 java binding version 1.7 with btree search bug fixed 2018-07-31 16:03:15 +08:00
lionsoul 37845e7dea Fix the bug for btree header fetch 2018-07-31 15:58:11 +08:00
lionsoul 84a8befd4b Update to the latest data of version 2018/07/26 2018-07-31 15:49:28 +08:00
lionsoul c8537541c3 Merge pull request #68 from RocherKong/master
Use the synchronization method as the first choice
2018-07-31 12:43:11 +08:00
Rocher 176081fcc7 Update README.md
Use the synchronization method as the first choice.
2018-07-31 11:37:25 +08:00
Rocher cc629102e5 Update README.md
Use the synchronization method as the first choice.
2018-07-31 11:36:14 +08:00
Rocher 9c69fc237d Merge pull request #3 from lionsoul2014/master
pull newest
2018-07-31 11:33:02 +08:00
lionsoul f4fba7db60 Merge pull request #67 from Maledong/master
[Fix] Fix csharp formations
2018-07-30 15:26:37 +08:00
MaleDong 09dda2c211 [Fix] Fix csharp formations
1) In `README.md`, we shouldn't use `c#` but `csharp` to make md
recoginize it's the C# formation of codes.

2) Add 'ConfigureAwait(false)' as for the MSDN magazine usage.

3) Don't need Contribution History in README.md, because we can see the
change logs in GitHub instead.
2018-07-30 14:44:47 +08:00
lionsoul 300d631caf Merge pull request #66 from Maledong/NeedDispose
[Fix] Add "Disposible" interface for final release of resouces
2018-07-30 11:31:18 +08:00
MaleDong fb564c79e5 [Fix] Add "Disposible" interface for final release of resouces
`_search` should release resouces because the file state is still open.
2018-07-30 11:21:31 +08:00
lionsoul 235cd0388c Merge pull request #65 from Maledong/ChangeToTask
[Update] Refactor codes according to RocherHong's changes
2018-07-30 10:56:10 +08:00
MaleDong 614ced34e4 [Update] Refactor codes according to RocherHong's changes
1) Considering using xUnit for UnitTest, remove my unit tests.
2) Remove useless `async`, because `async` is just the wrapper of `Task`.
3) Modify unit tests to be sure that the values MUST BE equal to each other when the IP is the same.
2018-07-30 10:48:30 +08:00
lionsoul ca3997f822 Merge pull request #61 from Maledong/master
New Feature of async for Nodejs, Refactor of codes in node.js
2018-07-30 10:41:23 +08:00
lionsoul d1c443f934 Fix the conflicts 2018-07-30 10:39:15 +08:00
lionsoul d239a43fa5 Merge pull request #63 from RocherKong/master
Code Refactoring
2018-07-30 10:26:21 +08:00
MaleDong 09a6d60620 Change from `async Task` to `Task`
Because the async is the wrapper of Task, we don't need to use async
again.
2018-07-30 09:56:25 +08:00
RocherKong 11f527b207 1.CodeStandardized 2.Support Netfx4.5 3.TestStandardized 2018-07-30 00:31:49 +08:00
Rocher 4120c4e9c8 Merge pull request #2 from lionsoul2014/master
merge newest
2018-07-30 00:27:46 +08:00
MaleDong 35fc3cbf23 Refactor of codes in node.js
1) Add async method: `binarySearch` and `btreeSearch`.
2) Refactor of tests by putting them into `tests` folder.
3) Create several new tests in details.
4) Remove useless and reformat codes to be clear.
5) Remove useless snapshots, because they can be recreated when you run `npm run test`.
2018-07-29 20:02:14 +08:00
lionsoul 0a7b2e225f Merge pull request #62 from Maledong/RefactorCSharp
Refactor of CSharp
2018-07-28 15:32:50 +08:00
MaleDong 23dbb5ab2c Refactor of CSharp:
1) Add `Async` methods.
2) Remove uesless variables, codes.
3) Remove ConsoleApp and use UnitTest instead.
4) Add README.md instead.
2018-07-20 13:57:32 +08:00
Rocher 05bc534d87 Merge pull request #1 from lionsoul2014/master
update to newest
2018-07-17 15:32:37 +08:00
lionsoul af12f97152 Merge pull request #57 from biluohc/master
Rust: 0.2.0, use std::net::Ipaddr and update Error.
2018-07-03 20:04:22 +08:00
biluohc 1605a3d9a5 Rust: 0.2.0, use std::net::Ipaddr and update Error. 2018-07-03 15:53:16 +08:00
dongyado 52bede553d update php_extension client intro file 2018-07-03 15:27:33 +08:00
dongyado 202f86eba7 add new author and email for nodejs binding client 2018-07-03 15:17:47 +08:00
Monster Slayer a823a70326 Merge pull request #50 from leeching/refactor/nodejs
refactor(nodejs): write nodejs script in javascript way
2018-07-03 15:14:41 +08:00
lionsoul 0aacd39882 Merge pull request #55 from biluohc/master
add rust implement.
2018-07-03 10:55:40 +08:00
leeching b705253664 feat: add destroy method for backward compatibility 2018-07-03 10:07:25 +08:00
leeching 7a020f52aa feat(nodejs): add create static method 2018-07-03 09:48:41 +08:00
leeching c7104902e3 refactor(nodejs): write nodejs script in javascript way 2018-07-03 09:48:41 +08:00
biluohc 42a6d37c43 add a example 2018-07-02 23:04:46 +08:00
biluohc 5287df2fe7 use workspace 2018-07-02 22:17:10 +08:00
biluohc 8a48d41657 add rust implement. 2018-06-30 16:16:00 +08:00
lionsoul ddb815aab6 Merge pull request #54 from Insua/master
fix issuse golang执行测试报错 #29
2018-06-30 10:40:37 +08:00
insua e70d1e473e fix(move golang package in directory and typo in readme): 2018-06-29 16:49:38 +08:00
lionsoul 7fbe737e92 Merge pull request #52 from felixonmars/patch-1
Fix some typos in Ip2Region.class.php
2018-06-28 10:51:19 +08:00
Felix Yan c9bf99d07f Fix some typos in Ip2Region.class.php 2018-06-28 02:00:05 +08:00
dongyado 22bd52fd65 keep php class and php_extention return data key same, we change php_extension return data key : cityId to city_id 2018-06-27 10:44:29 +08:00
lionsoul dfca3494ad Merge pull request #48 from leeching/fix/nodejs-var
remove unnecessary var
2018-06-26 17:34:03 +08:00
lionsoul 8b6e67f1d6 Merge pull request #49 from leeching/fix/nodejs-snapshot
test(nodejs): update snapshot
2018-06-26 17:33:08 +08:00
leeching 12dd913be4 test(nodejs): update snapshot 2018-06-26 17:10:56 +08:00
leeching dbcb33f608 remove unnecessary var 2018-06-26 16:26:00 +08:00
lionsoul ae6ae1cfe2 Merge pull request #47 from jwcn/master
重命名 php 测试脚本
2018-06-26 12:37:00 +08:00
Wei.Jiang ac63e52851 Merge pull request #1 from jwcn/jwcn-patch-1
重命名 php 测试脚本
2018-06-26 11:06:46 +08:00
Wei.Jiang d0c5ea05b6 重命名 php 测试脚本
重命名 php 测试脚本,与 README 和其他语言脚本名保持一致
2018-06-26 11:05:48 +08:00
lionsoul 4624e421b2 Merge pull request #46 from WoShiSunWuKong/patch-1
add nodejs sample
2018-06-26 10:09:09 +08:00
我是孙悟空 0c3f199116 add nodejs sampale 2018-06-25 12:59:52 +08:00
lionsoul 34043b50e1 fix the region and re-generate the ip2region.db file 2018-06-19 17:38:33 +08:00
dongyado 9527177a91 update php_extension install guides 2018-05-17 12:11:13 +08:00
lionsoul 3cf3f0cc36 Merge pull request #44 from Chunlin-Li/fix-binding-nodejs
fix: ReferenceError: m is not defined
2018-05-17 09:20:26 +08:00
Chunlin Li d8fcf0a31e fix: ReferenceError: m is not defined 2018-05-16 19:01:17 +08:00
lionsoul 55c3851872 Ignore the testSearch generated by Makefile 2018-04-09 09:31:48 +08:00
lionsoul 171d9319c5 Merge pull request #41 from helloKaiDing/master
Makefile for binding/c with ip2region.db auto generate.
2018-04-09 09:28:45 +08:00
helloKaiDing 174e4a46db Makefile for binding/c
This is an Makefile for the binding/c program, can use to generate the db file ip2config.db  and the test program testSearcher, can be very convient for the new starter.
2018-04-08 17:23:03 +08:00
lionsoul 0bc2060b31 Merge pull request #39 from panjunjie/patch-1
Update README.md
2018-03-30 00:39:55 +08:00
Jack a784a0295e Update README.md
更改 markdow h 标签错误格式!
2018-03-29 19:04:48 +08:00
lionsoul 2f26c9bc71 Update the ip data to version 2018/03/26 2018-03-27 14:38:33 +08:00
lionsoul de102702eb Merge pull request #31 from RocherKong/master
add C# support ( from RocherKong)
2018-02-11 11:56:51 +08:00
RocherKong 1258a74064 add support for C# 2018-02-09 22:04:01 +08:00
lionsoul 1a1e012387 Update the data to version 2018/01/12 2018-01-16 15:08:30 +08:00
lionsoul 32317fdbf4 Fix the doc error 2017-11-23 21:25:32 +08:00
lionsoul 5ffd7cb14c Untrack the binding/java/target/ directory 2017-10-28 15:38:09 +08:00
lionsoul 9e1bc19eda Update the REAMD 2017-10-23 15:31:46 +08:00
lionsoul b4828485ad Add the maven repository address 2017-10-23 13:50:57 +08:00
lionsoul 91d9dccb90 java binding maven upload & track the latest dbMaker.jar 2017-10-23 13:48:49 +08:00
lionsoul 9900f37782 bold the upgrade notice 2017-10-21 13:10:16 +08:00
lionsoul 078a0f2dfe Update the README 2017-10-21 13:08:26 +08:00
lionsoul 04545690c4 Update the README 2017-10-21 13:07:29 +08:00
lionsoul 5705993cec Update the notice 2017-10-21 13:06:08 +08:00
lionsoul 347bda5dd5 active the upgrade notice 2017-10-21 13:05:14 +08:00
lionsoul 5f3bddec08 Bug fixed: Adjust the super header length to 8192 for all client implementation 2017-10-21 12:47:24 +08:00
Wilson Wang eab6259b13 !4 python 3正常运行
Merge pull request !4 from Wilson Wang/master
2017-10-21 12:13:29 +08:00
wfifi 6d61beb592 add python3 support 2017-10-20 18:09:07 +08:00
lionsoul f4a33d36bb Merge branch 'master' of http://git.oschina.net/lionsoul/ip2region 2017-10-20 12:13:30 +08:00
lionsoul 532ede302b Update the to data 2017/10/09 version 2017-10-20 12:13:19 +08:00
lionsoul d589e0555a Merge pull request #19 from chengpiaopiao/master
fix incorrect python test helper.
2017-09-16 09:39:27 +08:00
chengpiaopiao 862d218756 fix incorrect python test helper. 2017-09-15 15:40:04 +08:00
dongyado 34f09aadb2 correct post path 2017-09-08 14:10:31 +08:00
lionsoul 30dfcbb035 Merge pull request #15 from mohong122/master
Golang binding 增加控制台实例
2017-08-01 12:18:56 +08:00
mohong 9232c8fec1 Merge pull request #1 from lionsoul2014/master
合并远程
2017-05-25 14:51:46 +08:00
lionsoul 939dc51a6f Merge branch 'master' of https://github.com/lionsoul2014/ip2region 2017-04-19 10:49:13 +08:00
lionsoul f93b8d2dfb Fix the demo doc error 2017-04-19 10:48:10 +08:00
lionsoul cb3304eb7d Merge pull request #11 from zhuangya/jest
chore: test in jest
2017-04-14 13:11:52 +08:00
Ya Zhuang 6d878babb4 chore: test in jest 2017-04-14 12:47:55 +08:00
lionsoul 90682e06eb track the latest binary db data 2017-04-10 11:04:02 +08:00
lionsoul 7d4a36d9bc Update the 1.2.4 version desc 2017-04-07 19:04:12 +08:00
lionsoul fe4057d8aa Add the changes for version 1.2.4 2017-04-07 18:53:55 +08:00
lionsoul bd3ffadabf Update the source and the binary ip data to version 2017/03/15 2017-04-07 18:53:29 +08:00
lionsoul b7376d5868 Add the author info for binding/c_mmap module 2017-01-12 18:21:36 +08:00
lionsoul 387f5a2097 Merge branch 'master' of http://git.oschina.net/lionsoul/ip2region 2017-01-12 18:14:16 +08:00
lionsoul 9de920ef2b stdlize the code style and add the author info 2017-01-12 18:14:09 +08:00
我的上铺叫路遥 639c852534 Merge pull request !3 from 我的上铺叫路遥/master 2017-01-12 18:13:16 +08:00
Leo Ma 3c8cfa1282 Fix memory leak
Signed-off-by: Leo Ma <begeekmyfriend@gmail.com>
2017-01-12 18:04:07 +08:00
我的上铺叫路遥 a67936d835 Merge pull request !2 from 我的上铺叫路遥/master 2017-01-12 17:51:43 +08:00
Leo Ma 9bdf030fe0 Add database file memory map as c_mmap binding
File I/O optimization.

- No more `fread` call when the file is mapped.

- Close database file as soon as possible after `mmap`

Signed-off-by: Leo Ma <begeekmyfriend@gmail.com>
2017-01-12 17:33:58 +08:00
lionsoul 7a2b9943b5 stdlize the license add free statement 2016-12-28 09:53:13 +08:00
lionsoul c0955fedff update the merge&binary db file to version/2016/12/14 2016-12-21 16:53:14 +08:00
lionsoul 21bb060839 fix 10+ rows failed ip range records 2016-11-07 14:07:03 +08:00
lionsoul b0f7bff2de text and binary data version 2016/11/02 2016-11-05 21:13:36 +08:00
lionsoul 643e6a2463 update the db make doc and jar file 2016-11-03 17:32:16 +08:00
lionsoul ec395be524 add db make doc & update the CHANGES 2016-11-03 16:40:28 +08:00
lionsoul 7dcec80080 track the db make jar tools 2016-11-03 15:53:20 +08:00
lionsoul bedaca5b70 Merge pull request #4 from mohong122/master
golang实现
2016-10-20 17:08:17 +08:00
350 changed files with 1222788 additions and 130747 deletions

0
.gitattributes vendored Normal file
View File

80
.gitignore vendored
View File

@ -3,12 +3,92 @@
*.o
*.pyc
*~
*.log
*.la
*.so
*.iml
META-INF/
.DS_Store
# Binary Files #
*.jar
!dbMaker-*.jar
# ignore all xdb except the one in ./data/
*.xdb
!/data/*.xdb
# Package Files #
.settings/
.classpath
.project
# vim swp file #
*.swp
.idea
.vscode
# binding
/binding/java/classes/
/binding/java/doc/
/binding/java/target/
/binding/java/*.jar
# python
/**/__pycache__
# clang
/binding/c/xdb_searcher
/binding/c/test_util
/binding/c/cmake-build-debug
# lua/luc_c
/binding/lua_c/cmake-build-debug
# golang
/binding/golang/searcher
/binding/golang/xdb_searcher
/binding/golang/golang
# rust
Cargo.lock
target
# VS ignore cases
/**/*.sln
/binding/c#/**/.vs/
/binding/c#/**/packages
/binding/c#/**/bin
/binding/c#/**/obj
# Nodejs
/binding/nodejs/tests/unitTests/__snapshots__
/binding/nodejs/coverage
/binding/nodejs/node_modules
/binding/nodejs/.nyc_output
/binging/nodejs/package-lock.json
# Javascript
/binding/javascript/tests/unitTests/__snapshots__
/binding/javascript/coverage
/binding/javascript/node_modules
/binding/javascript/.nyc_output
/binding/javascript/package-lock.json
# maker
## golang
/maker/golang/dbmaker
/maker/golang/xdb_maker
/maker/golang/golang
#erlang
/binding/erlang/_build
/binding/erlang/doc
#vscode
.vscode
build
# git worktrees
.worktrees/

View File

@ -1,7 +0,0 @@
### 1.2
1. 新增分列式升级算法大大的增加了数据的准确率基本避免之前各大网友反馈的些许ip定位错误
2. 数据升级是2016/06/30版
3. 优化数据文件生成算法ip2region.db文件由原来的3.5M降为1.5M42亿个IP地址皆大欢喜啊
4. C/PHP/JAVA客户端增加纯内存搜索模式python, php扩展nodejs会在后续版本加上
说明因为数据文件只有1.5M对于PHP,java这类IO优化类语言提升速度不大C有一个数量级的提升

View File

@ -1,3 +1,11 @@
ip2region is dual-licensed under the Apache License 2.0 and the MIT License.
You may use, distribute, and modify this software under either license,
at your option. The full text of both licenses appears below.
SPDX-License-Identifier: Apache-2.0 OR MIT
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -201,9 +209,9 @@
limitations under the License.
==========================================================================
The following license applies to the JQuery JavaScript library
The following license applies to the ip2region library
--------------------------------------------------------------------------
Copyright (c) 2010 John Resig, http://jquery.com/
Copyright (c) 2015 Lionsoul<chenxin619315@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@ -223,4 +231,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

191
README.md
View File

@ -1,66 +1,125 @@
ip2region - 最自由的ip地址查询库ip到地区的映射库提供Binary,B树和纯内存三种查询算法妈妈再也不用担心我的ip地址定位。
**1. 99.9%准确率,定时更新:**
数据聚合了一些知名ip到地名查询提供商的数据这些是他们官方的的准确率经测试着实比纯真啥的准确多了。<br />
每次聚合一下数据需要1-2天会不定时更新。
**2. 标准化的数据格式:**
每条ip数据段都固定了格式_城市Id|国家|区域|省份|城市|ISP_
只有中国的数据精确到了城市其他国家只能定位到国家后前的选项全部是0已经包含了全部你能查到的大大小小的国家。
请忽略前面的城市Id个人项目需求
**3. 体积小:**
生成的数据库文件ip2region.db只有1.5M1.2版本前是3.5M
**4. 多查询客户端的支持0.0x毫秒级别的查询**
已经集成的客户端有java, php, c, pythonnodejsphp扩展(支持linux, php5, php7版本已支持)。
提供了两种查询算法,响应时间如下:
客户端/binary算法/b-tree算法/Memory算法
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
php/0.x毫秒/0.1x毫秒/0.1x毫秒
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
python/0.x毫秒/0.1x毫秒/未知
任何客户端b-tree都比binary算法快当然Memory算法固然是最快的
**5. 测试程序:**
java:
cd binding/java
ant all
java -jar ip2region-{version}.jar ./data/ip2region.db
php:
php binding/php/testSearcher ./data/ip2region.db
c:
cd binding/c/
gcc -g -O2 testSearcher.c ip2region.c
./a.out ../../data/ip2region.db
python:
python binding/python/testSearcher ./data/ip2region.db
均会看到如下界面:
initializing B-tree ...
+----------------------------------+
| ip2region test script |
| Author: chenxin619315@gmail.com |
| Type 'quit' to exit program |
+----------------------------------+
p2region>> 101.105.35.57
2163|中国|华南|广东省|深圳市|鹏博士 in 0.02295 millseconds
输入ip地址开始测试第一次会稍微有点慢在运行命令后面接入binary,memory来尝试其他算法建议使用b-tree算法速度和并发需求的可以使用memory算法。
具体集成请参考不同客户端的测试源码。
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region
[ip2region](https://ip2region.net) - is an offline IP address localization library and IP localization data management framework. It supports both `IPv4` and `IPv6` with query efficiency at the 10-microsecond level. It provides `xdb` data generation and query client implementations for many mainstream programming languages.
# Features
### 1. Offline Localization Library
The project itself provides raw data for both IPv4 (`data/ipv4_source.txt`) and IPv6 (`data/ipv6_source.txt`), along with corresponding xdb files (`data/ip2region_v4.xdb` and `data/ip2region_v6.xdb`) to achieve city-level query localization. The field format is: `Country|Province|City|ISP|iso-alpha2-code`. Localization information for China is entirely in Chinese, while regional information for non-China areas is entirely in English.
### 2. Data Management Framework
`xdb` supports hundreds of millions of IP data segment rows. Region information supports full customization. The region information of the built-in data is fixed in the format: `Country|Province|City|ISP|iso-alpha2-Code`. You can append data for specific business needs to the region, such as: GPS information/International Standard Regional Codes/Zip codes, etc. In other words, you can fully use ip2region to manage your own IP localization data.
### 3. Data Deduplication and Compression
The `xdb` format generation program automatically processes the input raw data, checks and completes the merging of adjacent IP segments, and performs deduplication and compression of identical regional information.
### 4. High-Speed Query Response
Even for queries based entirely on the `xdb` file, the single query response time is at the 10-microsecond level. Memory-accelerated queries can be enabled through the following two methods:
1. `vIndex` Index Caching: Uses a fixed `512KiB` of memory to cache vector index data, reducing one disk IO operation and maintaining average query efficiency within 100 microseconds.
2. Entire `xdb` File Caching: Loads the entire `xdb` file into memory. Memory usage is equal to the `xdb` file size. There is no disk IO operation, maintaining 10-microsecond level query efficiency.
### 5. Unified Query Interface
`xdb` provides version-compatible query implementations. A unified API can simultaneously provide queries for both IPv4 and IPv6 data and return unified data.
# `xdb` Query
For API introductions, usage documentation, and test programs, please refer to the README introduction under the corresponding `searcher` query client. All query binding implementations are as follows:
| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](binding/golang/README.md) | golang query client | :white_check_mark: | :white_check_mark: |
| [PHP](binding/php/README.md) | php query client | :white_check_mark: | :white_check_mark: |
| [Java](binding/java/README.md) | java query client | :white_check_mark: | :white_check_mark: |
| [C](binding/c/README.md) | C[std=c99] query client | :white_check_mark: | :white_check_mark: |
| [Lua_c](binding/lua_c/README.md) | lua c extension query client | :white_check_mark: | :white_check_mark: |
| [Lua](binding/lua/README.md) | lua query client | :white_check_mark: | :white_check_mark: |
| [Rust](binding/rust/README.md) | rust query client | :white_check_mark: | :white_check_mark: |
| [Python](binding/python/README.md) | python query client | :white_check_mark: | :white_check_mark: |
| [Javascript](binding/javascript/README.md) | javascript query client | :white_check_mark: | :white_check_mark: |
| [Csharp](binding/csharp) | csharp query client | :white_check_mark: | :white_check_mark: |
| [Erlang](binding/erlang/README.md) | erlang query client | :white_check_mark: | :white_check_mark: |
| [Nginx](binding/nginx) | nginx extension query client | :white_check_mark: | :white_check_mark: |
| [C++](binding/cpp/README.md) | C++ query client | :white_check_mark: | :white_check_mark: |
| [Cangjie](binding/cangjie/README.md) | Cangjie query client | :white_check_mark: | :white_check_mark: |
The following toolchain implementations are contributed by community developers via third-party repositories:
| Language | Description |
| --- | --- |
| [ip2region-composer](https://github.com/zoujingli/ip2region) | php composer management client |
| [ip2region-ts](https://github.com/Steven-Qiang/ts-ip2region2) | node.js addon management client |
| [ruby-ip2region](https://github.com/jicheng1014/ruby-ip2region) | ruby xdb query client implementation |
| [Ip2regionTool](https://github.com/orestonce/Ip2regionTool) | ip2region data conversion tool |
# `xdb` Generation
For API introductions, usage documentation, and test programs, please refer to the README documents under the following `maker` generation programs:
| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](maker/golang/README.md) | golang xdb generation program | :white_check_mark: | :white_check_mark: |
| [Java](maker/java/README.md) | java xdb generation program | :white_check_mark: | :white_check_mark: |
| [Python](maker/python/README.md) | python xdb generation program | :white_check_mark: | :x: |
| [Csharp](maker/csharp/README.md) | csharp xdb generation program | :white_check_mark: | :x: |
| [Rust](maker/rust/README.md) | rust xdb generation program | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp) | C++ xdb generation program | :white_check_mark: | :white_check_mark: |
# `xdb` Update
The core of the ip2region project lies in **researching the design and implementation of IP data storage and fast querying**. The raw data `./data/ipv4_source.txt` and `./data/ipv6_source.txt` included in the project are updated irregularly. For scenarios with high requirements for data accuracy and update frequency, it is recommended to purchase commercial offline data from the [Ip2Region Community](https://ip2region.net/products/offline) or third-party vendors. You can try to update the data yourself using the following methods:
### Manual Editing and Updating
You can modify the data yourself based on the raw IP data provided by ip2region in `./data/ipv4_source.txt` and `./data/ipv6_source.txt` using the editing tools provided by ip2region. Currently, the data sources include:
1. Data provided by the ip2region community (please refer to the official account at the bottom for community notifications)
2. Project Issues tagged with `[Data_Updates]`
3. Other custom data: e.g., data provided by customers, data obtained through GPS and WIFI positioning, or legal and compliant data from other platforms.
For instructions on using the raw IP data editing tools, please refer to the README documents under the following `maker` generation programs:
| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](maker/golang/README.md#xdb-data-editing) | golang IP raw data editor | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp/README.md) | C++ IP raw data editor | :white_check_mark: | :white_check_mark: |
### Detection Automatic Update
If you want to update data via your own API or data source, you can refer to the update algorithm based on the "Detection Algorithm" shared in the following videos to write your own update program:
1. [Data Update Implementation Video Sharing - part1](https://www.bilibili.com/video/BV1934y1E7Q5/)
2. [Data Update Implementation Video Sharing - part2](https://www.bilibili.com/video/BV1pF411j7Aw/)
# Official Community
The Ip2Region official community was officially launched on `2025/06/12`. On one hand, it provides stable [commercial offline data](https://ip2region.net/products/offline) services. On the other hand, it facilitates the strengthening of the IP toolchain and data services outside the core code, such as [usage documentation](https://ip2region.net/doc/), [query testing](https://ip2region.net/search/demo), and data correction. For more information and services regarding the community, please visit the [Ip2Region Official Community](https://ip2region.net/).
# Related Remarks
### 1. xdb Technical Documents:
1. xdb Data Structure Analysis: ["ip2region xdb - Data Structure Description"](https://ip2region.net/doc/xdb/structure)
2. xdb Query Process Analysis: ["ip2region xdb - Query Process Description"](https://ip2region.net/doc/xdb/search)
3. xdb Generation Process Analysis: ["ip2region xdb - Generation Process Description"](https://ip2region.net/doc/xdb/generate)
4. xdb File Generation Tutorial: ["ip2region xdb - File Generation Tutorial"](https://ip2region.net/doc/data/xdb_make)
5. xdb Concurrent Safety Query: ["ip2region xdb - Concurrent Safety Query"](https://ip2region.net/doc/xdb/concurrent)
6. xdb Data Update Method: ["ip2region Data Update and Use of xdb Data Editor"](https://mp.weixin.qq.com/s/cZH5qIn4E5rQFy6N32RCzA)
### 3. Technical Information Blogs
1. WeChat Official Account - lionsoul-org, the author's active technical sharing channel
2. [Ip2Region Official Community](https://ip2region.net)

117
README_zh.md Normal file
View File

@ -0,0 +1,117 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region
[ip2region](https://ip2region.net) - 是一个离线IP地址定位库和IP定位数据管理框架同时支持 `IPv4``IPv6` 10微秒级别的查询效率提供了众多主流编程语言的 `xdb` 数据生成和查询客户端实现。
# 项目特性
### 1、离线定位库
项目本身同时了提供了一份 IPv4 (`data/ipv4_source.txt`) 和 IPv6 (`data/ipv6_source.txt`) 的原始数据和对应的 xdb 文件(`data/ip2region_v4.xdb` 和 `data/ip2region_v6.xdb`) 用于实现精确到城市的查询定位功能,字段格式为:`国家|省份|城市|ISP|iso-alpha2-code(国家两字母简称)`,中国的定位信息全部为中文,非中国地区的地域信息全部为英文。
### 2、数据管理框架
`xdb` 支持亿级别的 IP 数据段行数region 信息支持完全自定义,自带数据的 region 信息固定了格式为:`国家|省份|城市|ISP|iso-alpha2-Code`,你可以在 region 中追加特定业务需求的数据例如GPS信息/国际统一地域信息编码/邮编等。也就是你完全可以使用 ip2region 来管理你自己的 IP 定位数据。
### 3、数据去重和压缩
`xdb` 格式生成程序会自动处理输入的原始数据,检查并且完成相连 IP 段的的合并以及相同地域信息的去重和压缩。
### 4、极速查询响应
即使是完全基于 `xdb` 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:
1. `vIndex` 索引缓存 :使用固定的 `512KiB` 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作保持平均查询效率稳定在100微秒之内。
2. `xdb` 整个文件缓存:将整个 `xdb` 文件全部加载到内存,内存占用等同于 `xdb` 文件大小,无磁盘 IO 操作保持10微秒级别的查询效率。
### 5、统一的查询接口
`xdb` 提供了版本兼容的查询实现,一个统一的 API 可以同时提供对 IPv4 和 IPv6 数据的查询并且返回统一的数据。
# `xdb` 查询
API 介绍,使用文档和测试程序请参考对应 `searcher` 查询客户端下的 README 介绍,全部查询 binding 实现情况如下:
| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](binding/golang/README_zh.md) | golang 查询客户端 | :white_check_mark: | :white_check_mark: |
| [PHP](binding/php/README_zh.md) | php 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Java](binding/java/README_zh.md) | java 查询客户端 | :white_check_mark: | :white_check_mark: |
| [C](binding/c/README_zh.md) | C[std=c99] 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Lua_c](binding/lua_c/README_zh.md) | lua c 扩展查询客户端 | :white_check_mark: | :white_check_mark: |
| [Lua](binding/lua/README_zh.md) | lua 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Rust](binding/rust/README_zh.md) | rust 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Python](binding/python/README_zh.md) | python 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Javascript](binding/javascript/README_zh.md) | javascript 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Csharp](binding/csharp) | csharp 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Erlang](binding/erlang/README_zh.md) | erlang 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Nginx](binding/nginx) | nginx 扩展查询客户端 | :white_check_mark: | :white_check_mark: |
| [C++](binding/cpp/README_zh.md) | C++ 查询客户端 | :white_check_mark: | :white_check_mark: |
| [Cangjie](binding/cangjie/README.md) | 仓颉 查询客户端 | :white_check_mark: | :white_check_mark: |
以下工具链实现由社区开发者通过第三方仓库贡献:
| 编程语言 | 描述 |
| --- | --- |
| [ip2region-composer](https://github.com/zoujingli/ip2region) | php composer 管理客户端 |
| [ip2region-ts](https://github.com/Steven-Qiang/ts-ip2region2) | node.js addon 管理客户端|
| [ruby-ip2region](https://github.com/jicheng1014/ruby-ip2region) | ruby xdb 查询客户端实现 |
| [Ip2regionTool](https://github.com/orestonce/Ip2regionTool) | ip2region 数据转换工具 |
# `xdb` 生成
API 介绍,使用文档和测试程序请参考如下 `maker` 生成程序下的 README 文档:
| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](maker/golang/README_zh.md) | golang xdb 生成程序 | :white_check_mark: | :white_check_mark: |
| [Java](maker/java/README_zh.md) | java xdb 生成程序 | :white_check_mark: | :white_check_mark: |
| [Python](maker/python/README_zh.md) | python xdb 生成程序 | :white_check_mark: | :x: |
| [Csharp](maker/csharp/README_zh.md) | csharp xdb 生成程序 | :white_check_mark: | :x: |
| [Rust](maker/rust/README_zh.md) | rust xdb 生成程序 | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp) | C++ xdb 生成程序 | :white_check_mark: | :white_check_mark: |
# `xdb` 更新
ip2region 项目的核心在于 <b>研究 IP 数据的存储和快速查询的设计和实现</b> 项目自带的 `./data/ipv4_source.txt``./data/ipv6_source.txt` 原始数据不定期更新,对于数据精度和更新频率要求很高的使用场景建议到 [Ip2Region社区](https://ip2region.net/products/offline) 或者第三方购买商用离线数据,你可以使用如下几种方式来尝试自己更新数据:
### 手动编辑更新
你可以基于 ip2region 自带的 `./data/ipv4_source.txt``./data/ipv6_source.txt` 原始 IP 数据用 ip2region 提供的编辑工具来自己修改,目前数据源有如下几种方式:
1. ip2region 社区提供的数据(请参考地底部的公众号关注社区通知)
2. ip2region Github/Gitee 中带有 `[数据源补充]` 标签的 Issue
3. 其他自定义数据:例如客户提供的数据,或者通过 GPS 和 WIFI 定位得到的数据,或者来自其他平台的合法合规的数据
原始 IP 数据编辑工具使用方法请参考如下的 `maker` 生成程序下的 README 文档:
| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](maker/golang/README_zh.md#xdb-数据编辑) | golang IP 原始数据编辑器 | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp) | C++ IP 原始数据编辑器 | :white_check_mark: | :white_check_mark: |
### 检测自动更新
如果你想通过你自己的 API 或数据源来更新数据,你可以参考以下视频分享的 `基于检测算法` 的更新算法来自己编写一个更新程序:
1. [数据更新实现视频分享 - part1](https://www.bilibili.com/video/BV1934y1E7Q5/)
2. [数据更新实现视频分享 - part2](https://www.bilibili.com/video/BV1pF411j7Aw/)
# 官方社区
Ip2Region 官方社区正式上线于 `2025/06/12` 日,一方面提供了稳定的 [商用离线数据](https://ip2region.net/products/offline) 服务,另一方面便于在核心代码外强化 IP 工具链和数据服务,例如 [使用文档](https://ip2region.net/doc/)[查询测试](https://ip2region.net/search/demo),数据纠错等,更多关于社区的信息和服务请访问 [Ip2Region 官方社区](https://ip2region.net/)。
# 相关备注
### 1、xdb 技术文档:
1. xdb 数据结构分析:[“ip2region xdb-数据结构描述“](https://ip2region.net/doc/xdb/structure)
2. xdb 查询过程分析:[“ip2region xdb-查询过程描述”](https://ip2region.net/doc/xdb/search)
3. xdb 生成过程分析:[“ip2region xdb-生成过程描述”](https://ip2region.net/doc/xdb/generate)
4. xdb 文件生成教程:[“ip2region xdb-文件生成教程”](https://ip2region.net/doc/data/xdb_make)
5. xdb 并发安全查询:[“ip2region xdb-并发安全查询”](https://ip2region.net/doc/xdb/concurrent)
6. xdb 数据更新方法:[“ip2region 数据更新和 xdb 数据编辑器的使用”](https://mp.weixin.qq.com/s/cZH5qIn4E5rQFy6N32RCzA)
### 3、技术信息博客
1. 微信公众号 - lionsoul-org作者活跃的技术分享渠道
2. [Ip2Region 官方社区](https://ip2region.net)

27
binding/c/Makefile Normal file
View File

@ -0,0 +1,27 @@
all: xdb_searcher test_util
xdb_searcher: xdb_api.h xdb_util.c xdb_searcher.c main.c
gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
test_util: xdb_api.h xdb_util.c test_util.c
gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
xdb_searcher.o: xdb_searcher.c
gcc -std=c99 -Wall -c xdb_searcher.c
xdb_util.o: xdb_util.c
gcc -std=c99 -Wall -c xdb_util.c
xdb_searcher_lib: xdb_util.o xdb_searcher.o
mkdir -p build/lib
mkdir -p build/include
ar -rc build/lib/libxdb.a `find . -name "*.o"`
cp xdb_api.h build/include
clean:
find ./ -name \*.o | xargs rm -f
find ./ -name test_util | xargs rm -f
find ./ -name xdb_searcher | xargs rm -f
rm -rf build
.PHONY: all clean xdb_searcher test_util

370
binding/c/README.md Normal file
View File

@ -0,0 +1,370 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region c Query Client
# Usage
### About Query API
The prototype of the Query API is as follows:
```c
// Query via string IP
int xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);
// Query via binary IP returned by xdb_parse_ip
int xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);
```
If the query fails, a non-`0` error code will be returned. If the query is successful, the `region` information string can be obtained from `xdb_region_buffer_t`. If the input IP cannot be found, `xdb_region_buffer_t` will receive an empty string `""`.
### About IPv4 and IPv6
This xdb query client implementation supports both IPv4 and IPv6 queries. The usage is as follows:
```c
#include "xdb_api.h";
// For IPv4: Set xdb path to the v4 xdb file, specify IP version as IPv4
const char *db_path = "../../data/ip2region_v4.xdb"; // or your ipv4 xdb path
xdb_version_t *version = XDB_IPv4;
// For IPv6: Set xdb path to the v6 xdb file, specify IP version as IPv6
const char *db_path = "../../data/ip2region_v6.xdb"; // or your ipv6 xdb path
xdb_version_t *version = XDB_IPv6;
// The IP version of the xdb specified by db_path must be consistent with the version, otherwise an error will occur during query execution
// Note: The following demonstration directly uses db_path and version variables
```
### XDB File Verification
It is recommended that you proactively verify the applicability of the xdb file, as some future new features may cause the current Searcher version to be incompatible with the xdb file you are using. Verification can avoid unpredictable errors during runtime. You do not need to verify every time; for example, verify when the service starts or manually call a command to confirm version matching. Do not run verification every time a Searcher is created, as this will affect query response speed, especially in high-concurrency scenarios.
```c
#include "xdb_api.h";
int errcode = xdb_verify(db_path);
if ($err != 0) {
// Applicability verification failed!!!
// The current query client implementation is not suitable for querying the xdb file specified by db_path.
// You should stop the service and use a suitable xdb file or upgrade to a Searcher implementation compatible with db_path.
printf("failed to verify xdb file `%s`, errcode: %d\n", db_path, errcode);
return;
}
// Verification passed, the current Searcher can be safely used for query operations on the xdb pointed to by dbPath
```
### File-Based Query
```c
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_searcher_t searcher;
char region_buffer[512] = {'\0'};
xdb_region_buffer_t region;
// Initialize region_buffer_t using region_buffer from stack space
int err = xdb_region_buffer_init(&region, region_buffer, sizeof(region_buffer));
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 1;
}
// Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
err = xdb_init_winsock();
if (err != 0) {
printf("failed to init the winsock with errno=%d\n", err);
return 1;
}
// 1. Initialize xdb query object from db_path.
// @Note: Use the db_path and version described above to create the 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. Call search API to query, both IPv4 and IPv6 are supported.
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// Clean up memory resources for region info; must be called after every search
xdb_region_buffer_free(&region);
// Note: For concurrent use, each thread needs to define and initialize its own searcher query object independently.
// 3. Close xdb searcher
xdb_close(&searcher);
xdb_clean_winsock(); // Call on Windows
return 0;
}
```
### Caching `VectorIndex`
We can pre-load VectorIndex data from the xdb file and cache it globally. Using the global VectorIndex cache every time a Searcher object is created can reduce a fixed IO operation, thereby accelerating queries and reducing IO pressure.
```c
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_vector_index_t *v_index;
xdb_searcher_t searcher;
xdb_region_buffer_t region;
// Initialize region_buffer with NULL to let it manage memory allocation automatically
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 0;
}
// Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
err = xdb_init_winsock();
if (err != 0) {
printf("failed to init the winsock with errno=%d\n", err);
return 1;
}
// 1. Load VectorIndex from the db_path described above.
// Obtain v_index to create a global cache for subsequent repeated use.
// Note: v_index does not need to be loaded every time; it is recommended to load it once at service startup as a global resource.
v_index = xdb_load_vector_index_from_file(db_path);
if (v_index == NULL) {
printf("failed to load vector index from `%s`\n", db_path);
return 1;
}
// 2. Use the global VectorIndex variable to create an xdb searcher with VectorIndex cache.
// @Note: Use the db_path and version described above to create the 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. Call search API to query, both IPv4 and IPv6 are supported
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// Clean up memory resources for region info; must be called after every search
xdb_region_buffer_free(&region);
// Note: For concurrent use, each thread needs to define and initialize its own searcher query object independently.
// 4. Close xdb searcher; if the service is being shut down, the memory for v_index also needs to be freed.
xdb_close(&searcher);
xdb_close_vector_index(v_index);
xdb_clean_winsock();
return 0;
}
```
### Caching the Entire `xdb` File
We can also pre-load the entire xdb file into memory and then create a query object based on this data to achieve fully memory-based queries, similar to the previous memory search.
```c
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_content_t *c_buffer;
xdb_searcher_t searcher;
xdb_region_buffer_t region;
// Initialize region_buffer with NULL to let it manage memory allocation automatically
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 0;
}
// Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
err = xdb_init_winsock();
if (err != 0) {
printf("failed to init the winsock with errno=%d\n", err);
return 1;
}
// 1. Load the entire xdb data from the db_path described above.
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. Use the global c_buffer variable to create a fully memory-based xdb query object.
// @Note: Use the version described above to create the 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. Call search API to query, both IPv4 and IPv6 are supported
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// Clean up memory resources for region info; must be called after every search
xdb_region_buffer_free(&region);
// Note: For concurrent use, xdb query objects created this way can be safely used for concurrency.
// It is recommended to create them when the service starts and then use them safely in parallel until the service shuts down.
// 4. Close xdb searcher; memory for c_buffer needs to be freed when shutting down the service.
xdb_close(&searcher);
xdb_close_content(c_buffer);
xdb_clean_winsock();
return 0;
}
```
### About Storage of Location Information
In older implementations, search-related functions relied on a specified `region_buffer` memory to store location information, which had significant limitations.
The new implementation provides an `xdb_region_buffer_t` object to manage these memory allocations. You can still specify a fixed `region_buffer` to create memory management for the region; this is suitable when the maximum length of your location information is known, which helps reduce memory fragmentation during runtime. If the length of the location information is uncertain or if your program is not suited for pre-allocating a block of memory, you can initialize `xdb_region_buffer_t` by specifying `NULL`. In this case, the object will automatically manage memory allocation, making it suitable for storing location information of any length, though this approach will certainly increase memory fragmentation over long-term operation.
```c
// 1. Create region_buffer by specifying a memory block
char buffer[512];
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, buffer, sizeof(buffer));
if (err != 0) {
// Initialization failed
printf("failed to init region buffer width errcode=%d", err);
return;
}
// 2. Create region_buffer by specifying NULL to let it allocate memory as needed automatically
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
// Initialization failed
printf("failed to init region buffer width errcode=%d", err);
return;
}
// Note: After each query call, you must manually call the function to free memory.
// The search function will report an error if used with uncleaned region info.
xdb_region_buffer_free(&region);
```
# Compiling the Test Program
Compile and obtain the `xdb_searcher` executable as follows:
```bash
# cd to the c binding root directory
➜ c git:(master) ✗ make
gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
```
# Query Testing
Test queries against xdb via the `xdb_searcher search` command:
```bash
➜ 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
```
Example: performing IPv4 query testing using the default data/ip2region_v4.xdb:
```bash
➜ 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: Australia|Queensland|Brisbane|0|AU, io_count: 5, took: 39 μs}
ip2region>> 120.229.45.2
{region: 中国|广东省|深圳市|移动|CN, io_count: 3, took: 13 μs}
```
Example: performing IPv6 query testing using the default data/ip2region_v6.xdb:
```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: 1, took: 38 μs}
ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff
{region: United States|Florida|Miami|velia.net Internetdienste GmbH|US, io_count: 14, took: 76 μs}
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|电信|CN, io_count: 8, took: 42 μs}
```
Enter an IP to perform a query; enter `quit` to exit the test program. You can also set `cache-policy` to file/vectorIndex/content respectively to test the efficiency of the three different cache implementations.
# bench Testing
Perform bench testing via the `xdb_searcher bench` command. This ensures there are no errors in the query program and the `xdb` file, while also providing average query performance through a large number of queries:
```bash
➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench
./xdb_searcher bench [command options]
options:
--db string ip2region binary xdb file path
--src string source ip text file path
--cache-policy string cache policy: file/vectorIndex/content
```
Example: performing IPv4 bench testing via the default data/ip2region_v4.xdb and data/ipv4_source.txt:
```bash
➜ 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}
```
Example: performing IPv6 bench testing via the default data/ip2region_v6.xdb and data/ipv6_source.txt:
```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}
```
You can set the `cache-policy` parameter to test the efficiency of different cache mechanisms (file/vectorIndex/content). @Note: Please ensure that the `src` file used for benching is the same source file used to generate the corresponding `xdb` file.

357
binding/c/README_zh.md Normal file
View File

@ -0,0 +1,357 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region c 查询客户端
# 使用方式
### 关于查询 API
查询 API 的原型如下:
```c
// 通过字符串 IP 进行查询
int xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);
// 通过 xdb_parse_ip 返回的二进制 IP 进行查询
int xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);
```
如果查询失败将会返回非 `0` 的错误代码,如果查询成功 xdb_region_buffer_t 可以获取到字符串的 `region` 信息,如果输入的 IP 找不到相关的信息xdb_region_buffer_t 将会得到一个空的字符串 `""`
### 关于 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
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_searcher_t searcher;
char region_buffer[512] = {'\0'};
xdb_region_buffer_t region;
// 使用栈空间的 region_buffer 初始化 region_buffer_t
int err = xdb_region_buffer_init(&region, region_buffer, sizeof(region_buffer));
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 1;
}
// 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用
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 查询IPv4 和 IPv6 都支持.
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// 清理 region 信息的内存资源,每次 search 之后都得调用
xdb_region_buffer_free(&region);
// 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。
// 3、关闭 xdb 查询器
xdb_close(&searcher);
xdb_clean_winsock(); // windows 下调用
return 0;
}
```
### 缓存 `VectorIndex` 索引
我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。
```c
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_vector_index_t *v_index;
xdb_searcher_t searcher;
xdb_region_buffer_t region;
// 使用 NULL 初始化 region_buffer让其自动管理内存的分配
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 0;
}
// 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用
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);
if (v_index == NULL) {
printf("failed to load vector index from `%s`\n", db_path);
return 1;
}
// 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 查询IPv4 和 IPv6 都支持
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// 清理 region 信息的内存资源,每次 search 之后都得调用
xdb_region_buffer_free(&region);
// 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。
// 4、关闭 xdb 查询器,如果是要关闭服务,也需要释放 v_index 的内存。
xdb_close(&searcher);
xdb_close_vector_index(v_index);
xdb_clean_winsock();
return 0;
}
```
### 缓存整个 `xdb` 文件
我们也可以预先加载整个 xdb 文件到内存,然后基于这个数据创建查询对象来实现完全基于内存的查询,类似之前的 memory search。
```c
#include <stdio.h>
#include "xdb_api.h"
int main(int argc, char *argv[]) {
xdb_content_t *c_buffer;
xdb_searcher_t searcher;
xdb_region_buffer_t region;
// 使用 NULL 初始化 region_buffer让其自动管理内存的分配
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
printf("failed to init the region buffer with errcode=%d\n", err);
return 0;
}
// 在服务启动的时候初始化 winsock不需要重复调用只需要在 windows 系统下调用
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 查询对象.
// @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 查询IPv4 和 IPv6 都支持
const char *ip_string = "1.2.3.4";
// ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6
long cost_time = 0, s_time = xdb_now();
err = xdb_search_by_string(&searcher, ip_string, &region);
cost_time = (int) (xdb_now() - s_time);
if (err != 0) {
printf("failed search(%s) with errno=%d\n", ip_string, err);
} else {
printf("{region: %s, took: %d μs}", region.value, cost_time);
}
// 清理 region 信息的内存资源,每次 search 之后都得调用
xdb_region_buffer_free(&region);
// 备注:并发使用,使用这种方式创建的 xdb 查询对象可以安全用于并发。
// 建议在服务启动的时候创建好,然后一直安全并发使用,直到服务关闭。
// 4、关闭 xdb 查询器,关闭服务的时候需要释放 c_buffer 的内存。
xdb_close(&searcher);
xdb_close_content(c_buffer);
xdb_clean_winsock();
return 0;
}
```
### 关于定位信息的存储
在旧版本的实现中search相关的函数都是依靠指定一个 `region_buffer` 内存来用于存储地域信息,这种方式还是有很大的局限性。
新的实现提供了一个 `xdb_region_buffer_t` 对象来管理这些内存的分配,你依然可以指定一个固定的 `region_buffer` 来创建 region 的内存管理,这个情况适合当你的地域信息的最大长度是可知的,这种方式可以减少运行过程中内存的碎片堆积。如果地域信息的长度不确定或者你的程序不适合提前分配一块内存来管理,你可以通过指定 NULL 的方式来初始化 `xdb_region_buffer_t`,这样对象会自动管理内存的分配,也适合任意长度的地域信息的存储,不过这种方式在长期的运行过程中肯定会增加内存碎片的堆积。
```c
// 1, 通过指定一块内存来创建 region_buffer
char buffer[512];
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, buffer, sizeof(buffer));
if (err != 0) {
// 初始化失败
printf("failed to init region buffer width errcode=%d", err);
return;
}
// 2通过指定 NULL 来创建 region_buffer让其自动按需分配内存
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
// 初始化失败
printf("failed to init region buffer width errcode=%d", err);
return;
}
// 备注:在每次调用 search 完成 IP 定位信息的查询后,你需要手动调用函数来释放内存 .
// search 函数使用未经清理的 region 信息会报错。
xdb_region_buffer_free(&region);
```
# 测试程序编译
通过如下方式编译得到 xdb_searcher 可执行程序:
```bash
# cd 到 c binding 根目录
➜ c git:(master) ✗ make
gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
```
# 查询测试
通过 `xdb_searcher search` 命令来测试对 xdb 的查询:
```bash
➜ 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_v4.xdb 进行 IPv4 查询测试:
```bash
➜ 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: Australia|Queensland|Brisbane|0|AU, io_count: 5, took: 39 μs}
ip2region>> 120.229.45.2
{region: 中国|广东省|深圳市|移动|CN, io_count: 3, took: 13 μ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: 1, took: 38 μs}
ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff
{region: United States|Florida|Miami|velia.net Internetdienste GmbH|US, io_count: 14, took: 76 μs}
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|电信|CN, io_count: 8, took: 42 μs}
```
输入 ip 即可进行查询,输入 quit 即可退出测试程序。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同的缓存实现的效率。
# bench 测试
通过 `xdb_searcher bench` 命令来进行 bench 测试,一方面确保查询程序和 `xdb` 文件没有错误,另一方面可以通过大量的查询得到平均的查询性能:
```bash
➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher bench
./xdb_searcher bench [command options]
options:
--db string ip2region binary xdb file path
--src string source ip text file path
--cache-policy string cache policy: file/vectorIndex/content
```
例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 来进行 IPv4 的 bench 测试:
```bash
➜ 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

@ -1,430 +0,0 @@
/**
* default ip2region implementation
*
* @see #ip2region.h
* @author chenxin<chenxin619315@gmail.com>
* @date 2015-10-30
*/
#include "ip2region.h"
#include <stdio.h>
#include <stdlib.h>
/**
* create a new ip2region object
*
* @param dbFile path
*/
IP2R_API uint_t ip2region_create(ip2region_t ip2rObj, char *dbFile)
{
memset(ip2rObj, 0x00, sizeof(ip2region_entry));
ip2rObj->headerLen = 0;
ip2rObj->HeaderSip = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
if ( ip2rObj->HeaderSip == NULL ) {
return 0;
}
ip2rObj->HeaderPtr = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
if ( ip2rObj->HeaderPtr == NULL ) {
IP2R_FREE(ip2rObj->HeaderSip);
return 0;
}
//open the db file
ip2rObj->dbHandler = fopen(dbFile, "rb");
if ( ip2rObj->dbHandler == NULL ) {
IP2R_FREE(ip2rObj->HeaderSip);
IP2R_FREE(ip2rObj->HeaderPtr);
//fprintf(stderr, "Fail to open the db file %s\n", ip2rObj>dbFile);
//exit(-1);
return 0;
}
ip2rObj->firstIndexPtr = 0;
ip2rObj->lastIndexPtr = 0;
ip2rObj->totalBlocks = 0;
ip2rObj->dbBinStr = NULL;
return 1;
}
/**
* destroy the specifield ip2region object
*
* @param ip2region_t
*/
IP2R_API uint_t ip2region_destroy(ip2region_t ip2rObj)
{
IP2R_FREE(ip2rObj->HeaderSip);
ip2rObj->HeaderSip = NULL;
IP2R_FREE(ip2rObj->HeaderPtr);
ip2rObj->HeaderPtr = NULL;
//close the db file resource
if ( ip2rObj->dbHandler != NULL ) {
fclose(ip2rObj->dbHandler);
ip2rObj->dbHandler = NULL;
}
//free the db binary string
if ( ip2rObj->dbBinStr != NULL ) {
IP2R_FREE(ip2rObj->dbBinStr);
ip2rObj->dbBinStr = NULL;
}
return 1;
}
/**
* get the region associated with the specified ip address with the memory binary search algorithm
*
* @param ip2rObj
* @param ip
* @param datablock
*/
IP2R_API uint_t ip2region_memory_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
{
int l, h, m, p;
uint_t sip, eip, dptr;
int dataLen, dataptr;
long filesize;
char *buffer;
if ( ip2rObj->dbBinStr == NULL ) {
//get the size of the file
fseek(ip2rObj->dbHandler, 0, SEEK_END);
filesize = ftell(ip2rObj->dbHandler);
fseek(ip2rObj->dbHandler, 0, SEEK_SET);
//alloc the buffer size
ip2rObj->dbBinStr = IP2R_MALLOC(filesize);
if ( ip2rObj->dbBinStr == NULL ) {
return 0;
}
//now read the whole file
if ( fread(ip2rObj->dbBinStr, filesize, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
buffer = ip2rObj->dbBinStr;
ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0);
ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4);
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
}
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
while ( l <= h ) {
m = (l + h) >> 1;
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
buffer = ip2rObj->dbBinStr + p;
sip = getUnsignedInt(buffer, 0);
if ( ip < sip ) {
h = m - 1;
} else {
eip = getUnsignedInt(buffer, 4);
if ( ip > eip ) {
l = m + 1;
} else {
dptr = getUnsignedInt(buffer, 8);
break;
}
}
}
if ( dptr == 0 ) return 0;
//get the data
dataLen = ((dptr >> 24) & 0xFF);
dataptr = (dptr & 0x00FFFFFF);
buffer = ip2rObj->dbBinStr + dataptr;
//fill the data to the datablock
datablock->city_id = getUnsignedInt(buffer, 0);
dataLen -= 4; //reduce the length of the city_id
memcpy(datablock->region, buffer + 4, dataLen);
datablock->region[dataLen] = '\0';
return 1;
}
IP2R_API uint_t ip2region_memory_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
{
return ip2region_memory_search(ip2rObj, ip2long(ip), datablock);
}
/**
* get the region associated with the specifield ip address with binary search algorithm
*
* @param ip2rObj
* @param ip
* @param datablock
* @return uint_t
*/
IP2R_API uint_t ip2region_binary_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
{
int l, h, m, p;
uint_t sip, eip, dptr;
char buffer[256];
int dataLen, dataptr;
if ( ip2rObj->totalBlocks == 0 ) {
fseek(ip2rObj->dbHandler, 0, 0);
if ( fread(buffer, 8, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0);
ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4);
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
}
//binary search the index blocks to define the data block
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
while ( l <= h ) {
m = (l + h) >> 1;
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
fseek(ip2rObj->dbHandler, p, 0);
if ( fread(buffer, INDEX_BLOCK_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
sip = getUnsignedInt(buffer, 0);
if ( ip < sip ) {
h = m - 1;
} else {
eip = getUnsignedInt(buffer, 4);
if ( ip > eip ) {
l = m + 1;
} else {
dptr = getUnsignedInt(buffer, 8);
break;
}
}
}
if ( dptr == 0 ) return 0;
//get the data
dataLen = ((dptr >> 24) & 0xFF);
dataptr = (dptr & 0x00FFFFFF);
//memset(data, 0x00, sizeof(data));
fseek(ip2rObj->dbHandler, dataptr, 0);
if ( fread(buffer, dataLen, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
//fill the data to the datablock
datablock->city_id = getUnsignedInt(buffer, 0);
dataLen -= 4; //reduce the length of the city_id
memcpy(datablock->region, buffer + 4, dataLen);
datablock->region[dataLen] = '\0';
return 1;
}
IP2R_API uint_t ip2region_binary_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
{
return ip2region_binary_search(ip2rObj, ip2long(ip), datablock);
}
/**
* get the region associated with the specifield ip address with b-tree algorithm
*
* @param ip2rObj
* @param ip
* @param datablock
* @return uint_t
*/
IP2R_API uint_t ip2region_btree_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
{
int i, idx;
int l, m, h, p, sptr, eptr, indexBlockLen, dataLen, dataptr;
uint_t sip, eip, idxptr, dptr;
char buffer[TOTAL_HEADER_LENGTH];
if ( ip2rObj->headerLen == 0 ) {
idx = 0;
fseek(ip2rObj->dbHandler, 8, 0); //pass the super block
if ( fread(buffer, TOTAL_HEADER_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
for ( i = 0; i < TOTAL_HEADER_LENGTH; i += 8 ) {
sip = getUnsignedInt(buffer, i);
idxptr = getUnsignedInt(buffer, i + 4);
if ( idxptr == 0 ) break;
ip2rObj->HeaderSip[idx] = sip;
ip2rObj->HeaderPtr[idx] = idxptr;
idx++;
}
ip2rObj->headerLen = idx;
}
//search the header block to define the index block
l = 0; h = ip2rObj->headerLen; sptr = 0; eptr = 0;
while ( l <= h ) {
m = ((l + h) >> 1);
//perfetc matched, just return it
if ( ip == ip2rObj->HeaderSip[m] ) {
if ( m > 0 ) {
sptr = ip2rObj->HeaderPtr[m-1];
eptr = ip2rObj->HeaderPtr[m ];
} else {
sptr = ip2rObj->HeaderPtr[m ];
eptr = ip2rObj->HeaderPtr[m+1];
}
break;
}
//less then the middle value
if ( ip < ip2rObj->HeaderSip[m] ) {
if ( m == 0 ) {
sptr = ip2rObj->HeaderPtr[m ];
eptr = ip2rObj->HeaderPtr[m+1];
break;
} else if ( ip > ip2rObj->HeaderSip[m-1] ) {
sptr = ip2rObj->HeaderPtr[m-1];
eptr = ip2rObj->HeaderPtr[m ];
break;
}
h = m - 1;
} else {
if ( m == ip2rObj->headerLen - 1 ) {
sptr = ip2rObj->HeaderPtr[m-1];
eptr = ip2rObj->HeaderPtr[m ];
break;
} else if ( ip <= ip2rObj->HeaderSip[m+1] ) {
sptr = ip2rObj->HeaderPtr[m ];
eptr = ip2rObj->HeaderPtr[m+1];
break;
}
l = m + 1;
}
}
//not matched just stop it
if ( sptr == 0 ) return 0;
indexBlockLen = eptr - sptr;
fseek(ip2rObj->dbHandler, sptr, 0);
if ( fread(buffer, indexBlockLen + INDEX_BLOCK_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
dptr = 0; l = 0; h = indexBlockLen / INDEX_BLOCK_LENGTH;
while ( l <= h ) {
m = ((l + h) >> 1);
p = m * INDEX_BLOCK_LENGTH;
sip = getUnsignedInt(buffer, p);
if ( ip < sip ) {
h = m - 1;
} else {
eip = getUnsignedInt(buffer, p + 4);
if ( ip > eip ) {
l = m + 1;
} else {
dptr = getUnsignedInt(buffer, p + 8);
break;
}
}
}
if ( dptr == 0 ) return 0;
dataLen = ((dptr >> 24) & 0xFF);
dataptr = (dptr & 0x00FFFFFF);
fseek(ip2rObj->dbHandler, dataptr, 0);
if ( fread(buffer, dataLen, 1, ip2rObj->dbHandler) != 1 ) {
return 0;
}
datablock->city_id = getUnsignedInt(buffer, 0);
dataLen -= 4;
memcpy(datablock->region, buffer + 4, dataLen);
datablock->region[dataLen] = '\0';
return 1;
}
IP2R_API uint_t ip2region_btree_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
{
return ip2region_btree_search(ip2rObj, ip2long(ip), datablock);
}
/**
* get a unsinged long(4bytes) from a specifield buffer start from the specifield offset
*
* @param buffer
* @param offset
* @return uint_t
*/
IP2R_API uint_t getUnsignedInt(char *buffer, int offset)
{
return (
((buffer[offset ]) & 0x000000FF) |
((buffer[offset+1] << 8) & 0x0000FF00) |
((buffer[offset+2] << 16) & 0x00FF0000) |
((buffer[offset+3] << 24) & 0xFF000000)
);
}
/**
* string ip to long
*
* @param ip
* @return uint_t
*/
IP2R_API uint_t ip2long(char *ip)
{
int i = 0, p = 24;
char buffer[4], *cs = ip;
uint_t ipval = 0;
while ( *cs != '\0' ) {
if ( *cs == '.' ) {
//single part length limit
if ( i > 3 ) {
ipval = 0;
break;
}
if ( p < 0 ) break;
buffer[i] = '\0';
ipval |= (atoi(buffer) << p);
p -= 8;
i = 0;
} else {
buffer[i++] = *cs;
}
cs++;
}
//append the rest parts
if ( i > 3 ) return 0;
buffer[i] = '\0';
ipval |= atoi(buffer);
return ipval;
}
/**
* long to string ip
*
* @param ip
* @param buffer
* @return uint_t(1 for success and 0 for failed)
*/
IP2R_API uint_t long2ip(uint_t ip, char *buffer)
{
return 0;
}

View File

@ -1,150 +0,0 @@
/**
* ip2region header file
*
* @author chenxin<chenxin619315@gmail.com>
*/
#ifndef _IP2REGION_H
#define _IP2REGION_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//yat, just take it as this way, 99 percent and they works find
#if ( defined(_WIN32) || defined(_WINDOWS_) || defined(__WINDOWS_) )
# define OS_WINDOW
#else
# define OS_LINUX
#endif
#ifdef OS_WINDOW
# define IP2R_API extern __declspec(dllexport)
#else
/*platform shared library statement :: unix*/
# define IP2R_API extern
#endif
#define print(str) printf("%s", str)
#define println(str) printf("%s\n", str)
/*
* memory allocation macro definition.
* cause we should use emalloc,ecalloc .ege. in php.
* so you could make it better apdat the php environment.
*/
#define IP2R_CALLOC(_bytes, _blocks) calloc(_bytes, _blocks)
#define IP2R_MALLOC(_bytes) malloc(_bytes)
#define IP2R_FREE(_ptr) free(_ptr)
typedef unsigned short ushort_t;
typedef unsigned char uchar_t;
typedef unsigned int uint_t;
typedef unsigned long ulong_t;
#define INDEX_BLOCK_LENGTH 12
#define TOTAL_HEADER_LENGTH 4096
/*
* ip2region properties struct
*/
typedef struct {
uint_t *HeaderSip; //header start ip blocks
uint_t *HeaderPtr; //header ptr blocks
uint_t headerLen; //header block number
char *dbFile; //path of db file
FILE *dbHandler; //file handler
char *dbBinStr; //db binary string for memory search mode
uint_t firstIndexPtr; //first index ptr
uint_t lastIndexPtr; //last index ptr
uint_t totalBlocks; //total index blocks number
} ip2region_entry;
typedef ip2region_entry * ip2region_t;
/*
* data block
*/
typedef struct {
uint_t city_id;
char region[256];
} datablock_entry;
typedef datablock_entry * datablock_t;
/**
* create a new ip2region object
*
* @param ip2rObj
* @param dbFile path
*/
IP2R_API uint_t ip2region_create(ip2region_t, char *);
/**
* destroy the specified ip2region object
*
* @param ip2region_t
*/
IP2R_API uint_t ip2region_destroy(ip2region_t);
/**
* get the region associated with the specified ip address with the memory binary search algorithm
*
* @param ip2region_t
* @param uint_t
* @param datablock_t
* @date 2016/06/30
*/
IP2R_API uint_t ip2region_memory_search(ip2region_t, uint_t, datablock_t);
IP2R_API uint_t ip2region_memory_search_string(ip2region_t, char *, datablock_t);
/**
* get the region associated with the specified ip address with binary search algorithm
*
* @param ip2rObj
* @param ip
* @param datablock
* @return uint_t
*/
IP2R_API uint_t ip2region_binary_search(ip2region_t, uint_t, datablock_t);
IP2R_API uint_t ip2region_binary_search_string(ip2region_t, char *, datablock_t);
/**
* get the region associated with the specified ip address with b-tree algorithm
*
* @param ip2rObj
* @param ip
* @param datablock
* @return uint_t
*/
IP2R_API uint_t ip2region_btree_search(ip2region_t, uint_t, datablock_t);
IP2R_API uint_t ip2region_btree_search_string(ip2region_t, char *, datablock_t);
/**
* get a unsinged long(4bytes) from a specified buffer start from the specified offset
*
* @param buffer
* @param offset
* @return uint_t
*/
IP2R_API uint_t getUnsignedInt(char *, int);
/**
* string ip to long
*
* @param ip
* @return uint_t
*/
IP2R_API uint_t ip2long(char *);
/**
* long to string ip
*
* @param ip
* @param buffer
* @return uint_t(1 for success and 0 for failed)
*/
IP2R_API uint_t long2ip(uint_t, char *);
#endif /*end ifndef*/

428
binding/c/main.c Normal file
View File

@ -0,0 +1,428 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date 2022/06/28
#include "stdio.h"
#include "xdb_api.h"
struct searcher_test_entry {
xdb_searcher_t searcher;
xdb_vector_index_t *v_index;
xdb_content_t *c_buffer;
// xdb region buffer
// char region_buffer[256];
xdb_region_buffer_t region;
};
typedef struct searcher_test_entry searcher_test_t;
int init_searcher_test(searcher_test_t *test, char *db_path, char *cache_policy) {
int err, errcode = 0;
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return -1;
}
// auto detect the version from the xdb header
xdb_header_t *header = xdb_load_header(handle);
if (header == NULL) {
printf("failed to load header from `%s`\n", db_path);
errcode = 1;
goto defer;
}
// verify the current xdb
err = xdb_verify_from_header(handle, header);
if (err != 0) {
printf("failed to verify xdb file `%s` with errno=%d\n", db_path, err);
errcode = 2;
goto defer;
}
xdb_version_t *version = xdb_version_from_header(header);
if (version == NULL) {
printf("failed to load version from header\n");
errcode = 3;
goto defer;
}
test->v_index = NULL;
test->c_buffer = NULL;
if (strcmp(cache_policy, "file") == 0) {
err = xdb_new_with_file_only(version, &test->searcher, db_path);
if (err != 0) {
printf("failed to create searcher with errcode=%d\n", err);
errcode = 4;
goto defer;
}
} else if (strcmp(cache_policy, "vectorIndex") == 0) {
test->v_index = xdb_load_vector_index_from_file(db_path);
if (test->v_index == NULL) {
printf("failed to load vector index from `%s`\n", db_path);
errcode = 4;
goto defer;
}
err = xdb_new_with_vector_index(version, &test->searcher, db_path, test->v_index);
if (err != 0) {
printf("failed to create vector index cached searcher with errcode=%d\n", err);
errcode = 5;
goto defer;
}
} else if (strcmp(cache_policy, "content") == 0) {
test->c_buffer = xdb_load_content_from_file(db_path);
if (test->c_buffer == NULL) {
printf("failed to load xdb content from `%s`\n", db_path);
errcode = 4;
goto defer;
}
err = xdb_new_with_buffer(version, &test->searcher, test->c_buffer);
if (err != 0) {
printf("failed to create content cached searcher with errcode=%d\n", err);
errcode = 5;
goto defer;
}
} else {
printf("invalid cache policy `%s`, options: file/vectorIndex/content\n", cache_policy);
errcode = 6;
goto defer;
}
// init the region buffer
// err = xdb_region_buffer_init(&test->region, test->region_buffer, sizeof(test->region_buffer));
err = xdb_region_buffer_init(&test->region, NULL, 0);
if (err != 0) {
printf("failed to init the region buffer with err=%d\n", err);
errcode = 7;
goto defer;
}
defer:
if (header != NULL) {
xdb_free_header(header);
}
if (handle != NULL) {
fclose(handle);
}
return errcode;
}
void destroy_searcher_test(searcher_test_t *test) {
xdb_close(&test->searcher);
// check and free the vector index
if (test->v_index != NULL) {
xdb_free_vector_index(test->v_index);
test->v_index = NULL;
}
// check and free the content buffer
if (test->c_buffer != NULL) {
xdb_free_content(test->c_buffer);
test->c_buffer = NULL;
}
}
//read a line from a command line.
static char *get_line(FILE *fp, char *__dst) {
register int c;
register char *cs;
cs = __dst;
while ( ( c = getc( fp ) ) != EOF ) {
if ( c == '\n' ) break;
*cs++ = c;
}
*cs = '\0';
return ( c == EOF && cs == __dst ) ? NULL : __dst;
}
void print_help(char *argv[]) {
printf("ip2region xdb searcher\n");
printf("%s [command] [command options]\n", argv[0]);
printf("Command: \n");
printf(" search search input test\n");
printf(" bench search bench test\n");
}
void test_search(int argc, char *argv[]) {
int i, n, err;
// for args parse
char *r, key[33] = {'\0'}, val[256] = {'\0'};
char db_file[256] = {'\0'}, cache_policy[16] = {"vectorIndex"};
// for search
long s_time, c_time;
char line[512] = {'\0'};
// ip parse
xdb_version_t *version;
bytes_ip_t ip_bytes[16] = {'\0'};
searcher_test_t test;
for (i = 2; i < argc; i++) {
r = argv[i];
if (strlen(r) < 5) {
continue;
}
if (r[0] != '-' || r[1] != '-') {
continue;
}
if (strchr(r, '=') == NULL) {
printf("missing = for args pair '%s'\n", r);
return;
}
n = sscanf(r+2, "%32[^=]=%255[^\n]", key, val);
if (n != 2) {
printf("invalid option flag `%s`\n", r);
return;
}
// printf("key=%s, val=%s\n", key, val);
if (strcmp(key, "db") == 0) {
snprintf(db_file, sizeof(db_file), "%s", val);
} else if (strcmp(key, "cache-policy") == 0) {
memcpy(cache_policy, val, sizeof(cache_policy) - 1);
// snprintf(cache_policy, sizeof(cache_policy), "%s", val);
} else {
printf("undefined option `%s`\n", r);
return;
}
}
if (strlen(db_file) < 1) {
printf("%s search [command options]\n", argv[0]);
printf("options:\n");
printf(" --db string ip2region binary xdb file path\n");
printf(" --cache-policy string cache policy: file/vectorIndex/content\n");
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) {
// init program will print the error reasons;
return;
}
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);
if ( strlen(line) < 2 ) {
continue;
}
if (strcmp(line, "quit") == 0 ) {
break;
}
version = xdb_parse_ip(line, ip_bytes, sizeof(ip_bytes));
if (version == NULL) {
printf("invalid ip address `%s`\n", line);
continue;
}
s_time = xdb_now();
err = xdb_search(&test.searcher, ip_bytes, version->bytes, &test.region);
if (err != 0) {
printf("{err: %d, io_count: %d}\n", err, xdb_get_io_count(&test.searcher));
} else {
c_time = xdb_now() - s_time;
printf("{region: %s, io_count: %d, took: %ld μs}\n", test.region.value, xdb_get_io_count(&test.searcher), c_time);
}
// free the region
xdb_region_buffer_free(&test.region);
}
destroy_searcher_test(&test);
xdb_clean_winsock();
printf("searcher test program exited, thanks for trying\n");
}
void test_bench(int argc, char *argv[]) {
int i, n, err;
char *r, key[33] = {'\0'}, val[256] = {'\0'};
char db_file[256] = {'\0'}, src_file[256] = {'\0'}, cache_policy[16] = {"vectorIndex"};
FILE *handle;
char line[1024] = {'\0'}, sip_str[INET6_ADDRSTRLEN+1] = {'\0'}, eip_str[INET6_ADDRSTRLEN+1] = {'\0'};
char src_region[512] = {'\0'};
int count = 0, took;
long s_time, t_time, c_time = 0;
// ip parse
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) {
continue;
}
if (r[0] != '-' || r[1] != '-') {
continue;
}
if (strchr(r, '=') == NULL) {
printf("missing = for args pair '%s'\n", r);
return;
}
n = sscanf(r+2, "%32[^=]=%255[^\n]", key, val);
if (n != 2) {
printf("invalid option flag `%s`\n", r);
return;
}
if (strcmp(key, "db") == 0) {
snprintf(db_file, sizeof(db_file), "%s", val);
} else if (strcmp(key, "src") == 0) {
snprintf(src_file, sizeof(src_file), "%s", val);
} else if (strcmp(key, "cache-policy") == 0) {
memcpy(cache_policy, val, sizeof(cache_policy) - 1);
} else {
printf("undefined option `%s`\n", r);
return;
}
}
if (strlen(db_file) < 1 || strlen(src_file) < 1) {
printf("%s bench [command options]\n", argv[0]);
printf("options:\n");
printf(" --db string ip2region binary xdb file path\n");
printf(" --src string source ip text file path\n");
printf(" --cache-policy string cache policy: file/vectorIndex/content\n");
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);
if (err != 0) {
// the init function will print the details;
return;
}
// open the source file
handle = fopen(src_file, "r");
if (handle == NULL) {
printf("failed to open source text file `%s`\n", src_file);
return;
}
while(fgets(line, sizeof(line), handle) != NULL) {
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;
}
s_version = xdb_parse_ip(sip_str, sip_bytes, sizeof(sip_bytes));
if (s_version == NULL) {
printf("invalid start ip `%s`\n", sip_str);
return;
}
e_version = xdb_parse_ip(eip_str, eip_bytes, sizeof(eip_bytes));
if (e_version == NULL) {
printf("invalid end ip `%s`\n", sip_str);
return;
}
if (s_version->id != e_version->id) {
printf("start ip and end ip version not match for line `%s`\n", line);
return;
}
if (xdb_ip_sub_compare(sip_bytes, s_version->bytes, (string_ip_t *) eip_bytes, 0) > 0) {
printf("start ip(%s) should not be greater than end ip(%s)\n", sip_str, eip_str);
return;
}
ip_list[0] = sip_bytes;
ip_list[1] = eip_bytes;
for (i = 0; i < 2; i++) {
t_time = xdb_now();
err = xdb_search(&test.searcher, ip_list[i], s_version->bytes, &test.region);
c_time += xdb_now() - t_time;
if (err != 0) {
xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string));
printf("failed to search ip `%s` with errno=%d\n", ip_string, err);
return;
}
// check the region info
if (strcmp(test.region.value, src_region) != 0) {
xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string));
printf("failed to search(%s) with (%s != %s)\n", ip_string, test.region.value, src_region);
return;
}
// free the region buffer
xdb_region_buffer_free(&test.region);
count++;
}
};
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));
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_help(argv);
return 0;
}
char *opt = argv[1];
if (strcmp(opt, "search") == 0) {
test_search(argc, argv);
} else if (strcmp(opt, "bench") == 0) {
test_bench(argc, argv);
} else {
print_help(argv);
}
return 0;
}

View File

@ -1,102 +0,0 @@
/**
* test ip2region searcher program
*
* @author chenxin<chenxin619315@gmail.com>
* @date 2015-10-30
*/
#include "ip2region.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#define __PRINT_ABOUT__ \
println("+-------------------------------------+"); \
println("| ip2region test program |"); \
println("| Author: chenxin619315@gmail.com. |"); \
println("| Type 'quit' to exit the program. |"); \
println("+-------------------------------------+");
//read a line from a command line.
static char *getLine( FILE *fp, char *__dst )
{
register int c;
register char *cs;
cs = __dst;
while ( ( c = getc( fp ) ) != EOF ) {
if ( c == '\n' ) break;
*cs++ = c;
}
*cs = '\0';
return ( c == EOF && cs == __dst ) ? NULL : __dst;
}
static double getTime()
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return (tv.tv_sec * 1000 + ((double)tv.tv_usec)/1000);
}
int main( int argc, char **argv )
{
ip2region_entry ip2rEntry;
datablock_entry datablock;
char *dbFile = NULL, *algorithm = NULL;
char line[256];
uint_t (*func_ptr)(ip2region_t, char *, datablock_t);
double s_time, c_time;
memset(&datablock, 0x00, sizeof(datablock_entry));
if ( argc < 2 ) {
printf("Usage: a.out [ip2region db file path] [algorithm]\n");
return 0;
}
dbFile = argv[1];
algorithm = "B-tree";
func_ptr = ip2region_btree_search_string;
if ( argc >= 3 ) {
if ( strcmp(argv[2], "binary") == 0 ) {
algorithm = "Binary";
func_ptr = ip2region_binary_search_string;
} else if ( strcmp(argv[2], "memory") == 0 ) {
algorithm = "Memory";
func_ptr = ip2region_memory_search_string;
}
}
//create a new ip2rObj
printf("+--initializing %s ... \n", algorithm);
if ( ip2region_create(&ip2rEntry, dbFile) == 0 ) {
println("Error: Fail to create the ip2region object\n");
return 0;
}
__PRINT_ABOUT__;
while ( 1 ) {
print("ip2region>> ");
getLine( stdin, line );
if ( strlen(line) < 2 ) continue;
if ( strcasecmp( line, "quit" ) == 0 ) {
println("+--Bye!");
break;
}
s_time = getTime();
func_ptr(&ip2rEntry, line, &datablock);
c_time = getTime() - s_time;
printf("%d|%s in %.5f millseconds\n", datablock.city_id, datablock.region, c_time);
}
//destory the ip2rObj
ip2region_destroy(&ip2rEntry);
return 0;
}

213
binding/c/test_util.c Normal file
View File

@ -0,0 +1,213 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date 2022/06/27
#include "stdio.h"
#include "xdb_api.h"
typedef void (* test_func_ptr) ();
struct test_func_entry {
char *name;
test_func_ptr func;
};
typedef struct test_func_entry test_func_t;
void test_load_header() {
xdb_header_t *header = xdb_load_header_from_file("../../data/ip2region_v4.xdb");
if (header == NULL) {
printf("failed to load header");
} else {
printf("header loaded: {\n"
" version: %d, \n"
" index_policy: %d, \n"
" 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->ip_version, header->runtime_ptr_bytes, header->length
);
}
xdb_free_header(header);
}
void test_load_vector_index() {
xdb_vector_index_t *v_index = xdb_load_vector_index_from_file("../../data/ip2region_v4.xdb");
if (v_index == NULL) {
printf("failed to load vector index from file\n");
} else {
printf("vector index loaded from file, length=%d\n", v_index->length);
}
xdb_free_vector_index(v_index);
}
void test_load_content() {
xdb_content_t *content = xdb_load_content_from_file("../../data/ip2region_v4.xdb");
if (content == NULL) {
printf("failed to load content from file\n");
} else {
printf("content loaded from file, length=%d\n", content->length);
}
xdb_free_content(content);
}
void test_parse_ip() {
const char *ip_list[] = {
"1.0.0.0", "58.251.30.115", "192.168.1.100",
"::", "2c0f:fff0::", "2fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "240e:982:e617:ffff:ffff:ffff:ffff:ffff",
"219.xx.xx.11", "::xx:ffff",
NULL
};
int errcode;
xdb_version_t *version;
bytes_ip_t ip_bytes[16] = {'\0'};
string_ip_t ip_string[INET6_ADDRSTRLEN] = {'\0'};
// init the sock env (for windows)
if ((errcode = xdb_init_winsock()) != 0) {
printf("failed to init the winsock");
return;
}
for (int i = 0;; i++) {
if (ip_list[i] == NULL) {
break;
}
version = xdb_parse_ip(ip_list[i], ip_bytes, sizeof(ip_bytes));
if (version == NULL) {
printf("failed to parse ip `%s`\n", ip_list[i]);
continue;
}
xdb_ip_to_string(ip_bytes, version->bytes, ip_string, sizeof(ip_string));
printf("ip: %s (version=v%d), toString: %s\n", ip_list[i], version->id, ip_string);
}
// clean up the winsock
xdb_clean_winsock();
}
struct ip_pair {
const char *sip;
const char *eip;
};
void test_ip_compare() {
struct ip_pair ip_pair_list[] = {
{"1.0.0.0", "1.0.0.1"},
{"192.168.1.101", "192.168.1.90"},
{"219.133.111.87", "114.114.114.114"},
{"1.0.4.0", "1.0.1.0"},
{"1.0.4.0", "1.0.3.255"},
{"2000::", "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"},
{"2001:4:112::", "2001:4:112:ffff:ffff:ffff:ffff:ffff"},
{"ffff::", "2001:4:ffff:ffff:ffff:ffff:ffff:ffff"},
{NULL, NULL}
};
struct ip_pair *pair_ptr = NULL;
bytes_ip_t sip_bytes[16] = {'\0'};
bytes_ip_t eip_bytes[16] = {'\0'};
xdb_version_t *s_version, *e_version;
int errcode;
// init the sock env (for windows)
if ((errcode = xdb_init_winsock()) != 0) {
printf("failed to init the winsock");
return;
}
for (int i = 0; ;i++) {
pair_ptr = &ip_pair_list[i];
if (pair_ptr->sip == NULL) {
break;
}
s_version = xdb_parse_ip(pair_ptr->sip, sip_bytes, sizeof(sip_bytes));
if (s_version == NULL) {
printf("failed to parse sip `%s`", pair_ptr->sip);
continue;
}
e_version = xdb_parse_ip(pair_ptr->eip, eip_bytes, sizeof(eip_bytes));
if (e_version == NULL) {
printf("failed to parse eip `%s`", pair_ptr->eip);
continue;
}
if (s_version->id != e_version->id) {
printf("sip and eip version not match `%s` != `%s`\n", s_version->name, e_version->name);
continue;
}
printf(
"ip_sub_compare(%s, %s): %d\n",
pair_ptr->sip, pair_ptr->eip,
xdb_ip_sub_compare(sip_bytes, s_version->bytes, (string_ip_t *) eip_bytes, 0)
);
}
// clean up the winsock
xdb_clean_winsock();
}
// please register your function heare
static test_func_t _test_function_list[] = {
// xdb buffer
{"test_load_header", test_load_header},
{"test_load_vector_index", test_load_vector_index},
{"test_load_content", test_load_content},
// ip utils
{"test_parse_ip", test_parse_ip},
{"test_ip_compare", test_ip_compare},
{NULL, NULL}
};
// valgrind --tool=memcheck --leak-check=full ./a.out
int main(int argc, char *argv[]) {
int i;
char *name;
// check and call the function
if (argc < 2) {
printf("please specified the function name to call\n");
return 1;
}
name = argv[1];
test_func_ptr func = NULL;
for (i = 0; ; i++) {
if (_test_function_list[i].name == NULL) {
break;
}
if (strcmp(name, _test_function_list[i].name) == 0) {
func = _test_function_list[i].func;
break;
}
}
if (func == NULL) {
printf("can't find test function `%s`\n", name);
return 1;
}
// call the function
func();
return 0;
}

285
binding/c/xdb_api.h Normal file
View File

@ -0,0 +1,285 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date 2022/06/27
#ifndef C_IP2REGION_XDB_H
#define C_IP2REGION_XDB_H
// @Note:
// this define must be put before any header include
// force the LFS for ftell
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if ( defined(WIN32) || defined(_WIN32) || defined(__WINDOWS_) || defined(WINNT) )
# define XDB_PUBLIC(type) extern __declspec(dllexport) type
# define XDB_PRIVATE(type) static type
# define XDB_WINDOWS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#elif (defined(linux) || defined(_UNIX) || defined(__APPLE__) || defined(unix) || defined(__unix) || defined(__unix__) || defined(__linux__) || defined(linux) || defined(__linux))
# define XDB_PUBLIC(type) extern type
# define XDB_PRIVATE(type) static inline type
# define XDB_LINUX
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
# define XDB_PUBLIC(type) type
# define XDB_PRIVATE(type) static type
#endif
#define xdb_calloc( _blocks, _bytes ) calloc( _blocks, _bytes )
#define xdb_malloc( _bytes ) malloc( _bytes )
#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
#define xdb_vector_index_size 8
#define xdb_v4_index_size 14 // 4 + 4 + 2 + 4
#define xdb_v6_index_size 38 // 16 + 16 + 2 + 4
// --- ip version info
#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
// --- xdb buffer functions
// use the following buffer struct to wrap the binary buffer data
// since the buffer data could not be operated with the string API.
struct xdb_header {
unsigned short version;
unsigned short index_policy;
unsigned int created_at;
unsigned int start_index_ptr;
unsigned int end_index_ptr;
// since 3.0+ with IPv6 supporting
unsigned short ip_version;
unsigned short runtime_ptr_bytes;
// the original buffer
unsigned int length;
char buffer[xdb_header_info_length];
};
typedef struct xdb_header xdb_header_t;
XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *);
XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *);
XDB_PUBLIC(void) xdb_free_header(void *);
// --- vector index buffer
struct xdb_vector_index {
unsigned int length;
char buffer[xdb_vector_index_length];
};
typedef struct xdb_vector_index xdb_vector_index_t;
XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *);
XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *);
XDB_PUBLIC(void) xdb_free_vector_index(void *);
// --- content buffer
struct xdb_content {
unsigned int length;
char *buffer;
};
typedef struct xdb_content xdb_content_t;
XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *);
XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *);
XDB_PUBLIC(void) xdb_free_content(void *);
// --- xdb verify
// Verify if the current Searcher could be used to search the specified xdb file.
// Why do we need this check ?
// The future features of the xdb impl may cause the current searcher not able to work properly.
//
// @Note: You Just need to check this ONCE when the service starts
// Or use another process (eg, A command) to check once Just to confirm the suitability.
XDB_PUBLIC(int) xdb_verify(FILE *);
XDB_PUBLIC(int) xdb_verify_from_header(FILE *handle, xdb_header_t *);
XDB_PUBLIC(int) xdb_verify_from_file(const char *);
// --- 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);
// large file seek and tell
XDB_PUBLIC(int) xdb_fseek(FILE *, long long, int);
XDB_PUBLIC(long long) xdb_ftell(FILE *);
// --- END xdb utils
// --- xdb searcher api
// xdb region info structure
#define xdb_region_buffer_wrapper 1
#define xdb_region_buffer_auto 2
struct xdb_region_buffer_entry {
int type; // buffer type
char *value; // region value
size_t length; // buffer length
};
typedef struct xdb_region_buffer_entry xdb_region_buffer_t;
// wrapper the region from a local stack buffer.
// returns: 0 for succeed or failed
XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *, char *, size_t);
// do the buffer alloc.
// returns: 0 for ok or failed
XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *, int);
// empty alloc - empty string
// returns: 0 - always
XDB_PUBLIC(int) xdb_region_buffer_empty(xdb_region_buffer_t *);
XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *);
// xdb searcher structure
struct xdb_searcher_entry {
// ip version
xdb_version_t *version;
// xdb file handle
FILE *handle;
// header info
const char *header;
int io_count;
// vector index buffer cache.
// preload the vector index will reduce the number of IO operations
// thus speedup the search process.
const xdb_vector_index_t *v_index;
// content buffer.
// cache the whole xdb content.
const xdb_content_t *content;
};
typedef struct xdb_searcher_entry xdb_searcher_t;
// xdb searcher new api define
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_version_t *, xdb_searcher_t *, const char *, const xdb_vector_index_t *);
XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *, xdb_searcher_t *, const xdb_content_t *);
XDB_PUBLIC(void) xdb_close(void *);
// xdb searcher search api define
XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);
XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);
XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *);
XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *);
// --- END xdb searcher api
#endif // C_IP2REGION_XDB_H

280
binding/c/xdb_searcher.c Normal file
View File

@ -0,0 +1,280 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date 2022/06/27
#include "xdb_api.h"
// --- region buffer
XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *region, char *buffer, size_t length) {
if (buffer == NULL) {
region->type = xdb_region_buffer_auto;
region->length = 0;
} else if (length <= 0) {
return 1;
} else {
region->type = xdb_region_buffer_wrapper;
region->length = length;
memset(buffer, 0x00, length); // zero-fill the buffer
}
region->value = buffer;
return 0;
}
XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *region, int length) {
if (length <= 0) {
return 1;
}
// no allocation supports for the buffer wapper
if (region->type == xdb_region_buffer_wrapper) {
if (length >= region->length) {
return 2;
}
region->value[length] = '\0';
return 0;
}
// ensure that the value were freed
// by calling #xdb_region_buffer_free
if (region->value != NULL) {
return 3;
}
char *ptr = (char *) xdb_malloc(length + 1);
if (ptr == NULL) {
return 4;
}
ptr[length] = '\0'; // NULL-end
region->value = ptr;
region->length = length;
return 0;
}
// fixed internal empty string ptr
static char * _empty_region_string = "\0";
XDB_PUBLIC(int) xdb_region_buffer_empty(xdb_region_buffer_t *region) {
// no allocation supports for the buffer wapper
if (region->type == xdb_region_buffer_wrapper) {
region->value[0] = '\0';
return 0;
}
// ensure that the value were freed
// by calling #xdb_region_buffer_free
if (region->value != NULL) {
return 3;
}
region->value = _empty_region_string;
region->length = 0;
return 0;
}
XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *region) {
if (region->type == xdb_region_buffer_auto) {
// empty string interception
if (region->length == 0
|| region->value == _empty_region_string) {
// do nothing for empty string
} else {
xdb_free(region->value);
}
// reset the value
region->value = NULL;
}
}
// --- END region buffer
// internal function prototype define
XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length);
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
xdb->version = version;
// check the content buffer first
if (c_buffer != NULL) {
xdb->v_index = NULL;
xdb->content = c_buffer;
return 0;
}
// open the xdb binary file
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return 1;
}
xdb->handle = handle;
xdb->v_index = v_index;
return 0;
}
// xdb searcher new api define
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_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_version_t *version, xdb_searcher_t *xdb, const xdb_content_t *c_buffer) {
return xdb_new_base(version, xdb, NULL, NULL, c_buffer);
}
XDB_PUBLIC(void) xdb_close(void *ptr) {
xdb_searcher_t *xdb = (xdb_searcher_t *) ptr;
if (xdb->handle != NULL) {
fclose(xdb->handle);
xdb->handle = NULL;
}
}
// --- xdb searcher search api define
XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_ip_t *ip_string, xdb_region_buffer_t *region) {
bytes_ip_t ip_bytes[16] = {'\0'};
xdb_version_t *version = xdb_parse_ip(ip_string, ip_bytes, sizeof(ip_bytes));
if (version == NULL) {
return 10;
} else {
return xdb_search(xdb, ip_bytes, version->bytes, region);
}
}
XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int ip_len, xdb_region_buffer_t *region) {
int il0, il1, idx, err, bytes, d_bytes;
register int seg_index_size, l, h, m, p;
unsigned int s_ptr, e_ptr, data_ptr, data_len;
char vector_buffer[xdb_vector_index_size];
char segment_buffer[xdb_v6_index_size];
// ip version check
if (ip_len != xdb->version->bytes) {
return -1;
}
// some resets
err = 0;
data_len = 0;
bytes = xdb->version->bytes;
d_bytes = xdb->version->bytes << 1;
xdb->io_count = 0;
// locate the segment index block based on the vector index
il0 = (int) (ip_bytes[0]);
il1 = (int) (ip_bytes[1]);
idx = il0 * xdb_vector_index_cols * xdb_vector_index_size + il1 * xdb_vector_index_size;
if (xdb->v_index != NULL) {
s_ptr = xdb_le_get_uint32(xdb->v_index->buffer, idx);
e_ptr = xdb_le_get_uint32(xdb->v_index->buffer, idx + 4);
} else if (xdb->content != NULL) {
s_ptr = xdb_le_get_uint32(xdb->content->buffer, xdb_header_info_length + idx);
e_ptr = xdb_le_get_uint32(xdb->content->buffer, xdb_header_info_length + idx + 4);
} else {
err = read(xdb, xdb_header_info_length + idx, vector_buffer, sizeof(vector_buffer));
if (err != 0) {
return 10 + err;
}
s_ptr = xdb_le_get_uint32(vector_buffer, 0);
e_ptr = xdb_le_get_uint32(vector_buffer, 4);
}
// printf("s_ptr=%u, e_ptr=%u\n", s_ptr, e_ptr);
// @Note: ptr validate, zero ptr means source data missing
// so we could just stop here and return an empty string.
if (s_ptr == 0 || e_ptr == 0) {
xdb_region_buffer_empty(region);
return err;
}
// binary search to get the final region info
// segment_buffer = xdb_malloc(seg_index_size);
seg_index_size = xdb->version->segment_index_size;
data_len = 0, data_ptr = 0;
l = 0, h = ((int) (e_ptr - s_ptr)) / seg_index_size;
while (l <= h) {
m = (l + h) >> 1;
p = s_ptr + m * seg_index_size;
// read the segment index item
err = read(xdb, p, segment_buffer, seg_index_size);
if (err != 0) {
return 20 + err;
}
// decode the data fields as needed
if (xdb->version->ip_compare(ip_bytes, bytes, segment_buffer, 0) < 0) {
h = m - 1;
} else if (xdb->version->ip_compare(ip_bytes, bytes, segment_buffer, bytes) > 0) {
l = m + 1;
} else {
data_len = xdb_le_get_uint16(segment_buffer, d_bytes);
data_ptr = xdb_le_get_uint32(segment_buffer, d_bytes + 2);
break;
}
}
// printf("data_len=%u, data_ptr=%u\n", data_len, data_ptr);
if (data_len == 0) {
// return 100;
xdb_region_buffer_empty(region);
return err;
}
// buffer alloc checking
err = xdb_region_buffer_alloc(region, data_len);
if (err != 0) {
return 100 + err;
}
err = read(xdb, data_ptr, region->value, data_len);
if (err != 0) {
return 30 + err;
}
return err;
}
XDB_PRIVATE(int) read(xdb_searcher_t *xdb, long offset, char *buffer, size_t length) {
// check the xdb content cache first
if (xdb->content != NULL) {
memcpy(buffer, xdb->content->buffer + offset, length);
return 0;
}
// seek to the offset
if (fseek(xdb->handle, offset, SEEK_SET) == -1) {
return 1;
}
xdb->io_count++;
if (fread(buffer, 1, length, xdb->handle) != length) {
return 2;
}
return 0;
}
XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *xdb) {
return xdb->version;
}
XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *xdb) {
return xdb->io_count;
}

525
binding/c/xdb_util.c Normal file
View File

@ -0,0 +1,525 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date 2022/06/27
#include "xdb_api.h"
#include <ctype.h>
// for Linux
#ifdef XDB_LINUX
#include "sys/time.h"
#endif
#ifdef XDB_WINDOWS
#include <time.h>
#endif
// @Note: since 2023/10/13 to compatible with the windows system
#ifdef XDB_WINDOWS
static int winsock_initialized = 0;
XDB_PUBLIC(int) xdb_init_winsock() {
if (winsock_initialized == 1) {
return 0;
}
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
return -1;
}
winsock_initialized = 1;
return 0;
}
XDB_PUBLIC(void) xdb_clean_winsock() {
if (winsock_initialized == 1) {
WSACleanup();
winsock_initialized = 0;
}
}
XDB_PRIVATE(int) gettimeofday(struct timeval* tp, void* tzp) {
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm.tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#else
XDB_PUBLIC(int) xdb_init_winsock() {return 0;}
XDB_PUBLIC(void) xdb_clean_winsock() {}
#endif
// --- xdb buffer function implementations
XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *handle) {
xdb_header_t *header;
unsigned int size = xdb_header_info_length;
// entry alloc
header = (xdb_header_t *) xdb_malloc(sizeof(xdb_header_t));
if (header == NULL) {
return NULL;
}
if (fseek(handle, 0, SEEK_SET) == -1) {
xdb_free(header);
return NULL;
}
if (fread(header->buffer, 1,size, handle) != size) {
xdb_free(header);
return NULL;
}
// fill the fields
header->length = size;
header->version = (unsigned short) xdb_le_get_uint16(header->buffer, 0);
header->index_policy = (unsigned short) xdb_le_get_uint16(header->buffer, 2);
header->created_at = xdb_le_get_uint32(header->buffer, 4);
header->start_index_ptr = xdb_le_get_uint32(header->buffer, 8);
header->end_index_ptr = xdb_le_get_uint32(header->buffer,12);
// since IPv6 supporting
header->ip_version = xdb_le_get_uint16(header->buffer, 16);
header->runtime_ptr_bytes = xdb_le_get_uint16(header->buffer, 18);
return header;
}
XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *db_path) {
xdb_header_t *header;
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return NULL;
}
header = xdb_load_header(handle);
fclose(handle);
return header;
}
XDB_PUBLIC(void) xdb_free_header(void *ptr) {
xdb_header_t *header = (xdb_header_t *) ptr;
if (header->length > 0) {
header->length = 0;
xdb_free(header);
}
}
// --- vector index
XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *handle) {
xdb_vector_index_t *v_index;
unsigned int size = xdb_vector_index_length;
// seek to the vector index offset
if (fseek(handle, xdb_header_info_length, SEEK_SET) == -1) {
return NULL;
}
// do the buffer read
v_index = (xdb_vector_index_t *) xdb_malloc(sizeof(xdb_vector_index_t));
if (v_index == NULL) {
return NULL;
}
v_index->length = size;
if (fread(v_index->buffer, 1, size, handle) != size) {
xdb_free(v_index);
return NULL;
}
return v_index;
}
XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *db_path) {
xdb_vector_index_t *v_index;
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return NULL;
}
v_index = xdb_load_vector_index(handle);
fclose(handle);
return v_index;
}
XDB_PUBLIC(void) xdb_free_vector_index(void *ptr) {
xdb_vector_index_t *v_index = (xdb_vector_index_t *) ptr;
if (v_index->length > 0) {
v_index->length = 0;
xdb_free(v_index);
}
}
// --- content buffer
XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *handle) {
unsigned int size;
xdb_content_t *content;
// determine the file size
if (fseek(handle, 0, SEEK_END) == -1) {
return NULL;
}
size = (unsigned int) ftell(handle);
if (fseek(handle, 0, SEEK_SET) == -1) {
return NULL;
}
// do the file read
content = (xdb_content_t *) xdb_malloc(sizeof(xdb_content_t));
if (content == NULL) {
return NULL;
}
// do the buffer alloc
content->buffer = (char *) xdb_malloc(size);
if (content->buffer == NULL) {
xdb_free(content);
return NULL;
}
// read the content into the buffer
content->length = size;
if (fread(content->buffer, 1, size, handle) != size) {
xdb_free(content);
return NULL;
}
return content;
}
XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *db_path) {
xdb_content_t *content;
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return NULL;
}
content = xdb_load_content(handle);
fclose(handle);
return content;
}
XDB_PUBLIC(void) xdb_free_content(void *ptr) {
xdb_content_t *content = (xdb_content_t *) ptr;
if (content->length > 0) {
content->length = 0;
xdb_free(content->buffer);
content->buffer = NULL;
xdb_free(content);
}
}
XDB_PUBLIC(int) xdb_verify_from_header(FILE *handle, xdb_header_t *header) {
unsigned int runtime_ptr_bytes = 0; // runtime ptr bytes
if (header->version == xdb_structure_20) {
runtime_ptr_bytes = 4;
} else if (header->version == xdb_structure_30) {
runtime_ptr_bytes = header->runtime_ptr_bytes;
} else {
return 2;
}
// 1, confirm the xdb file size.
// to ensure that the maximum file pointer does not overflow.
int err = fseek(handle, 0L, SEEK_END);
if (err != 0) {
return 3;
}
long long fileBytes = xdb_ftell(handle);
long long maxFilePtr = (1LL << (runtime_ptr_bytes * 8)) - 1;
// printf("fileBytes: %lld, maxFilePtr: %lld\n", fileBytes, maxFilePtr);
if (fileBytes > maxFilePtr) {
return 4;
}
return 0;
}
XDB_PUBLIC(int) xdb_verify(FILE *handle) {
xdb_header_t *header = xdb_load_header(handle);
if (header == NULL) {
return 1;
}
int errcode = xdb_verify_from_header(handle, header);
if (errcode != 0) {
goto done;
}
// what next ?
done:
xdb_free_header(header);
return errcode;
}
XDB_PUBLIC(int) xdb_verify_from_file(const char *db_path) {
FILE *handle = fopen(db_path, "rb");
if (handle == NULL) {
return -1;
}
int r = xdb_verify(handle);
fclose(handle);
return r;
}
// --- 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, xdb_v4_index_size, _ipv4_sub_compare},
// 38 = 16 + 16 + 2 + 4
{xdb_ipv6_id, "IPv6", xdb_ipv6_bytes, xdb_v6_index_size, 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) {
char *d_ptr = strchr(ip_string, '.');
char *c_ptr = strchr(ip_string, ':');
// version check
if (d_ptr != NULL && c_ptr == NULL) {
return xdb_parse_v4_ip(ip_string, buffer, length);
} else if (c_ptr != 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;
}
XDB_PUBLIC(int) xdb_fseek(FILE *handle, long long offset, int whence) {
// we may have to use the large file solution later
// #if defined(XDB_LINUX)
// return fseeko(handle, (off_t) offset, whence);
// #elif defined(XDB_WINDOWS)
// return _fseeki64(handle, (__int64) offset, whence)
// #else
// return fseek(handle, (long) offset, whence);
// #endif
return fseek(handle, (long) offset, whence);
}
XDB_PUBLIC(long long) xdb_ftell(FILE *handle) {
// we may have to use the large file solution later
// #if defined(XDB_LINUX)
// return (long long) ftello(handle);
// #elif defined(XDB_WINDOWS)
// return (long long) _ftelli64(handle);
// #else
// // report error ?
// return (long long) ftell(handle);
// #endif
return (long long) ftell(handle);
}

5
binding/cangjie/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# build cache
.cache/
# dependency lock
cjpm.lock

172
binding/cangjie/README.md Normal file
View File

@ -0,0 +1,172 @@
# ip2region Cangjie Query Client
This implementation supports both `IPv4` and `IPv6`, and provides a `thread-safe` service layer.
## Architecture
```
binding/cangjie/
├── src/
│ ├── xdb/ # Low-level xdb query engine
│ │ ├── searcher.cj # Searcher (file-only / vector-index / content-buff modes)
│ │ ├── util.cj # IP parsing/comparison, byte readers
│ │ ├── header.cj # xdb header + version parsing
│ │ ├── version.cj # IPv4/IPv6 version definitions
│ │ ├── *_test.cj # Unit tests (24 cases)
│ ├── service/ # High-level thread-safe service layer
│ │ ├── config.cj # Config (cache policy, pool size, pre-loaded data)
│ │ ├── searcher_pool.cj # SearcherPool (Semaphore + ConcurrentLinkedQueue)
│ │ ├── ip2region.cj # Ip2Region unified API
│ │ ├── *_test.cj # Unit tests (17 cases)
├── example/ # Executable CLI demo (separate project)
│ ├── cjpm.toml
│ └── src/main.cj # 5 usage demos + benchmarks
├── cjpm.toml # Static library project
└── README.md
```
**Two API layers:**
- **`ip2region.xdb`** — Low-level Searcher. Not thread-safe (mutable `ioCount` + `File` seek/read state). Supports three cache modes.
- **`ip2region.service`** — High-level `Ip2Region` class wrapping `SearcherPool`. Thread-safe, handles v4/v6 dispatch automatically.
## Build
```bash
cd binding/cangjie
cjpm build # static library
cd example
cjpm build # example executable
```
## Test
41 tests across both packages (24 xdb + 17 service):
```bash
cd binding/cangjie
cjpm test
```
## Cache Policies
| Policy | Memory | Speed | Thread-safe* |
|--------|--------|-------|-------------|
| `FileOnly` (0) | ~0 MB | ~21 µs/op (v4) | Via SearcherPool |
| `VectorIndex` (1) | ~4 MB | ~20 µs/op (v4) | Via SearcherPool |
| `ContentBuff` (2) | ~full xdb | ~5 µs/op (v4) | Via SearcherPool |
\* Low-level `Searcher` is NOT thread-safe. The service layer (`SearcherPool` / `Ip2Region`) provides thread safety by pooling searchers (FileOnly/VectorIndex) or sharing a read-only buffer (ContentBuff).
## Benchmark Results
Benchmarked on Windows 11, AMD Ryzen 7, 487k IPv4 records / 638k IPv6 records:
### IPv4 (`ip2region_v4.xdb`, 487,167 records)
| Cache Policy | Total | Time | Avg |
|-------------|-------|------|-----|
| ContentBuff | 487,167 | 3,059 ms | **5 µs/op** |
| VectorIndex | 487,167 | 10,413 ms | **20 µs/op** |
| FileOnly | 487,167 | 11,271 ms | **21 µs/op** |
### IPv6 (`ip2region_v6.xdb`, 638,953 records)
| Cache Policy | Total | Time | Avg |
|-------------|-------|------|-----|
| ContentBuff | 638,953 | 37,080 ms | **56 µs/op** |
| VectorIndex | 638,953 | 38,443 ms | **58 µs/op** |
| FileOnly | 638,953 | 19,148 ms | **29 µs/op** |
Run your own:
```bash
cd example
cjpm run -- bench --db ../../../data/ip2region_v4.xdb --src ../../../data/ipv4_source.txt --cache-policy content
cjpm run -- bench --db ../../../data/ip2region_v6.xdb --src ../../../data/ipv6_source.txt --cache-policy file
```
## Usage
### Low-level API (`ip2region.xdb`)
```cangjie
import ip2region.xdb.*
// Content buffer mode (fastest)
let content = File.readFrom(Path("ip2region_v4.xdb"))
let header = newHeaderFromBytes(content)
let version = versionFromHeader(header)
let searcher = Searcher(version, content, "ip2region_v4.xdb")
let region = searcher.search(parseIP("220.181.108.183"))
searcher.close()
// Vector index mode
let vIndex = loadVectorIndex(content)
let searcher2 = Searcher(version, "ip2region_v4.xdb", vIndex)
searcher2.close()
// File only mode
let searcher3 = Searcher(version, "ip2region_v4.xdb")
searcher3.close()
```
### High-level API (`ip2region.service`)
```cangjie
import ip2region.service.*
// Via factory (VectorIndex, 20 searchers)
let region = newIp2Region("ip2region_v4.xdb", "ip2region_v6.xdb")
let result = region.search("220.181.108.183")
region.close()
// Custom config
let v4Cfg = Config(ContentBuff, IPv4, "ip2region_v4.xdb", 10)
let v6Cfg = Config(VectorIndex, IPv6, "ip2region_v6.xdb", 20)
let svc = Ip2Region(v4Cfg, v6Cfg)
let result = svc.search("2408:8266:100:1000::")
svc.close()
```
### Single-version API
```cangjie
import ip2region.service.*
// IPv4 only — Ip2Region(config, IPv4)
let v4Cfg = Config(ContentBuff, IPv4, "ip2region_v4.xdb", 10)
let v4 = Ip2Region(v4Cfg, IPv4)
let r4 = v4.search("220.181.108.183")
v4.close()
// IPv6 only — Ip2Region(config, IPv6)
let v6Cfg = Config(ContentBuff, IPv6, "ip2region_v6.xdb", 10)
let v6 = Ip2Region(v6Cfg, IPv6)
let r6 = v6.search("2408:8266:100:1000::")
v6.close()
// Factory functions
let v4Only = newIp2RegionV4(v4Cfg)
let v6Only = newIp2RegionV6(v6Cfg)
v4Only.close(); v6Only.close()
```
### CLI
```bash
cd example
cjpm run
```
## Thread Safety
- **`xdb.Searcher`** — NOT thread-safe. Keep one per thread or use a pool.
- **`service.SearcherPool`** — Thread-safe. Pre-allocates N searchers, uses `Semaphore` for backpressure and `ConcurrentLinkedQueue` for storage. `borrow()` blocks until a searcher is available.
- **`service.Ip2Region`** — Thread-safe. Delegates to pool or shared in-mem searcher based on cache policy. ContentBuff mode shares a single Searcher (Array<Byte> is read-only), FileOnly/VectorIndex use the pool.
- **`service.Config`** — Immutable after construction. Safe to share.
## Requirements
- Cangjie SDK 1.1.0+
- `cjpm` build tool

15
binding/cangjie/cjpm.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
cjc-version = "1.1.0"
name = "ip2region"
organization = ""
description = "ip2region xdb searcher library for Cangjie"
version = "1.0.0"
target-dir = ""
script-dir = ""
output-type = "static"
compile-option = "-Woff unused"
override-compile-option = ""
link-option = ""
package-configuration = {}
[dependencies]

View File

@ -0,0 +1,10 @@
[package]
cjc-version = "1.1.0"
name = "ip2regionExample"
version = "1.0.0"
output-type = "executable"
compile-option = "-Woff unused"
description = "ip2region Cangjie CLI example"
[dependencies]
ip2region = { path = ".." }

View File

@ -0,0 +1,196 @@
package ip2regionExample
import std.fs.*
import std.time.*
import std.collection.*
import ip2region.xdb.*
import ip2region.service.*
// ip2region Cangjie 使用示例
//
// 构建: cjpm build
// 运行: cjpm run
//
// 默认从 ../../../data/ 查找 xdb 文件,
// 也可通过命令行参数指定路径:
// cjpm run -- --v4-db <path> --v6-db <path>
func printHelp() {
println("ip2region Cangjie 使用示例")
println("用法: cjpm run -- [options]")
println("选项:")
println(" --v4-db <path> IPv4 xdb 文件路径 (默认: ../../../data/ip2region_v4.xdb)")
println(" --v6-db <path> IPv6 xdb 文件路径 (默认: ../../../data/ip2region_v6.xdb)")
println(" --help 显示此帮助")
}
// ============================================================
// 方式一: 使用底层 Searcher API (ip2region.xdb)
// 可自行选择缓存策略,单线程使用
// ============================================================
func demoXdbSearcher(v4Path: String, v6Path: String) {
println("\n=== 方式一: 底层 Searcher API ===")
// 1. 读取 xdb 文件到内存
let v4Content = File.readFrom(Path(v4Path))
// 2. 解析文件头,获取版本信息
let v4Header = newHeaderFromBytes(v4Content)
let v4Version = versionFromHeader(v4Header)
// 3. 选择缓存策略创建 Searcher
// 支持三种模式: ContentBuff(最快), VectorIndex(均衡), FileOnly(省内存)
let vIndex = loadVectorIndex(v4Content)
let searcher = Searcher(v4Version, v4Path, vIndex) // VectorIndex 模式
// 4. 查询 IP
let ip = parseIP("220.181.108.183")
let region = searcher.search(ip)
println(" IP: 220.181.108.183")
println(" 结果: ${region}")
searcher.close()
}
// ============================================================
// 方式二: 使用 Ip2Region 服务 API (ip2region.service)
// 自动管理连接池,支持多线程安全访问
// ============================================================
func demoIp2Region(v4Path: String, v6Path: String) {
println("\n=== 方式二: Ip2Region 服务 API ===")
// 1. 创建配置 (缓存策略 + 连接池大小)
let v4Cfg = Config(VectorIndex, IPv4, v4Path, 10)
let v6Cfg = Config(VectorIndex, IPv6, v6Path, 10)
// 2. 创建服务 (同时支持 IPv4 + IPv6)
let region = Ip2Region(v4Cfg, v6Cfg)
// 3. 查询 IPv4
let r1 = region.search("220.181.108.183")
println(" IPv4: 220.181.108.183 -> ${r1}")
// 4. 查询 IPv6
let r2 = region.search("2408:8266:100:1000::")
println(" IPv6: 2408:8266:100:1000:: -> ${r2}")
region.close()
}
// ============================================================
// 方式三: 单版本服务 (仅 IPv4 或 仅 IPv6)
// 使用 init(config, ipVersion) 构造函数
// ============================================================
func demoSingleVersion(v4Path: String, v6Path: String) {
println("\n=== 方式三: 单版本服务 ===")
// IPv4 only
let v4Cfg = Config(ContentBuff, IPv4, v4Path, 3)
let v4Region = Ip2Region(v4Cfg, IPv4)
let r4 = v4Region.search("220.181.108.183")
println(" IPv4 only: 220.181.108.183 -> ${r4}")
v4Region.close()
// IPv6 only
let v6Cfg = Config(ContentBuff, IPv6, v6Path, 3)
let v6Region = Ip2Region(v6Cfg, IPv6)
let r6 = v6Region.search("2408:8266:100:1000::")
println(" IPv6 only: 2408:8266:100:1000:: -> ${r6}")
v6Region.close()
}
// ============================================================
// 方式四: 使用简便工厂函数 (ip2region.service)
// 一行创建,默认 VectorIndex + 20 连接池
// ============================================================
func demoFactory(v4Path: String, v6Path: String) {
println("\n=== 方式四: 简便工厂函数 ===")
let region = newIp2Region(v4Path, v6Path)
let result = region.search("220.181.108.183")
println(" 220.181.108.183 -> ${result}")
region.close()
}
// ============================================================
// 性能测试: 测试三种缓存策略的查询速度
// ============================================================
func bench(v4Path: String) {
println("\n=== 性能测试 (IPv4, 487k 条记录) ===")
let content = File.readFrom(Path(v4Path))
let header = newHeaderFromBytes(content)
let version = versionFromHeader(header)
let vIndex = loadVectorIndex(content)
// 从数据文件读取测试 IP 列表
let srcPath = v4Path.replace("ip2region_v4.xdb", "ipv4_source.txt")
let srcContent = File.readFrom(Path(srcPath))
let lines = String.fromUtf8(srcContent).split("\n")
var ipList = ArrayList<Array<Byte>>()
for (line in lines) {
let trimmed = line.trimAsciiStart().trimAsciiEnd()
if (trimmed.size == 0) { continue }
let parts = trimmed.split("|")
if (parts.size < 3) { continue }
try { ipList.add(parseIP(parts[0])) } catch (_) { continue }
}
println(" 加载了 ${ipList.size} 个 IP")
// 测试 ContentBuff 模式
println("\n [ContentBuff 模式]")
let s1 = Searcher(version, content, v4Path)
benchSearcher(s1, ipList)
// 测试 VectorIndex 模式
println(" [VectorIndex 模式]")
let s2 = Searcher(version, v4Path, vIndex)
benchSearcher(s2, ipList)
// 测试 FileOnly 模式
println(" [FileOnly 模式]")
let s3 = Searcher(version, v4Path)
benchSearcher(s3, ipList)
s1.close(); s2.close(); s3.close()
}
func benchSearcher(searcher: Searcher, ipList: ArrayList<Array<Byte>>) {
let count = ipList.size
let start = MonoTime.now()
for (i in 0..count) {
let _ = searcher.search(ipList[i])
}
let elapsed = MonoTime.now() - start
let avg = elapsed.toMicroseconds() / count
println(" 总量: ${count}, 耗时: ${elapsed.toMilliseconds()} ms, 平均: ${avg} us/op")
}
main(args: Array<String>): Int64 {
var v4Path = "../../../data/ip2region_v4.xdb"
var v6Path = "../../../data/ip2region_v6.xdb"
var i = 0
while (i < args.size) {
if (args[i] == "--v4-db" && i + 1 < args.size) {
v4Path = args[i + 1]; i = i + 2
} else if (args[i] == "--v6-db" && i + 1 < args.size) {
v6Path = args[i + 1]; i = i + 2
} else if (args[i] == "--help") {
printHelp(); return 0
} else { i = i + 1 }
}
println("ip2region Cangjie 使用示例")
println("xdb 路径: ${v4Path}")
demoXdbSearcher(v4Path, v6Path)
demoIp2Region(v4Path, v6Path)
demoSingleVersion(v4Path, v6Path)
demoFactory(v4Path, v6Path)
bench(v4Path)
println("\n完成!")
return 0
}

225
binding/cangjie/src/main.cj Normal file
View File

@ -0,0 +1,225 @@
package ip2region
import std.fs.*
import std.time.*
import std.env.*
import ip2region.xdb.*
func printHelp() {
println("ip2region xdb searcher")
println("Usage: ip2region [command] [command options]")
println("Command:")
println(" search search input test")
println(" bench search bench test")
}
func runSearch(args: Array<String>) {
var v4DbPath = "../../data/ip2region_v4.xdb"
var v6DbPath = "../../data/ip2region_v6.xdb"
var v4CachePolicy = "vectorIndex"
var v6CachePolicy = "vectorIndex"
var showHelp = false
var i = 1 // skip command name
while (i < args.size) {
let arg = args[i]
if (arg == "--v4-db" && i + 1 < args.size) {
v4DbPath = args[i + 1]
i = i + 2
} else if (arg == "--v6-db" && i + 1 < args.size) {
v6DbPath = args[i + 1]
i = i + 2
} else if (arg == "--v4-cache-policy" && i + 1 < args.size) {
v4CachePolicy = args[i + 1]
i = i + 2
} else if (arg == "--v6-cache-policy" && i + 1 < args.size) {
v6CachePolicy = args[i + 1]
i = i + 2
} else if (arg == "--help") {
showHelp = true
i = i + 1
} else {
i = i + 1
}
}
if (showHelp) {
println("ip2region search [command options]")
println("options:")
println(" --v4-db <path> ip2region v4 binary xdb file path")
println(" --v4-cache-policy <mode> v4 cache policy: file/vectorIndex/content")
println(" --v6-db <path> ip2region v6 binary xdb file path")
println(" --v6-cache-policy <mode> v6 cache policy: file/vectorIndex/content")
println(" --help print this help")
return
}
// Load v4 xdb
print("Loading v4 xdb ... ")
let v4Content = File.readFrom(Path(v4DbPath))
let v4Header = newHeaderFromBytes(v4Content)
let v4Version = versionFromHeader(v4Header)
let v4Searcher = createSearcher(v4Version, v4Content, v4DbPath, v4CachePolicy)
println("done")
// Load v6 xdb
print("Loading v6 xdb ... ")
let v6Content = File.readFrom(Path(v6DbPath))
let v6Header = newHeaderFromBytes(v6Content)
let v6Version = versionFromHeader(v6Header)
let v6Searcher = createSearcher(v6Version, v6Content, v6DbPath, v6CachePolicy)
println("done")
println("ip2region search service test program")
println("type 'quit' to exit")
let reader = getStdIn()
while (true) {
print("ip2region>> ")
let lineOpt = reader.readln()
if (lineOpt == None) {
break
}
let line = lineOpt.getOrThrow()
if (line == "quit" || line == "exit") {
break
}
if (line.size == 0) {
continue
}
let start = MonoTime.now()
let region = searchIP(line, v4Searcher, v6Searcher)
let elapsed = MonoTime.now() - start
println("${line} -> ${region} (took: ${elapsed.toMicroseconds()} us)")
}
v4Searcher.close()
v6Searcher.close()
}
func runBench(args: Array<String>) {
var dbFile = ""
var srcFile = ""
var cachePolicy = "content"
var i = 1 // skip command name
while (i < args.size) {
let arg = args[i]
if (arg == "--db" && i + 1 < args.size) {
dbFile = args[i + 1]
i = i + 2
} else if (arg == "--src" && i + 1 < args.size) {
srcFile = args[i + 1]
i = i + 2
} else if (arg == "--cache-policy" && i + 1 < args.size) {
cachePolicy = args[i + 1]
i = i + 2
} else {
i = i + 1
}
}
if (dbFile == "" || srcFile == "") {
println("ip2region bench [command options]")
println("options:")
println(" --db <path> ip2region binary xdb file path")
println(" --src <path> source ip text file path")
println(" --cache-policy <mode> cache policy: file/vectorIndex/content")
return
}
println("Loading xdb from: ${dbFile}")
let content = File.readFrom(Path(dbFile))
let header = newHeaderFromBytes(content)
let version = versionFromHeader(header)
let searcher = createSearcher(version, content, dbFile, cachePolicy)
println("Loading source data from: ${srcFile}")
let srcContent = File.readFrom(Path(srcFile))
let srcStr = String.fromUtf8(srcContent)
let lines = srcStr.split("\n")
var count: Int64 = 0
var totalCost: Int64 = 0
let tStart = MonoTime.now()
for (line in lines) {
let trimmed = line.trimAsciiStart().trimAsciiEnd()
if (trimmed.size == 0) {
continue
}
let parts = trimmed.split("|")
if (parts.size < 3) {
continue
}
let ipBytes = parseIP(parts[0])
let t0 = MonoTime.now()
let _ = searcher.search(ipBytes)
let t1 = MonoTime.now()
totalCost = totalCost + (t1 - t0).toNanoseconds()
count = count + 1
if (count % 10000 == 0) {
print(".")
}
}
let tEnd = MonoTime.now()
let totalTime = tEnd - tStart
println("")
println("Bench finished:")
println(" cachePolicy: ${cachePolicy}")
println(" total: ${count}")
println(" took: ${totalTime.toMilliseconds()} ms")
if (count > 0) {
println(" avg: ${totalCost / count / 1000} us/op")
}
searcher.close()
}
func createSearcher(version: Version, content: Array<Byte>, dbPath: String, cachePolicy: String): Searcher {
if (cachePolicy == "content") {
println("Using content buffer mode")
return Searcher(version, content, dbPath)
} else if (cachePolicy == "vectorIndex") {
println("Using vector index mode")
let vIndex = loadVectorIndex(content)
return Searcher(version, dbPath, vIndex)
} else {
println("Using file only mode")
return Searcher(version, dbPath)
}
}
func searchIP(ipStr: String, v4Searcher: Searcher, v6Searcher: Searcher): String {
let ipBytes = parseIP(ipStr)
if (ipBytes.size == 4) {
return v4Searcher.search(ipBytes)
} else if (ipBytes.size == 16) {
return v6Searcher.search(ipBytes)
}
return "Invalid IP"
}
main(args: Array<String>): Int64 {
if (args.size == 0) {
printHelp()
return 0
}
let cmd = args[0]
if (cmd == "search") {
runSearch(args)
} else if (cmd == "bench") {
runBench(args)
} else {
printHelp()
}
return 0
}

View File

@ -0,0 +1,72 @@
package ip2region.service
import std.fs.*
import ip2region.xdb.*
// Cache policy constants - match Searcher modes
public let FileOnly: Int64 = 0
public let VectorIndex: Int64 = 1
public let ContentBuff: Int64 = 2
// Config holds xdb file configuration and pre-loaded data for creating Searcher instances
public class Config {
public let cachePolicy: Int64
public let ipVersion: Version
public let xdbPath: String
public let header: Header
public let vIndex: Array<Byte>
public let cBuffer: Array<Byte>
public let searchers: Int64
public init(cachePolicy: Int64, ipVersion: Version, xdbPath: String, searchers: Int64) {
if (searchers <= 0) {
throw Exception("searchers must be > 0")
}
this.cachePolicy = cachePolicy
this.ipVersion = ipVersion
this.xdbPath = xdbPath
this.searchers = searchers
let content = File.readFrom(Path(xdbPath))
this.header = newHeaderFromBytes(content)
// Verify IP version matches
let detectedVersion = versionFromHeader(this.header)
if (detectedVersion.id != ipVersion.id) {
throw Exception("xdb file IP version mismatch: expected ${ipVersion.name}, got ${detectedVersion.name}")
}
if (cachePolicy == VectorIndex) {
this.vIndex = loadVectorIndex(content)
this.cBuffer = Array<Byte>(0, repeat: 0)
} else if (cachePolicy == ContentBuff) {
this.vIndex = Array<Byte>(0, repeat: 0)
this.cBuffer = content
} else {
this.vIndex = Array<Byte>(0, repeat: 0)
this.cBuffer = Array<Byte>(0, repeat: 0)
}
}
public init(cachePolicy: Int64, ipVersion: Version, xdbPath: String) {
this(cachePolicy, ipVersion, xdbPath, 20)
}
}
// Static factories for IPv4 and IPv6
public func newV4Config(cachePolicy: Int64, xdbPath: String, searchers: Int64): Config {
return Config(cachePolicy, IPv4, xdbPath, searchers)
}
public func newV6Config(cachePolicy: Int64, xdbPath: String, searchers: Int64): Config {
return Config(cachePolicy, IPv6, xdbPath, searchers)
}
public func newV4Config(cachePolicy: Int64, xdbPath: String): Config {
return Config(cachePolicy, IPv4, xdbPath, 20)
}
public func newV6Config(cachePolicy: Int64, xdbPath: String): Config {
return Config(cachePolicy, IPv6, xdbPath, 20)
}

View File

@ -0,0 +1,161 @@
package ip2region.service
import ip2region.xdb.*
// Ip2Region is the high-level API for searching IP addresses.
// It supports both IPv4 and IPv6 with thread-safe access via SearcherPool.
public class Ip2Region {
let v4Pool: SearcherPool
let v6Pool: SearcherPool
let v4InMemSearcher: Searcher
let v6InMemSearcher: Searcher
let hasV4: Bool
let hasV6: Bool
let v4IsInMem: Bool
let v6IsInMem: Bool
// Create Ip2Region with both v4 and v6 configurations.
public init(v4Config: Config, v6Config: Config) {
// v4 setup
if (v4Config.cachePolicy == ContentBuff) {
this.hasV4 = true
this.v4IsInMem = true
this.v4InMemSearcher = Searcher(v4Config.ipVersion, v4Config.cBuffer, v4Config.xdbPath)
this.v4Pool = SearcherPool(v4Config)
} else {
this.hasV4 = true
this.v4IsInMem = false
this.v4InMemSearcher = Searcher(v4Config.ipVersion, Array<Byte>(0, repeat: 0), v4Config.xdbPath)
this.v4Pool = SearcherPool(v4Config)
}
// v6 setup
if (v6Config.cachePolicy == ContentBuff) {
this.hasV6 = true
this.v6IsInMem = true
this.v6InMemSearcher = Searcher(v6Config.ipVersion, v6Config.cBuffer, v6Config.xdbPath)
this.v6Pool = SearcherPool(v6Config)
} else {
this.hasV6 = true
this.v6IsInMem = false
this.v6InMemSearcher = Searcher(v6Config.ipVersion, Array<Byte>(0, repeat: 0), v6Config.xdbPath)
this.v6Pool = SearcherPool(v6Config)
}
}
// Single-version: provide Config and IP version.
// Examples:
// Ip2Region(v4Config, IPv4) — IPv4 only
// Ip2Region(v6Config, IPv6) — IPv6 only
public init(config: Config, ipVersion: Version) {
if (ipVersion.id == IPv4VersionNo) {
this.hasV4 = true
this.v4IsInMem = (config.cachePolicy == ContentBuff)
if (config.cachePolicy == ContentBuff) {
this.v4InMemSearcher = Searcher(config.ipVersion, config.cBuffer, config.xdbPath)
} else {
this.v4InMemSearcher = Searcher(config.ipVersion, Array<Byte>(0, repeat: 0), config.xdbPath)
}
this.v4Pool = SearcherPool(config)
// IPv6 disabled
this.hasV6 = false
this.v6IsInMem = false
this.v6InMemSearcher = Searcher(IPv4, Array<Byte>(0, repeat: 0), config.xdbPath)
this.v6Pool = SearcherPool(Config(ContentBuff, IPv4, config.xdbPath, 1))
} else {
this.hasV6 = true
this.v6IsInMem = (config.cachePolicy == ContentBuff)
if (config.cachePolicy == ContentBuff) {
this.v6InMemSearcher = Searcher(config.ipVersion, config.cBuffer, config.xdbPath)
} else {
this.v6InMemSearcher = Searcher(config.ipVersion, Array<Byte>(0, repeat: 0), config.xdbPath)
}
this.v6Pool = SearcherPool(config)
// IPv4 disabled
this.hasV4 = false
this.v4IsInMem = false
this.v4InMemSearcher = Searcher(IPv6, Array<Byte>(0, repeat: 0), config.xdbPath)
this.v4Pool = SearcherPool(Config(ContentBuff, IPv6, config.xdbPath, 1))
}
}
// Search an IP string and return the region info.
public func search(ipStr: String): String {
try {
let ipBytes = parseIP(ipStr)
if (ipBytes.size == 4) {
return this.searchV4(ipBytes)
} else if (ipBytes.size == 16) {
return this.searchV6(ipBytes)
}
} catch (_) {
return "Invalid IP"
}
return "Invalid IP"
}
func searchV4(ip: Array<Byte>): String {
if (!this.hasV4) {
return ""
}
if (this.v4IsInMem) {
return this.v4InMemSearcher.search(ip)
}
let searcher = this.v4Pool.borrow()
let region = searcher.search(ip)
this.v4Pool.return_(searcher)
return region
}
func searchV6(ip: Array<Byte>): String {
if (!this.hasV6) {
return ""
}
if (this.v6IsInMem) {
return this.v6InMemSearcher.search(ip)
}
let searcher = this.v6Pool.borrow()
let region = searcher.search(ip)
this.v6Pool.return_(searcher)
return region
}
// close releases all resources.
public func close() {
if (this.hasV4) {
this.v4Pool.close()
}
if (this.hasV6) {
this.v6Pool.close()
}
}
// closeTimeout releases all resources with the specified timeout.
public func closeTimeout(timeout: Duration) {
if (this.hasV4) {
this.v4Pool.closeTimeout(timeout)
}
if (this.hasV6) {
this.v6Pool.closeTimeout(timeout)
}
}
}
// Create an Ip2Region instance using xdb file paths.
// Uses VectorIndex cache policy with 20 searchers by default.
public func newIp2Region(v4XdbPath: String, v6XdbPath: String): Ip2Region {
let v4Config = Config(VectorIndex, IPv4, v4XdbPath, 20)
let v6Config = Config(VectorIndex, IPv6, v6XdbPath, 20)
return Ip2Region(v4Config, v6Config)
}
// Create an IPv4-only Ip2Region service.
public func newIp2RegionV4(config: Config): Ip2Region {
return Ip2Region(config, IPv4)
}
// Create an IPv6-only Ip2Region service.
public func newIp2RegionV6(config: Config): Ip2Region {
return Ip2Region(config, IPv6)
}

View File

@ -0,0 +1,111 @@
package ip2region.service
import std.sync.*
import std.core.*
import std.time.*
import std.collection.concurrent.*
import ip2region.xdb.*
// SearcherPool is a thread-safe pool of xdb.Searcher instances.
// Uses Semaphore for backpressure and ConcurrentLinkedQueue for storage.
public class SearcherPool {
let config: Config
let queue: ConcurrentLinkedQueue<Searcher>
let semaphore: Semaphore
let closing: AtomicBool
let loanCnt: AtomicInt64
public init(config: Config) {
this.config = config
this.queue = ConcurrentLinkedQueue<Searcher>()
this.semaphore = Semaphore(config.searchers)
this.closing = AtomicBool(false)
this.loanCnt = AtomicInt64(0)
let poolSize = config.searchers
for (_ in 0..poolSize) {
let searcher = createPoolSearcher(config)
this.queue.add(searcher)
this.semaphore.release(amount: 1)
}
}
// borrow takes a Searcher from the pool, blocking until one is available.
// Throws if the pool is closing or closed.
public func borrow(): Searcher {
while (!this.closing.load()) {
if (this.semaphore.tryAcquire(amount: 1)) {
let opt = this.queue.remove()
if (opt.isNone()) {
this.semaphore.release(amount: 1)
continue
}
let s = opt.getOrThrow()
this.loanCnt.fetchAdd(1)
return s
}
sleep(Duration.millisecond * 5)
}
throw Exception("SearcherPool is closing")
}
// return_ returns a Searcher to the pool, or closes it if the pool is shutting down.
public func return_(searcher: Searcher) {
if (this.closing.load()) {
searcher.close()
} else {
this.queue.add(searcher)
this.semaphore.release(amount: 1)
}
this.loanCnt.fetchSub(1)
}
// close closes the pool with a default 10 second timeout.
public func close() {
this.closeTimeout(Duration.second * 10)
}
// closeTimeout closes the pool with the specified timeout.
public func closeTimeout(timeout: Duration) {
this.closing.store(true)
// Drain the queue: acquire all permits and close searchers
while (true) {
if (!this.semaphore.tryAcquire(amount: 1)) {
break
}
try {
let opt = this.queue.remove()
if (opt.isSome()) {
opt.getOrThrow().close()
}
} catch (_) {
break
}
}
// Wait for outstanding loans to return (with timeout)
let deadline = MonoTime.now() + timeout
while (this.loanCnt.load() > 0) {
if (MonoTime.now() >= deadline) {
break
}
sleep(Duration.millisecond * 10)
}
}
public func getLoanCount(): Int64 {
return this.loanCnt.load()
}
}
// Helper to create a Searcher based on Config's cache policy
func createPoolSearcher(config: Config): Searcher {
if (config.cachePolicy == ContentBuff) {
return Searcher(config.ipVersion, config.cBuffer, config.xdbPath)
} else if (config.cachePolicy == VectorIndex) {
return Searcher(config.ipVersion, config.xdbPath, config.vIndex)
} else {
return Searcher(config.ipVersion, config.xdbPath)
}
}

View File

@ -0,0 +1,50 @@
package ip2region.tests.service
import ip2region.xdb.*
import ip2region.service.*
@Test
class ConfigV4Tests {
@TestCase
func testCreateFileOnly() {
let cfg = Config(FileOnly, IPv4, "../../data/ip2region_v4.xdb", 5)
if (cfg.cachePolicy != FileOnly) { throw Exception("wrong cache policy") }
if (cfg.searchers != 5) { throw Exception("wrong searchers count") }
if (cfg.ipVersion.id != IPv4VersionNo) { throw Exception("wrong version") }
}
@TestCase
func testCreateVectorIndex() {
let cfg = Config(VectorIndex, IPv4, "../../data/ip2region_v4.xdb", 10)
if (cfg.vIndex.size == 0) { throw Exception("vIndex should be loaded") }
let expectedSize = VectorIndexRows * VectorIndexCols * VectorIndexSize
if (cfg.vIndex.size != expectedSize) {
throw Exception("vIndex size mismatch: ${cfg.vIndex.size} vs ${expectedSize}")
}
}
@TestCase
func testCreateContentBuff() {
let cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 3)
if (cfg.cBuffer.size == 0) { throw Exception("cBuffer should be loaded") }
}
@TestCase
func testFactoryV4() {
let cfg = newV4Config(VectorIndex, "../../data/ip2region_v4.xdb", 8)
if (cfg.ipVersion.id != IPv4VersionNo) { throw Exception("should be IPv4") }
if (cfg.searchers != 8) { throw Exception("wrong searchers") }
}
@TestCase
func testFactoryV6() {
let cfg = newV6Config(VectorIndex, "../../data/ip2region_v6.xdb", 8)
if (cfg.ipVersion.id != IPv6VersionNo) { throw Exception("should be IPv6") }
}
@TestCase
func testFactoryDefaultSearchers() {
let cfg = newV4Config(ContentBuff, "../../data/ip2region_v4.xdb")
if (cfg.searchers != 20) { throw Exception("default searchers should be 20") }
}
}

View File

@ -0,0 +1,108 @@
package ip2region.tests.service
import ip2region.xdb.*
import ip2region.service.*
@Test
class Ip2RegionBasicTests {
@TestCase
func testSearchWithContentBuff() {
let v4Cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 1)
let v6Cfg = Config(ContentBuff, IPv6, "../../data/ip2region_v6.xdb", 1)
let region = Ip2Region(v4Cfg, v6Cfg)
let r1 = region.search("220.181.108.183")
if (r1.size == 0) {
throw Exception("v4 search should return result")
}
let r2 = region.search("2408:8266:100:1000::")
if (r2.size == 0) {
throw Exception("v6 search should return result")
}
region.close()
}
@TestCase
func testSearchWithVectorIndex() {
let v4Cfg = Config(VectorIndex, IPv4, "../../data/ip2region_v4.xdb", 5)
let v6Cfg = Config(VectorIndex, IPv6, "../../data/ip2region_v6.xdb", 5)
let region = Ip2Region(v4Cfg, v6Cfg)
let r = region.search("220.181.108.183")
if (r.size == 0) {
throw Exception("v4 search should return result")
}
region.close()
}
@TestCase
func testInvalidIP() {
let v4Cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 1)
let v6Cfg = Config(ContentBuff, IPv6, "../../data/ip2region_v6.xdb", 1)
let region = Ip2Region(v4Cfg, v6Cfg)
let r = region.search("invalid")
if (r != "Invalid IP") {
throw Exception("expected 'Invalid IP', got '${r}'")
}
region.close()
}
@TestCase
func testClose() {
let v4Cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 1)
let v6Cfg = Config(ContentBuff, IPv6, "../../data/ip2region_v6.xdb", 1)
let region = Ip2Region(v4Cfg, v6Cfg)
region.search("220.181.108.183")
region.close()
}
@TestCase
func testV4OnlyInit() {
let cfg = Config(VectorIndex, IPv4, "../../data/ip2region_v4.xdb", 3)
let region = Ip2Region(cfg, IPv4)
let r = region.search("220.181.108.183")
if (r.size == 0) {
throw Exception("v4-only region should find v4 IP")
}
let r6 = region.search("2408:8266:100:1000::")
if (r6.size != 0) {
throw Exception("v4-only region should not find v6 IP")
}
region.close()
}
@TestCase
func testV6OnlyInit() {
let cfg = Config(VectorIndex, IPv6, "../../data/ip2region_v6.xdb", 3)
let region = Ip2Region(cfg, IPv6)
let r = region.search("2408:8266:100:1000::")
if (r.size == 0) {
throw Exception("v6-only region should find v6 IP")
}
let r4 = region.search("220.181.108.183")
if (r4.size != 0) {
throw Exception("v6-only region should not find v4 IP")
}
region.close()
}
@TestCase
func testNewV4Factory() {
let cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 2)
let region = newIp2RegionV4(cfg)
let r = region.search("220.181.108.183")
if (r.size == 0) {
throw Exception("newIp2RegionV4 should work")
}
region.close()
}
@TestCase
func testNewV6Factory() {
let cfg = Config(ContentBuff, IPv6, "../../data/ip2region_v6.xdb", 2)
let region = newIp2RegionV6(cfg)
let r = region.search("2408:8266:100:1000::")
if (r.size == 0) {
throw Exception("newIp2RegionV6 should work")
}
region.close()
}
}

View File

@ -0,0 +1,55 @@
package ip2region.tests.service
import ip2region.xdb.*
import ip2region.service.*
@Test
class SearcherPoolTests {
@TestCase
func testBorrowAndReturn() {
let cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 3)
let pool = SearcherPool(cfg)
let s = pool.borrow()
if (pool.getLoanCount() != 1) {
throw Exception("loan count should be 1, got ${pool.getLoanCount()}")
}
pool.return_(s)
if (pool.getLoanCount() != 0) {
throw Exception("loan count should be 0 after return")
}
pool.close()
}
@TestCase
func testBorrowAll() {
let cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 5)
let pool = SearcherPool(cfg)
let s1 = pool.borrow()
let s2 = pool.borrow()
let s3 = pool.borrow()
let s4 = pool.borrow()
let s5 = pool.borrow()
if (pool.getLoanCount() != 5) {
throw Exception("should have 5 loans, got ${pool.getLoanCount()}")
}
pool.return_(s5)
pool.return_(s4)
pool.return_(s3)
pool.return_(s2)
pool.return_(s1)
if (pool.getLoanCount() != 0) {
throw Exception("all should be returned, got ${pool.getLoanCount()}")
}
pool.close()
}
@TestCase
func testClosePool() {
let cfg = Config(ContentBuff, IPv4, "../../data/ip2region_v4.xdb", 2)
let pool = SearcherPool(cfg)
let s = pool.borrow()
pool.return_(s)
pool.close()
pool.close()
}
}

View File

@ -0,0 +1 @@
package ip2region.tests

View File

@ -0,0 +1,83 @@
package ip2region.tests.xdb
import ip2region.xdb.*
@Test
class HeaderTests {
func makeHeaderBytes(): Array<Byte> {
let buf = Array<Byte>(256, repeat: 0)
var v: Byte
v = 3; buf[0] = v; v = 0; buf[1] = v
v = 1; buf[2] = v; v = 0; buf[3] = v
v = 0xA0; buf[4] = v; v = 0x86; buf[5] = v; v = 0x01; buf[6] = v; v = 0x00; buf[7] = v
v = 0xE8; buf[8] = v; v = 0x03; buf[9] = v; v = 0x00; buf[10] = v; v = 0x00; buf[11] = v
v = 0xD0; buf[12] = v; v = 0x07; buf[13] = v; v = 0x00; buf[14] = v; v = 0x00; buf[15] = v
v = 4; buf[16] = v; v = 0; buf[17] = v
v = 0; buf[18] = v; v = 0; buf[19] = v
return buf
}
@TestCase
func testNewHeaderFromBytes() {
let buf = makeHeaderBytes()
let h = newHeaderFromBytes(buf)
if (h.version != UInt16(3)) {
throw Exception("expected version 3, got ${h.version}")
}
if (h.ipVersion != UInt16(4)) {
throw Exception("expected ipVersion 4, got ${h.ipVersion}")
}
}
@TestCase
func testHeaderToString() {
let buf = makeHeaderBytes()
let h = newHeaderFromBytes(buf)
let s = h.toString()
if (s.size == 0) {
throw Exception("toString should not be empty")
}
}
@TestCase
func testVersionFromHeaderV4() {
let buf = makeHeaderBytes()
let h = newHeaderFromBytes(buf)
let v = versionFromHeader(h)
if (v.id != IPv4VersionNo) {
throw Exception("expected IPv4, got ${v.name}")
}
}
@TestCase
func testVersionFromHeaderV6() {
let buf = makeHeaderBytes()
var v: Byte = 6; buf[16] = v
let h = newHeaderFromBytes(buf)
let ver = versionFromHeader(h)
if (ver.id != IPv6VersionNo) {
throw Exception("expected IPv6, got ${ver.name}")
}
}
@TestCase
func testVersionFromHeader20() {
let buf = makeHeaderBytes()
var v: Byte = 2; buf[0] = v
v = 0; buf[16] = v
let h = newHeaderFromBytes(buf)
let ver = versionFromHeader(h)
if (ver.id != IPv4VersionNo) {
throw Exception("expected IPv4 for v2.0")
}
}
@TestCase
func testHeaderConstants() {
if (HeaderInfoLength != 256) { throw Exception("expected 256") }
if (VectorIndexRows != 256) { throw Exception("expected 256") }
if (VectorIndexSize != 8) { throw Exception("expected 8") }
if (IPv4SegmentIndexSize != 14) { throw Exception("expected 14") }
if (IPv6SegmentIndexSize != 38) { throw Exception("expected 38") }
}
}

View File

@ -0,0 +1,132 @@
package ip2region.tests.xdb
import std.fs.*
import ip2region.xdb.*
@Test
class SearcherV4Tests {
var v4Content: Array<Byte> = Array<Byte>(0, repeat: 0)
var initialized: Bool = false
@BeforeAll
func setup() {
this.v4Content = File.readFrom(Path("../../data/ip2region_v4.xdb"))
this.initialized = true
}
@TestCase
func testSearchContentMode() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v4Content)
let version = versionFromHeader(header)
let searcher = Searcher(version, this.v4Content, "../../data/ip2region_v4.xdb")
let ip = parseIP("220.181.108.183")
let region = searcher.search(ip)
if (region.size == 0) {
throw Exception("should find region for known IP")
}
searcher.close()
}
@TestCase
func testSearchVectorIndexMode() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v4Content)
let version = versionFromHeader(header)
let vIndex = loadVectorIndex(this.v4Content)
let searcher = Searcher(version, "../../data/ip2region_v4.xdb", vIndex)
let ip = parseIP("220.181.108.183")
let region = searcher.search(ip)
if (region.size == 0) {
throw Exception("should find region for known IP")
}
searcher.close()
}
@TestCase
func testSearchFileMode() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v4Content)
let version = versionFromHeader(header)
let searcher = Searcher(version, "../../data/ip2region_v4.xdb")
let ip = parseIP("220.181.108.183")
let region = searcher.search(ip)
if (region.size == 0) {
throw Exception("should find region for known IP")
}
searcher.close()
}
@TestCase
func testAllModesSameResult() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v4Content)
let version = versionFromHeader(header)
let vIndex = loadVectorIndex(this.v4Content)
let s1 = Searcher(version, this.v4Content, "../../data/ip2region_v4.xdb")
let s2 = Searcher(version, "../../data/ip2region_v4.xdb", vIndex)
let s3 = Searcher(version, "../../data/ip2region_v4.xdb")
let ip = parseIP("1.2.3.4")
let r1 = s1.search(ip)
let r2 = s2.search(ip)
let r3 = s3.search(ip)
s1.close(); s2.close(); s3.close()
if (r1 != r2 || r2 != r3) {
throw Exception("all modes should return same result")
}
}
@TestCase
func testSearchByString() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v4Content)
let version = versionFromHeader(header)
let searcher = Searcher(version, this.v4Content, "../../data/ip2region_v4.xdb")
let region = searcher.searchByString("220.181.108.183")
if (region.size == 0) {
throw Exception("searchByString should find region")
}
searcher.close()
}
}
@Test
class SearcherV6Tests {
var v6Content: Array<Byte> = Array<Byte>(0, repeat: 0)
var initialized: Bool = false
@BeforeAll
func setup() {
this.v6Content = File.readFrom(Path("../../data/ip2region_v6.xdb"))
this.initialized = true
}
@TestCase
func testSearchV6ContentMode() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v6Content)
let version = versionFromHeader(header)
let searcher = Searcher(version, this.v6Content, "../../data/ip2region_v6.xdb")
let ip = parseIP("2408:8266:100:1000::")
let region = searcher.search(ip)
if (region.size == 0) {
throw Exception("should find region for known IPv6")
}
searcher.close()
}
@TestCase
func testSearchV6VectorIndexMode() {
if (!this.initialized) { return }
let header = newHeaderFromBytes(this.v6Content)
let version = versionFromHeader(header)
let vIndex = loadVectorIndex(this.v6Content)
let searcher = Searcher(version, "../../data/ip2region_v6.xdb", vIndex)
let ip = parseIP("2408:8266:100:1000::")
let region = searcher.search(ip)
if (region.size == 0) {
throw Exception("should find region for known IPv6")
}
searcher.close()
}
}

View File

@ -0,0 +1,133 @@
package ip2region.tests.xdb
import ip2region.xdb.*
@Test
class UtilTests {
@TestCase
func testParseIPv4() {
let ip = parseIP("1.2.3.4")
if (ip.size != 4) {
throw Exception("expected 4 bytes, got ${ip.size}")
}
if (Int64(ip[0]) != 1 || Int64(ip[1]) != 2 || Int64(ip[2]) != 3 || Int64(ip[3]) != 4) {
throw Exception("IPv4 parse mismatch")
}
}
@TestCase
func testParseIPv6() {
let ip = parseIP("::1")
if (ip.size != 16) {
throw Exception("expected 16 bytes for IPv6, got ${ip.size}")
}
}
@TestCase
func testIPCompareEqual() {
let a = Array<Byte>(4, repeat: 0)
var v: Byte = 1; a[0] = v; v = 2; a[1] = v; v = 3; a[2] = v; v = 4; a[3] = v
let b = Array<Byte>(4, repeat: 0)
v = 1; b[0] = v; v = 2; b[1] = v; v = 3; b[2] = v; v = 4; b[3] = v
let r = ipCompare(a, b)
if (r != 0) {
throw Exception("expected 0, got ${r}")
}
}
@TestCase
func testIPCompareLess() {
let a = Array<Byte>(4, repeat: 0)
var v: Byte = 1; a[0] = v; v = 2; a[1] = v; v = 3; a[2] = v; v = 4; a[3] = v
let b = Array<Byte>(4, repeat: 0)
v = 1; b[0] = v; v = 2; b[1] = v; v = 3; b[2] = v; v = 5; b[3] = v
let r = ipCompare(a, b)
if (r != -1) {
throw Exception("expected -1, got ${r}")
}
}
@TestCase
func testIPCompareGreater() {
let a = Array<Byte>(4, repeat: 0)
var v: Byte = 1; a[0] = v; v = 2; a[1] = v; v = 3; a[2] = v; v = 5; a[3] = v
let b = Array<Byte>(4, repeat: 0)
v = 1; b[0] = v; v = 2; b[1] = v; v = 3; b[2] = v; v = 4; b[3] = v
let r = ipCompare(a, b)
if (r != 1) {
throw Exception("expected 1, got ${r}")
}
}
@TestCase
func testReadLEUint32() {
let buf = Array<Byte>(4, repeat: 0)
var v: Byte = 0x78; buf[0] = v; v = 0x56; buf[1] = v; v = 0x34; buf[2] = v; v = 0x12; buf[3] = v
let val = readLEUint32(buf, 0)
let expected: Int64 = 0x12345678
if (val != expected) {
throw Exception("expected 0x12345678, got ${val}")
}
}
@TestCase
func testReadLEUint16() {
let buf = Array<Byte>(2, repeat: 0)
var v: Byte = 0x34; buf[0] = v; v = 0x12; buf[1] = v
let val = readLEUint16(buf, 0)
if (val != 0x1234) {
throw Exception("expected 0x1234, got ${val}")
}
}
@TestCase
func testIpToString() {
let ip = Array<Byte>(4, repeat: 0)
var v: Byte = 127; ip[0] = v; v = 0; ip[1] = v; v = 0; ip[2] = v; v = 1; ip[3] = v
let s = ipToString(ip)
if (s != "127.0.0.1") {
throw Exception("expected 127.0.0.1, got ${s}")
}
}
@TestCase
func testCompareIPv4() {
let ip = Array<Byte>(4, repeat: 0)
var v: Byte = 1; ip[0] = v; v = 2; ip[1] = v; v = 3; ip[2] = v; v = 4; ip[3] = v
let entry = Array<Byte>(8, repeat: 0)
v = 4; entry[0] = v; v = 3; entry[1] = v; v = 2; entry[2] = v; v = 1; entry[3] = v
v = 8; entry[4] = v; v = 7; entry[5] = v; v = 6; entry[6] = v; v = 5; entry[7] = v
let r = compareIPv4(ip, entry, 0)
if (r != 0) {
throw Exception("expected equal, got ${r}")
}
}
@TestCase
func testCompareIPv6() {
let ip = Array<Byte>(16, repeat: 0)
var v: Byte = 0x20; ip[0] = v; v = 0x01; ip[1] = v; v = 0x0d; ip[2] = v; v = 0xb8; ip[3] = v
let entry = Array<Byte>(16, repeat: 0)
v = 0x20; entry[0] = v; v = 0x01; entry[1] = v; v = 0x0d; entry[2] = v; v = 0xb8; entry[3] = v
let r = compareIPv6(ip, entry, 0)
if (r != 0) {
throw Exception("expected equal, got ${r}")
}
}
@TestCase
func testLoadVectorIndex() {
let viSize = VectorIndexRows * VectorIndexCols * VectorIndexSize
let cBuff = Array<Byte>(HeaderInfoLength + viSize, repeat: 0)
var v: Byte = 0x42
cBuff[HeaderInfoLength] = v
let vi = loadVectorIndex(cBuff)
if (vi.size != viSize) {
throw Exception("expected size ${viSize}, got ${vi.size}")
}
let firstVal = Int64(vi[0])
if (firstVal != 0x42) {
throw Exception("expected first byte 0x42, got ${firstVal}")
}
}
}

View File

@ -0,0 +1,77 @@
package ip2region.xdb
// xdb file format constants
public let HeaderInfoLength: Int64 = 256
public let VectorIndexRows: Int64 = 256
public let VectorIndexCols: Int64 = 256
public let VectorIndexSize: Int64 = 8
public let IPv4SegmentIndexSize: Int64 = 14 // 4 + 4 + 2 + 4
public let IPv6SegmentIndexSize: Int64 = 38 // 16 + 16 + 2 + 4
public let Structure20: Int64 = 2
public let Structure30: Int64 = 3
// xdb header parsed from the first 256 bytes of the xdb file
public class Header {
public let version: UInt16
public let indexPolicy: UInt16
public let createdAt: UInt32
public let startIndexPtr: UInt32
public let endIndexPtr: UInt32
public let ipVersion: UInt16
public let runtimePtrBytes: UInt16
public init(
ver: UInt16,
idxPolicy: UInt16,
createAt: UInt32,
sPtr: UInt32,
ePtr: UInt32,
ipVer: UInt16,
rtPtrBytes: UInt16
) {
this.version = ver
this.indexPolicy = idxPolicy
this.createdAt = createAt
this.startIndexPtr = sPtr
this.endIndexPtr = ePtr
this.ipVersion = ipVer
this.runtimePtrBytes = rtPtrBytes
}
public func toString(): String {
return "{version: ${this.version}, index_policy: ${this.indexPolicy}, " +
"created_at: ${this.createdAt}, start_index_ptr: ${this.startIndexPtr}, " +
"end_index_ptr: ${this.endIndexPtr}, ip_version: ${this.ipVersion}, " +
"runtime_ptr_bytes: ${this.runtimePtrBytes}}"
}
}
// Read a little-endian UInt16 from byte array at offset
func readHeaderUInt16(buf: Array<Byte>, offset: Int64): UInt16 {
let b0 = UInt16(buf[offset])
let b1 = UInt16(buf[offset + 1]) << 8
return b0 | b1
}
// Read a little-endian UInt32 from byte array at offset
func readHeaderUInt32(buf: Array<Byte>, offset: Int64): UInt32 {
let b0 = UInt32(buf[offset])
let b1 = UInt32(buf[offset + 1]) << 8
let b2 = UInt32(buf[offset + 2]) << 16
let b3 = UInt32(buf[offset + 3]) << 24
return b0 | b1 | b2 | b3
}
// Create a Header from raw byte buffer (first 256 bytes of xdb file)
public func newHeaderFromBytes(buf: Array<Byte>): Header {
return Header(
readHeaderUInt16(buf, 0),
readHeaderUInt16(buf, 2),
readHeaderUInt32(buf, 4),
readHeaderUInt32(buf, 8),
readHeaderUInt32(buf, 12),
readHeaderUInt16(buf, 16),
readHeaderUInt16(buf, 18)
)
}

View File

@ -0,0 +1,146 @@
package ip2region.xdb
import std.fs.*
import std.io.SeekPosition
// Searcher for ip2region xdb database
public class Searcher {
let version: Version
let handle: File
var ioCount: Int64
let vectorIndex: Array<Byte>
let contentBuff: Array<Byte>
let mode: Int64
public let FileOnlyMode: Int64 = 0
public let VectorIndexMode: Int64 = 1
public let ContentBuffMode: Int64 = 2
public init(ver: Version, dbFile: String) {
this.version = ver
this.handle = File(dbFile, Read)
this.vectorIndex = Array<Byte>(0, repeat: 0)
this.contentBuff = Array<Byte>(0, repeat: 0)
this.mode = FileOnlyMode
this.ioCount = 0
}
public init(ver: Version, dbFile: String, vIndex: Array<Byte>) {
this.version = ver
this.handle = File(dbFile, Read)
this.vectorIndex = vIndex
this.contentBuff = Array<Byte>(0, repeat: 0)
this.mode = VectorIndexMode
this.ioCount = 0
}
public init(ver: Version, cBuff: Array<Byte>, dbPath: String) {
this.version = ver
this.handle = File(dbPath, Read)
this.vectorIndex = Array<Byte>(0, repeat: 0)
this.contentBuff = cBuff
this.mode = ContentBuffMode
this.ioCount = 0
}
public func close() {
this.handle.close()
}
public func getIOCount(): Int64 {
return this.ioCount
}
public func searchByString(ipStr: String): String {
let ipBytes = parseIP(ipStr)
return this.search(ipBytes)
}
public func search(ip: Array<Byte>): String {
this.ioCount = 0
let il0 = Int64(ip[0])
let il1 = Int64(ip[1])
let idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize
var sPtr: Int64 = 0
var ePtr: Int64 = 0
if (this.mode == VectorIndexMode) {
sPtr = readLEUint32(this.vectorIndex, idx)
ePtr = readLEUint32(this.vectorIndex, idx + 4)
} else if (this.mode == ContentBuffMode) {
sPtr = readLEUint32(this.contentBuff, HeaderInfoLength + idx)
ePtr = readLEUint32(this.contentBuff, HeaderInfoLength + idx + 4)
} else {
let buff = Array<Byte>(VectorIndexSize, repeat: 0)
this.readFromFile(HeaderInfoLength + idx, buff)
sPtr = readLEUint32(buff, 0)
ePtr = readLEUint32(buff, 4)
}
if (sPtr == 0 || ePtr == 0) {
return ""
}
let segIndexSize = this.version.segmentIndexSize
let bytes = this.version.bytes
let dBytes = bytes * 2
var dataLen: Int64 = 0
var dataPtr: Int64 = 0
var l: Int64 = 0
var h: Int64 = (ePtr - sPtr) / segIndexSize
let buff = Array<Byte>(segIndexSize, repeat: 0)
while (l <= h) {
let m = (l + h) / 2
let p = sPtr + m * segIndexSize
this.read(p, buff)
this.ioCount = this.ioCount + 1
if (this.compareIP(ip, buff, 0) < 0) {
h = m - 1
} else if (this.compareIP(ip, buff, bytes) > 0) {
l = m + 1
} else {
dataLen = readLEUint16(buff, dBytes)
dataPtr = readLEUint32(buff, dBytes + 2)
break
}
}
if (dataLen == 0) {
return ""
}
let regionBuff = Array<Byte>(dataLen, repeat: 0)
this.read(dataPtr, regionBuff)
return String.fromUtf8(regionBuff)
}
func read(offset: Int64, buff: Array<Byte>) {
if (this.mode == ContentBuffMode) {
for (i in 0..buff.size) {
buff[i] = this.contentBuff[offset + i]
}
} else {
this.readFromFile(offset, buff)
()
}
}
func readFromFile(offset: Int64, buff: Array<Byte>) {
this.handle.seek(SeekPosition.Begin(offset))
this.handle.read(buff)
()
}
func compareIP(ip: Array<Byte>, entry: Array<Byte>, offset: Int64): Int64 {
if (this.version.id == IPv4VersionNo) {
return compareIPv4(ip, entry, offset)
}
return compareIPv6(ip, entry, offset)
}
}

View File

@ -0,0 +1,108 @@
package ip2region.xdb
import std.net.IPAddress
// --- IP address parsing and formatting
// Parse IP string to byte array
// For IPv4, returns 4 bytes in big-endian order (network byte order)
// For IPv6, returns 16 bytes in big-endian order
public func parseIP(ip: String): Array<Byte> {
let addr = IPAddress.parse(ip)
return addr.getAddressBytes()
}
// Convert IP byte array back to string
public func ipToString(ip: Array<Byte>): String {
if (ip.size == 4) {
return "${Int64(ip[0])}.${Int64(ip[1])}.${Int64(ip[2])}.${Int64(ip[3])}"
}
// IPv6 simplified format
var result = ""
for (i in 0..ip.size) {
if (i > 0 && i % 2 == 0) {
result = result + ":"
}
result = result + "${Int64(ip[i])}"
}
return result
}
// Compare two IP byte arrays.
// Returns: -1 if ip1 < ip2, 0 if equal, 1 if ip1 > ip2
public func ipCompare(ip1: Array<Byte>, ip2: Array<Byte>): Int64 {
var len = ip1.size
if (ip2.size < len) {
len = ip2.size
}
for (i in 0..len) {
if (ip1[i] < ip2[i]) {
return -1
} else if (ip1[i] > ip2[i]) {
return 1
}
}
if (ip1.size < ip2.size) {
return -1
} else if (ip1.size > ip2.size) {
return 1
}
return 0
}
// --- Binary data reading helpers for xdb format
// Read a little-endian UInt32 from a byte buffer at the given offset
public func readLEUint32(buf: Array<Byte>, offset: Int64): Int64 {
let b0 = Int64(buf[offset])
let b1 = Int64(buf[offset + 1]) << 8
let b2 = Int64(buf[offset + 2]) << 16
let b3 = Int64(buf[offset + 3]) << 24
return b0 | b1 | b2 | b3
}
// Read a little-endian UInt16 from a byte buffer at the given offset
public func readLEUint16(buf: Array<Byte>, offset: Int64): Int64 {
let b0 = Int64(buf[offset])
let b1 = Int64(buf[offset + 1]) << 8
return b0 | b1
}
// Compare IPv4 address (big-endian) with xdb stored IPv4 (little-endian)
// xdb stores IPv4 in little-endian, so we compare ip[i] with xdbEntry[3-i]
public func compareIPv4(ip: Array<Byte>, xdbEntry: Array<Byte>, entryOffset: Int64): Int64 {
for (i in 0..4) {
let ipByte = Int64(ip[i])
let xdbByte = Int64(xdbEntry[entryOffset + 3 - i])
if (ipByte < xdbByte) {
return -1
} else if (ipByte > xdbByte) {
return 1
}
}
return 0
}
// Load the vector index from a content buffer
// The vector index starts at HeaderInfoLength and has VectorIndexRows * VectorIndexCols * VectorIndexSize bytes
public func loadVectorIndex(cBuff: Array<Byte>): Array<Byte> {
let start = HeaderInfoLength
let len = VectorIndexRows * VectorIndexCols * VectorIndexSize
let vi = Array<Byte>(len, repeat: 0)
for (i in 0..len) {
vi[i] = cBuff[start + i]
}
return vi
}
// Compare IPv6 address (big-endian) with xdb stored IPv6 (big-endian)
public func compareIPv6(ip: Array<Byte>, xdbEntry: Array<Byte>, entryOffset: Int64): Int64 {
for (i in 0..16) {
if (ip[i] < xdbEntry[entryOffset + i]) {
return -1
} else if (ip[i] > xdbEntry[entryOffset + i]) {
return 1
}
}
return 0
}

View File

@ -0,0 +1,41 @@
package ip2region.xdb
// IP version definitions for ip2region xdb searcher
public class Version {
public let id: Int64
public let name: String
public let bytes: Int64
public let segmentIndexSize: Int64
public init(id: Int64, name: String, bytes: Int64, segmentIndexSize: Int64) {
this.id = id
this.name = name
this.bytes = bytes
this.segmentIndexSize = segmentIndexSize
}
public func toString(): String {
return "{id: ${this.id}, name: ${this.name}, bytes: ${this.bytes}, " +
"segment_index_size: ${this.segmentIndexSize}}"
}
}
public let IPv4VersionNo: Int64 = 4
public let IPv6VersionNo: Int64 = 6
// Pre-defined IP versions
public let IPv4 = Version(4, "IPv4", 4, 14)
public let IPv6 = Version(6, "IPv6", 16, 38)
// Determine IP version from header info
public func versionFromHeader(header: Header): Version {
// Old structure (2.0) with IPv4 only
if (Int64(header.version) == Structure20) {
return IPv4
}
// Structure 3.0+
if (Int64(header.ipVersion) == IPv4VersionNo) {
return IPv4
}
return IPv6
}

1
binding/cpp/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin/

27
binding/cpp/Makefile Normal file
View File

@ -0,0 +1,27 @@
all: bin header search bench make edit
FILES=$(wildcard src/*.cc)
bin:
mkdir -p bin
header: $(FILES) test/header.cc
g++ -std=c++11 -O2 $^ -o bin/$@
search: $(FILES) test/search.cc
g++ -std=c++11 -O2 $^ -o bin/$@
bench: $(FILES) test/bench.cc
g++ -std=c++11 -O2 $^ -o bin/$@
make: $(FILES) test/make.cc
g++ -std=c++11 -O2 $^ -o bin/$@
edit: $(FILES)
g++ -std=c++11 -O2 $^ test/edit_v4.cc -o bin/edit_v4
g++ -std=c++11 -O2 $^ test/edit_v6.cc -o bin/edit_v6
clean:
rm -rf bin

137
binding/cpp/README.md Normal file
View File

@ -0,0 +1,137 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region C++ query client
## 0. File Description
```
Makefile --------- Build
src ------------------ Source directory
src/base.* ----------- Constants and utility functions
src/ip.* ------------- IP processing implementation
src/header.* --------- xdb header parsing implementation
src/search.* --------- xdb search implementation
src/bench.* ---------- Search benchmarking implementation
src/make.* ----------- xdb file generation implementation
src/edit.* ----------- Raw data editing implementation
test ---------------- Test directory
test/header.cc ------ Test header
test/search.cc ------ Test search
test/bench.cc ------- Benchmarking
test/make.cc -------- Generate xdb file
test/edit_v4.cc ----- Test raw data editing (ipv4)
test/edit_v6.cc ----- Test raw data editing (ipv6)
bin --------------- Executable directory (generated via make)
bin/header -------- Test header
bin/search -------- Test search
bin/bench --------- Benchmarking
bin/make ---------- Generate xdb file
bin/edit_v4 ------- Test raw data editing (ipv4)
bin/edit_v6 ------- Test raw data editing (ipv6)
readme.md --------- readme
```
## 1. Compilation
```
$ make
```
## 2. Search
### 2.1 Example
```cpp
#include "src/search.h"
// IP Version: xdb::ipv4 xdb::ipv6
// Policy: xdb::policy_file xdb::policy_vector xdb::policy_content
// No cache Partial cache Full cache
int main() {
std::string xdb_name = "../../data/ip2region_v6.xdb";
int version = xdb::ipv6;
int policy = xdb::policy_content;
std::string ip = "2001:200:124::";
xdb::search_t s(xdb_name, version, policy);
std::cout << s.search(ip) << std::endl;
return 0;
}
// $ g++ src/*.cc 1.cc --- Compile
// $ ./a.out ------------- Test
// Japan|Tokyo|Asagaya-minami|WIDE Project|JP
```
### 2.2 Test xdb Header
```
$ ./bin/header
Test IPv4
Version: 3
Cache Policy: 1
File Generation Time: 2025-09-06 02:24:16
Index Start Address: 955933
Index End Address: 11042415
IP Version: 4
Pointer Bytes: 4
Test IPv6
Version: 3
Cache Policy: 1
File Generation Time: 2025-10-17 04:41:04
Index Start Address: 3094259
Index End Address: 36258303
IP Version: 6
Pointer Bytes: 4
```
### 2.3 Test Search
```
$ ./bin/search
Test IPv4 No cache: Success
Test IPv4 Partial cache: Success
Test IPv4 Full cache: Success
Test IPv6 No cache: Success
Test IPv6 Partial cache: Success
Test IPv6 Full cache: Success
```
## 3. Benchmarking and Correctness Verification
```
./bin/bench
Test IPv4, No cache, total: 3910284, took: 27.60s, cost: 6.59μs/op, io count: 28227147
Test IPv4, Partial cache, total: 3910284, took: 21.85s, cost: 5.15μs/op, io count: 24316863
Test IPv4, Full cache, total: 3910284, took: 2.26s, cost: 0.25μs/op, io count: 0
Test IPv6, No cache, total: 4792520, took: 100.40s, cost: 20.22μs/op, io count: 80758866
Test IPv6, Partial cache, total: 4792520, took: 93.06s, cost: 18.71μs/op, io count: 75966346
Test IPv6, Full cache, total: 4792520, took: 6.24s, cost: 0.81μs/op, io count: 0
```
## 4. Generate xdb File
### 4.1 Generate xdb File
```
$ ./bin/make
Generate ipv4 xdb file, took: 0.57s
Generate ipv6 xdb file, took: 1.24s
```
## 5. Raw Data Editing
### 5.1. Instructions for Use
* New IP attribution files can contain empty lines
* New IP attribution files can be out of order; the program will automatically sort them
* New IP attribution files can overlap; as long as there is no ambiguity, the program will automatically merge them
* The final result will automatically merge adjacent lines with the same attribution
* For the following tests, the original file uses the data file provided in the repository, and the new file uses 1.txt in the current directory

125
binding/cpp/README_zh.md Normal file
View File

@ -0,0 +1,125 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region C++ 查询客户端
## 0. 文件说明
```
Makefile --------- 构建
src ------------------ 源文件目录
src/base.* ----------- 常量及工具函数
src/ip.* ------------- 实现 IP 处理
src/header.* --------- 实现 xdb 头部解析
src/search.* --------- 实现 xdb 查找
src/bench.* ---------- 实现 查找 测速
src/make.* ----------- 实现 生成 xdb 文件
src/edit.* ----------- 实现 原始数据编辑
test ---------------- 测试目录
test/header.cc ------ 测试 头部
test/search.cc ------ 测试 查找
test/bench.cc ------- 测速
test/make.cc -------- 生成 xdb 文件
test/edit_v4.cc ----- 测试 原始数据编辑(ipv4)
test/edit_v6.cc ----- 测试 原始数据编辑(ipv6)
bin --------------- 可执行文件目录(通过 make 生成)
bin/header -------- 测试 头部
bin/search -------- 测试 查找
bin/bench --------- 测速
bin/make ---------- 生成 xdb 文件
bin/edit_v4 ------- 测试 原始数据编辑(ipv4)
bin/edit_v6 ------- 测试 原始数据编辑(ipv6)
readme.md --------- readme
```
## 1. 编译
```
$ make
```
## 2. 查找
### 2.1 示例
```cpp
#include "src/search.h"
// IP 版本: xdb::ipv4 xdb::ipv6
// 策略: xdb::policy_file xdb::policy_vector xdb::policy_content
// 不缓存 部分缓存 全部缓存
int main() {
std::string xdb_name = "../../data/ip2region_v6.xdb";
int version = xdb::ipv6;
int policy = xdb::policy_content;
std::string ip = "2001:200:124::";
xdb::search_t s(xdb_name, version, policy);
std::cout << s.search(ip) << std::endl;
return 0;
}
// $ g++ src/*.cc 1.cc --- 编译
// $ ./a.out ------------- 测试
// Japan|Tokyo|Asagaya-minami|WIDE Project|JP
```
### 2.2 测试 xdb 头部
```
$ ./bin/header
测试 IPv4
版本号: 3
缓存策略: 1
文件生成时间: 2025-09-06 02:24:16
索引起始地址: 955933
索引结束地址: 11042415
IP版本: 4
指针字节数: 4
测试 IPv6
版本号: 3
缓存策略: 1
文件生成时间: 2025-10-17 04:41:04
索引起始地址: 3094259
索引结束地址: 36258303
IP版本: 6
指针字节数: 4
```
### 2.3 测试查找
```
$ ./bin/search
测试 IPv4 不缓存: 成功
测试 IPv4 部分缓存: 成功
测试 IPv4 全部缓存: 成功
测试 IPv6 不缓存: 成功
测试 IPv6 部分缓存: 成功
测试 IPv6 全部缓存: 成功
```
## 3. 测速以及检验正确性
```
./bin/bench
测试 IPv4, 不缓存, total: 3910284, took: 27.60s, cost: 6.59μs/op, io count: 28227147
测试 IPv4, 部分缓存, total: 3910284, took: 21.85s, cost: 5.15μs/op, io count: 24316863
测试 IPv4, 全部缓存, total: 3910284, took: 2.26s, cost: 0.25μs/op, io count: 0
测试 IPv6, 不缓存, total: 4792520, took: 100.40s, cost: 20.22μs/op, io count: 80758866
测试 IPv6, 部分缓存, total: 4792520, took: 93.06s, cost: 18.71μs/op, io count: 75966346
测试 IPv6, 全部缓存, total: 4792520, took: 6.24s, cost: 0.81μs/op, io count: 0
```
## 4. 生成 xdb 文件
### 4.1 生成 xdb 文件
```
$ ./bin/make
生成 ipv4 的 xdb 文件, took: 0.57s
生成 ipv6 的 xdb 文件, took: 1.24s
```
## 5. 原始数据编辑
### 5.1. 使用说明
* 新的IP归属地文件可以包含空行
* 新的IP归属地文件顺序可以乱序, 程序会自动排序
* 新的IP归属地文件顺序可以重叠, 只要无二义性, 程序会自动合并
* 最终的结果会将相邻的且归属地相同的行自动合并
* 以下测试, 原文件使用仓库自带的数据文件, 新文件使用当前目录下的 1.txt

74
binding/cpp/src/base.cc Normal file
View File

@ -0,0 +1,74 @@
#include "base.h"
namespace xdb {
int ip_version; // ip 版本
int ip_size; // ip 占的字节数
int content_size;
void init_xdb(int version) {
ip_version = version;
ip_size = version == ipv4 ? 4 : 16;
content_size = ip_size * 2 + 2 + 4;
}
void log_exit(const string &msg) {
std::cout << msg << std::endl;
exit(-1);
}
void read_bin(int index, char *buf, size_t len, FILE *db) {
fseek(db, index, SEEK_SET);
if (fread(buf, 1, len, db) != len)
log_exit(__func__);
}
unsigned to_uint(const char *buf) {
return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00) |
((buf[2] << 16) & 0x00FF0000) | ((buf[3] << 24) & 0xFF000000);
}
unsigned to_ushort(const char *buf) {
return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00);
}
unsigned to_int(const char *buf, int n) {
return n == 2 ? to_ushort(buf) : to_uint(buf);
}
void write_uint(unsigned data, char buf[]) {
buf[0] = (data >> 0) & 0xFF;
buf[1] = (data >> 8) & 0xFF;
buf[2] = (data >> 16) & 0xFF;
buf[3] = (data >> 24) & 0xFF;
}
void write_uint(unsigned data, FILE *dst) {
char buf[4];
write_uint(data, buf);
fwrite(buf, 1, sizeof(buf), dst);
}
void write_ushort(unsigned data, char buf[]) {
buf[0] = (data >> 0) & 0xFF;
buf[1] = (data >> 8) & 0xFF;
}
void write_ushort(unsigned data, FILE *dst) {
char buf[2];
write_ushort(data, buf);
fwrite(buf, 1, sizeof(buf), dst);
}
void write_string(const char *buf, unsigned len, FILE *dst) {
fwrite(buf, 1, len, dst);
}
unsigned long long get_time() {
struct timeval tv1;
gettimeofday(&tv1, NULL);
return (unsigned long long)tv1.tv_sec * 1000 * 1000 + tv1.tv_usec;
}
} // namespace xdb

58
binding/cpp/src/base.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef BASE_H
#define BASE_H
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
namespace xdb {
using std::string;
constexpr int ipv4 = 4;
constexpr int ipv6 = 6;
constexpr int policy_file = 0;
constexpr int policy_vector = 1;
constexpr int policy_content = 2;
constexpr int length_header = 256;
constexpr int length_vector = 256 * 256 * 8;
extern int ip_version; // ip 版本
extern int ip_size; // ip 占的字节数
extern int content_size;
void init_xdb(int version);
void log_exit(const string &msg);
void read_bin(int index, char *buf, size_t len, FILE *db);
unsigned to_uint(const char *buf);
unsigned to_ushort(const char *buf);
unsigned to_int(const char *buf, int n);
void write_uint(unsigned data, char buf[]);
void write_uint(unsigned data, FILE *dst);
void write_ushort(unsigned data, char buf[]);
void write_ushort(unsigned data, FILE *dst);
void write_string(const char *buf, unsigned len, FILE *dst);
unsigned long long get_time();
} // namespace xdb
#endif

65
binding/cpp/src/bench.cc Normal file
View File

@ -0,0 +1,65 @@
#include "bench.h"
namespace xdb {
bench_t::bench_t(const std::string &file_name, int version, int policy)
: search(file_name, version, policy) {
}
void bench_t::test_one(const ip_t &ip, const string region) {
if (search.search(ip.to_string()) != region)
xdb::log_exit("failed: " + ip.to_string() + " " + region);
sum_io_count += search.get_io_count();
sum_cost_time += search.get_cost_time();
sum_count++;
}
void bench_t::test_line(char *buf) {
size_t buf_len = strlen(buf);
if (buf_len == 0)
return;
buf[buf_len - 1] = '\0'; // 去掉换行符
node_t node(buf);
// 只测五个
for (int i = 0; i < 5 && node.ip1 < node.ip2; ++i) {
test_one(node.ip1, node.region);
node.ip1 = node.ip1 + 1;
}
test_one(node.ip2, node.region);
}
void bench_t::test_file(const std::string &file_name) {
FILE *f = fopen(file_name.data(), "r");
if (f == NULL)
xdb::log_exit("can't open " + file_name);
char buf[1024];
while (fgets(buf, sizeof(buf), f) != NULL)
test_line(buf);
}
void bench_t::test(const string &file_name) {
sum_io_count = 0;
sum_cost_time = 0;
sum_count = 0;
unsigned long long tv1 = xdb::get_time();
test_file(file_name);
unsigned long long tv2 = xdb::get_time();
double took = (tv2 - tv1) * 1.0 / 1000 / 1000;
double cost = sum_cost_time * 1.0 / sum_count;
printf(
"total: %llu, took: %8.2fs, cost: %6.2fμs/op, io "
"count: "
"%llu\n",
sum_count,
took,
cost,
sum_io_count);
}
} // namespace xdb

27
binding/cpp/src/bench.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef BENCH_H
#define BENCH_H
#include "search.h"
namespace xdb {
class bench_t {
public:
bench_t(const string &file_name, int version, int policy);
void test(const string &file_name);
private:
void test_one(const ip_t &ip, const string region);
void test_line(char *buf);
void test_file(const std::string &file_name);
search_t search;
unsigned long long sum_io_count;
unsigned long long sum_cost_time;
unsigned long long sum_count;
};
} // namespace xdb
#endif

170
binding/cpp/src/edit.cc Normal file
View File

@ -0,0 +1,170 @@
#include "edit.h"
namespace xdb {
void handle_ip_txt(const string& name, std::list<node_t>& regions) {
FILE* f = fopen(name.data(), "r");
if (f == NULL)
log_exit("can't open " + name);
char buf[1024];
while (fgets(buf, sizeof(buf), f) != NULL) {
unsigned int buf_len = strlen(buf);
// 去掉多余的空
while (buf_len > 0 && isspace(buf[buf_len - 1]))
--buf_len;
if (buf_len == 0)
continue;
buf[buf_len] = '\0';
regions.push_back(node_t(buf));
}
fclose(f);
}
void edit_t::handle_new_file(const std::string& file_name) {
handle_ip_txt(file_name, new_regions); // 输入
new_regions.sort(); // 排序
// 检验及其去重
auto it = new_regions.begin();
for (;;) {
if (it == new_regions.end())
break;
auto next = it;
++next;
if (next == new_regions.end())
break;
if (it->ip1 > it->ip2)
it = new_regions.erase(it); // 非法, 直接跳过
else if (it->ip1 == next->ip1 || next->ip1 <= it->ip2) {
// 数据重叠
if (it->region != next->region)
log_exit("数据有二义性: " + it->to_string() + ", " +
next->to_string());
it->ip2 = std::max(it->ip2, next->ip2);
new_regions.erase(next);
} else if (it->ip2 + 1 == next->ip1 && it->region == next->region) {
// 数据连接
it->ip2 = next->ip2;
new_regions.erase(next);
} else {
++it;
}
}
}
void edit_t::handle_old_file(const std::string& file_name) {
handle_ip_txt(file_name, old_regions);
}
void edit_t::merge() {
auto it1 = old_regions.begin();
auto it2 = new_regions.begin();
for (;;) {
if (it2 == new_regions.end())
break;
if (it2->ip1 > it2->ip2) {
++it2;
continue;
}
// it1->ip1 it1->ip2 it2->ip1 it2->ip2
while (it1->ip2 < it2->ip1)
++it1;
if (it1->ip2 <= it2->ip2) {
// it1->ip1 it2->ip1 it1->ip2 it2->ip2
node_t node;
node.ip1 = it2->ip1;
node.ip2 = it1->ip2;
node.region = it2->region;
it1->ip2 = node.ip1 - 1;
it2->ip1 = node.ip2 + 1;
++it1;
it1 = old_regions.insert(it1, node);
++it1;
} else {
// it1->ip1 it2->ip1 it2->ip2 it1->ip2
node_t node;
node.ip1 = it2->ip2 + 1;
node.ip2 = it1->ip2;
node.region = it1->region;
it1->ip2 = it2->ip1 - 1;
++it1;
it1 = old_regions.insert(it1, *it2);
++it1;
it1 = old_regions.insert(it1, node);
++it2;
}
}
}
void edit_t::write_old_file(const std::string& file_name) {
FILE* f = fopen(file_name.data(), "w");
if (f == NULL)
log_exit("can't open " + file_name);
auto it = old_regions.begin();
// 删除非法的数据
for (;;) {
if (it == old_regions.end())
break;
if (it->ip1 > it->ip2)
it = old_regions.erase(it);
else
++it;
}
// 合并数据域相同的相邻数据
it = old_regions.begin();
for (;;) {
if (it == old_regions.end())
break;
auto next = it;
++next;
if (next == old_regions.end())
break;
if (it->region == next->region) {
it->ip2 = next->ip2;
old_regions.erase(next);
} else {
++it;
}
}
for (auto& d : old_regions) {
string res =
d.ip1.to_string() + "|" + d.ip2.to_string() + "|" + d.region + "\n";
fputs(res.data(), f);
}
fclose(f);
}
edit_t::edit_t(const string& name_old, const string& name_new, int version) {
unsigned long long tv1 = get_time();
init_xdb(version);
handle_new_file(name_new);
handle_old_file(name_old);
merge();
write_old_file(name_old);
unsigned long long tv2 = get_time();
double took = (tv2 - tv1) * 1.0 / 1000 / 1000;
printf("took: %.2fs\n", took);
}
} // namespace xdb

24
binding/cpp/src/edit.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef EDIT_H
#define EDIT_H
#include "ip.h"
namespace xdb {
class edit_t {
public:
edit_t(const string& old_name, const string& new_name, int version);
private:
void handle_new_file(const string& file_name);
void handle_old_file(const string& file_name);
void merge();
void write_old_file(const string& file_name);
std::list<node_t> old_regions;
std::list<node_t> new_regions;
};
} // namespace xdb
#endif

41
binding/cpp/src/header.cc Normal file
View File

@ -0,0 +1,41 @@
#include "header.h"
namespace xdb {
header_t::header_t(FILE* db) {
read_bin(0, header, sizeof(header), db);
}
header_t::~header_t() {
}
int header_t::version() {
return to_int(header, 2); // 版本号(2)
}
int header_t::index_policy() {
return to_int(header + 2, 2); // 缓存策略(2)
}
int header_t::create_at() {
return to_int(header + 4, 4); // 文件生成时间(4)
}
int header_t::index_start() {
return to_int(header + 8, 4); // 索引起始地址(4)
}
int header_t::index_end() {
return to_int(header + 12, 4); // 索引结束地址(4)
}
int header_t::ip_version() {
return to_int(header + 16, 2); // IP 版本(2)
}
int header_t::ptr() {
return to_int(header + 18, 2); // 指针字节数(2)
}
} // namespace xdb

27
binding/cpp/src/header.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef HEADER_H
#define HEADER_H
#include "base.h"
namespace xdb {
class header_t {
public:
header_t(FILE* db);
virtual ~header_t();
int version(); // 版本号
int index_policy(); // 缓存策略
int create_at(); // 文件生成时间
int index_start(); // 索引起始地址
int index_end(); // 索引结束地址
int ip_version(); // IP 版本
int ptr(); // 指针字节数
protected:
char header[length_header];
};
} // namespace xdb
#endif

158
binding/cpp/src/ip.cc Normal file
View File

@ -0,0 +1,158 @@
#include "ip.h"
namespace xdb {
ip_t::ip_t() {
memset(p, '\0', sizeof(p));
}
ip_t::ip_t(const ip_t& rhs, int val) {
memcpy(p, rhs.p, ip_size);
if (val == 0 || val == 255)
for (int i = 2; i < ip_size; ++i)
p[i] = val;
}
ip_t::ip_t(const char* p) {
from_xdb(p);
}
bool ip_t::from_str(const string& str) {
int af_inet = ip_version == ipv4 ? AF_INET : AF_INET6;
return inet_pton(af_inet, str.data(), p) == 1;
}
void ip_t::from_xdb(const char str[16]) {
for (int i = 0; i < ip_size; ++i)
if (ip_version == ipv6)
p[i] = str[i];
else
p[i] = str[ip_size - 1 - i];
}
ip_t& ip_t::operator=(const ip_t& rhs) {
memcpy(p, rhs.p, ip_size);
return *this;
}
int ip_t::compare(const ip_t& rhs) const {
for (int i = 0; i < ip_size; ++i) {
if ((unsigned char)p[i] > (unsigned char)rhs.p[i])
return 1;
if ((unsigned char)p[i] < (unsigned char)rhs.p[i])
return -1;
}
return 0;
}
bool ip_t::operator<(const ip_t& rhs) const {
return compare(rhs) < 0;
}
bool ip_t::operator<=(const ip_t& rhs) const {
return compare(rhs) <= 0;
}
bool ip_t::operator>(const ip_t& rhs) const {
return compare(rhs) > 0;
}
bool ip_t::operator>=(const ip_t& rhs) const {
return compare(rhs) >= 0;
}
bool ip_t::operator==(const ip_t& rhs) const {
return compare(rhs) == 0;
}
bool ip_t::operator!=(const ip_t& rhs) const {
return compare(rhs) != 0;
}
string ip_t::to_string() const {
char buf[INET6_ADDRSTRLEN + 1];
int af_inet = ip_version == ipv4 ? AF_INET : AF_INET6;
inet_ntop(af_inet, p, buf, sizeof(buf));
return string(buf);
}
string ip_t::to_bit() const {
string str;
for (int i = 0; i < ip_size; ++i)
if (ip_version == ipv6)
str.push_back(p[i]);
else
str.push_back(p[ip_size - 1 - i]);
return str;
}
ip_t operator+(const ip_t& lhs, int v) {
ip_t ip;
int i = ip_size;
while (--i >= 0) {
v += lhs.p[i];
ip.p[i] = v % 256;
v /= 256;
}
return ip;
}
ip_t operator-(const ip_t& lhs, int v) {
ip_t ip;
int i = ip_size;
v = -v;
while (--i >= 0) {
v += lhs.p[i];
if (v == -1)
ip.p[i] = 255;
else {
ip.p[i] = v;
v = 0;
}
}
return ip;
}
// node_t
node_t::node_t() {
}
node_t::node_t(char* buf) {
char* pos1 = strchr(buf, '|');
if (pos1 == NULL)
log_exit("invalid data: " + std::string(buf));
char* pos2 = strchr(pos1 + 1, '|');
if (pos2 == NULL)
log_exit("invalid data: " + std::string(buf));
*pos1 = '\0';
*pos2 = '\0';
region = pos2 + 1;
if (!ip1.from_str(buf) || !ip2.from_str(pos1 + 1) || ip2 < ip1 ||
region.empty()) {
*pos1 = *pos2 = '|';
log_exit(string("invalid data: ") + buf);
}
}
bool node_t::operator<(const node_t& rhs) const {
if (ip1 < rhs.ip1)
return true;
return ip2 < rhs.ip2;
}
string node_t::to_string() const {
return ip1.to_string() + "|" + ip2.to_string() + "|" + region;
}
string node_t::to_bit() const {
return ip1.to_bit() + ip2.to_bit();
}
} // namespace xdb

52
binding/cpp/src/ip.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef IP_H
#define IP_H
#include "base.h"
namespace xdb {
struct ip_t {
unsigned char p[16];
ip_t();
ip_t(const char* p);
// val 为 0 或 255 时, 将 ip 的后几位置为 val
ip_t(const ip_t& rhs, int val = -1);
bool from_str(const string& str);
void from_xdb(const char str[16]);
ip_t& operator=(const ip_t& rhs);
int compare(const ip_t& rhs) const;
bool operator<(const ip_t& rhs) const;
bool operator<=(const ip_t& rhs) const;
bool operator>(const ip_t& rhs) const;
bool operator>=(const ip_t& rhs) const;
bool operator==(const ip_t& rhs) const;
bool operator!=(const ip_t& rhs) const;
string to_string() const;
string to_bit() const;
};
ip_t operator+(const ip_t& lhs, int v);
ip_t operator-(const ip_t& lhs, int v);
struct node_t {
ip_t ip1;
ip_t ip2;
string region;
node_t();
node_t(char* buf);
bool operator<(const node_t& rhs) const;
string to_string() const;
string to_bit() const;
};
} // namespace xdb
#endif

167
binding/cpp/src/make.cc Normal file
View File

@ -0,0 +1,167 @@
#include "make.h"
namespace xdb {
void make_t::vector_index_push_back(int row, int col, const node_t &node) {
vector_index[row][col].push_back(
std::make_pair<string, string>(node.to_bit(), string(node.region)));
}
void make_t::vector_index_push_back(node_t &node) {
ip_t ip1 = node.ip1;
ip_t ip2 = node.ip2;
unsigned ip1_1 = ip1.p[0];
unsigned ip1_2 = ip1.p[1];
unsigned ip2_1 = ip2.p[0];
unsigned ip2_2 = ip2.p[1];
if (ip1_1 == ip2_1 && ip1_2 == ip2_2) {
vector_index_push_back(ip1_1, ip1_2, node);
return;
}
node.ip1 = ip1;
node.ip2 = ip_t(ip1, 255);
vector_index_push_back(ip1_1, ip1_2, node);
node.ip1 = ip_t(ip2, 0);
node.ip2 = ip2;
vector_index_push_back(ip2_1, ip2_2, node);
for (;;) {
++ip1_2;
if (ip1_2 == 256) {
++ip1_1;
ip1_2 = 0;
}
if (ip1_1 == ip2_1 && ip1_2 == ip2_2)
break;
ip1.p[0] = ip1_1;
ip1.p[1] = ip1_2;
node.ip1 = ip_t(ip1, 0);
node.ip2 = ip_t(ip1, 255);
vector_index_push_back(ip1_1, ip1_2, node);
}
}
void make_t::handle_input_help(char *buf) {
// 去掉多余的空
unsigned int buf_len = strlen(buf);
while (buf_len > 0 && isspace(buf[buf_len - 1]))
--buf_len;
if (buf_len == 0)
return;
buf[buf_len] = '\0';
node_t node(buf);
if (node.ip1 < next_ip) {
log_exit("ip 未排序: " + node.ip1.to_string() + ", " +
next_ip.to_string());
}
next_ip = node.ip2 + 1;
if (region.find(node.region) == region.end()) {
region[node.region] = region_index;
region_index += node.region.size();
}
vector_index_push_back(node);
}
void make_t::handle_input(const std::string &file_name) {
FILE *src = fopen(file_name.data(), "r");
if (src == NULL)
log_exit("can't open " + file_name);
char buf[1024];
while (fgets(buf, sizeof(buf), src) != NULL)
handle_input_help(buf);
fclose(src);
}
void make_t::handle_header() {
char buf[length_header];
memset(buf, 0, length_header);
write_ushort(3, buf); // 版本号
write_ushort(1, buf + 2); // 缓存策略
write_uint(time(NULL), buf + 4); // 时间
// 索引
unsigned int content_left = length_header + length_vector;
for (auto &d : region)
content_left += d.first.size();
unsigned int content_right = content_left;
for (int i = 0; i < 256; ++i)
for (int j = 0; j < 256; ++j)
content_right += vector_index[i][j].size() * content_size;
content_right -= content_size;
write_uint(content_left, buf + 8);
write_uint(content_right, buf + 12);
write_ushort(ip_version, buf + 16); // IP
write_ushort(4, buf + 18); // 指针数
write_string(buf, length_header, db);
}
void make_t::handle_vector_index() {
unsigned index = length_header + length_vector;
for (auto &d : region)
index += d.first.size();
for (unsigned i = 0; i < 256; ++i)
for (unsigned j = 0; j < 256; ++j)
if (vector_index[i][j].size() == 0) {
write_uint(0, db);
write_uint(0, db);
} else {
write_uint(index, db);
index += content_size * vector_index[i][j].size();
write_uint(index, db);
}
}
void make_t::handle_region() {
for (auto &d : region) {
fseek(db, d.second, SEEK_SET);
write_string(d.first.data(), d.first.size(), db);
}
}
void make_t::handle_content() {
fseek(db, 0, SEEK_END);
for (unsigned i = 0; i < 256; ++i)
for (unsigned j = 0; j < 256; ++j)
for (auto d : vector_index[i][j]) {
write_string(d.first.data(), d.first.size(), db);
write_ushort(d.second.size(), db);
write_uint(region[d.second], db);
}
}
make_t::make_t(const string &src, const string &dst, int version)
: region_index(length_vector + length_header) {
unsigned long long tv1 = get_time();
init_xdb(version);
handle_input(src);
db = fopen(dst.data(), "w");
if (db == NULL)
log_exit("can't open " + dst);
handle_header();
handle_vector_index();
handle_region();
handle_content();
fclose(db);
unsigned long long tv2 = get_time();
printf("took: %.2fs\n", (tv2 - tv1) * 1.0 / 1000 / 1000);
}
} // namespace xdb

35
binding/cpp/src/make.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef MAKE_H
#define MAKE_H
#include "ip.h"
namespace xdb {
class make_t {
public:
make_t(const string &src, const string &dst, int version);
private:
void vector_index_push_back(int row, int col, const node_t &node);
void vector_index_push_back(node_t &node);
void handle_input_help(char buf[]);
void handle_input(const std::string &file_name);
void handle_header();
void handle_vector_index();
void handle_region();
void handle_content();
FILE *db = NULL;
std::vector<std::pair<string, string>> vector_index[256][256];
std::unordered_map<string, unsigned> region;
unsigned region_index;
ip_t next_ip;
};
} // namespace xdb
#endif

137
binding/cpp/src/search.cc Normal file
View File

@ -0,0 +1,137 @@
#include "search.h"
namespace xdb {
search_t::search_t(const string &file, int version, int p)
: db(fopen(file.data(), "r")), header(db), policy(p) {
init_xdb(version);
if (db == NULL)
log_exit("can't open " + file);
if (header.ip_version() != version)
log_exit("ip 版本不匹配");
if (policy != policy_file) {
read_bin(length_header, vector, length_vector, db);
if (policy == policy_content) {
fseek(db, 0, SEEK_END);
int size = ftell(db) - length_vector - length_header;
content = (char *)malloc(size);
read_bin(length_vector + length_header, content, size, db);
}
}
}
search_t::~search_t() {
fclose(db);
if (policy == policy_content)
free(content);
}
int search_t::get_io_count() {
return io_count;
}
int search_t::get_cost_time() {
return cost_time;
}
char const *search_t::get_content_index_help(int index) {
if (policy != policy_file)
return vector + index;
++io_count;
static char v[8];
read_bin(length_header + index, v, sizeof(v), db);
return v;
}
void search_t::get_content_index(const ip_t &ip, int &left, int &right) {
int index = ((unsigned char)ip.p[0] * 256 + (unsigned char)ip.p[1]) * 8;
const char *p = get_content_index_help(index);
left = to_uint(p);
right = to_uint(p + 4);
}
char const *search_t::get_content_help(int index) {
if (policy == policy_content)
return content + index - length_header - length_vector;
++io_count;
static char v[16 + 16 + 2 + 4];
read_bin(index, v, content_size, db);
return v;
}
string search_t::get_region(int index, int len) {
if (policy == policy_content)
return string(content + index - length_header - length_vector, len);
++io_count;
char *p = (char *)malloc(sizeof(char) * len);
read_bin(index, p, len, db);
string res(p, len);
free(p);
return res;
}
void search_t::get_content(int index,
ip_t &ip_left,
ip_t &ip_right,
int &region_len,
int &region_index) {
const char *p = get_content_help(index);
ip_left.from_xdb(p);
ip_right.from_xdb(p + ip_size);
region_len = to_ushort(p + ip_size * 2);
region_index = to_uint(p + ip_size * 2 + 2);
}
string search_t::search(const ip_t &ip) {
io_count = 0;
int content_left, content_right;
get_content_index(ip, content_left, content_right);
if (content_left == 0 || content_right == 0)
return "";
ip_t ip_left, ip_right;
int region_len;
int region_index;
int left = 0;
int right = (content_right - content_left) / content_size;
for (;;) {
int mid = left + (right - left) / 2;
int mid_index = content_left + mid * content_size;
get_content(mid_index, ip_left, ip_right, region_len, region_index);
// ip ip_left ip_right
if (ip < ip_left)
right = mid - 1;
// ip_left ip_right ip
else if (ip_right < ip)
left = mid + 1;
else
return get_region(region_index, region_len);
}
}
string search_t::search(const string &str) {
unsigned long long t1 = get_time();
ip_t ip;
if (ip.from_str(str) == false)
return "invalid ipv" + std::to_string(ip_version) + ": " + str;
string region = search(ip);
unsigned long long t2 = get_time();
cost_time = t2 - t1;
return region;
}
} // namespace xdb

49
binding/cpp/src/search.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef SEARCH_H
#define SEARCH_H
#include "header.h"
#include "ip.h"
namespace xdb {
class search_t {
protected:
FILE *db;
header_t header;
int policy;
int io_count;
int cost_time;
char vector[length_vector];
char *content;
public:
search_t(const string &file_name, int version, int policy);
virtual ~search_t();
int get_io_count();
int get_cost_time();
string search(const string &str);
protected:
string search(const ip_t &ip);
void get_content_index(const ip_t &ip1, int &left, int &right);
void get_content(int index,
ip_t &left,
ip_t &right,
int &region_len,
int &region_index);
char const *get_content_index_help(int index);
char const *get_content_help(int index);
string get_region(int index, int len);
};
} // namespace xdb
#endif

31
binding/cpp/test/bench.cc Normal file
View File

@ -0,0 +1,31 @@
#include "../src/bench.h"
std::map<int, std::string> prompt;
void test_ipv4(int policy) {
std::cout << "测试 IPv4, " << prompt[policy];
xdb::bench_t("../../data/ip2region_v4.xdb", xdb::ipv4, policy)
.test("../../data/ipv4_source.txt");
}
void test_ipv6(int policy) {
std::cout << "测试 IPv6, " << prompt[policy];
xdb::bench_t("../../data/ip2region_v6.xdb", xdb::ipv6, policy)
.test("../../data/ipv6_source.txt");
}
int main() {
prompt[xdb::policy_file] = " 不缓存, ";
prompt[xdb::policy_vector] = "部分缓存, ";
prompt[xdb::policy_content] = "全部缓存, ";
test_ipv4(xdb::policy_file);
test_ipv4(xdb::policy_vector);
test_ipv4(xdb::policy_content);
test_ipv6(xdb::policy_file);
test_ipv6(xdb::policy_vector);
test_ipv6(xdb::policy_content);
return 0;
}

View File

@ -0,0 +1,9 @@
#include "../src/edit.h"
int main() {
std::string file_name_old = "../../data/ipv4_source.txt";
std::string file_name_new = "./1.txt";
xdb::edit_t xdb(file_name_old, file_name_new, xdb::ipv4);
return 0;
}

View File

@ -0,0 +1,9 @@
#include "../src/edit.h"
int main() {
std::string file_name_old = "../../data/ipv6_source.txt";
std::string file_name_new = "./1.txt";
xdb::edit_t xdb(file_name_old, file_name_new, xdb::ipv6);
return 0;
}

View File

@ -0,0 +1,30 @@
#include "../src/header.h"
void test(const std::string& prompt, const std::string& file_name) {
std::cout << prompt << std::endl;
xdb::header_t head(fopen(file_name.data(), "r"));
std::cout << "版本号: " << head.version() << std::endl;
std::cout << "缓存策略: " << head.index_policy() << std::endl;
time_t rawtime = head.create_at();
struct tm* info = localtime(&rawtime);
char buf[80];
strftime(buf, 80, "%Y-%m-%d %H:%M:%S", info);
std::cout << "文件生成时间: " << buf << std::endl;
std::cout << "索引起始地址: " << head.index_start() << std::endl;
std::cout << "索引结束地址: " << head.index_end() << std::endl;
std::cout << "IP版本: " << head.ip_version() << std::endl;
std::cout << "指针字节数: " << head.ptr() << std::endl;
std::cout << std::endl;
}
int main() {
test("测试 IPv4", "../../data/ip2region_v4.xdb");
test("测试 IPv6", "../../data/ip2region_v6.xdb");
return 0;
}

26
binding/cpp/test/make.cc Normal file
View File

@ -0,0 +1,26 @@
#include "../src/make.h"
void test(const std::string& prompt,
const std::string& filename_xdb,
const std::string& filename_src,
int version
) {
std::cout << prompt;
xdb::make_t(filename_xdb, filename_src, version);
}
int main() {
test("生成 ipv4 的 xdb 文件, ",
"../../data/ipv4_source.txt",
"./ip2region_v4.xdb",
xdb::ipv4);
test("生成 ipv6 的 xdb 文件, ",
"../../data/ipv6_source.txt",
"./ip2region_v6.xdb",
xdb::ipv6);
return 0;
}

View File

@ -0,0 +1,46 @@
#include "../src/search.h"
std::map<int, std::string> prompt;
void test(xdb::search_t& s, const std::string& ip, const std::string& region) {
if (s.search(ip) != region)
xdb::log_exit("测试失败, ip " + ip + ", region " + region);
}
void test_ipv4(int policy) {
std::cout << "测试 IPv4 " << prompt[policy];
xdb::search_t s("../../data/ip2region_v4.xdb", xdb::ipv4, policy);
test(s, "0.0.0.0", "Reserved|Reserved|Reserved|0|0");
test(s, "1.2.3.4", "Australia|Queensland|Brisbane|0|AU");
std::cout << " 成功" << std::endl;
}
void test_ipv6(int policy) {
std::cout << "测试 IPv6 " << prompt[policy];
xdb::search_t s("../../data/ip2region_v6.xdb", xdb::ipv6, policy);
test(s, "::1", "");
test(s, "2001:200:124::", "Japan|Tokyo|Asagaya-minami|WIDE Project|JP");
test(s, "2001:200:124::", "Japan|Tokyo|Asagaya-minami|WIDE Project|JP");
test(s, "240e:3b7:3273:51d0:cd38:8ae1:e3c0:b708", "中国|广东省|深圳市|电信|CN");
std::cout << " 成功" << std::endl;
}
int main() {
prompt[xdb::policy_file] = " 不缓存:";
prompt[xdb::policy_vector] = "部分缓存:";
prompt[xdb::policy_content] = "全部缓存:";
test_ipv4(xdb::policy_file);
test_ipv4(xdb::policy_vector);
test_ipv4(xdb::policy_content);
test_ipv6(xdb::policy_file);
test_ipv6(xdb::policy_vector);
test_ipv6(xdb::policy_content);
return 0;
}

View File

@ -0,0 +1,25 @@
root = true
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
[*]
charset = utf-8
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true
spelling_exclusion_path = .\exclusion.dic
[*.csproj]
charset = utf-8
insert_final_newline = true
[*.{xml,config,csproj,nuspec,props,resx,targets,yml,tasks,json}]
indent_size = 2
[*.sh]
end_of_line = lf
[*.cs]
csharp_style_namespace_declarations = file_scoped:silent
[*.cs]
file_header_template = Copyright 2025 The Ip2Region Authors. All rights reserved.\nUse of this source code is governed by a Apache2.0-style\nlicense that can be found in the LICENSE file.\n@Author Alan <lzh.shap@gmail.com>\n@Date 2023/07/25\nUpdated by Argo Zhang <argo@live.ca> at 2025/11/21

337
binding/csharp/.gitignore vendored Normal file
View File

@ -0,0 +1,337 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/

View File

@ -0,0 +1,24 @@
# Changelog
All notable changes to this project will be documented in this file.
## [3.0.0] - 2025-11-22
- 支持 .NET 10.0
- 增加 IPv6 支持
- 修复若干 bug
## [2.0.1] - 2023-07-30
### Added
- Support netstandard2.0
## [2.0.0] - 2023-07-26
### Removed
- Remove nuget include xdb file
- Searcher cache policy default parameters
- Searcher xdb file path default parameters
### Added
- Dependent file query policies CachePolicy.VectorIndex, CachePolicy.File support thread-safe concurrent queries
- Dramatically optimizes overall performance

View File

@ -0,0 +1,62 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using BenchmarkDotNet.Attributes;
using IP2Region.Net.XDB;
namespace IP2Region.Net.BenchMark;
[MemoryDiagnoser]
public class Benchmarks
{
private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb");
private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb");
private static readonly Searcher _contentV4Searcher = new(CachePolicy.Content, XdbPathV4);
private static readonly Searcher _vectorV4Searcher = new(CachePolicy.VectorIndex, XdbPathV4);
private static readonly Searcher _fileV4Searcher = new(CachePolicy.File, XdbPathV4);
private static readonly Searcher _contentV6Searcher = new(CachePolicy.Content, XdbPathV6);
private static readonly Searcher _vectorV6Searcher = new(CachePolicy.VectorIndex, XdbPathV6);
private static readonly Searcher _fileV6Searcher = new(CachePolicy.File, XdbPathV6);
private readonly string _testIPv4Address = "114.114.114.114";
private readonly string _testIPv6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
public Benchmarks()
{
_contentV4Searcher.Search(_testIPv4Address);
_vectorV4Searcher.Search(_testIPv4Address);
_fileV4Searcher.Search(_testIPv4Address);
_contentV6Searcher.Search(_testIPv6Address);
_vectorV6Searcher.Search(_testIPv6Address);
_fileV6Searcher.Search(_testIPv6Address);
}
[Benchmark]
[BenchmarkCategory("IPv4")]
public void ContentIPv4() => _contentV4Searcher.Search(_testIPv4Address);
[Benchmark]
[BenchmarkCategory("IPv4")]
public void VectorIPv4() => _vectorV4Searcher.Search(_testIPv4Address);
[Benchmark]
[BenchmarkCategory("IPv4")]
public void FileIPv4() => _fileV4Searcher.Search(_testIPv4Address);
[Benchmark]
[BenchmarkCategory("IPv6")]
public void ContentIPv6() => _contentV6Searcher.Search(_testIPv6Address);
[Benchmark]
[BenchmarkCategory("IPv6")]
public void VectorIPv6() => _vectorV6Searcher.Search(_testIPv6Address);
[Benchmark]
[BenchmarkCategory("IPv6")]
public void FileIPv6() => _fileV6Searcher.Search(_testIPv6Address);
}

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.6" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.15.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IP2Region.Net\IP2Region.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="..\..\..\data\ip2region_v4.xdb">
<Link>IP2Region/ip2region_v4.xdb</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\data\ip2region_v6.xdb">
<Link>IP2Region/ip2region_v6.xdb</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -0,0 +1,11 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using BenchmarkDotNet.Running;
using IP2Region.Net.BenchMark;
BenchmarkRunner.Run<Benchmarks>();

View File

@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.*" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.*">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IP2Region.Net\IP2Region.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="..\..\..\data\ipv4_source.txt">
<Link>TestData/ipv4_source.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\data\ip2region_v4.xdb">
<Link>TestData/ip2region_v4.xdb</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\data\ipv6_source.txt">
<Link>TestData/ipv6_source.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\data\ip2region_v6.xdb">
<Link>TestData/ip2region_v6.xdb</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -0,0 +1,163 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
using Xunit;
namespace IP2Region.Net.Test;
public class SearcherTest
{
private readonly string _xdbPathV4 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb");
private readonly string _xdbPathV6 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v6.xdb");
[Theory]
[InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
[InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
[InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
[InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
[InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
[InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
[InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
public void TestSearchCacheContent(string ip, string expected, string version)
{
var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
var contentSearcher = new Searcher(CachePolicy.Content, _xdbPath);
var region = contentSearcher.Search(ip);
Assert.Equal(expected, region);
}
[Theory]
[InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
[InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
[InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
[InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
[InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
[InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
[InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
public void TestSearchCacheVector(string ip, string expected, string version)
{
var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
var vectorSearcher = new Searcher(CachePolicy.VectorIndex, _xdbPath);
var region = vectorSearcher.Search(ip);
Assert.Equal(expected, region);
}
[Theory]
[InlineData("58.251.0.0", "中国|广东省|深圳市|联通|CN", "v4")]
[InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN", "v4")]
[InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
[InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
[InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
[InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
[InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
[InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
[InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
[InlineData("240e:044d:2d00:0000:0000:0000:0000:0000", "中国|云南|楚雄|电信|CN", "v6")]
public void TestSearchCacheFile(string ip, string expected, string version)
{
var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
var fileSearcher = new Searcher(CachePolicy.File, _xdbPath);
var region = fileSearcher.Search(ip);
Assert.Equal(expected, region);
}
[Fact]
public void IoCount_File_Ok()
{
var searcher = new Searcher(CachePolicy.File, _xdbPathV4);
searcher.Search("58.251.27.201");
Assert.Equal(3, searcher.IoCount);
searcher.Search("58.251.27.201");
Assert.Equal(3, searcher.IoCount);
searcher.Dispose();
}
[Fact]
public void IoCount_Vector_Ok()
{
var searcher = new Searcher(CachePolicy.VectorIndex, _xdbPathV4);
searcher.Search("58.251.27.201");
Assert.Equal(2, searcher.IoCount);
searcher.Search("58.251.27.201");
Assert.Equal(2, searcher.IoCount);
searcher.Dispose();
}
[Fact]
public void IoCount_Content_Ok()
{
var searcher = new Searcher(CachePolicy.Content, _xdbPathV4);
searcher.Search("58.251.27.201");
Assert.Equal(0, searcher.IoCount);
searcher.Search("58.251.27.201");
Assert.Equal(0, searcher.IoCount);
searcher.Dispose();
}
[Theory]
[InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN")]
public void Search_Ip_Ok(string ipStr, string expected)
{
var fileSearcher = new Searcher(CachePolicy.File, _xdbPathV4);
var ipAddress = IPAddress.Parse(ipStr);
var region = fileSearcher.Search(ipAddress);
Assert.Equal(expected, region);
}
[Theory]
[InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN")]
public void AddIP2RegionService_Ok(string ipStr, string expected)
{
var services = new ServiceCollection();
services.AddIP2RegionService(_xdbPathV4, CachePolicy.File);
var provider = services.BuildServiceProvider();
var searcher = provider.GetRequiredService<ISearcher>();
var region = searcher.Search(ipStr);
Assert.Equal(expected, region);
searcher = provider.GetRequiredKeyedService<ISearcher>("IP2Region.Net");
region = searcher.Search(ipStr);
Assert.Equal(expected, region);
}
[Theory]
[InlineData(CachePolicy.Content, "v4")]
[InlineData(CachePolicy.VectorIndex, "v4")]
[InlineData(CachePolicy.File, "v4")]
[InlineData(CachePolicy.Content, "v6")]
[InlineData(CachePolicy.VectorIndex, "v6")]
[InlineData(CachePolicy.File, "v6")]
public void TestBenchSearch(CachePolicy cachePolicy, string version)
{
var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
var searcher = new Searcher(cachePolicy, _xdbPath);
var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt");
foreach (var line in File.ReadLines(srcPath))
{
var ps = line.Trim().Split("|", 3);
var sip = ps[0];
var eip = ps[1];
var s1 = searcher.Search(sip);
var s2 = searcher.Search(eip);
Assert.Equal(s1, ps[2]);
Assert.Equal(s2, ps[2]);
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using Xunit;
namespace IP2Region.Net.Test;
public class UtilTest
{
[Fact]
public void IpAddressToUInt32_Ok()
{
var uintIp = XDB.Util.IpAddressToUInt32("114.114.114.114");
Assert.Equal((uint)1920103026, uintIp);
}
[Fact]
public void GetMidIp_Ok()
{
var uintIp = XDB.Util.GetMidIp(1, 10);
Assert.Equal((uint)5, uintIp);
}
}

View File

@ -0,0 +1,49 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using Xunit;
namespace IP2Region.Net.Test;
public class XdbTest
{
[Fact]
public async Task VersionIPV4_Ok()
{
var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v4.xdb");
var version = await XDB.Util.GetVersionAsync(db);
Assert.Equal(3, version.Ver);
Assert.Equal(1, version.CachePolice);
//Assert.Equal("2025-09-06 02:24:16", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"));
//Assert.Equal((uint)955933, version.StartIndex);
//Assert.Equal((uint)11042415, version.EndIndex);
Assert.Equal(4, version.IPVer);
Assert.Equal(4, version.BytesCount);
}
[Fact]
public async Task VersionIPV6_Ok()
{
var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v6.xdb");
var version = await XDB.Util.GetVersionAsync(db);
Assert.Equal(3, version.Ver);
Assert.Equal(1, version.CachePolice);
//Assert.Equal("2025-10-17 04:41:04", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"));
//Assert.Equal((uint)3094259, version.StartIndex);
//Assert.Equal((uint)36258303, version.EndIndex);
Assert.Equal(6, version.IPVer);
Assert.Equal(4, version.BytesCount);
}
[Fact]
public async Task GetVersionAsync_Error()
{
await Assert.ThrowsAsync<ArgumentNullException>(async () => await XDB.Util.GetVersionAsync(null!));
await Assert.ThrowsAsync<FileNotFoundException>(async () => await XDB.Util.GetVersionAsync(Path.Combine(AppContext.BaseDirectory, "test.xdb")));
}
}

View File

@ -0,0 +1,9 @@
<Solution>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path="README.md" />
</Folder>
<Project Path="IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj" />
<Project Path="IP2Region.Net.Test/IP2Region.Net.Test.csproj" />
<Project Path="IP2Region.Net/IP2Region.Net.csproj" />
</Solution>

View File

@ -0,0 +1,40 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using System.Net;
namespace IP2Region.Net.Abstractions;
/// <summary>
/// IP 转化为地理位置搜索器接口
/// </summary>
public interface ISearcher : IDisposable
{
/// <summary>
/// 搜索方法
/// </summary>
/// <param name="ipStr">IP 地址字符串 如 192.168.0.1</param>
/// <returns></returns>
string? Search(string ipStr);
/// <summary>
/// 搜索方法
/// </summary>
string? Search(IPAddress ipAddress);
/// <summary>
/// 搜索方法 仅限 IPv4 使用
/// </summary>
/// <param name="ipAddress">IPv4 地址字节数组小端读取 uint 数值</param>
[Obsolete("已弃用请改用其他方法Deprecated; please use Search(string) or Search(IPAddress) method.")]
string? Search(uint ipAddress);
/// <summary>
/// 获得 内部 IO 访问次数
/// </summary>
int IoCount { get; }
}

View File

@ -0,0 +1,40 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// IP2Region 服务扩展类
/// </summary>
public static class IP2RegionExtensions
{
/// <summary>
/// 添加 IP2RegionService 服务。
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> 集合</param>
/// <param name="path">IP2Region 数据库文件的路径。</param>
/// <param name="cachePolicy">缓存策略,默认为 <see cref="CachePolicy.Content"/>。</param>
public static IServiceCollection AddIP2RegionService(this IServiceCollection services, string path, CachePolicy cachePolicy = CachePolicy.Content)
{
services.TryAddSingleton<ISearcher>(provider =>
{
return new Searcher(cachePolicy, path);
});
#if NET8_0_OR_GREATER
services.TryAddKeyedSingleton("IP2Region.Net", (provider, _) =>
{
return provider.GetRequiredService<ISearcher>();
});
#endif
return services;
}
}

View File

@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<id>IP2Region.Net</id>
<version>3.0.2</version>
<title>IP2Region.Net</title>
<authors>Alan Lee;Argo Zhang(argo@live.ca)</authors>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageProjectUrl>https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp</RepositoryUrl>
<PackageReleaseNotes>Please refer to CHANGELOG.md for details</PackageReleaseNotes>
<Description>.NET client library for ip2region</Description>
<PackageTags>IP2Region GeoIP IPSearch</PackageTags>
<RepositoryType>git</RepositoryType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<UserSecretsId>c2f07fe1-a300-4de3-8200-1278ed8cb5b7</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
</ItemGroup>
<ItemGroup>
<None Include="..\CHANGELOG.md">
<Link>CHANGELOG.md</Link>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using IP2Region.Net.XDB;
namespace IP2Region.Net.Internal;
static class CacheStrategyFactory
{
public static ICacheStrategy CreateCacheStrategy(CachePolicy cachePolicy, string xdbPath) => cachePolicy switch
{
CachePolicy.Content => new ContentCacheStrategy(xdbPath),
CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath),
_ => new FileCacheStrategy(xdbPath),
};
}

View File

@ -0,0 +1,34 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Wong <vcd.hai@outlook.com> at 2025/12/31
namespace IP2Region.Net.Internal;
class ContentCacheStrategy(string xdbPath) : ICacheStrategy
{
// TODO: these constants can be moved to the interface as defaults when using .NET 10
private const int HeaderInfoLength = 256;
private const int VectorIndexSize = 8;
private readonly ReadOnlyMemory<byte> _cacheData = File.ReadAllBytes(xdbPath);
public int IoCount => 0;
public void ResetIoCount()
{
// Do nothing
}
public ReadOnlyMemory<byte> GetVectorIndex(int offset)
=> _cacheData.Slice(HeaderInfoLength + offset, VectorIndexSize);
public ReadOnlyMemory<byte> GetData(long offset, int length) => _cacheData.Slice((int)offset, length);
public void Dispose()
{
// Do nothing
}
}

View File

@ -0,0 +1,85 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using System.Buffers;
namespace IP2Region.Net.Internal;
class FileCacheStrategy(string xdbPath) : ICacheStrategy
{
protected const int HeaderInfoLength = 256;
protected const int VectorIndexSize = 8;
protected const int BufferSize = 64 * 1024;
protected FileStream XdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess);
public int IoCount { get; set; }
public void ResetIoCount()
{
IoCount = 0;
}
public virtual ReadOnlyMemory<byte> GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize);
public virtual ReadOnlyMemory<byte> GetData(long offset, int length)
{
var buffer = ArrayPool<byte>.Shared.Rent(length);
try
{
int totalBytesRead = 0;
XdbFileStream.Seek(offset, SeekOrigin.Begin);
int bytesRead;
while (totalBytesRead < length)
{
bytesRead = XdbFileStream.Read(buffer, totalBytesRead, length - totalBytesRead);
if (bytesRead == 0)
{
break;
}
totalBytesRead += bytesRead;
IoCount++;
}
var ret = new byte[totalBytesRead];
if (totalBytesRead > 0)
{
Array.Copy(buffer, 0, ret, 0, totalBytesRead);
}
return ret;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
/// <summary>
/// 释放文件句柄
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
XdbFileStream.Close();
XdbFileStream.Dispose();
}
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

View File

@ -0,0 +1,19 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
namespace IP2Region.Net.Internal;
internal interface ICacheStrategy : IDisposable
{
int IoCount { get; }
void ResetIoCount();
ReadOnlyMemory<byte> GetVectorIndex(int offset);
ReadOnlyMemory<byte> GetData(long offset, int length);
}

View File

@ -0,0 +1,23 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
namespace IP2Region.Net.Internal;
class VectorIndexCacheStrategy : FileCacheStrategy
{
private const int VectorIndexRows = 256;
private const int VectorIndexCols = 256;
private readonly ReadOnlyMemory<byte> _vectorCache;
public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath)
{
_vectorCache = GetData(HeaderInfoLength, VectorIndexRows * VectorIndexCols * VectorIndexSize);
}
public override ReadOnlyMemory<byte> GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize);
}

View File

@ -0,0 +1,29 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
namespace IP2Region.Net.XDB;
/// <summary>
/// 缓存策略枚举
/// </summary>
public enum CachePolicy
{
/// <summary>
/// no cache
/// </summary>
File,
/// <summary>
/// cache vector index , reduce the number of IO operations
/// </summary>
VectorIndex,
/// <summary>
/// default cache policy , cache whole xdb file
/// </summary>
Content
}

View File

@ -0,0 +1,167 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using IP2Region.Net.Abstractions;
using IP2Region.Net.Internal;
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;
namespace IP2Region.Net.XDB;
/// <summary>
/// <see cref="ISearcher"/> 实现类
/// </summary>
/// <remarks>
/// <inheritdoc/>
/// </remarks>
public class Searcher(CachePolicy cachePolicy, string xdbPath) : ISearcher
{
private readonly ICacheStrategy _cacheStrategy = CacheStrategyFactory.CreateCacheStrategy(cachePolicy, xdbPath);
/// <summary>
/// <inheritdoc/>
/// </summary>
public int IoCount => _cacheStrategy.IoCount;
/// <summary>
/// <inheritdoc/>
/// </summary>
public string? Search(string ipStr)
{
var ipAddress = IPAddress.Parse(ipStr);
return SearchCore(ipAddress.GetAddressBytes());
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public string? Search(IPAddress ipAddress) => SearchCore(ipAddress.GetAddressBytes());
/// <summary>
/// <inheritdoc/>
/// </summary>
[Obsolete("已弃用请改用其他方法Deprecated; please use Search(string) or Search(IPAddress) method.")]
[ExcludeFromCodeCoverage]
public string? Search(uint ipAddress)
{
var bytes = BitConverter.GetBytes(ipAddress);
Array.Reverse(bytes);
return SearchCore(bytes);
}
string? SearchCore(byte[] ipBytes)
{
// 重置 IO 计数器
_cacheStrategy.ResetIoCount();
// 每个 vector 索引项的字节数
var vectorIndexSize = 8;
// vector 索引的列数
var vectorIndexCols = 256;
// 计算得到 vector 索引项的开始地址。
var il0 = ipBytes[0];
var il1 = ipBytes[1];
var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize;
var vector = _cacheStrategy.GetVectorIndex(idx);
var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span);
var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span.Slice(4));
// @Note: ptr validate, zero ptr means source data missing
// so we could just stop here and return an empty string.
if (sPtr == 0 || ePtr == 0)
{
return "";
}
var length = ipBytes.Length;
var indexSize = length * 2 + 6;
var l = 0;
var h = (ePtr - sPtr) / indexSize;
var dataLen = 0;
long dataPtr = 0;
while (l <= h)
{
int m = (int)(l + h) >> 1;
var p = sPtr + m * indexSize;
var buff = _cacheStrategy.GetData(p, indexSize);
var s = buff.Span.Slice(0, length);
var e = buff.Span.Slice(length, length);
if (ByteCompare(ipBytes, s) < 0)
{
h = m - 1;
}
else if (ByteCompare(ipBytes, e) > 0)
{
l = m + 1;
}
else
{
dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.Span.Slice(length * 2, 2));
dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span.Slice(length * 2 + 2, 4));
break;
}
}
var regionBuff = _cacheStrategy.GetData(dataPtr, dataLen);
return Encoding.UTF8.GetString(regionBuff.Span.ToArray());
}
static int ByteCompare(byte[] ip1, ReadOnlySpan<byte> ip2) => ip1.Length == 4 ? IPv4Compare(ip1, ip2) : IPv6Compare(ip1, ip2);
static int IPv4Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
{
var ret = 0;
for (int i = 0; i < ip1.Length; i++)
{
var ip2Index = ip1.Length - 1 - i;
if (ip1[i] < ip2[ip2Index])
{
return -1;
}
else if (ip1[i] > ip2[ip2Index])
{
return 1;
}
}
return ret;
}
static int IPv6Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
{
var ret = 0;
for (int i = 0; i < ip1.Length; i++)
{
if (ip1[i] < ip2[i])
{
return -1;
}
else if (ip1[i] > ip2[i])
{
return 1;
}
}
return ret;
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public void Dispose()
{
_cacheStrategy.Dispose();
GC.SuppressFinalize(this);
}
}

View File

@ -0,0 +1,91 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
using System.Buffers;
using System.Buffers.Binary;
using System.Net;
using System.Runtime.InteropServices;
namespace IP2Region.Net.XDB;
/// <summary>
/// 工具类
/// </summary>
public static class Util
{
public static uint IpAddressToUInt32(string ipAddress)
{
var address = IPAddress.Parse(ipAddress);
return IpAddressToUInt32(address);
}
public static uint IpAddressToUInt32(IPAddress ipAddress)
{
byte[] bytes = ipAddress.GetAddressBytes();
Array.Reverse(bytes);
return MemoryMarshal.Read<uint>(bytes);
}
public static uint GetMidIp(uint x, uint y)
=> (x & y) + ((x ^ y) >> 1);
public static async Task<XdbVersion> GetVersionAsync(string dbPath, CancellationToken token = default)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException(nameof(dbPath));
}
if (!File.Exists(dbPath))
{
throw new FileNotFoundException("xdb file not found.", dbPath);
}
using var reader = File.OpenRead(dbPath);
return await GetVersionAsync(reader, token);
}
internal static async Task<XdbVersion> GetVersionAsync(FileStream reader, CancellationToken token = default)
{
XdbVersion ret = default;
var buffer = ArrayPool<byte>.Shared.Rent(256);
try
{
var length = await reader.ReadAsync(buffer, 0, 256, token);
if (length == 256)
{
ret = Parse(buffer);
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
return ret;
}
private static XdbVersion Parse(ReadOnlySpan<byte> buffer)
{
var ret = new XdbVersion
{
Ver = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)),
CachePolice = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)),
StartIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(8, 4)),
EndIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(12, 4)),
IPVer = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(16, 2)),
BytesCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(18, 2))
};
var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(4, 4));
var dtm = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(0));
ret.CreatedTime = dtm.AddSeconds(createdAt);
return ret;
}
}

View File

@ -0,0 +1,49 @@
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date 2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21
namespace IP2Region.Net.XDB;
/// <summary>
/// XdbVersion 结构体
/// </summary>
public struct XdbVersion
{
/// <summary>
/// 获得/设置 版本号
/// </summary>
public ushort Ver { get; set; }
/// <summary>
/// 获得/设置 缓存策略
/// </summary>
public ushort CachePolice { get; set; }
/// <summary>
/// 获得/设置 文件生成时间
/// </summary>
public DateTimeOffset CreatedTime { get; set; }
/// <summary>
/// 获得/设置 索引起始地址
/// </summary>
public uint StartIndex { get; set; }
/// <summary>
/// 获得/设置 索引结束地址
/// </summary>
public uint EndIndex { get; set; }
/// <summary>
/// 获得/设置 IP版本
/// </summary>
public ushort IPVer { get; set; }
/// <summary>
/// 获得/设置 指针字节数
/// </summary>
public ushort BytesCount { get; set; }
}

99
binding/csharp/README.md Normal file
View File

@ -0,0 +1,99 @@
# IP2Region.Net
.NET client library for IP2Region
## Installation
Install the package with [NuGet](https://www.nuget.org/packages/IP2Region.Net)
```bash
Install-Package IP2Region.Net
```
## Usage
```csharp
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
ISearcher searcher = new Searcher(CachePolicy , "your xdb file path");
```
### Cache Policy Description
| Cache Policy | Description | Thread Safe |
|-------------------------|------------------------------------------------------------------------------------------------------------|-------------|
| CachePolicy.Content | Cache the entire `xdb` data. | Yes |
| CachePolicy.VectorIndex | Cache `vecotorIndex` to speed up queries and reduce system io pressure by reducing one fixed IO operation. | Yes |
| CachePolicy.File | Completely file-based queries | Yes |
### XDB File Description
Generate using [maker](https://github.com/lionsoul2014/ip2region/tree/master/maker/csharp), or [download](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb) pre-generated xdb files
## ASP.NET Core Usage
```csharp
services.AddIP2RegionService("your xdb file path", cachePolicy: CachePolicy.Content);
```
NET6/7
```csharp
provider.GetRequiredService<ISearcher>()
```
NET8+ support keyed service
```csharp
provider.GetRequiredKeyedService<ISearcher>("IP2Region.Net");
```
## TargetFrameworks
netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0
## Performance
// * Summary *
BenchmarkDotNet v0.15.6, Windows 11 (10.0.26200.7171)
13th Gen Intel Core i7-13700 2.10GHz, 1 CPU, 24 logical and 16 physical cores
.NET SDK 10.0.100
[Host] : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
DefaultJob : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|------------ |-------------:|-----------:|-----------:|-------:|----------:|
| ContentIPv4 | 53.70 ns | 0.296 ns | 0.277 ns | 0.0086 | 136 B |
| VectorIPv4 | 4,446.04 ns | 18.673 ns | 15.593 ns | 0.0076 | 232 B |
| FileIPv4 | 6,712.40 ns | 15.718 ns | 13.934 ns | 0.0153 | 264 B |
| ContentIPv6 | 145.53 ns | 0.331 ns | 0.277 ns | 0.0126 | 200 B |
| VectorIPv6 | 7,058.39 ns | 125.505 ns | 117.398 ns | 0.0381 | 712 B |
| FileIPv6 | 10,657.97 ns | 53.907 ns | 50.425 ns | 0.0458 | 744 B |
// * Hints *
Outliers
Benchmarks.VectorIPv4: Default -> 2 outliers were removed (4.55 us, 4.58 us)
Benchmarks.FileIPv4: Default -> 1 outlier was removed (6.79 us)
Benchmarks.ContentIPv6: Default -> 2 outliers were removed (148.08 ns, 152.27 ns)
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Gen0 : GC Generation 0 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ns : 1 Nanosecond (0.000000001 sec)
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
Run time: 00:02:06 (126.09 sec), executed benchmarks: 6
Global total time: 00:02:13 (133.47 sec), executed benchmarks: 6
// * Artifacts cleanup *
Artifacts cleanup is finished
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
## License
[Apache License 2.0](https://github.com/lionsoul2014/ip2region/blob/master/LICENSE.md)

18
binding/erlang/Makefile Normal file
View File

@ -0,0 +1,18 @@
.PHONY: all compile test clean bench-v4 bench-v6
all: compile
compile:
rebar3 compile
test:
rebar3 eunit
clean:
rebar3 clean
bench-v4:
sh benchmarks/xdb-benchmark.sh ipv4
bench-v6:
sh benchmarks/xdb-benchmark.sh ipv6

180
binding/erlang/README.md Normal file
View File

@ -0,0 +1,180 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region Erlang query client
### Introduction
This binding implements the xdb query client in `Erlang`, based on the `Erlang OTP Application`. The query logic is implemented by the `ip2region_worker` worker process, supporting multiple worker processes for load balancing.
### Application Configuration
The configurable parameters for this application are in `ip2region.app.src`, as follows:
```erlang
{env,[
{poolargs, [
{size, 1}, %% Default number of worker processes
{max_overflow, 5} %% Maximum number of worker processes
]},
{db, [
{ipv4, "ip2region.xdb"} %% Default IPv4 xdb file
]}
]}
```
### Dual-stack configuration (IPv4 + IPv6)
To enable IPv6 queries, add the `ipv6` entry to the `db` list and place both xdb files under `priv/`:
```erlang
{env,[
{poolargs, [
{size, 1},
{max_overflow, 5}
]},
{db, [
{ipv4, "ip2region.xdb"},
{ipv6, "ip2region_v6.xdb"}
]}
]}
```
The `xdb:search/1` interface automatically detects IPv4 and IPv6 inputs and routes them to the correct worker pool.
### Compile
```bash
$ rebar3 compile
```
### Run
Place the xdb file in the `priv` directory, then start the Erlang node:
```bash
$ rebar3 shell
```
Call the `xdb:search/1` interface in the Erlang shell to query IP address information. This interface supports IP addresses represented as list strings, binary strings, tuples, and integers:
```erlang
1> xdb:search("1.0.8.0").
[20013,22269,124,24191,19996,30465,124,24191,24030,24066,
124,20013,22269,30005,20449,124,67,78]
2>
3> io:format("~ts~n", [xdb:search("1.0.8.0")]).
中国|广东省|广州市|中国电信|CN
4> io:format("~ts~n", [xdb:search(<<"1.0.8.0">>)]).
中国|广东省|广州市|中国电信|CN
5> io:format("~ts~n", [xdb:search({1,0,8,0})]).
中国|广东省|广州市|中国电信|CN
6> io:format("~ts~n", [xdb:search(16779264)]).
中国|广东省|广州市|中国电信|CN
```
With dual-stack enabled, IPv6 addresses are supported in the same way:
```erlang
1> io:format("~ts~n", [xdb:search("2001:4860:4860::8888")]).
United States|Florida|Miami|Google LLC|US
2> io:format("~ts~n", [xdb:search(<<"2001:4860:4860::8888">>)]).
United States|Florida|Miami|Google LLC|US
3> io:format("~ts~n", [xdb:search({8193,18528,18528,0,0,0,0,34952})]).
United States|Florida|Miami|Google LLC|US
```
### Usage
- Add the dependency in `rebar.config`
```erlang
{deps, [
ip2region
]}.
```
- Start the ip2region Application
```erlang
{ok, _} = application:ensure_all_started(ip2region).
```
- Call the `xdb:search/1` interface to query IP information
```erlang
xdb:search("1.0.8.0").
```
### Unit Test
```bash
$ rebar3 eunit
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling ip2region
===> Performing EUnit tests...
=INFO REPORT==== 28-Jun-2026::04:53:28 ===
XdbFile:/Users/nana/Documents/code/ip2region/.worktrees/erlang-ipv6/binding/erlang/_build/test/lib/ip2region/priv/ip2region.xdb
....
Finished in 0.192 seconds
63 tests, 0 failures
```
### Benchmark
Both IPv4 and IPv6 benchmarks share the same script. Run it with the desired IP version:
> `cold` = first pass over the source file: each IP triggers a real search and the
> result is written into the ETS cache. `warm` = second pass over the same list,
> where every lookup is served directly from the ETS cache.
```bash
$ cd benchmarks/
$ sh xdb-benchmark.sh ipv4
```
For IPv6:
```bash
$ sh xdb-benchmark.sh ipv6
```
Or use the Makefile targets from the `binding/erlang` directory:
```bash
$ make bench-v4
$ make bench-v6
```
#### IPv4 benchmark example
```bash
System:
CPU : Apple M4
Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 487169 IPs in 1.335 s
Benchmarks:
cold total= 9.601s count= 487169 qps= 50740.66 avg= 0.019708 ms/op (19.708 us/op)
warm total= 0.160s count= 487169 qps= 3053164.29 avg= 0.000328 ms/op ( 0.328 us/op)
Done.
```
#### IPv6 benchmark example
```bash
System:
CPU : Apple M4
Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 638953 IPs in 2.949 s
Benchmarks:
cold total= 20.504s count= 638953 qps= 31162.52 avg= 0.032090 ms/op (32.090 us/op)
warm total= 0.444s count= 638953 qps= 1437781.56 avg= 0.000696 ms/op ( 0.696 us/op)
Done.
```

179
binding/erlang/README_zh.md Normal file
View File

@ -0,0 +1,179 @@
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)
# ip2region Erlang 查询客户端
### 简介
该 bingding 以 `Erlang` 语言实现 xdb 查询客户端,基于 `Erlang OTP Application`,查询逻辑由 `ip2region_worker` 工作进程实现,支持配多个工作进程来进行负载均衡。
### 应用配置
该应用可配置的参数在 `ip2region.app.src` 中, 如下:
```erlang
{env,[
{poolargs, [
{size, 1}, %% 工作进程默认数量
{max_overflow, 5} %% 工作进程最大数量
]},
{db, [
{ipv4, "ip2region.xdb"} %% 默认 IPv4 xdb 文件
]}
]}
```
### 双栈配置IPv4 + IPv6
如需启用 IPv6 查询,在 `db` 列表中加入 `ipv6` 项,并将两个 xdb 文件放到 `priv/` 目录下:
```erlang
{env,[
{poolargs, [
{size, 1},
{max_overflow, 5}
]},
{db, [
{ipv4, "ip2region.xdb"},
{ipv6, "ip2region_v6.xdb"}
]}
]}
```
`xdb:search/1` 接口会自动识别 IPv4 与 IPv6 输入,并将其路由到对应的工作进程池。
### 编译
```bash
$ rebar3 compile
```
### 运行
将 xdb 文件放到 `priv` 目录下,然后启动 Erlang 节点:
```bash
$ rebar3 shell
```
在 Erlang shell 中调用 `xdb:search/1` 接口查询 IP 地址信息。该接口支持 list 字符串、binary 字符串、tuple 和整数表示的 IP 地址:
```erlang
1> xdb:search("1.0.8.0").
[20013,22269,124,24191,19996,30465,124,24191,24030,24066,
124,20013,22269,30005,20449,124,67,78]
2>
3> io:format("~ts~n", [xdb:search("1.0.8.0")]).
中国|广东省|广州市|中国电信|CN
4> io:format("~ts~n", [xdb:search(<<"1.0.8.0">>)]).
中国|广东省|广州市|中国电信|CN
5> io:format("~ts~n", [xdb:search({1,0,8,0})]).
中国|广东省|广州市|中国电信|CN
6> io:format("~ts~n", [xdb:search(16779264)]).
中国|广东省|广州市|中国电信|CN
```
启用双栈后IPv6 地址也按同样方式支持:
```erlang
1> io:format("~ts~n", [xdb:search("2001:4860:4860::8888")]).
United States|Florida|Miami|Google LLC|US
2> io:format("~ts~n", [xdb:search(<<"2001:4860:4860::8888">>)]).
United States|Florida|Miami|Google LLC|US
3> io:format("~ts~n", [xdb:search({8193,18528,18528,0,0,0,0,34952})]).
United States|Florida|Miami|Google LLC|US
```
### 使用方法
- 在 `rebar.config` 中引入依赖
```erlang
{deps, [
ip2region
]}.
```
- 启动 ip2region Application
```erlang
{ok, _} = application:ensure_all_started(ip2region).
```
- 调用 `xdb:search/1` 接口查询 IP 信息
```erlang
xdb:search("1.0.8.0").
```
### 单元测试
```bash
$ rebar3 eunit
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling ip2region
===> Performing EUnit tests...
=INFO REPORT==== 28-Jun-2026::04:53:28 ===
XdbFile:/Users/nana/Documents/code/ip2region/.worktrees/erlang-ipv6/binding/erlang/_build/test/lib/ip2region/priv/ip2region.xdb
....
Finished in 0.192 seconds
63 tests, 0 failures
```
### 基准测试
IPv4 与 IPv6 共用一个脚本,通过参数指定版本:
> `cold` = 第一次遍历源文件:每个 IP 都会触发真实查询,并把结果写入 ETS 缓存。
> `warm` = 第二次遍历同一列表,所有查询都直接命中 ETS 缓存。
```bash
$ cd benchmarks/
$ sh xdb-benchmark.sh ipv4
```
IPv6
```bash
$ sh xdb-benchmark.sh ipv6
```
也可以在 `binding/erlang` 目录直接用 Makefile
```bash
$ make bench-v4
$ make bench-v6
```
#### IPv4 基准测试示例
```bash
System:
CPU : Apple M4
Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 487169 IPs in 1.335 s
Benchmarks:
cold total= 9.601s count= 487169 qps= 50740.66 avg= 0.019708 ms/op (19.708 us/op)
warm total= 0.160s count= 487169 qps= 3053164.29 avg= 0.000328 ms/op ( 0.328 us/op)
Done.
```
#### IPv6 基准测试示例
```bash
System:
CPU : Apple M4
Cores : 10 cores / 10 threads
Erlang : Erlang/OTP 29 [erts-17.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]
Loaded : 638953 IPs in 2.949 s
Benchmarks:
cold total= 20.504s count= 638953 qps= 31162.52 avg= 0.032090 ms/op (32.090 us/op)
warm total= 0.444s count= 638953 qps= 1437781.56 avg= 0.000696 ms/op ( 0.696 us/op)
Done.
```

View File

@ -0,0 +1,37 @@
#!/bin/bash
set -e
# Run from binding/erlang so paths and rebar3 artifacts are correct.
cd "$(dirname "$0")/.."
VERSION="${1:-ipv4}"
PROJECT_ROOT="$(cd ../.. && pwd)"
if [ "$VERSION" = "ipv6" ]; then
DB_FILE="${PROJECT_ROOT}/data/ip2region_v6.xdb"
DATA_FILE="${PROJECT_ROOT}/data/ipv6_source.txt"
DB_CONFIG="[{ipv6, \"${DB_FILE}\"}]"
elif [ "$VERSION" = "ipv4" ]; then
DATA_FILE="${PROJECT_ROOT}/data/ipv4_source.txt"
DB_CONFIG="[{ipv4, \"ip2region.xdb\"}]"
else
echo "Usage: $0 [ipv4|ipv6]"
exit 1
fi
rebar3 compile
# shellcheck disable=SC2089
EVAL="application:load(ip2region), \
application:set_env(ip2region, db, ${DB_CONFIG}), \
application:set_env(ip2region, poolargs, [{size,4},{max_overflow,8}]), \
{ok,_}=application:ensure_all_started(ip2region), \
xdb_benchmark:main(\"${DATA_FILE}\"), \
init:stop()."
# shellcheck disable=SC2090
erl -pa _build/default/lib/poolboy/ebin \
-pa _build/default/lib/ip2region/ebin \
-kernel logger_level error \
-noshell \
-eval "${EVAL}"

View File

@ -0,0 +1,42 @@
-ifndef(IP2REGION_HRL).
-define(IP2REGION_HRL, true).
-define(NONE, none).
-define(APP_NAME, ip2region).
-define(XDB_HEADER_SIZE, 256).
-define(XDB_VECTOR_COLS, 256).
-define(XDB_VECTOR_INDEX_SIZE, 8).
-define(XDB_VECTOR_INDEX_COUNT, (16#10000)). %% 256*256
%% IP versions as stored in xdb header
-define(IP_VERSION_4, 4).
-define(IP_VERSION_6, 6).
%% Segment index sizes (bytes): start_ip + end_ip + data_len + data_ptr
-define(XDB_SEGMENT_INDEX_SIZE_V4, 14). %% 4 + 4 + 2 + 4
-define(XDB_SEGMENT_INDEX_SIZE_V6, 38). %% 16 + 16 + 2 + 4
%% Per-version ETS tables
-define(XDB_VECTOR_INDEX_V4, ets_xdb_vector_index_v4).
-define(XDB_VECTOR_INDEX_V6, ets_xdb_vector_index_v6).
-define(XDB_SEGMENT_INDEX_V4, ets_xdb_segment_index_v4).
-define(XDB_SEGMENT_INDEX_V6, ets_xdb_segment_index_v6).
-define(IP2REGION_CACHE_V4, ets_ip2region_cache_v4).
-define(IP2REGION_CACHE_V6, ets_ip2region_cache_v6).
%% Per-version pool names.
-define(IP2REGION_POOL_V4, ip2region_pool_v4).
-define(IP2REGION_POOL_V6, ip2region_pool_v6).
-record(xdb_header, {
version :: non_neg_integer(),
index_policy :: non_neg_integer(),
created_at :: non_neg_integer(),
start_index_ptr :: non_neg_integer(),
end_index_ptr :: non_neg_integer(),
ip_version :: non_neg_integer(),
runtime_ptr_bytes :: non_neg_integer()
}).
-endif.

View File

View File

@ -0,0 +1,24 @@
{erl_opts, [
debug_info,
export_all,
nowarn_export_all
]}.
{plugins, [rebar3_hex, rebar3_ex_doc]}.
{deps, [
poolboy
]}.
{shell, [
% {config, "config/sys.config"},
{apps, [ip2region]}
]}.
{ex_doc, [
{extras, ["README.md"]},
{main, "README.md"},
{source_url, "https://github.com/lionsoul2014/ip2region/tree/master/binding/erlang"}
]}.
{hex, [{doc, ex_doc}]}.

View File

@ -0,0 +1,8 @@
{"1.2.0",
[{<<"poolboy">>,{pkg,<<"poolboy">>,<<"1.5.2">>},0}]}.
[
{pkg_hash,[
{<<"poolboy">>, <<"392B007A1693A64540CEAD79830443ABF5762F5D30CF50BC95CB2C1AAAFA006B">>}]},
{pkg_hash_ext,[
{<<"poolboy">>, <<"DAD79704CE5440F3D5A3681C8590B9DC25D1A561E8F5A9C995281012860901E3">>}]}
].

View File

@ -0,0 +1,24 @@
{application, ip2region,
[{description, "ip2region xdb client application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {ip2region_app, []}},
{applications,
[kernel,
stdlib,
poolboy
]},
{env,[
{poolargs, [
{size, 1},
{max_overflow, 5}
]},
{db, [
{ipv4, "ip2region.xdb"}
]}
]},
{modules, []},
{licenses, ["Apache-2.0"]},
{links, [{"Github", "https://github.com/lionsoul2014/ip2region/tree/master/binding/erlang"}]}
]}.

View File

@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
ip2region_sup:start_link().
stop(_State) ->
ok.
%% internal functions

View File

@ -0,0 +1,78 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc ip2region top level supervisor.
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_sup).
-behaviour(supervisor).
-include("ip2region.hrl").
-export([start_link/0]).
-export([init/1, create_table/0]).
-define(SERVER, ?MODULE).
start_link() ->
{ok, SupPid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []),
{ok, SupPid}.
%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
create_table(),
SupFlags = #{strategy => one_for_one,
intensity => 10,
period => 5},
ChildSpecs = pool_child_specs(),
{ok, {SupFlags, ChildSpecs}}.
%% internal functions
%%
create_table() ->
Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}],
%% Version-specific tables for dual-stack support
ensure_table(?XDB_VECTOR_INDEX_V4, Opts),
ensure_table(?XDB_VECTOR_INDEX_V6, Opts),
ensure_table(?XDB_SEGMENT_INDEX_V4, Opts),
ensure_table(?XDB_SEGMENT_INDEX_V6, Opts),
ensure_table(?IP2REGION_CACHE_V4, Opts),
ensure_table(?IP2REGION_CACHE_V6, Opts).
ensure_table(Name, Opts) ->
case ets:whereis(Name) of
undefined -> ets:new(Name, Opts);
_ -> ok
end.
pool_child_specs() ->
{ok, DbConfig} = application:get_env(db),
{ok, PoolArgsCfg} = application:get_env(poolargs),
lists:foldl(
fun({ipv4, File}, Acc) ->
[make_pool_spec(?IP2REGION_POOL_V4, ipv4, File, PoolArgsCfg) | Acc];
({ipv6, File}, Acc) ->
[make_pool_spec(?IP2REGION_POOL_V6, ipv6, File, PoolArgsCfg) | Acc];
(_, Acc) ->
Acc
end, [], DbConfig).
make_pool_spec(PoolName, Version, File, PoolArgsCfg) ->
PoolArgs = [
{strategy, fifo},
{name, {local, PoolName}},
{worker_module, ip2region_worker}
| PoolArgsCfg
],
WorkerArgs = [{xdb_file, File}, {expected_version, Version}],
poolboy:child_spec(PoolName, PoolArgs, WorkerArgs).

View File

@ -0,0 +1,71 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% ip2region utils: IPv4/IPv6 parsing and version detection.
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_util).
-export([ipv4_to_n/1, ip_version/1, ip_to_bytes/1]).
-define(IS_UINT8(V), is_integer(V), V >= 0, V =< 255).
-define(IS_UINT16(V), is_integer(V), V >= 0, V =< 65535).
-spec ip_version(Ip :: tuple() | list() | binary() | integer()) ->
ipv4 | ipv6 | {error, atom()}.
ip_version(Ip) when is_integer(Ip), Ip >= 0, Ip =< 16#FFFFFFFF -> ipv4;
ip_version({A, B, C, D}) when ?IS_UINT8(A), ?IS_UINT8(B), ?IS_UINT8(C), ?IS_UINT8(D) -> ipv4;
ip_version({A, B, C, D, E, F, G, H}) when ?IS_UINT16(A), ?IS_UINT16(B), ?IS_UINT16(C), ?IS_UINT16(D),
?IS_UINT16(E), ?IS_UINT16(F), ?IS_UINT16(G), ?IS_UINT16(H) -> ipv6;
ip_version(Ip) when is_binary(Ip) ->
ip_version(binary_to_list(Ip));
ip_version(Ip) when is_list(Ip) ->
case inet_parse:address(Ip) of
{ok, {_, _, _, _}} -> ipv4;
{ok, {_, _, _, _, _, _, _, _}} -> ipv6;
_ -> {error, bad_ip_format}
end;
ip_version(_) ->
{error, bad_ip_format}.
-spec ip_to_bytes(Ip :: tuple() | list() | binary() | integer()) ->
{ok, ipv4 | ipv6, binary()} | {error, atom()}.
ip_to_bytes(Ip) when is_integer(Ip), Ip >= 0, Ip =< 16#FFFFFFFF ->
{ok, ipv4, <<Ip:32>>};
ip_to_bytes({A, B, C, D}) when ?IS_UINT8(A), ?IS_UINT8(B), ?IS_UINT8(C), ?IS_UINT8(D) ->
{ok, ipv4, <<A, B, C, D>>};
ip_to_bytes({A, B, C, D, E, F, G, H}) when ?IS_UINT16(A), ?IS_UINT16(B), ?IS_UINT16(C), ?IS_UINT16(D),
?IS_UINT16(E), ?IS_UINT16(F), ?IS_UINT16(G), ?IS_UINT16(H) ->
{ok, ipv6, <<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>};
ip_to_bytes(Ip) when is_binary(Ip) ->
ip_to_bytes(binary_to_list(Ip));
ip_to_bytes(Ip) when is_list(Ip) ->
case inet_parse:address(Ip) of
{ok, {A, B, C, D}} ->
{ok, ipv4, <<A, B, C, D>>};
{ok, {A, B, C, D, E, F, G, H}} ->
{ok, ipv6, <<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>};
_ ->
{error, bad_ip_format}
end;
ip_to_bytes(_) ->
{error, bad_ip_format}.
-spec ipv4_to_n(Ip :: tuple() | list() | binary() | integer()) ->
non_neg_integer() | {error, atom()}.
ipv4_to_n(IntIp) when is_integer(IntIp), IntIp >= 0, IntIp =< 16#FFFFFFFF -> IntIp;
ipv4_to_n({A, B, C, D}) when ?IS_UINT8(A), ?IS_UINT8(B), ?IS_UINT8(C), ?IS_UINT8(D) ->
<<N:32>> = <<A, B, C, D>>,
N;
ipv4_to_n(Ip) when is_binary(Ip) ->
ipv4_to_n(binary_to_list(Ip));
ipv4_to_n(Ip) when is_list(Ip) ->
case ip_to_bytes(Ip) of
{ok, ipv4, <<N:32>>} -> N;
_ -> {error, bad_ip_format}
end;
ipv4_to_n(_) ->
{error, bad_ip_format}.

View File

@ -0,0 +1,241 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% ip2region xdb client worker, now version-aware (IPv4/IPv6).
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_worker).
-behaviour(gen_server).
-include("ip2region.hrl").
%% API
-export([start/1, stop/1, start_link/1]).
-export([search/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {
xdb_fd,
version :: ipv4 | ipv6
}).
%%==========================================
%% API
%% =========================================
start(Args) ->
Opts = [{spawn_opt, [{min_heap_size, 6000}]}],
gen_server:start(?MODULE, Args, Opts).
start_link(Args) ->
Opts = [{spawn_opt, [{min_heap_size, 6000}]}],
gen_server:start_link(?MODULE, Args, Opts).
stop(Pid) ->
gen_server:call(Pid, stop).
search(Pid, Ip) ->
gen_server:call(Pid, {search, Ip}).
%%==========================================
%% gen_server callbacks
%% =========================================
init(Args) ->
process_flag(trap_exit, true),
AppName =
case application:get_application() of
{ok, AName} -> AName;
_ -> ?APP_NAME
end,
PrivDir = code:priv_dir(AppName),
XdbFileName =
case proplists:get_value(xdb_file, Args) of
undefined -> filename:join([PrivDir, "ip2region.xdb"]);
Path ->
case filename:pathtype(Path) of
absolute -> Path;
_ -> filename:join([PrivDir, Path])
end
end,
error_logger:info_report(io_lib:format("XdbFile:~s~n", [XdbFileName])),
{ok, IoDevice} = file:open(XdbFileName, [read, binary]),
{ok, HeaderBin} = file:read(IoDevice, ?XDB_HEADER_SIZE),
{ok, Header} = ip2region_xdb:parse_header(HeaderBin),
ExpectedVersion = proplists:get_value(expected_version, Args),
case resolve_version(Header) of
{ok, Version} when ExpectedVersion =:= undefined; ExpectedVersion =:= Version ->
load_vector_index(IoDevice, Version),
{ok, #state{xdb_fd = IoDevice, version = Version}};
{ok, Version} ->
file:close(IoDevice),
{stop, {xdb_version_mismatch, ExpectedVersion, Version}};
{error, Reason} ->
file:close(IoDevice),
{stop, Reason}
end.
handle_call(Request, From, State) ->
try
do_call(Request, From, State)
catch
Class:Error:Stacktrace ->
error_logger:error_report(io_lib:format("~p handle call error, Req:~p ~p, stacktrace:~p~n",
[?MODULE, Request, {Class, Error}, Stacktrace])),
{reply, {error, {Class, Error}}, State}
end.
handle_cast(Msg, State) ->
try
do_cast(Msg, State)
catch
Class:Error:Stacktrace ->
error_logger:error_report(io_lib:format("~p handle cast error, Msg:~p, ~p, stacktrace:~w~n",
[?MODULE, Msg, {Class, Error}, Stacktrace])),
{noreply, State}
end.
handle_info(Info, State) ->
try
do_info(Info, State)
catch
Class:Error:Stacktrace ->
error_logger:error_report(io_lib:format("~p handle info error, Info:~p, ~p, stacktrace:~p~n",
[?MODULE, Info, {Class, Error}, Stacktrace])),
{noreply, State}
end.
terminate(_Reason, State) ->
#state{xdb_fd = XdbFd} = State,
case is_pid(XdbFd) of
true -> file:close(XdbFd);
_ -> skip
end,
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%==========================================
%% Internal function
%% =========================================
do_call({search, Ip}, _From, #state{xdb_fd = IoDevice} = State) ->
Reply = search_ip(IoDevice, Ip, State),
{reply, Reply, State};
do_call(stop, _From, State) ->
{stop, normal, stopped, State};
do_call(Request, From, State) ->
error_logger:error_report(io_lib:format("unknown request: ~p, from:~p", [Request, From])),
{reply, {error, unknown_request}, State}.
do_cast(Msg, State) ->
error_logger:error_report(io_lib:format("unknown msg: ~p", [Msg])),
{noreply, State}.
do_info(Info, State) ->
error_logger:error_report(io:format("unknown info: ~p", [Info])),
{noreply, State}.
-spec resolve_version(#xdb_header{}) -> {ok, ipv4 | ipv6} | {error, term()}.
resolve_version(Header) ->
case ip2region_xdb:header_version(Header) of
2 -> {ok, ipv4};
3 ->
case ip2region_xdb:header_ip_version(Header) of
?IP_VERSION_4 -> {ok, ipv4};
?IP_VERSION_6 -> {ok, ipv6};
V -> {error, {invalid_xdb_ip_version, V}}
end;
V -> {error, {invalid_xdb_version, V}}
end.
load_vector_index(IoDevice, Version) ->
%% The vector index ETS table is global and shared by all pool workers.
%% Only the first worker (or the first worker after a restart) populates it.
%% Subsequent workers skip the file:read here; search_ip/2 uses file:pread/3,
%% so the current file pointer position does not affect correctness.
Table = ip2region_xdb:vector_index_table(Version),
case ets:info(Table, size) of
?XDB_VECTOR_INDEX_COUNT ->
ok;
undefined ->
Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}],
ets:new(Table, Opts),
load_vector_index_data(IoDevice, Table);
_ ->
%% A previous worker may have crashed midway; reload to be safe.
load_vector_index_data(IoDevice, Table)
end.
load_vector_index_data(IoDevice, Table) ->
{ok, VectorIndexBin} =
file:read(IoDevice, ?XDB_VECTOR_INDEX_COUNT * ?XDB_VECTOR_INDEX_SIZE),
load_vector_index_aux(VectorIndexBin, 0, Table).
load_vector_index_aux(<<>>, _Index, _Table) -> ok;
load_vector_index_aux(<<SPtr:32/little, EPtr:32/little, VectorIndexBin/binary>>, Index, Table) ->
ets:insert(Table, {Index, SPtr, EPtr}),
load_vector_index_aux(VectorIndexBin, Index + 1, Table).
search_ip(IoDevice, Ip, #state{version = Version}) ->
SegSize = ip2region_xdb:segment_index_size(Version),
VectorTable = ip2region_xdb:vector_index_table(Version),
SegmentTable = ip2region_xdb:segment_index_table(Version),
<<A:8, B:8, _/binary>> = Ip,
VectorIdx = A * ?XDB_VECTOR_COLS + B,
[{_, SPtr, EPtr}] = ets:lookup(VectorTable, VectorIdx),
search_ip(IoDevice, Ip, SPtr, EPtr, 0,
(EPtr - SPtr) div SegSize, SegSize, Version, SegmentTable).
search_ip(IoDevice, Ip, SPtr, EPtr, Low, High, SegSize, Version, SegmentTable) when Low =< High ->
Middle = (Low + High) bsr 1,
SPtr2 = SPtr + Middle * SegSize,
{SIp, EIp, DataLen, DataPtr} = read_segment_index(IoDevice, SPtr2, SegSize, SegmentTable),
case ip_in_range(Ip, SIp, EIp, Version) of
below ->
search_ip(IoDevice, Ip, SPtr, EPtr, Low, Middle - 1, SegSize, Version, SegmentTable);
above ->
search_ip(IoDevice, Ip, SPtr, EPtr, Middle + 1, High, SegSize, Version, SegmentTable);
inside ->
{ok, DataBin} = read_file(IoDevice, DataPtr, DataLen),
unicode:characters_to_nfc_list(DataBin)
end;
search_ip(_IoDevice, _Ip, _SPtr, _EPtr, _Low, _High, _SegSize, _Version, _SegmentTable) ->
{error, unknown}.
ip_in_range(Ip, SIp, EIp, ipv4) ->
<<InputInt:32>> = Ip,
if
InputInt < SIp -> below;
InputInt > EIp -> above;
true -> inside
end;
ip_in_range(Ip, SIp, EIp, ipv6) ->
if
Ip < SIp -> below;
Ip > EIp -> above;
true -> inside
end.
read_file(IoDevice, Position, DataLength) ->
file:pread(IoDevice, Position, DataLength).
read_segment_index(IoDevice, SPtr, SegSize, SegmentTable) ->
case ets:lookup(SegmentTable, SPtr) of
[{_SPtr, SIp, EIp, DataLen, DataPtr}] ->
{SIp, EIp, DataLen, DataPtr};
_ ->
{ok, Bin} = read_file(IoDevice, SPtr, SegSize),
{SIp, EIp, DataLen, DataPtr} = decode_segment_index(Bin, SegSize),
ets:insert_new(SegmentTable, {SPtr, SIp, EIp, DataLen, DataPtr}),
{SIp, EIp, DataLen, DataPtr}
end.
decode_segment_index(Bin, ?XDB_SEGMENT_INDEX_SIZE_V4) ->
<<SIp:32/little, EIp:32/little, DataLen:16/little, DataPtr:32/little>> = Bin,
{SIp, EIp, DataLen, DataPtr};
decode_segment_index(Bin, ?XDB_SEGMENT_INDEX_SIZE_V6) ->
<<SIp:16/binary, EIp:16/binary, DataLen:16/little, DataPtr:32/little>> = Bin,
{SIp, EIp, DataLen, DataPtr}.

View File

@ -0,0 +1,60 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% ip2region xdb binary format helpers.
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_xdb).
-export([parse_header/1]).
-export([header_version/1, header_index_policy/1, header_created_at/1,
header_start_index_ptr/1, header_end_index_ptr/1,
header_ip_version/1, header_runtime_ptr_bytes/1]).
-export([segment_index_size/1]).
-export([vector_index_table/1, segment_index_table/1, cache_table/1]).
-include("ip2region.hrl").
-spec parse_header(binary()) -> {ok, #xdb_header{}} | {error, invalid_header}.
parse_header(Bin) when is_binary(Bin), byte_size(Bin) >= 20 ->
<<Version:16/little, IndexPolicy:16/little, CreatedAt:32/little,
StartIndexPtr:32/little, EndIndexPtr:32/little,
IpVersion:16/little, RuntimePtrBytes:16/little, _/binary>> = Bin,
{ok, #xdb_header{
version = Version,
index_policy = IndexPolicy,
created_at = CreatedAt,
start_index_ptr = StartIndexPtr,
end_index_ptr = EndIndexPtr,
ip_version = IpVersion,
runtime_ptr_bytes = RuntimePtrBytes
}};
parse_header(_) ->
{error, invalid_header}.
header_version(#xdb_header{version = V}) -> V.
header_index_policy(#xdb_header{index_policy = P}) -> P.
header_created_at(#xdb_header{created_at = T}) -> T.
header_start_index_ptr(#xdb_header{start_index_ptr = P}) -> P.
header_end_index_ptr(#xdb_header{end_index_ptr = P}) -> P.
header_ip_version(#xdb_header{ip_version = V}) -> V.
header_runtime_ptr_bytes(#xdb_header{runtime_ptr_bytes = B}) -> B.
-spec segment_index_size(ipv4 | ipv6) -> pos_integer().
segment_index_size(ipv4) -> ?XDB_SEGMENT_INDEX_SIZE_V4;
segment_index_size(ipv6) -> ?XDB_SEGMENT_INDEX_SIZE_V6.
-spec vector_index_table(ipv4 | ipv6) -> atom().
vector_index_table(ipv4) -> ?XDB_VECTOR_INDEX_V4;
vector_index_table(ipv6) -> ?XDB_VECTOR_INDEX_V6.
-spec segment_index_table(ipv4 | ipv6) -> atom().
segment_index_table(ipv4) -> ?XDB_SEGMENT_INDEX_V4;
segment_index_table(ipv6) -> ?XDB_SEGMENT_INDEX_V6.
-spec cache_table(ipv4 | ipv6) -> atom().
cache_table(ipv4) -> ?IP2REGION_CACHE_V4;
cache_table(ipv6) -> ?IP2REGION_CACHE_V6.

View File

@ -0,0 +1,48 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% ip2region xdb client search api with IPv4/IPv6 auto-routing.
%% @end
%%%-------------------------------------------------------------------
-module(xdb).
-include("ip2region.hrl").
-export([search/1]).
-spec search(Ip :: tuple() | list() | binary() | integer()) ->
Result :: list() | {error, term()}.
search(Ip) ->
case ip2region_util:ip_to_bytes(Ip) of
{ok, ipv4, IpBin} ->
do_search(?IP2REGION_POOL_V4, ipv4, IpBin);
{ok, ipv6, IpBin} ->
do_search(?IP2REGION_POOL_V6, ipv6, IpBin);
Ret ->
Ret
end.
do_search(PoolName, Version, IpBin) ->
CacheTable = ip2region_xdb:cache_table(Version),
case ets:lookup(CacheTable, IpBin) of
[{_, Region}] -> Region;
_ ->
case whereis(PoolName) of
undefined ->
{error, pool_not_configured};
_ ->
Worker = poolboy:checkout(PoolName, true, infinity),
try
case ip2region_worker:search(Worker, IpBin) of
{error, _} = Err -> Err;
Region ->
ets:insert(CacheTable, {IpBin, Region}),
Region
end
after
poolboy:checkin(PoolName, Worker)
end
end
end.

Some files were not shown because too many files have changed in this diff Show More