From 1c0829a5938fe15dfe99b9dc005c1d9d7b9cdcb3 Mon Sep 17 00:00:00 2001 From: RuntimeBroker <1714985309@qq.com> Date: Tue, 23 Sep 2025 17:06:35 +0800 Subject: [PATCH] feature: new add support ipv4 and ipv6 xdb-parse crate search in binding rust folder --- .gitattributes | 2 - .gitignore | 92 - LICENSE.md | 225 - ReadMe.md | 115 - binding/c/Makefile | 27 - binding/c/ReadMe.md | 341 - binding/c/main.c | 428 - binding/c/test_util | Bin 22576 -> 0 bytes binding/c/test_util.c | 213 - binding/c/xdb_api.h | 268 - binding/c/xdb_searcher.c | 257 - binding/c/xdb_util.c | 494 - binding/cpp/Makefile | 11 - binding/cpp/readme.md | 108 - binding/cpp/xdb_bench.cc | 135 - binding/cpp/xdb_bench.h | 28 - binding/cpp/xdb_bench_test.cc | 70 - binding/cpp/xdb_search.cc | 190 - binding/cpp/xdb_search.h | 50 - binding/cpp/xdb_search_test.cc | 73 - binding/csharp/.gitignore | 337 - binding/csharp/CHANGELOG.md | 21 - .../IP2Region.Net.BenchMark.csproj | 26 - .../csharp/IP2Region.Net.BenchMark/Program.cs | 29 - .../IP2Region.Net.Test.csproj | 34 - .../csharp/IP2Region.Net.Test/SearcherTest.cs | 80 - binding/csharp/IP2Region.Net.Test/Usings.cs | 1 - binding/csharp/IP2Region.Net.Test/UtilTest.cs | 14 - .../IP2Region.Net/Abstractions/ISearcher.cs | 20 - .../csharp/IP2Region.Net/IP2Region.Net.csproj | 34 - .../Abstractions/AbstractCacheStrategy.cs | 64 - .../Internal/CacheStrategyFactory.cs | 31 - .../Internal/ContentCacheStrategy.cs | 32 - .../Internal/FileCacheStrategy.cs | 22 - .../Internal/VectorIndexCacheStrategy.cs | 26 - .../csharp/IP2Region.Net/XDB/CachePolicy.cs | 17 - binding/csharp/IP2Region.Net/XDB/Searcher.cs | 85 - binding/csharp/IP2Region.Net/XDB/Util.cs | 23 - binding/csharp/README.md | 62 - binding/erlang/README.md | 129 - binding/erlang/benchmarks/xdb-benchmark.sh | 5 - binding/erlang/include/ip2region.hrl | 26 - binding/erlang/priv/dummy | 0 binding/erlang/rebar.config | 24 - binding/erlang/rebar.lock | 8 - binding/erlang/src/ip2region.app.src | 20 - binding/erlang/src/ip2region_app.erl | 22 - binding/erlang/src/ip2region_sup.erl | 55 - binding/erlang/src/ip2region_util.erl | 26 - binding/erlang/src/ip2region_worker.erl | 183 - binding/erlang/src/xdb.erl | 32 - binding/erlang/src/xdb_benchmark.erl | 75 - binding/erlang/test/xdb_test.erl | 18 - binding/golang/Makefile | 10 - binding/golang/ReadMe.md | 190 - binding/golang/go.mod | 5 - binding/golang/go.sum | 2 - binding/golang/main.go | 298 - binding/golang/xdb/searcher.go | 267 - binding/golang/xdb/util.go | 245 - binding/golang/xdb/util_test.go | 78 - binding/golang/xdb/version.go | 97 - binding/java/ReadMe.md | 256 - binding/java/pom.xml | 204 - .../org/lionsoul/ip2region/SearchApp.java | 251 - .../org/lionsoul/ip2region/xdb/Header.java | 45 - .../java/org/lionsoul/ip2region/xdb/IPv4.java | 47 - .../java/org/lionsoul/ip2region/xdb/IPv6.java | 29 - .../ip2region/xdb/InetAddressException.java | 13 - .../lionsoul/ip2region/xdb/LittleEndian.java | 58 - .../java/org/lionsoul/ip2region/xdb/Log.java | 119 - .../lionsoul/ip2region/xdb/LongByteArray.java | 130 - .../org/lionsoul/ip2region/xdb/Searcher.java | 272 - .../java/org/lionsoul/ip2region/xdb/Util.java | 82 - .../org/lionsoul/ip2region/xdb/Version.java | 83 - .../lionsoul/ip2region/xdb/XdbException.java | 13 - .../org/lionsoul/ip2region/xdb/IPv4Test.java | 44 - .../ip2region/xdb/LittleEndianTest.java | 36 - .../org/lionsoul/ip2region/xdb/UtilTest.java | 61 - .../lionsoul/ip2region/xdb/VersionTest.java | 41 - binding/lua/ReadMe.md | 153 - binding/lua/bench_test.lua | 175 - binding/lua/search_test.lua | 137 - binding/lua/util_test.lua | 98 - binding/lua/xdb_searcher.lua | 334 - binding/lua_c/Makefile | 16 - binding/lua_c/ReadMe.md | 235 - binding/lua_c/bench_test.lua | 201 - binding/lua_c/search_test.lua | 172 - binding/lua_c/util_test.lua | 140 - binding/lua_c/xdb_searcher.c | 761 - binding/nginx/README.md | 72 - binding/nginx/config | 22 - binding/nginx/src/ngx_http_ip2region_module.c | 345 - binding/nginx/src/ngx_http_ip2region_module.h | 23 - binding/nginx/t/http_ip2region.t | 27 - binding/nodejs/.eslintrc.js | 17 - binding/nodejs/.mocharc.json | 5 - binding/nodejs/.nycrc.json | 5 - binding/nodejs/ReadMe.md | 155 - binding/nodejs/index.js | 201 - binding/nodejs/package.json | 32 - binding/nodejs/tests/bench.app.js | 146 - binding/nodejs/tests/benchmark.js | 38 - binding/nodejs/tests/function.test.js | 33 - binding/nodejs/tests/test.app.js | 79 - binding/php/ReadMe.md | 221 - binding/php/batch_test.php | 161 - binding/php/bench_test.php | 218 - binding/php/search_test.php | 171 - binding/php/xdb/Searcher.class.php | 580 - binding/php/xdb/util_test.php | 124 - binding/python/ReadMe.md | 114 - binding/python/bench_test.py | 120 - binding/python/iptest.py | 56 - binding/python/search_test.py | 95 - binding/python/xdbSearcher.py | 191 - binding/rust/Cargo.toml | 2 - binding/rust/xdb/Cargo.toml | 24 +- binding/rust/{ => xdb}/ReadMe.md | 0 binding/rust/{ => xdb}/example/Cargo.toml | 0 binding/rust/{ => xdb}/example/src/cmd.rs | 0 binding/rust/{ => xdb}/example/src/main.rs | 0 binding/rust/xdb/xdb/Cargo.toml | 22 + binding/rust/xdb/{ => xdb}/benches/search.rs | 0 binding/rust/xdb/{ => xdb}/src/ip_value.rs | 0 binding/rust/xdb/{ => xdb}/src/lib.rs | 0 binding/rust/xdb/{ => xdb}/src/searcher.rs | 0 binding/rust/xdb_parse/.gitignore | 25 + binding/rust/xdb_parse/Cargo.toml | 24 + binding/rust/xdb_parse/LICENSE-APACHE.txt | 176 + binding/rust/xdb_parse/LICENSE-MIT.txt | 23 + binding/rust/xdb_parse/README.md | 247 + binding/rust/xdb_parse/benches/search.rs | 50 + binding/rust/xdb_parse/examples/search_ip.rs | 18 + binding/rust/xdb_parse/gitignore.txt | 2 + binding/rust/xdb_parse/src/error.rs | 28 + binding/rust/xdb_parse/src/lib.rs | 20 + binding/rust/xdb_parse/src/utils.rs | 268 + binding/rust/xdb_parse/src/xdb.rs | 220 + binding/typescript/.gitattributes | 1 - binding/typescript/.gitignore | 4 - binding/typescript/README.md | 44 - binding/typescript/package.json | 59 - .../src/cache/abstract-cache-strategy.ts | 93 - .../src/cache/cache-strategy-factory.ts | 22 - .../src/cache/content-cache-strategy.ts | 45 - .../src/cache/file-cache-strategy.ts | 15 - .../src/cache/vector-index-cache-strategy.ts | 32 - binding/typescript/src/index.ts | 3 - binding/typescript/src/models/cache-policy.ts | 5 - binding/typescript/src/searcher/ISearcher.ts | 6 - binding/typescript/src/searcher/searcher.ts | 84 - binding/typescript/src/util/ip-util.ts | 33 - .../test/cache/content-cache-strategy.test.ts | 31 - .../test/cache/file-cache-strategy.test.ts | 36 - .../cache/vector-index-cache-strategy.test.ts | 40 - .../typescript/test/searcher/searcher.test.ts | 39 - binding/typescript/test/util/ip-util.test.ts | 16 - binding/typescript/tsconfig.json | 8 - binding/typescript/yarn.lock | 6370 - data/fix/github-issue-196.fix | 47 - data/fix/github-issue-200.fix | 28 - data/fix/github-issue-243.fix | 162 - data/github-issue-287.bug | 3 - data/global_region.csv | 3461 - data/ip.test.txt | 25 - data/ip2region_v4.xdb | Bin 11042429 -> 0 bytes data/ip2region_v6.xdb | 3 - data/ipv4_source.txt | 683843 --------------- data/ipv6_source.txt | 3 - data/segments.tests | 5 - data/segments.tests.mixed | 10 - maker/c/ReadMe.md | 7 - maker/cpp/Makefile | 11 - maker/cpp/readme.md | 217 - maker/cpp/xdb_edit.cc | 247 - maker/cpp/xdb_edit.h | 35 - maker/cpp/xdb_edit_test.cc | 50 - maker/cpp/xdb_make.cc | 240 - maker/cpp/xdb_make.h | 48 - maker/cpp/xdb_make_test.cc | 50 - maker/csharp/.gitignore | 37 - .../IP2RegionMaker.Test.csproj | 23 - maker/csharp/IP2RegionMaker.Test/Usings.cs | 1 - maker/csharp/IP2RegionMaker.Test/UtilTest.cs | 42 - .../IP2RegionMaker/IP2RegionMaker.csproj | 10 - maker/csharp/IP2RegionMaker/Program.cs | 72 - .../PublishProfiles/FolderProfile.pubxml | 13 - .../csharp/IP2RegionMaker/XDB/IndexPolicy.cs | 15 - maker/csharp/IP2RegionMaker/XDB/Maker.cs | 256 - maker/csharp/IP2RegionMaker/XDB/Segment.cs | 85 - maker/csharp/IP2RegionMaker/XDB/Util.cs | 77 - maker/csharp/ReadMe.md | 36 - maker/golang/Makefile | 10 - maker/golang/ReadMe.md | 143 - maker/golang/go.mod | 3 - maker/golang/go.sum | 0 maker/golang/main.go | 721 - maker/golang/make.bat | 18 - maker/golang/xdb/editor.go | 299 - maker/golang/xdb/index.go | 28 - maker/golang/xdb/maker.go | 378 - maker/golang/xdb/processor.go | 121 - maker/golang/xdb/searcher.go | 226 - maker/golang/xdb/segment.go | 161 - maker/golang/xdb/util.go | 225 - maker/golang/xdb/util_test.go | 170 - maker/golang/xdb/version.go | 96 - maker/java/ReadMe.md | 47 - maker/java/pom.xml | 200 - .../java/org/lionsoul/ip2region/MakerApp.java | 188 - .../java/org/lionsoul/ip2region/xdb/IPv4.java | 26 - .../java/org/lionsoul/ip2region/xdb/IPv6.java | 24 - .../lionsoul/ip2region/xdb/IndexPolicy.java | 25 - .../xdb/InvalidInetAddressException.java | 13 - .../lionsoul/ip2region/xdb/LittleEndian.java | 58 - .../java/org/lionsoul/ip2region/xdb/Log.java | 119 - .../org/lionsoul/ip2region/xdb/Maker.java | 335 - .../org/lionsoul/ip2region/xdb/Segment.java | 214 - .../java/org/lionsoul/ip2region/xdb/Util.java | 129 - .../org/lionsoul/ip2region/xdb/Version.java | 59 - .../ip2region/xdb/IndexPolicyTest.java | 21 - .../ip2region/xdb/LittleEndianTest.java | 36 - .../org/lionsoul/ip2region/xdb/MakerTest.java | 15 - .../lionsoul/ip2region/xdb/SegmentTest.java | 63 - .../org/lionsoul/ip2region/xdb/UtilTest.java | 95 - .../lionsoul/ip2region/xdb/VersionTest.java | 22 - maker/python/README.md | 38 - maker/python/main.py | 87 - maker/python/xdb/__init__.py | 7 - maker/python/xdb/index.py | 63 - maker/python/xdb/maker.py | 309 - maker/python/xdb/segment.py | 71 - maker/python/xdb/util.py | 48 - maker/rust/README.md | 32 - maker/rust/maker/Cargo.toml | 12 - maker/rust/maker/src/ip_value.rs | 61 - maker/rust/maker/src/main.rs | 215 - maker/rust/vimdiff.png | Bin 21382 -> 0 bytes 240 files changed, 1125 insertions(+), 714760 deletions(-) delete mode 100644 .gitattributes delete mode 100644 .gitignore delete mode 100644 LICENSE.md delete mode 100644 ReadMe.md delete mode 100644 binding/c/Makefile delete mode 100644 binding/c/ReadMe.md delete mode 100644 binding/c/main.c delete mode 100755 binding/c/test_util delete mode 100644 binding/c/test_util.c delete mode 100644 binding/c/xdb_api.h delete mode 100644 binding/c/xdb_searcher.c delete mode 100644 binding/c/xdb_util.c delete mode 100644 binding/cpp/Makefile delete mode 100644 binding/cpp/readme.md delete mode 100644 binding/cpp/xdb_bench.cc delete mode 100644 binding/cpp/xdb_bench.h delete mode 100644 binding/cpp/xdb_bench_test.cc delete mode 100644 binding/cpp/xdb_search.cc delete mode 100644 binding/cpp/xdb_search.h delete mode 100644 binding/cpp/xdb_search_test.cc delete mode 100644 binding/csharp/.gitignore delete mode 100644 binding/csharp/CHANGELOG.md delete mode 100644 binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj delete mode 100644 binding/csharp/IP2Region.Net.BenchMark/Program.cs delete mode 100644 binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj delete mode 100644 binding/csharp/IP2Region.Net.Test/SearcherTest.cs delete mode 100644 binding/csharp/IP2Region.Net.Test/Usings.cs delete mode 100644 binding/csharp/IP2Region.Net.Test/UtilTest.cs delete mode 100644 binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs delete mode 100644 binding/csharp/IP2Region.Net/IP2Region.Net.csproj delete mode 100644 binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs delete mode 100644 binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs delete mode 100644 binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs delete mode 100644 binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs delete mode 100644 binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs delete mode 100644 binding/csharp/IP2Region.Net/XDB/CachePolicy.cs delete mode 100644 binding/csharp/IP2Region.Net/XDB/Searcher.cs delete mode 100644 binding/csharp/IP2Region.Net/XDB/Util.cs delete mode 100644 binding/csharp/README.md delete mode 100644 binding/erlang/README.md delete mode 100755 binding/erlang/benchmarks/xdb-benchmark.sh delete mode 100644 binding/erlang/include/ip2region.hrl delete mode 100644 binding/erlang/priv/dummy delete mode 100644 binding/erlang/rebar.config delete mode 100644 binding/erlang/rebar.lock delete mode 100644 binding/erlang/src/ip2region.app.src delete mode 100644 binding/erlang/src/ip2region_app.erl delete mode 100644 binding/erlang/src/ip2region_sup.erl delete mode 100644 binding/erlang/src/ip2region_util.erl delete mode 100644 binding/erlang/src/ip2region_worker.erl delete mode 100644 binding/erlang/src/xdb.erl delete mode 100644 binding/erlang/src/xdb_benchmark.erl delete mode 100644 binding/erlang/test/xdb_test.erl delete mode 100644 binding/golang/Makefile delete mode 100644 binding/golang/ReadMe.md delete mode 100644 binding/golang/go.mod delete mode 100644 binding/golang/go.sum delete mode 100644 binding/golang/main.go delete mode 100644 binding/golang/xdb/searcher.go delete mode 100644 binding/golang/xdb/util.go delete mode 100644 binding/golang/xdb/util_test.go delete mode 100644 binding/golang/xdb/version.go delete mode 100644 binding/java/ReadMe.md delete mode 100644 binding/java/pom.xml delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/SearchApp.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java delete mode 100644 binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java delete mode 100644 binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java delete mode 100644 binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java delete mode 100644 binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java delete mode 100644 binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java delete mode 100644 binding/lua/ReadMe.md delete mode 100644 binding/lua/bench_test.lua delete mode 100644 binding/lua/search_test.lua delete mode 100644 binding/lua/util_test.lua delete mode 100644 binding/lua/xdb_searcher.lua delete mode 100644 binding/lua_c/Makefile delete mode 100644 binding/lua_c/ReadMe.md delete mode 100644 binding/lua_c/bench_test.lua delete mode 100644 binding/lua_c/search_test.lua delete mode 100644 binding/lua_c/util_test.lua delete mode 100644 binding/lua_c/xdb_searcher.c delete mode 100644 binding/nginx/README.md delete mode 100644 binding/nginx/config delete mode 100644 binding/nginx/src/ngx_http_ip2region_module.c delete mode 100644 binding/nginx/src/ngx_http_ip2region_module.h delete mode 100644 binding/nginx/t/http_ip2region.t delete mode 100644 binding/nodejs/.eslintrc.js delete mode 100644 binding/nodejs/.mocharc.json delete mode 100644 binding/nodejs/.nycrc.json delete mode 100644 binding/nodejs/ReadMe.md delete mode 100644 binding/nodejs/index.js delete mode 100644 binding/nodejs/package.json delete mode 100644 binding/nodejs/tests/bench.app.js delete mode 100644 binding/nodejs/tests/benchmark.js delete mode 100644 binding/nodejs/tests/function.test.js delete mode 100644 binding/nodejs/tests/test.app.js delete mode 100644 binding/php/ReadMe.md delete mode 100644 binding/php/batch_test.php delete mode 100644 binding/php/bench_test.php delete mode 100644 binding/php/search_test.php delete mode 100644 binding/php/xdb/Searcher.class.php delete mode 100644 binding/php/xdb/util_test.php delete mode 100644 binding/python/ReadMe.md delete mode 100644 binding/python/bench_test.py delete mode 100644 binding/python/iptest.py delete mode 100644 binding/python/search_test.py delete mode 100644 binding/python/xdbSearcher.py delete mode 100644 binding/rust/Cargo.toml rename binding/rust/{ => xdb}/ReadMe.md (100%) rename binding/rust/{ => xdb}/example/Cargo.toml (100%) rename binding/rust/{ => xdb}/example/src/cmd.rs (100%) rename binding/rust/{ => xdb}/example/src/main.rs (100%) create mode 100644 binding/rust/xdb/xdb/Cargo.toml rename binding/rust/xdb/{ => xdb}/benches/search.rs (100%) rename binding/rust/xdb/{ => xdb}/src/ip_value.rs (100%) rename binding/rust/xdb/{ => xdb}/src/lib.rs (100%) rename binding/rust/xdb/{ => xdb}/src/searcher.rs (100%) create mode 100644 binding/rust/xdb_parse/.gitignore create mode 100644 binding/rust/xdb_parse/Cargo.toml create mode 100644 binding/rust/xdb_parse/LICENSE-APACHE.txt create mode 100644 binding/rust/xdb_parse/LICENSE-MIT.txt create mode 100644 binding/rust/xdb_parse/README.md create mode 100644 binding/rust/xdb_parse/benches/search.rs create mode 100644 binding/rust/xdb_parse/examples/search_ip.rs create mode 100644 binding/rust/xdb_parse/gitignore.txt create mode 100644 binding/rust/xdb_parse/src/error.rs create mode 100644 binding/rust/xdb_parse/src/lib.rs create mode 100644 binding/rust/xdb_parse/src/utils.rs create mode 100644 binding/rust/xdb_parse/src/xdb.rs delete mode 100644 binding/typescript/.gitattributes delete mode 100644 binding/typescript/.gitignore delete mode 100644 binding/typescript/README.md delete mode 100644 binding/typescript/package.json delete mode 100644 binding/typescript/src/cache/abstract-cache-strategy.ts delete mode 100644 binding/typescript/src/cache/cache-strategy-factory.ts delete mode 100644 binding/typescript/src/cache/content-cache-strategy.ts delete mode 100644 binding/typescript/src/cache/file-cache-strategy.ts delete mode 100644 binding/typescript/src/cache/vector-index-cache-strategy.ts delete mode 100644 binding/typescript/src/index.ts delete mode 100644 binding/typescript/src/models/cache-policy.ts delete mode 100644 binding/typescript/src/searcher/ISearcher.ts delete mode 100644 binding/typescript/src/searcher/searcher.ts delete mode 100644 binding/typescript/src/util/ip-util.ts delete mode 100644 binding/typescript/test/cache/content-cache-strategy.test.ts delete mode 100644 binding/typescript/test/cache/file-cache-strategy.test.ts delete mode 100644 binding/typescript/test/cache/vector-index-cache-strategy.test.ts delete mode 100644 binding/typescript/test/searcher/searcher.test.ts delete mode 100644 binding/typescript/test/util/ip-util.test.ts delete mode 100644 binding/typescript/tsconfig.json delete mode 100644 binding/typescript/yarn.lock delete mode 100644 data/fix/github-issue-196.fix delete mode 100644 data/fix/github-issue-200.fix delete mode 100644 data/fix/github-issue-243.fix delete mode 100644 data/github-issue-287.bug delete mode 100644 data/global_region.csv delete mode 100644 data/ip.test.txt delete mode 100644 data/ip2region_v4.xdb delete mode 100644 data/ip2region_v6.xdb delete mode 100644 data/ipv4_source.txt delete mode 100644 data/ipv6_source.txt delete mode 100644 data/segments.tests delete mode 100644 data/segments.tests.mixed delete mode 100644 maker/c/ReadMe.md delete mode 100644 maker/cpp/Makefile delete mode 100644 maker/cpp/readme.md delete mode 100644 maker/cpp/xdb_edit.cc delete mode 100644 maker/cpp/xdb_edit.h delete mode 100644 maker/cpp/xdb_edit_test.cc delete mode 100644 maker/cpp/xdb_make.cc delete mode 100644 maker/cpp/xdb_make.h delete mode 100644 maker/cpp/xdb_make_test.cc delete mode 100644 maker/csharp/.gitignore delete mode 100644 maker/csharp/IP2RegionMaker.Test/IP2RegionMaker.Test.csproj delete mode 100644 maker/csharp/IP2RegionMaker.Test/Usings.cs delete mode 100644 maker/csharp/IP2RegionMaker.Test/UtilTest.cs delete mode 100644 maker/csharp/IP2RegionMaker/IP2RegionMaker.csproj delete mode 100644 maker/csharp/IP2RegionMaker/Program.cs delete mode 100644 maker/csharp/IP2RegionMaker/Properties/PublishProfiles/FolderProfile.pubxml delete mode 100644 maker/csharp/IP2RegionMaker/XDB/IndexPolicy.cs delete mode 100644 maker/csharp/IP2RegionMaker/XDB/Maker.cs delete mode 100644 maker/csharp/IP2RegionMaker/XDB/Segment.cs delete mode 100644 maker/csharp/IP2RegionMaker/XDB/Util.cs delete mode 100644 maker/csharp/ReadMe.md delete mode 100644 maker/golang/Makefile delete mode 100644 maker/golang/ReadMe.md delete mode 100644 maker/golang/go.mod delete mode 100644 maker/golang/go.sum delete mode 100644 maker/golang/main.go delete mode 100644 maker/golang/make.bat delete mode 100644 maker/golang/xdb/editor.go delete mode 100644 maker/golang/xdb/index.go delete mode 100644 maker/golang/xdb/maker.go delete mode 100644 maker/golang/xdb/processor.go delete mode 100644 maker/golang/xdb/searcher.go delete mode 100644 maker/golang/xdb/segment.go delete mode 100644 maker/golang/xdb/util.go delete mode 100644 maker/golang/xdb/util_test.go delete mode 100644 maker/golang/xdb/version.go delete mode 100644 maker/java/ReadMe.md delete mode 100644 maker/java/pom.xml delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/MakerApp.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/IndexPolicy.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/InvalidInetAddressException.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/Maker.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/Segment.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java delete mode 100644 maker/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/IndexPolicyTest.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/MakerTest.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/SegmentTest.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java delete mode 100644 maker/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java delete mode 100644 maker/python/README.md delete mode 100644 maker/python/main.py delete mode 100644 maker/python/xdb/__init__.py delete mode 100644 maker/python/xdb/index.py delete mode 100644 maker/python/xdb/maker.py delete mode 100644 maker/python/xdb/segment.py delete mode 100644 maker/python/xdb/util.py delete mode 100644 maker/rust/README.md delete mode 100644 maker/rust/maker/Cargo.toml delete mode 100644 maker/rust/maker/src/ip_value.rs delete mode 100644 maker/rust/maker/src/main.rs delete mode 100644 maker/rust/vimdiff.png diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index acad55b..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -data/ipv6_source.txt filter=lfs diff=lfs merge=lfs -text -data/ip2region_v6.xdb filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 67acc77..0000000 --- a/.gitignore +++ /dev/null @@ -1,92 +0,0 @@ -*.class -*.out -*.o -*.pyc -*~ -*.log -*.la -*.so -*.xdb -*.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 -/v1.0//binding/java/classes/ -/v1.0//binding/java/doc/ -/v1.0//binding/java/target/ -/v1.0//binding/java/*.jar -/binding/java/classes/ -/binding/java/doc/ -/binding/java/target/ -/binding/java/*.jar - -# clang -/binding/c/xdb_searcher -/binding/c/util_test -/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 -/v1.0/binding/c#/**/.vs/ -/v1.0/binding/c#/**/packages -/v1.0/binding/c#/**/bin -/v1.0/binding/c#/**/obj -/binding/c#/**/.vs/ -/binding/c#/**/packages -/binding/c#/**/bin -/binding/c#/**/obj - -# Nodejs -/v1.0/binding/nodejs/tests/unitTests/__snapshots__ -/v1.0/binding/nodejs/coverage -/binding/nodejs/tests/unitTests/__snapshots__ -/binding/nodejs/coverage -/binding/nodejs/node_modules -/binding/nodejs/.nyc_output -/binging/nodejs/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 diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 739ef00..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,225 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -========================================================================== -The following license applies to the ip2region library --------------------------------------------------------------------------- -Copyright (c) 2015 Lionsoul - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -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. diff --git a/ReadMe.md b/ReadMe.md deleted file mode 100644 index e185273..0000000 --- a/ReadMe.md +++ /dev/null @@ -1,115 +0,0 @@ -# Ip2region 是什么 - -[ip2region](https://ip2region.net) - 是一个离线IP地址定位库和IP定位数据管理框架,同时支持`IPv4`和`IPv6`,10微秒级别的查询效率,提供了众多主流编程语言的 `xdb` 数据生成和查询客户端实现。 - - - -# Ip2region 特性 - -### 1、离线定位库 - -项目本身同时了提供了一份 IPv4(`data/ipv4_source.txt`) 和 IPv6(`data/ipv6_source.txt`) 的原始数据和对应的 xdb 文件(`data/ip2region_v4.xdb` 和 `data/ip2region_v6.xdb`) 用于实现精确到城市的的查询定位功能。 - -### 2、数据管理框架 - -`xdb` 支持亿级别的 IP 数据段行数,region 信息支持完全自定义,自带数据的 region 信息固定了格式为:`国家|省份|城市|ISP`,你可以在 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) | golang xdb 查询客户端 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [PHP](binding/php) | php xdb 查询客户端 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Java](binding/java) | java xdb 查询客户端 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [C](binding/c) | POSIX C xdb 查询客户端 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Lua_c](binding/lua_c) | lua c 扩展 xdb 查询客户端 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Lua](binding/lua) | lua xdb 查询客户端 | :white_check_mark: | :x: | [Lion](https://github.com/lionsoul2014) | -| [Rust](binding/rust) | rust xdb 查询客户端 | :white_check_mark: | :x: | [gongzhengyang](https://github.com/gongzhengyang) | -| [Python](binding/python) | python xdb 查询客户端 | :white_check_mark: | :x: | [厉害的花花](https://github.com/luckydog6132) | -| [Nodejs](binding/nodejs) | nodejs xdb 查询客户端 | :white_check_mark: | :x: | [Wu Jian Ping](https://github.com/wujjpp) | -| [Csharp](binding/csharp) | csharp xdb 查询客户端 | :white_check_mark: | :x: | [Alen Lee](https://github.com/malus2077) | -| [Erlang](binding/erlang) | erlang xdb 查询客户端 | :white_check_mark: | :x: | [malou](https://github.com/malou996) | -| [Nginx](binding/nginx) | nginx 扩展 xdb 查询客户端 | :white_check_mark: | :x: | [Wu Jian Ping](https://github.com/wujjpp) | -| [C++](binding/cpp) | C++ xdb 查询客户端 | :white_check_mark: | :x: | [Yunbin Liu](https://github.com/liuyunbin) | -| [Typescript](binding/typescript) | Typescript xdb 查询客户端 | :white_check_mark: | :x: | [Alan Lee](https://github.com/malus2077) | - - -以下工具链实现由社区开发者通过第三方仓库贡献: -| 编程语言 | 描述 | 贡献者 | -|:--------------------------------------------------------------- |:------------------------|:-----------------------------------------------| -| [ip2region-composer](https://github.com/zoujingli/ip2region) | php composer 管理客户端 | [邹景立](https://github.com/zoujingli) | -| [ruby-ip2region](https://github.com/jicheng1014/ruby-ip2region) | ruby xdb 查询客户端实现 | [jicheng1014](https://github.com/jicheng1014) | -| [Ip2regionTool](https://github.com/orestonce/Ip2regionTool) | ip2region 数据转换工具 | [orestonce](https://github.com/orestonce) | - - -# `xdb` 数据生成 - -API 介绍,使用文档和测试程序请参考如下 `maker` 生成程序下的 ReadMe 文档: - -| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 | 贡献者 | -| :---------------------- | :------------------- |:-------------------|:-------------------| :----------------------------------------- | -| [Golang](maker/golang) | golang xdb 生成程序 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Java](maker/java) | java xdb 生成程序 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Python](maker/python) | python xdb 生成程序 | :white_check_mark: | :x: | [leolin49](https://github.com/leolin49) | -| [Csharp](maker/csharp) | csharp xdb 生成程序 | :white_check_mark: | :x: | [Alan Lee](https://github.com/malus2077) | -| [Rust](maker/rust) | rust xdb 生成程序 | :white_check_mark: | :x: | [KevinWang](https://github.com/KevinWL) | -| [C++](maker/cpp) | C++ xdb 生成程序 | :white_check_mark: | :x: | [Yunbin Liu](https://github.com/liuyunbin) | - - -# `xdb` 数据更新 - -ip2region 项目的核心在于 研究 IP 数据的存储和快速查询的设计和实现, 项目自带的 `./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#xdb-数据编辑) | golang IP 原始数据编辑器 | :white_check_mark: | :white_check_mark: | [Lion](https://github.com/lionsoul2014) | -| [Java](maker/java#xdb-数据编辑) | java IP 原始数据编辑器 | :white_check_mark: | :soon: | [Lion](https://github.com/lionsoul2014) | -| [C++](maker/cpp#xdb-数据编辑) | C++ IP 原始数据编辑器 | :white_check_mark: | :x: | [Yunbin Liu](https://github.com/liuyunbin) | - - -### 检测自动更新 -如果你想通过你自己的 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 整个缓存的查询都 并发安全的,基于文件的查询都 不是 并发安全的实现,不同进程/线程/协程需要通过创建不同的查询对象来安全使用,并发量很大的情况下,基于文件查询的方式可能会导致打开文件数过多的错误,请修改内核的最大允许打开文件数(fs.file-max=一个更高的值),或者将整个xdb加载到内存进行安全并发使用。 - -### 2、技术资源分享 -1. xdb 数据结构分析:[“ip2region xdb-数据结构描述“](https://ip2region.net/doc/xdb/ipv4_structure) -2. xdb 查询过程分析:[“ip2region xdb-查询过程描述”](https://ip2region.net/doc/xdb/ipv4_search) -3. xdb 生成过程分析:[“ip2region xdb-生成过程描述”](https://ip2region.net/doc/xdb/ipv4_generate) -4. xdb 文件生成教程:[“ip2region xdb-文件生成教程”](https://ip2region.net/doc/data/ipv4_xdb_make) -5. xdb 数据更新方法:[“ip2region 数据更新和 xdb 数据编辑器的使用”](https://mp.weixin.qq.com/s/cZH5qIn4E5rQFy6N32RCzA) - -### 3、技术信息博客 -1. [Ip2Region 官方社区](https://ip2region.net) -2. 请先关注微信公众号 lionsoul-org (狮子的魂) diff --git a/binding/c/Makefile b/binding/c/Makefile deleted file mode 100644 index f4724ba..0000000 --- a/binding/c/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -all: xdb_searcher test_util - -xdb_searcher: xdb_api.h xdb_util.c xdb_searcher.c main.c - gcc -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 -O2 -I./ xdb_util.c test_util.c -o test_util - -xdb_searcher.o: xdb_searcher.c - gcc -c xdb_searcher.c - -xdb_util.o: xdb_util.c - gcc -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 diff --git a/binding/c/ReadMe.md b/binding/c/ReadMe.md deleted file mode 100644 index cb133c1..0000000 --- a/binding/c/ReadMe.md +++ /dev/null @@ -1,341 +0,0 @@ -# ip2region xdb c 查询客户端实现 - - -# 使用方式 - -### 关于 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 -#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(®ion, region_buffer, sizeof(region_buffer)); - if (err != 0) { - printf("failed to init the region buffer with errcode=%d\n", err); - return 1; - } - - // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用 - 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 = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - - long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, ®ion); - cost_time = (int) (xdb_now() - s_time); - if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip_string, err); - } else { - printf("{region: %s, took: %d μs}", region.value, cost_time); - } - - // 清理 region 信息的内存资源,每次 search 之后都得调用 - xdb_region_buffer_free(®ion); - - // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。 - - // 3、关闭 xdb 查询器 - xdb_close(&searcher); - xdb_clean_winsock(); // windows 下调用 - return 0; -} -``` - -### 缓存 `VectorIndex` 索引 - -我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。 -```c -#include -#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(®ion, 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 = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - - long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, ®ion); - cost_time = (int) (xdb_now() - s_time); - if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip_string, err); - } else { - printf("{region: %s, took: %d μs}", region.value, cost_time); - } - - // 清理 region 信息的内存资源,每次 search 之后都得调用 - xdb_region_buffer_free(®ion); - - - // 备注:并发使用,每一个线程需要单独定义并且初始化一个 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 -#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(®ion, 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 = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - - long cost_time = 0, s_time = xdb_now(); - err = xdb_search_by_string(&searcher, ip_string, ®ion); - cost_time = (int) (xdb_now() - s_time); - if (err != 0) { - printf("failed search(%s) with errno=%d\n", ip_string, err); - } else { - printf("{region: %s, took: %d μs}", region.value, cost_time); - } - - // 清理 region 信息的内存资源,每次 search 之后都得调用 - xdb_region_buffer_free(®ion); - - - // 备注:并发使用,使用这种方式创建的 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(®ion, 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(®ion, NULL, 0); -if (err != 0) { - // 初始化失败 - printf("failed to init region buffer width errcode=%d", err); - return; -} - - -// 备注:在每次调用 search 完成 IP 定位信息的查询后,你需要手动调用函数来释放内存 . -// search 函数使用未经清理的 region 信息会报错。 -xdb_region_buffer_free(®ion); -``` - - -# 测试程序编译 - -通过如下方式编译得到 xdb_searcher 可执行程序: -```bash -# cd 到 c binding 根目录 -➜ c git:(fr_c_ipv6) ✗ make -gcc -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher -gcc -O2 -I./ xdb_util.c test_util.c -o test_util -``` - - -# 查询测试 - -通过 `xdb_searcher search` 命令来测试对 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>> 120.229.45.2 -{region: 中国|广东省|深圳市|移动, io_count: 3, took: 29 μs} -``` - -例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 查询测试: -```bash -➜ c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v6.xdb -ip2region xdb searcher test program -source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) -type 'quit' to exit -ip2region>> :: -{region: |||, io_count: 2, took: 38 μs} -ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff -{region: 中国|广东省|深圳市|数据中心, io_count: 13, took: 77 μs} -``` - -输入 ip 即可进行查询,输入 quit 即可退出测试程序。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同的缓存实现的效率。 - - - -# 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 文件相同的源文件。 diff --git a/binding/c/main.c b/binding/c/main.c deleted file mode 100644 index 1a7f39b..0000000 --- a/binding/c/main.c +++ /dev/null @@ -1,428 +0,0 @@ -// 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 -// @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)); - 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; - unsigned int ip; - char line[512] = {'\0'}, region[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 (strcasecmp( 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, 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; -} diff --git a/binding/c/test_util b/binding/c/test_util deleted file mode 100755 index 3024380d49e6a066fe52ca64f1b58aa7682eff22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22576 zcmeHP3v^V~x!!qzBjC({0V8TT#ZW_qaV7)|sniT4a6*Gb2uKw=88Qno+869bA?Db>o{@86G^Glz`r zx{r0&y(=3s|JmRE|9|iO?|(neKIiPO)-^0FEGS?q6tT}TiVaK%sG{=c+^$Fi$LYl z@JBwi=!O*x?bb(_YRp=YK{#RBnxuXy9$}9=IZhab7lOZN7gxjol8o%yISkP}cvT*JO&6b2CM2t|xmLRf1@ zFi1cC09zL{j8JG!pU zBK~mb%Rv@5I$GK~SgR2XhgoY&I2spNdyKWlgTY&cWXy<080n-AH-{~1sGq;USK*rF zs?1zxuV!U0D_pZP46~T8zG;as5bOx93&o9KN7Ir8;b$#lJUf3;xA&a%J{{czmUC&I0ZG2sPVf`&Ra+2{G#wKi4R!tUXRd|vfzDJ2>i4K ze@OD5wcs_M5d2KePb%Mkll(Rd{;=eCTJVFCztnSl2KQ#;G|>$HBpas*a`0cWahezh|2`Y1325-H zY@8;Z!H#U4CY-^w**Hx!gUhmUnqUU!XX7-n3|3_0G@%Sm&BkdW8Jv)f(}X-&n2kFq z{_Z(d-}JOcKg(FFV@fUK_DD}Vhuau){EQ|3mL>kWCH|@<{u@jD7nb+|OZ;g|e5WP; zs3rbgOZ*{A`~ge+UQ7J$oVa(TcZIiU#q#cS!NxTWy)(B|GuF`4YV(}ZduNwpA-5#? z*3cwjpZcEl&id{H&hGsM`sOo+w;}mPN{6MB{hRv|$Mlv{`koWT^*v29ZTjXT##B#V z`bhHVEo;21y=%N*=+}Ga^?#DF)K0X;&&cbBjx{8YA(vNxK0|Kj=}Xl^-O%qHQ@Rl4 z?c+IBpM2p9IvBf;pKffb^YnS1g(fff(i8i!LGxa#%Q0mWu`}QyR8H?5wU)@-v&KYp zjqieqy2gd+0jalNQt;2^{Ie^FOh2b5PwRW$x?bONwn#5Hpucd=D1m~6RA5UF2sOWoc-bXS^T)jf?p0|uH_2da-oZd696_Hf(qjWkIKv@rrdLHqDHIQXy`N7+eXsT~- ztv$W@%*IN6o8EFpFW9RWoEw@X({KNj-hHq@?|!pDpZh0cykn=sv9oaQo{ewpCUf?0 z_4GkA{CYPTr(g8%`s7)to2|Dz3w@qGz2N6V7b|;_zCP$WU`+Hnc3#IEJI58y{pH4# zP}e%FKI8!-2zyig`&+a2r}zpz`O!0_D9>^lnNpue{mJ+N&k@fVJ^8kt>`$!#)_pc@ z=z8y0O7-O66SVk!nyQfc{$Id8kK&0>>pja$dlr-*Tu?%BJ$W$zExEv{Czso3jMK@t zP~jS+y_RZ(r01of*G3+3ZGaZ4Tf;NOxQ$8OOiGje^~rsd z>$L|_M;rXJvzS*K*P_~wK`*L}cw%fra)7!FGUWB@L{gpS_$_OI12yF?+L!8g_EJ^9 z$(H)voUNaa@))Lm7od8hh2IR09j1OOJ!hVv&MAfoJ^5kko8Y24c9Pv!AtKuLHPkY- zL8h&Kh8FCpn-PU^>yrnl{)_68CmND(r)oJ`hZa0YaCvPqh`Ro^A^D~-$9FzRr|akL zPdILANS;>12e-%DsWzn5dCm+SM_l+D>R+0vKWR3#Z?&oZ5d16kUyj=8Jzuq@iikJ) z+AV`0{22|N8DDsQoC&V&*fpuO`-4K`=G|l+Toxs29lIhExUtCBUrU9dCb^gI5^p|| z_!L3Y0rieM1qsx6FYjpjuLktFhaC^~Id=B#87QPOO22-p7;Hn2=JYr2X6Ra_se6TQ z2_2hXGA8KBSJA^#D^cg3yCm-gL= zaxcoWe-2}qzh)@5ImSY;{gY(732aYL`)%$6?@z(2JL>kzRzp=W{&w9_NR!YVNJ~6q zYEO)|8l@R83_Ysq>)3S(SeJp7d()>qecY9%I(FUUM8aH=P;86`JX-Iq(RkjJWj`OE z;0ooQGXcG)cv?Aks@k$r4D*{%$*B0&>u|qk<78H@4~~5oh8@e7imR%j+I_``26uG zBM(l9b1Af47g>zj*SoSldFE*sV%`v>tLu1QR zh{Fw+Y(8$RA@6Ih@BYZKezD%$REA06t!HTEm^uok?z07nS*?y}E%4qeCe21(j`5>) zxO_RPFASAfit4-h93s~znD1U1`W^QhD1Tw-k*&~QwEht3A4mGBpQk#d{vY!W)O*FY zN@!1x9(p8YM~Z29mi|grbLLddhUUR&vEHL6|46zq7n~EiFPFM6f$rjD5t_dm+K0xd z`4)m`LR+>8N23nTTl!Pqc%Ryk+n4gwa`Gd0=>EVV^20g`xp7N93L&!8rLa_S88SOH z3%3d5i>&#XT!!+~kl&Tr{E!{S@w46MijBtZbI!zP^v&rS$Nd=ZkANJkv7Jkl>)n5z zxqd3f3X$v#& zmDQ#C7Z$1*Eyw_l2lm(wC9X#K46J)j+LABm$#*lhXStfp>!_UZKcjDEUVYrUJN}qc z$-MfOV-dO(n^mc$)LpSELH!oh_UN|So|R2e$JV9%-hUDusQPX;=p0C3aJ0JyTTY*#>GWOfJaB62(G_jxi zG4vIX$Mpy^X2;a4^*i^GrPV#3*T?5|C_RDOa8;Hd6b|i8y?Q#QG3EG0?K$s46hZI3 z=p=7X*@nGWyaF9$OWa9baT#&kdGZi4%W2l#0MH=cm#oy9kIHLm$*1TVZANO)hNGTD zoUAWe7CAqHELFqt*6ah#ewUOy5-YFlZ`_enB2nN%u#+tI1<# z&c^;nL+=mEh_Rvfan3m8lH)0cmHM8WM7yGiduJD-IH?!krnbBlYo>zWJ1~LHfsA zv?|ik<%EAYHr+!yd#;qpXXy5SI`wLlVH4%oWAn~+B);YCxy|W$wjud0*7~F~WsnDv zkN%#!fHz55??6G}NUKEVaLiMQuOYsjd)uZmYRIf(eG!gDZJpy-SQoqrMO0(uT4SJX z(2ZGy`>EJoaH;gv$+wV7TSc<}KzIqZkCgE9NFM4=Ed&v36+XAo6os5e!iWV%EHGk$ z5etl1V8j9=78tR>hy_M0Fk*rKO$!v@?Z%F^tiEw&CH>67&w9FAKZm(o*WiD^Z}_hX z#VY9KNW3t)zS7khSj*@|(O^I`qFOlW4`^+8KQ!3EJT5o=W3#JV6|+6AS#FodGn;wl zR=7NKs$3qI$L(enE$-Ip*49>cbv3K-%yo5k(tnSKRabX*QbMaqc_c{#t=sLXuB`TWDo}JPC=p9};<7;JB?MJMDCUbN*7{na?WjSpd|G^l zHZ4BATALOa!;E0uz&pAAfKM7U3#<>e7|{-2C=v*En)q6x5hEBe1j0??3&lhX8Ocn@ zzl1{GTERfI_LVUj!unuG9F0nu&(Ju+_4{JcaHwU232wm)z(z3O^BVvYl2^R$Dfs-8H9|>sW=dz4iBx-2wexs$0_fhSW^JHHo#j$YEkNz8v1zSR`AsR~1%UctX z76T5cMf~kS8eCfNemO4&Yw<^}GSIllf;vT(Rdjz=a9Po3=$n!0U|;o8I=uzgZ7-+O zJ3u=@4-tI@yE1r}sPxrzI)=FIwRCy_bQS1(pxZ%hcqXtL^m5Qcpl;Ar1L^c?&~2bw zC>``c(5m03)4vAo1jQ@cY#V3^Xw|WFx(T!svTjge2Iy|k zZJ?`C>GYGJ+d+Q~dKUCFXy;%$t>Im(qo6gQRd1%#&7iiEup7ODg@A2q9tys^j1_b` z3oaXXfo&^nMfm0TdlMmo9dUz0KCeuAe;j z)3eH^0_F0nL3jKPd<4iqdJX(-2TrdH7yukPZ)MNr|jJLlVNwPPFj zWu4syKLBeNZGkB?sd8}i5N6Kn=}PlK2H!;N-lcVT0{s)vrWV)UomNGG?o zm+ZGl+8VVNwss@g05A2)n=-t$!q)CKVJNWHk8R?H!X=vlzU`3N1eqAgWbNza_N~p> zcQL&RNNq&lCGG?NIO0bj>?E*03r79=Nnq+-bK;{uPGf|^sC~cuFw_-bZ?LyYmjz=W}E0@w;ej4DD9} zn8v&N!LyKfzF>E*22Qrj{wdOHEUd~tgzYFJ0U?EakHiFQXuH<1rs z4wmP@-w6H{8UA)K+%UEg43vHn>09%pHz1wH*uzNQi#|m*{d!@ez4YFq8|+$d@qByv zJ=B-(9__VPbziW^K2T8f#ey;RDq!em=x-V8qUG@n1b?fcuMuPS-@)%p`oMdhzMJh` zg+(Wa*H=sWuEXCCp)dBEboyJk7Hy6^&+qFuqpzD=n_TPvO;F!MsqdfE_fG2jCiOj& z`hH1$ucW?DQr{z~?~l~?Mu)ZGO+>Ch;W0s&8c%rV2ByY{)mI7z)koELT$f2XxF`>J z&5#H6Js0^U1(p8(xpb7`JJ2^NsQD}>ekzMU%zs@j_R76mm&iCFggRrgYX zkZV$)d58k_RSIfuy+lM=pZwTI;XCAoDz{TAl5D(hlc@TPO!496J6=Cz$DwmF-YE5@ zv*|o%JOJkWQ)T*}WgL&+|3`xQ9^0ziO#?APjiil|HcJ|lv`f;hl0Gcy4oUkYJuK;f zq^BiivSLn2wg0Ay+8Raqt-mzHQW)7F?!!J`!$D{x0&IbFgZr$v1GR?ObPQ`g);{#L+>**&s9Xfhe^ zKma|Te+uWngsJZ)n(7B}0?PRvJ}FR1c;R@a<}0PATJo!TbA?Q} zF^~Rm9{j6HkDNGYte~(};%c70UBK)SiJz7emYPo=%cK7}$=@Z%A2mO|DseTh^EDju zpf&#l>PK>7R_pL$vN9hVx4%6Pcd}r+ov>#HTJ> zfR8Vn%&g;djO%f-t+JeIeQ_7@Q_H)B1~rb~C;3lH`}2D&u+aX*aDMU=$v(hj$Y;xf8dq4HS!QKRIqk>%lQY{;zOxD+;#h|+(B02j)%yCkmGo2nn( zFL5Tzqx|qkN{{qsxy<2*i%@+VWxLb3OyP*qFBg_-Tsp>a!DIe?PvtAyQRQ2LjzjvJ zrJqZ}$|Sy3mS>YxRw?mDY3G>4>m{yP?63wn`Lor|A>f+1fbzOq>5=)WdFla)V`|{x zNy$(5LR00nnH(hkLU;$dwmCen^vjAWJFw*~9frph#eot2wIQG3Unh3g6GkZP!r>Ww zhhFVl`ocJ9fIW7K~B2Je#k1;_)Z()j9hnX1<=W4=WVOXqtV zd`lNDTwd4YYx2%-sDs4$$Bfiu&hW^d4U)m=*bX{qBnO}(XP#*IP_Ud6Ml!58{Ugh{ zehxkUpg5^RrA!Y^KJGLSIvgdVQI^IZ$U%HTdJ-m&EXhfs>|jA7iqk}BGoJC55^lmD z=HwQ`a@q70?h&7QoW{L3 zV`Q@`dRC&ILJ=N`-o^_vT!qLm;v^Mbh4xrZuKcK%9K?F|iZ2k4`r7dH08efxuW-c#&Vc%cS~p~mipkM#t_2xH_y;d{juB1I$HXx}1r_;Wk_W`LM~GcpK#upSa9` z$c#Vuq9oq=RSR?8tUYH{@H|A5V;Deh;V1cIN}-(Yymq!^!uyxW1v$SdE}F<9#4|Ki z&F90JJZhAo!+M52frr;A=c%bOG(Ef&SI!Zc?1(WJ9%ltz>mmtPtOJK1br>5=(Aq=@M}>s~67tTkpGi{%XK(Yz+n6h` zA%ZLfH9AC!be)`t4{062u%86vRV-{U7qu#L;nKA(Dlg)}7UnX7oru!h0PZLsqg=r@ zRE_3_wg9xLn9w1n1tFoXNs2Os0$xCWdkC6^F1RjpVRCAx$ISorK+6)`!r-1$gi@^M zC@3m#M+Asp;x&gW5ur7ZlCP5Tipu3~Mnvr^DFENX<;YvlyDL+Sl45z36X-s`oWI&v zP*ls|rhD$(KCq#z-bRp$sd*VS5)n@Q5ng0N?tC@80|BV zh>}?&X97d{EAP}jYy|GJr!5`dWtqnd29J4%zAM_ zHdXRFh!H_iRezPgwf_GAUXq_rLI`T#QqeA1VUnjMrB(hBV8o*IXV#H&J*oC{!NP;J z{T~Esme-`5qWqbulqI5;sfvEjBJY;_e~KzTr9#CO-C>chk@AZ2!YN@;C8y|5t@84I zNZl7rmim+mC8y%gSmf0{gQDvGUh!M)w_nPu_E-0}iXOBGsPu*)K^S>l&Mm~^S~tNYKc8o{XYQDT&rIebFq?>2+DR`OR{ za3yc18Q(SW?x+!wm@EK27ol5nRUS2dUkFTR)VVevneui~ZdIh_0j@_Ely J76D6?{RcVTP<{Xa diff --git a/binding/c/test_util.c b/binding/c/test_util.c deleted file mode 100644 index de41057..0000000 --- a/binding/c/test_util.c +++ /dev/null @@ -1,213 +0,0 @@ -// 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 -// @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 bytes, 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, 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; -} diff --git a/binding/c/xdb_api.h b/binding/c/xdb_api.h deleted file mode 100644 index 2db43cf..0000000 --- a/binding/c/xdb_api.h +++ /dev/null @@ -1,268 +0,0 @@ -// 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 -// @Date 2022/06/27 - -#ifndef C_IP2REGION_XDB_H -#define C_IP2REGION_XDB_H - -#include -#include -#include - -#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 -#include -#include -#pragma comment(lib, "ws2_32.lib") -#elif ( defined(linux) || defined(_UNIX) || defined(__APPLE__) ) -# define XDB_PUBLIC(type) extern type -# define XDB_PRIVATE(type) static inline type -# define XDB_LINUX -#include -#include -#include -#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_segment_index_size 14 - -// --- 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); - -// --- 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); - -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 diff --git a/binding/c/xdb_searcher.c b/binding/c/xdb_searcher.c deleted file mode 100644 index 8eba674..0000000 --- a/binding/c/xdb_searcher.c +++ /dev/null @@ -1,257 +0,0 @@ -// 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 -// @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; -} - -XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *region) { - if (region->type == xdb_region_buffer_auto) { - xdb_free(region->value); - region->value = NULL; - } -} - -// --- END region buffer - -// internal function prototype define -XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length); - -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 = NULL; - - // 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); - // binary search to get the final region info - seg_index_size = xdb->version->segment_index_size; - segment_buffer = xdb_malloc(seg_index_size); - if (segment_buffer == NULL) { - return -2; - } - - 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) { - err += 20; - goto defer; - } - - // 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) { - err = 100; - goto defer; - } - - // buffer alloc checking - err = xdb_region_buffer_alloc(region, data_len); - if (err != 0) { - err += 100; - goto defer; - } - - err = read(xdb, data_ptr, region->value, data_len); - if (err != 0) { - err += 30; - goto defer; - } - -defer: - // checn and free the segment buffer - if (segment_buffer != NULL) { - xdb_free(segment_buffer); - segment_buffer = NULL; - } - - 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; -} diff --git a/binding/c/xdb_util.c b/binding/c/xdb_util.c deleted file mode 100644 index c2e8ef9..0000000 --- a/binding/c/xdb_util.c +++ /dev/null @@ -1,494 +0,0 @@ -// 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 -// @Date 2022/06/27 - -#include "xdb_api.h" -#include - -// for Linux -#ifdef XDB_LINUX -#include "sys/time.h" -#elif XDB_WINDOWS -#include -#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) { - 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 int fileBytes = ftell(handle); - long int maxFilePtr = (1L << (runtime_ptr_bytes * 8)) - 1; - // printf("fileBytes: %ld, maxFilePtr: %ld\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, 14, _ipv4_sub_compare}, - - // 38 = 16 + 16 + 2 + 4 - {xdb_ipv6_id, "IPv6", xdb_ipv6_bytes, 38, xdb_ip_sub_compare}, - - // END - {0, NULL, 0, 0, NULL} -}; - -XDB_PUBLIC(xdb_version_t *) xdb_version_v4() { - return &_ip_version_list[0]; -} - -XDB_PUBLIC(xdb_version_t *) xdb_version_v6() { - return &_ip_version_list[1]; -} - -XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *version) { - return version->id == xdb_ipv4_id; -} - -XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *version) { - return version->id == xdb_ipv6_id; -} - -XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *name) { - // to upper case the name - for (int i = 0; name[i] != '\0'; i++) { - name[i] = toupper((unsigned char) name[i]); - } - - if (strcmp(name, "V4") == 0 || strcmp(name, "IPV4") == 0) { - return xdb_version_v4(); - } else if (strcmp(name, "V6") == 0 || strcmp(name, "IPV6") == 0) { - return xdb_version_v6(); - } else { - return NULL; - } -} - -XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *header) { - // Old structure with ONLY IPv4 supports - if (header->version == xdb_structure_20) { - return xdb_version_v4(); - } - - // structure 3.0 with IPv6 supporting - if (header->version != xdb_structure_30) { - return NULL; - } - - if (header->ip_version == xdb_ipv4_id) { - return xdb_version_v4(); - } else if (header->ip_version == xdb_ipv6_id) { - return xdb_version_v6(); - } else { - return NULL; - } -} - -// --- END ip version - -XDB_PUBLIC(long) xdb_now() { - struct timeval c_time; - gettimeofday(&c_time, NULL); - return c_time.tv_sec * (int)1e6 + c_time.tv_usec; -} - -XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *buffer, int offset) { - return ( - ((buffer[offset ]) & 0x000000FF) | - ((buffer[offset+1] << 8) & 0x0000FF00) | - ((buffer[offset+2] << 16) & 0x00FF0000) | - ((buffer[offset+3] << 24) & 0xFF000000) - ); -} - -XDB_PUBLIC(int) xdb_le_get_uint16(const char *buffer, int offset) { - return ( - ((buffer[offset ]) & 0x000000FF) | - ((buffer[offset+1] << 8) & 0x0000FF00) - ); -} - -XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - // version check - if (strchr(ip_string, '.') != NULL && strchr(ip_string, ':') == NULL) { - return xdb_parse_v4_ip(ip_string, buffer, length); - } else if (strchr(ip_string, ':') != NULL) { - return xdb_parse_v6_ip(ip_string, buffer, length); - } - - return NULL; -} - -XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - struct in_addr addr; - - // buffer length checking - if (length < xdb_ipv4_bytes) { - return NULL; - } - - if (inet_pton(AF_INET, ip_string, &addr) != 1) { - return NULL; - } - - // encode the address to buffer with big endian byte bufffer. - buffer[0] = (addr.s_addr) & 0xFF; - buffer[1] = (addr.s_addr >> 8) & 0xFF; - buffer[2] = (addr.s_addr >> 16) & 0xFF; - buffer[3] = (addr.s_addr >> 24) & 0xFF; - return XDB_IPv4; -} - -XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) { - struct in6_addr addr; - - // buffer length checking - if (length < xdb_ipv6_bytes) { - return NULL; - } - - if (inet_pton(AF_INET6, ip_string, &addr) != 1) { - return NULL; - } - - memcpy(buffer, addr.s6_addr, xdb_ipv6_bytes); - return XDB_IPv6; -} - -XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *ip_bytes, int bytes, char *ip_string, size_t length) { - if (bytes == xdb_ipv4_bytes) { - return xdb_v4_ip_to_string(ip_bytes, ip_string, length); - } else if (bytes == xdb_ipv6_bytes) { - return xdb_v6_ip_to_string(ip_bytes, ip_string, length); - } - - return -1; -} - -XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { - if (!ip_bytes || !ip_string || length == 0) { - return -1; - } - - // buffer length checking - if (length < INET_ADDRSTRLEN) { - return -1; - } - - if (inet_ntop(AF_INET, ip_bytes, ip_string, length) == NULL) { - return -1; - } - - return 0; -} - -XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) { - if (!ip_bytes || !ip_string || length == 0) { - return -1; - } - - if (length < INET6_ADDRSTRLEN) { - return -1; - } - - if (inet_ntop(AF_INET6, ip_bytes, ip_string, length) == NULL) { - return -1; - } - - return 0; -} - -XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *ip1, int bytes, const char *buffer, int offset) { - register int i, i1, i2; - for (i = 0; i < bytes; i++) { - i1 = ip1[i]; - i2 = buffer[offset + i] & 0xFF; - if (i1 > i2) { - return 1; - } else if (i1 < i2) { - return -1; - } - } - return 0; -} diff --git a/binding/cpp/Makefile b/binding/cpp/Makefile deleted file mode 100644 index 2f810d6..0000000 --- a/binding/cpp/Makefile +++ /dev/null @@ -1,11 +0,0 @@ - -all: xdb_search xdb_bench - -xdb_search: xdb_search.cc xdb_search_test.cc - g++ -std=c++11 -O2 $^ -o $@ - -xdb_bench: xdb_search.cc xdb_bench.cc xdb_bench_test.cc - g++ -std=c++11 -O2 $^ -o $@ - -clean: - rm -f xdb_search xdb_bench diff --git a/binding/cpp/readme.md b/binding/cpp/readme.md deleted file mode 100644 index bac5bda..0000000 --- a/binding/cpp/readme.md +++ /dev/null @@ -1,108 +0,0 @@ -# ip2region xdb C++ 查询客户端实现 - -## 使用方式 -### 完全基于文件的查询 -``` -#include - -#include "xdb_search.h" - -int main(int argc, char* argv[]) { - char file_name[] = "../../data/ip2region.xdb"; - char ip[] = "1.2.3.4"; - - xdb_search_t xdb(file_name); - xdb.init_file(); - - std::cout << xdb.search(ip) << std::endl; - return 0; -} -``` - -### 缓存 `vector_index` 索引 -``` -#include - -#include "xdb_search.h" - -int main(int argc, char* argv[]) { - char file_name[] = "../../data/ip2region.xdb"; - char ip[] = "1.2.3.4"; - - xdb_search_t xdb(file_name); - xdb.init_vector_index(); - - std::cout << xdb.search(ip) << std::endl; - return 0; -} -``` - -### 缓存整个 `xdb` 数据 -``` -#include - -#include "xdb_search.h" - -int main(int argc, char* argv[]) { - char file_name[] = "../../data/ip2region.xdb"; - char ip[] = "1.2.3.4"; - - xdb_search_t xdb(file_name); - xdb.init_content(); - - std::cout << xdb.search(ip) << std::endl; - return 0; -} -``` - -## 测试程序编译 -1. 切换到当前目录 -2. 编译 - -``` -$ make -g++ -std=c++11 -O2 xdb_search.cc xdb_search_test.cc -o xdb_search -g++ -std=c++11 -O2 xdb_search.cc xdb_bench.cc xdb_bench_test.cc -o xdb_bench -``` - -## 测试查询 -### 说明 -``` -$ ./xdb_search --help -./xdb_search [command options] -options: - --db string ip2region binary xdb file path - --cache-policy string cache policy: file/vector_index/content - --help print help -``` - -### 测试 -``` -$ ./xdb_search --db ../../data/ip2region.xdb --cache-policy vector_index -cache policy : vector_index -ip2region>> 1.2.3.4 -美国|0|华盛顿|0|谷歌 -``` - -## bench 测试 -### 说明 -``` -$ ./xdb_bench --help -./xdb_bench [command options] -options: - --db string ip2region binary xdb file path - --src string source ip text file path - --cache-policy string cache policy: file/vector_index/content - --help print help -``` - -### 测试 -``` -$ ./xdb_bench --db ../../data/ip2region.xdb --src ../../data/ip.merge.txt --cache-policy content -total: 3419220, took: 3.44 s, cost: 0.27 μs/op, io count: 0 -$ ./xdb_bench --db ../../data/ip2region.xdb --src ../../data/ip.merge.txt --cache-policy vector_index -total: 3419220, took: 45.99 s, cost: 12.24 μs/op, io count: 21739300 -$ ./xdb_bench --db ../../data/ip2region.xdb --src ../../data/ip.merge.txt --cache-policy file -total: 3419220, took: 60.39 s, cost: 16.32 μs/op, io count: 25158520 -``` - diff --git a/binding/cpp/xdb_bench.cc b/binding/cpp/xdb_bench.cc deleted file mode 100644 index a4e04cb..0000000 --- a/binding/cpp/xdb_bench.cc +++ /dev/null @@ -1,135 +0,0 @@ - -#include "xdb_bench.h" - -#include -#include -#include -#include - -#include -#include - -static void log_exit(const std::string &msg) { - std::cout << msg << std::endl; - exit(-1); -} - -static unsigned long long get_time() { - struct timeval tv1; - gettimeofday(&tv1, NULL); - return (unsigned long long)tv1.tv_sec * 1000 * 1000 + tv1.tv_usec; -} - -static bool ip2uint(const char *buf, unsigned int &ip) { - struct in_addr addr; - if (inet_pton(AF_INET, buf, &addr) == 0) - return false; - // 网络字节序为大端存储, 在此转换为小端存储 - ip = (((addr.s_addr >> 0) & 0xFF) << 24) | - (((addr.s_addr >> 8) & 0xFF) << 16) | - (((addr.s_addr >> 16) & 0xFF) << 8) | - (((addr.s_addr >> 24) & 0xFF) << 0); - return true; -} - -static std::string uint2ip(unsigned int ip) { - char buf[16]; - snprintf(buf, - sizeof(buf), - "%d.%d.%d.%d", - (ip >> 24) & 0xFF, - (ip >> 16) & 0xFF, - (ip >> 8) & 0xFF, - ip & 0xFF); - return std::string(buf); -} - -xdb_bench_t::xdb_bench_t(const std::string &file_name) : xdb_search(file_name) { -} - -void xdb_bench_t::init_file() { - xdb_search.init_file(); -} - -void xdb_bench_t::init_vector_index() { - xdb_search.init_vector_index(); -} - -void xdb_bench_t::init_content() { - xdb_search.init_content(); -} - -void xdb_bench_t::bench_test_one(unsigned int ip_uint, const char *region) { - if (xdb_search.search(uint2ip(ip_uint)) != region) - log_exit("failed: " + uint2ip(ip_uint)); - sum_io_count += xdb_search.get_io_count(); - sum_cost_time += xdb_search.get_cost_time(); - sum_count++; -} - -void xdb_bench_t::bench_test_line(char *buf) { - size_t buf_len = strlen(buf); - if (buf_len == 0) - return; - buf[buf_len - 1] = '\0'; // 去掉换行符 - - 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'; - - unsigned int ip1, ip2; - if (!ip2uint(buf, ip1) || !ip2uint(pos1 + 1, ip2) || ip1 > ip2) { - *pos1 = *pos2 = '|'; - log_exit(std::string("invalid data: ") + buf); - } - - const char *region = pos2 + 1; - - unsigned int ip_mid = ip1 + (ip2 - ip1) / 2; - std::vector ip_vec; - ip_vec.push_back(ip1); - ip_vec.push_back(ip1 + (ip_mid - ip1) / 2); - ip_vec.push_back(ip_mid); - ip_vec.push_back(ip_mid + (ip2 - ip_mid) / 2); - ip_vec.push_back(ip2); - - for (auto &d : ip_vec) - bench_test_one(d, region); -} - -void xdb_bench_t::bench_test_file(const std::string &file_name) { - FILE *f = fopen(file_name.data(), "r"); - if (f == NULL) - log_exit("can't open " + file_name); - char buf[1024]; - while (fgets(buf, sizeof(buf), f) != NULL) - bench_test_line(buf); -} - -void xdb_bench_t::bench(const std::string &file_name) { - sum_io_count = 0; - sum_cost_time = 0; - sum_count = 0; - - unsigned long long tv1 = get_time(); - bench_test_file(file_name); - unsigned long long tv2 = get_time(); - - double took = (tv2 - tv1) * 1.0 / 1000 / 1000; - double cost = sum_cost_time * 1.0 / sum_count; - - printf( - "total: %llu, took: %.2f s, cost: %.2f μs/op, io " - "count: " - "%llu\n", - sum_count, - took, - cost, - sum_io_count); -} diff --git a/binding/cpp/xdb_bench.h b/binding/cpp/xdb_bench.h deleted file mode 100644 index 1ec84c9..0000000 --- a/binding/cpp/xdb_bench.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef XDB_BENCH_H -#define XDB_BENCH_H - -#include "xdb_search.h" - -class xdb_bench_t { - public: - xdb_bench_t(const std::string &file_name); - - void init_file(); - void init_vector_index(); - void init_content(); - - void bench(const std::string &file_name); - - private: - void bench_test_one(unsigned int ip_uint, const char *region); - void bench_test_line(char *buf); - void bench_test_file(const std::string &file_name); - - xdb_search_t xdb_search; - - unsigned long long sum_io_count; - unsigned long long sum_cost_time; - unsigned long long sum_count; -}; - -#endif diff --git a/binding/cpp/xdb_bench_test.cc b/binding/cpp/xdb_bench_test.cc deleted file mode 100644 index c282fd7..0000000 --- a/binding/cpp/xdb_bench_test.cc +++ /dev/null @@ -1,70 +0,0 @@ - -#include "xdb_bench.h" - -#include - -#include - -void print_help(int argc, char* argv[]) { - printf("./xdb_bench [command options]\n"); - 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/vector_index/content\n"); - printf(" --help print help\n"); - exit(-1); -} - -int main(int argc, char* argv[]) { - struct option long_options[] = { - {"db", required_argument, 0, 'd'}, - {"cache-policy", required_argument, 0, 't'}, - {"src", required_argument, 0, 's'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0 } - }; - - std::string db_file_name = "../../data/ip2region.xdb"; - std::string src_file_name = "../../data/ip.merge.txt"; - std::string cache_policy = "vector_index"; - - while (1) { - int c = getopt_long(argc, argv, "", long_options, NULL); - if (c == -1) - break; - switch (c) { - case 'd': - db_file_name = optarg; - break; - case 'h': - print_help(argc, argv); - break; - case 't': - cache_policy = optarg; - break; - case 's': - src_file_name = optarg; - break; - case '?': - exit(-1); - } - } - - xdb_bench_t xdb(db_file_name); - - if (cache_policy == "content") - xdb.init_content(); - else if (cache_policy == "vector_index") - xdb.init_vector_index(); - else if (cache_policy == "file") - xdb.init_file(); - else { - std::cout << "invalid cache policy: " << cache_policy << std::endl; - exit(-1); - } - - xdb.bench(src_file_name); - return 0; -} diff --git a/binding/cpp/xdb_search.cc b/binding/cpp/xdb_search.cc deleted file mode 100644 index e5937bf..0000000 --- a/binding/cpp/xdb_search.cc +++ /dev/null @@ -1,190 +0,0 @@ - -#include "xdb_search.h" - -#include -#include - -#include - -static void log_exit(const std::string &msg) { - std::cout << msg << std::endl; - exit(-1); -} - -static unsigned long long get_time() { - struct timeval tv1; - gettimeofday(&tv1, NULL); - return (unsigned long long)tv1.tv_sec * 1000 * 1000 + tv1.tv_usec; -} - -static 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__); -} - -static unsigned int read_uint(const char *buf) { - return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00) | - ((buf[2] << 16) & 0x00FF0000) | ((buf[3] << 24) & 0xFF000000); -} - -static unsigned short read_ushort(const char *buf) { - return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00); -} - -static bool ip2uint(const char *buf, unsigned int &ip) { - struct in_addr addr; - if (inet_pton(AF_INET, buf, &addr) == 0) - return false; - // 网络字节序为大端存储, 在此转换为小端存储 - ip = (((addr.s_addr >> 0) & 0xFF) << 24) | - (((addr.s_addr >> 8) & 0xFF) << 16) | - (((addr.s_addr >> 16) & 0xFF) << 8) | - (((addr.s_addr >> 24) & 0xFF) << 0); - return true; -} - -void xdb_search_t::get_content_index(unsigned int ip, - unsigned int &left, - unsigned int &right) { - unsigned int ip_1 = (ip >> 24) & 0xFF; - unsigned int ip_2 = (ip >> 16) & 0xFF; - unsigned int index = (ip_1 * vector_index_cols + ip_2) * vector_index_size; - - if (content != NULL) { - left = read_uint(content + index + header_length); - right = read_uint(content + index + header_length + 4); - } else if (vector_index != NULL) { - left = read_uint(vector_index + index); - right = read_uint(vector_index + index + 4); - } else { - ++io_count; - char buf[8]; - read_bin(header_length + index, buf, sizeof(buf), db); - left = read_uint(buf); - right = read_uint(buf + 4); - } -} - -void xdb_search_t::get_content(unsigned int index, - unsigned int &ip_left, - unsigned int &ip_right, - unsigned short ®ion_len, - unsigned int ®ion_index) { - char buf[segment_index_size]; // 4 + 4 + 2 + 4 - const char *p; - - if (content != NULL) { - p = content + index; - } else { - ++io_count; - read_bin(index, buf, sizeof(buf), db); - p = buf; - } - ip_left = read_uint(p); - ip_right = read_uint(p + 4); - region_len = read_ushort(p + 8); - region_index = read_uint(p + 10); -} - -std::string xdb_search_t::get_region(unsigned int index, unsigned short len) { - if (content != NULL) { - return std::string(content + index, len); - } else { - ++io_count; - char *buf = (char *)malloc(sizeof(char) * len); - read_bin(index, buf, len, db); - std::string res(buf, len); - free(buf); - return res; - } -} - -xdb_search_t::xdb_search_t(const std::string &file_name) { - db = fopen(file_name.data(), "r"); - vector_index = NULL; - content = NULL; - - if (db == NULL) - log_exit("can't open " + file_name); -} - -void xdb_search_t::init_file() { -} - -void xdb_search_t::init_vector_index() { - vector_index = (char *)malloc(vector_index_length); - read_bin(header_length, vector_index, vector_index_length, db); -} - -void xdb_search_t::init_content() { - fseek(db, 0, SEEK_END); - unsigned int size = ftell(db); - content = (char *)malloc(size); - read_bin(0, content, size, db); -} - -xdb_search_t::~xdb_search_t() { - if (db != NULL) { - fclose(db); - db = NULL; - } - if (vector_index != NULL) { - free(vector_index); - vector_index = NULL; - } - if (content != NULL) { - free(content); - content = NULL; - } -} - -unsigned long long xdb_search_t::get_io_count() { - return io_count; -} - -unsigned long long xdb_search_t::get_cost_time() { - return cost_time; -} - -std::string xdb_search_t::search(const std::string &ip_str) { - unsigned long long t1 = get_time(); - - unsigned int ip_uint; - if (!ip2uint(ip_str.data(), ip_uint)) - return "invalid ip: " + ip_str; - std::string region = search(ip_uint); - - unsigned long long t2 = get_time(); - cost_time = t2 - t1; - return region; -} - -std::string xdb_search_t::search(unsigned int ip_uint) { - io_count = 0; - - unsigned int content_index_left, content_index_right; - get_content_index(ip_uint, content_index_left, content_index_right); - - unsigned int left, right, mid; - unsigned int ip_left, ip_right; - unsigned short region_len; - unsigned int region_index; - unsigned int mid_index; - - left = 0; - right = (content_index_right - content_index_left) / segment_index_size; - - for (;;) { - mid = left + (right - left) / 2; - mid_index = content_index_left + mid * segment_index_size; - get_content(mid_index, ip_left, ip_right, region_len, region_index); - - if (ip_left > ip_uint) - right = mid - 1; - else if (ip_right < ip_uint) - left = mid + 1; - else - return get_region(region_index, region_len); - } -} diff --git a/binding/cpp/xdb_search.h b/binding/cpp/xdb_search.h deleted file mode 100644 index 44286ef..0000000 --- a/binding/cpp/xdb_search.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef XDB_SEARCH_H -#define XDB_SEARCH_H - -#include - -class xdb_search_t { - public: - xdb_search_t(const std::string &file_name); - ~xdb_search_t(); - - void init_file(); - void init_vector_index(); - void init_content(); - - unsigned long long get_io_count(); - unsigned long long get_cost_time(); - - std::string search(const std::string &ip); - - private: - void get_content_index(unsigned int ip, - unsigned int &left, - unsigned int &right); - - void get_content(unsigned int index, - unsigned int &ip_left, - unsigned int &ip_right, - unsigned short ®ion_len, - unsigned int ®ion_index); - - std::string get_region(unsigned int index, unsigned short len); - - std::string search(unsigned int ip_uint); - - FILE *db; - char *vector_index; - char *content; - unsigned long long io_count; - unsigned long long cost_time; - - static constexpr int header_length = 256; - static constexpr int vector_index_rows = 256; - static constexpr int vector_index_cols = 256; - static constexpr int vector_index_size = 8; - static constexpr int vector_index_length = - vector_index_rows * vector_index_cols * vector_index_size; - static constexpr int segment_index_size = 14; -}; - -#endif diff --git a/binding/cpp/xdb_search_test.cc b/binding/cpp/xdb_search_test.cc deleted file mode 100644 index a2deea2..0000000 --- a/binding/cpp/xdb_search_test.cc +++ /dev/null @@ -1,73 +0,0 @@ - -#include "xdb_search.h" - -#include - -#include - -void print_help(int argc, char* argv[]) { - printf("./xdb_search [command options]\n"); - printf("options:\n"); - printf(" --db string ip2region binary xdb file path\n"); - printf( - " --cache-policy string cache policy: " - "file/vector_index/content\n"); - printf(" --help print help\n"); - exit(-1); -} - -int main(int argc, char* argv[]) { - struct option long_options[] = { - {"db", required_argument, 0, 'd'}, - {"cache-policy", required_argument, 0, 't'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0 } - }; - - std::string db_file_name = "../../data/ip2region.xdb"; - std::string cache_policy = "vector_index"; - - while (1) { - int c = getopt_long(argc, argv, "", long_options, NULL); - if (c == -1) - break; - switch (c) { - case 'd': - db_file_name = optarg; - break; - case 'h': - print_help(argc, argv); - break; - case 't': - cache_policy = optarg; - break; - case '?': - exit(-1); - } - } - - xdb_search_t xdb(db_file_name); - - if (cache_policy == "content") - xdb.init_content(); - else if (cache_policy == "vector_index") - xdb.init_vector_index(); - else if (cache_policy == "file") - xdb.init_file(); - else { - std::cout << "invalid cache policy: " << cache_policy << std::endl; - exit(-1); - } - - std::string ip; - for (;;) { - std::cout << "ip2region>> "; - std::getline(std::cin, ip); - if (ip.empty()) - continue; - if (ip == "exit" || ip == "quit") - break; - std::cout << xdb.search(ip) << std::endl; - } - return 0; -} diff --git a/binding/csharp/.gitignore b/binding/csharp/.gitignore deleted file mode 100644 index 2ecf253..0000000 --- a/binding/csharp/.gitignore +++ /dev/null @@ -1,337 +0,0 @@ -## 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/ \ No newline at end of file diff --git a/binding/csharp/CHANGELOG.md b/binding/csharp/CHANGELOG.md deleted file mode 100644 index 7dd8005..0000000 --- a/binding/csharp/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -## [Unreleased] - -## [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 \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj b/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj deleted file mode 100644 index 9a77e8c..0000000 --- a/binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - - - - - - - - IP2Region/ip2region.xdb - PreserveNewest - - - - diff --git a/binding/csharp/IP2Region.Net.BenchMark/Program.cs b/binding/csharp/IP2Region.Net.BenchMark/Program.cs deleted file mode 100644 index c8c14e6..0000000 --- a/binding/csharp/IP2Region.Net.BenchMark/Program.cs +++ /dev/null @@ -1,29 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using IP2Region.Net.Abstractions; -using IP2Region.Net.XDB; - -BenchmarkRunner.Run(typeof(Program).Assembly); - -public class CachePolicyCompare -{ - private static readonly string XdbPath = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region.xdb"); - private readonly ISearcher _contentSearcher = new Searcher(CachePolicy.Content, XdbPath); - private readonly ISearcher _vectorSearcher = new Searcher(CachePolicy.VectorIndex,XdbPath); - private readonly ISearcher _fileSearcher = new Searcher(CachePolicy.File,XdbPath); - - private readonly string _testIpAddress = "114.114.114.114"; - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.Content))] - public void CachePolicy_Content() => _contentSearcher.Search(_testIpAddress); - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.VectorIndex))] - public void CachePolicy_VectorIndex() => _vectorSearcher.Search(_testIpAddress); - - - [Benchmark] - [BenchmarkCategory(nameof(CachePolicy.File))] - public void CachePolicy_File() => _fileSearcher.Search(_testIpAddress); -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj b/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj deleted file mode 100644 index e8a9f28..0000000 --- a/binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - net6.0 - enable - enable - - false - - - - - - - - - - - - - - - - - TestData/ip.merge.txt - PreserveNewest - - - TestData/ip2region.xdb - PreserveNewest - - - - diff --git a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs b/binding/csharp/IP2Region.Net.Test/SearcherTest.cs deleted file mode 100644 index 87337ff..0000000 --- a/binding/csharp/IP2Region.Net.Test/SearcherTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -using IP2Region.Net.XDB; - -namespace IP2Region.Net.Test; - -[TestFixture] -public class SearcherTest -{ - private readonly string _xdbPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region.xdb"); - - public static IEnumerable Ips() - { - yield return "114.114.114.114"; - yield return "119.29.29.29"; - yield return "223.5.5.5"; - yield return "180.76.76.76"; - yield return "8.8.8.8"; - } - - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] - public void TestSearchCacheContent(string ip) - { - var contentSearcher = new Searcher(CachePolicy.Content,_xdbPath); - var region = contentSearcher.Search(ip); - Console.WriteLine(region); - } - - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] - public void TestSearchCacheVector(string ip) - { - var vectorSearcher = new Searcher(CachePolicy.VectorIndex,_xdbPath); - var region = vectorSearcher.Search(ip); - Console.WriteLine(region); - } - - [TestCaseSource(nameof(Ips))] - [Parallelizable(ParallelScope.All)] - public void TestSearchCacheFile(string ip) - { - var fileSearcher = new Searcher(CachePolicy.File,_xdbPath); - var region = fileSearcher.Search(ip); - Console.WriteLine(region); - } - - [TestCase(CachePolicy.Content)] - [TestCase(CachePolicy.VectorIndex)] - [TestCase(CachePolicy.File)] - public void TestBenchSearch(CachePolicy cachePolicy) - { - Searcher searcher = new Searcher(cachePolicy,_xdbPath); - var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", "ip.merge.txt"); - - foreach (var line in File.ReadLines(srcPath)) - { - var ps = line.Trim().Split("|", 3); - - if (ps.Length != 3) - { - throw new ArgumentException($"invalid ip segment line {line}", nameof(line)); - } - - var sip = Util.IpAddressToUInt32(ps[0]); - var eip = Util.IpAddressToUInt32(ps[1]); - var mip = Util.GetMidIp(sip, eip); - - uint[] temp = { sip, Util.GetMidIp(sip, mip), mip, Util.GetMidIp(mip, eip), eip }; - - foreach (var ip in temp) - { - var region = searcher.Search(ip); - - if (region != ps[2]) - { - throw new Exception($"failed search {ip} with ({region}!={ps[2]})"); - } - } - } - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.Test/Usings.cs b/binding/csharp/IP2Region.Net.Test/Usings.cs deleted file mode 100644 index cefced4..0000000 --- a/binding/csharp/IP2Region.Net.Test/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using NUnit.Framework; \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net.Test/UtilTest.cs b/binding/csharp/IP2Region.Net.Test/UtilTest.cs deleted file mode 100644 index c25fe01..0000000 --- a/binding/csharp/IP2Region.Net.Test/UtilTest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using IP2Region.Net.XDB; - -namespace IP2Region.Net.Test; - -[TestFixture] -public class UtilTest -{ - [TestCase("114.114.114.114")] - public void TestIpAddressToUInt32(string value) - { - var uintIp = XDB.Util.IpAddressToUInt32(value); - Console.WriteLine(uintIp); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs b/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs deleted file mode 100644 index ffd64b9..0000000 --- a/binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using System.Net; - -namespace IP2Region.Net.Abstractions; - -public interface ISearcher -{ - string? Search(string ipStr); - - string? Search(IPAddress ipAddress); - - string? Search(uint ipAddress); - - int IoCount { get; } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj b/binding/csharp/IP2Region.Net/IP2Region.Net.csproj deleted file mode 100644 index 372dd42..0000000 --- a/binding/csharp/IP2Region.Net/IP2Region.Net.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - IP2Region.Net - 2.0.2 - IP2Region.Net - Alan Lee - Apache-2.0 - README.md - https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp - https://github.com/lionsoul2014/ip2region/tree/master/binding/csharp - Please refer to CHANGELOG.md for details - .NET client library for ip2region - IP2Region GeoIP IPSearch - git - enable - enable - netstandard2.0;netstandard2.1;net6.0;net7.0 - 10.0 - c2f07fe1-a300-4de3-8200-1278ed8cb5b7 - - - - - - - - - - - CHANGELOG.md - - - diff --git a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs deleted file mode 100644 index 13d19e5..0000000 --- a/binding/csharp/IP2Region.Net/Internal/Abstractions/AbstractCacheStrategy.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using System.Buffers; - -namespace IP2Region.Net.Internal.Abstractions; - -internal abstract class AbstractCacheStrategy -{ - protected const int HeaderInfoLength = 256; - protected const int VectorIndexRows = 256; - protected const int VectorIndexCols = 256; - protected const int VectorIndexSize = 8; - - protected readonly FileStream XdbFileStream; - private const int BufferSize = 4096; - - internal int IoCount { get; private set; } - - protected AbstractCacheStrategy(string xdbPath) - { - XdbFileStream = new FileStream(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, - useAsync: true); - } - - protected int GetVectorIndexStartPos(uint ip) - { - var il0 = ip >> 24 & 0xFF; - var il1 = ip >> 16 & 0xFF; - var idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; - return (int)idx; - } - - internal abstract ReadOnlyMemory GetVectorIndex(uint ip); - - internal virtual ReadOnlyMemory GetData(int offset, int length) - { - byte[] buffer = ArrayPool.Shared.Rent(length); - int totalBytesRead = 0; - try - { - XdbFileStream.Seek(offset, SeekOrigin.Begin); - - int bytesRead; - do - { - int bytesToRead = Math.Min(BufferSize, length - totalBytesRead); - bytesRead = XdbFileStream.Read(buffer, totalBytesRead, bytesToRead); - totalBytesRead += bytesRead; - - IoCount++; - } while (bytesRead > 0 && totalBytesRead < length); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - return new ReadOnlyMemory(buffer, 0, totalBytesRead); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs b/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs deleted file mode 100644 index 14e8eb0..0000000 --- a/binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using IP2Region.Net.Internal.Abstractions; -using IP2Region.Net.XDB; - -namespace IP2Region.Net.Internal; - -internal class CacheStrategyFactory -{ - private readonly string _xdbPath; - - public CacheStrategyFactory(string xdbPath) - { - _xdbPath = xdbPath; - } - - public AbstractCacheStrategy CreateCacheStrategy(CachePolicy cachePolicy) - { - return cachePolicy switch - { - CachePolicy.Content => new ContentCacheStrategy(_xdbPath), - CachePolicy.VectorIndex => new VectorIndexCacheStrategy(_xdbPath), - CachePolicy.File => new FileCacheStrategy(_xdbPath), - _ => throw new ArgumentException(nameof(cachePolicy)) - }; - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs deleted file mode 100644 index e3094e1..0000000 --- a/binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using IP2Region.Net.Internal.Abstractions; - -namespace IP2Region.Net.Internal; - -internal class ContentCacheStrategy : AbstractCacheStrategy -{ - private readonly ReadOnlyMemory _cacheData; - - public ContentCacheStrategy(string xdbPath) : base(xdbPath) - { - _cacheData = base.GetData(0, (int)XdbFileStream.Length); - XdbFileStream.Close(); - XdbFileStream.Dispose(); - } - - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - int idx = GetVectorIndexStartPos(ip); - return _cacheData.Slice(HeaderInfoLength + idx, VectorIndexSize); - } - - internal override ReadOnlyMemory GetData(int offset, int length) - { - return _cacheData.Slice(offset, length); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs deleted file mode 100644 index 31f90f3..0000000 --- a/binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using IP2Region.Net.Internal.Abstractions; - -namespace IP2Region.Net.Internal; - -internal class FileCacheStrategy : AbstractCacheStrategy -{ - public FileCacheStrategy(string xdbPath) : base(xdbPath) - { - } - - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - var idx = GetVectorIndexStartPos(ip); - return GetData(HeaderInfoLength + idx, VectorIndexSize); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs b/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs deleted file mode 100644 index f1d8ae6..0000000 --- a/binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2023 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 -// @Date 2023/07/25 - -using IP2Region.Net.Internal.Abstractions; - -namespace IP2Region.Net.Internal; - -internal class VectorIndexCacheStrategy : AbstractCacheStrategy -{ - private readonly ReadOnlyMemory _vectorIndex; - - public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath) - { - var vectorLength = VectorIndexRows * VectorIndexCols * VectorIndexSize; - _vectorIndex = base.GetData(HeaderInfoLength, vectorLength); - } - - internal override ReadOnlyMemory GetVectorIndex(uint ip) - { - var idx = GetVectorIndexStartPos(ip); - return _vectorIndex.Slice(idx, VectorIndexSize); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs b/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs deleted file mode 100644 index 0f4d035..0000000 --- a/binding/csharp/IP2Region.Net/XDB/CachePolicy.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace IP2Region.Net.XDB; - -public enum CachePolicy -{ - /// - /// no cache - /// - File, - /// - /// cache vector index , reduce the number of IO operations - /// - VectorIndex, - /// - /// default cache policy , cache whole xdb file - /// - Content -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/XDB/Searcher.cs b/binding/csharp/IP2Region.Net/XDB/Searcher.cs deleted file mode 100644 index de29dc9..0000000 --- a/binding/csharp/IP2Region.Net/XDB/Searcher.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2023 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 Lee -// @Date 2023/07/23 - -using System.Net; -using System.Runtime.InteropServices; -using System.Text; -using IP2Region.Net.Abstractions; -using IP2Region.Net.Internal; -using IP2Region.Net.Internal.Abstractions; - -namespace IP2Region.Net.XDB; - -public class Searcher : ISearcher -{ - const int SegmentIndexSize = 14; - - private readonly AbstractCacheStrategy _cacheStrategy; - public int IoCount => _cacheStrategy.IoCount; - - public Searcher(CachePolicy cachePolicy, string dbPath) - { - var factory = new CacheStrategyFactory(dbPath); - _cacheStrategy = factory.CreateCacheStrategy(cachePolicy); - } - - public string? Search(string ipStr) - { - var ip = Util.IpAddressToUInt32(ipStr); - return Search(ip); - } - - public string? Search(IPAddress ipAddress) - { - var ip = Util.IpAddressToUInt32(ipAddress); - return Search(ip); - } - - public string? Search(uint ip) - { - var index = _cacheStrategy.GetVectorIndex(ip); - uint sPtr = MemoryMarshal.Read(index.Span); - uint ePtr = MemoryMarshal.Read(index.Span.Slice(4)); - - var dataLen = 0; - uint dataPtr = 0; - uint l = 0; - uint h = (ePtr -sPtr) / SegmentIndexSize; - - while (l <= h) - { - var mid = Util.GetMidIp(l, h); - var pos = sPtr + mid * SegmentIndexSize; - - var buffer = _cacheStrategy.GetData((int)pos, SegmentIndexSize); - uint sip = MemoryMarshal.Read(buffer.Span); - uint eip = MemoryMarshal.Read(buffer.Span.Slice(4)); - - if (ip < sip) - { - h = mid - 1; - } - else if (ip > eip) - { - l = mid + 1; - } - else - { - dataLen = MemoryMarshal.Read(buffer.Span.Slice(8)); - dataPtr = MemoryMarshal.Read(buffer.Span.Slice(10)); - break; - } - } - - if (dataLen == 0) - { - return default; - } - - var regionBuff = _cacheStrategy.GetData((int)dataPtr,dataLen); - return Encoding.UTF8.GetString(regionBuff.Span.ToArray()); - } -} \ No newline at end of file diff --git a/binding/csharp/IP2Region.Net/XDB/Util.cs b/binding/csharp/IP2Region.Net/XDB/Util.cs deleted file mode 100644 index 1075377..0000000 --- a/binding/csharp/IP2Region.Net/XDB/Util.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Net; -using System.Runtime.InteropServices; - -namespace IP2Region.Net.XDB; - -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(bytes); - } - - public static uint GetMidIp(uint x, uint y) - => (x & y) + ((x ^ y) >> 1); -} \ No newline at end of file diff --git a/binding/csharp/README.md b/binding/csharp/README.md deleted file mode 100644 index 8f8c78c..0000000 --- a/binding/csharp/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# 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.AddSingleton(new Searcher(CachePolicy , "your xdb file path")); -``` - -## Performance - -``` ini - -BenchmarkDotNet=v0.13.2, OS=macOS 13.4.1 (c) (22F770820d) [Darwin 22.5.0] -Apple M1, 1 CPU, 8 logical and 8 physical cores -.NET SDK=7.0.306 - [Host] : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD - DefaultJob : .NET 6.0.20 (6.0.2023.32017), Arm64 RyuJIT AdvSIMD - - -``` -| Method | Mean | Error | StdDev | -|-------------------------|-----------:|---------:|---------:| -| CachePolicy_Content | 155.7 ns | 0.46 ns | 0.39 ns | -| CachePolicy_File | 2,186.8 ns | 34.27 ns | 32.06 ns | -| CachePolicy_VectorIndex | 1,570.3 ns | 27.53 ns | 22.99 ns | - - - -## 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) \ No newline at end of file diff --git a/binding/erlang/README.md b/binding/erlang/README.md deleted file mode 100644 index b3628c7..0000000 --- a/binding/erlang/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# ip2region xdb erlang 查询客户端 - -### 简介 -该bingding以erlang语言实现xdb查询客户端,基于Erlang OTP Application,查询逻辑由ip2region_worker工作进程实现,支持配多个工作进程来进行负载均衡。 - -### 应用配置 -该应用可配置的参数在ip2region.app.src中,如下: -``` erlang - {env,[ - {poolargs, [ - {size, 1}, %% 工作进程默认数量 - {max_overflow, 5} %% 工作进程最大数量 - ]} - ]} -``` - -### 编译 - -``` -$ rebar3 compile -``` -### 运行 -将xdb文件放到priv目录下,然后启动erlang节点: -``` -$ rebar3 shell - -``` -在erlang shell中调用xdb:search/1接口查询Ip地址信息, 该接口支持以list格式字符串、binary格式字符串、tuple和整数表示的IP地址,如下: -``` -1> xdb:search("1.0.8.0"). -[20013,22269,124,48,124,24191,19996,30465,124,24191,24030, - 24066,124,30005,20449] -2> -3> io:format("~ts~n", [xdb:search("1.0.8.0")]). -中国|0|广东省|广州市|电信 -io:format("~ts~n", [xdb:search(<<"1.0.8.0">>)]). -中国|0|广东省|广州市|电信 -4> io:format("~ts~n", [xdb:search({1,0,8,0})]). -中国|0|广东省|广州市|电信 -6> io:format("~ts~n", [xdb:search(16779264)]). -中国|0|广东省|广州市|电信 -``` - -### 使用方法 -* 在rebar.config中引入依赖 -``` -{deps, [ - ip2region -]}. - -``` -* 启动ip2region Application -``` -...... - -application:ensure_started(ip2region), - -...... -``` - -* 调用xdb:search/1接口查询IP信息 -``` -...... - -ip2region:search("1.0.8.0"), - -...... -``` - -### 单元测试 - -``` -$ rebar3 eunit -===> Verifying dependencies... -===> Analyzing applications... -===> Compiling ip2region -===> Performing EUnit tests... -=INFO REPORT==== 17-Jan-2023::11:52:59.920155 === -XdbFile:/home/admin/erl-workspace/ip2region/binding/erlang/_build/test/lib/ip2region/priv/ip2region.xdb - -.... -Finished in 0.074 seconds -4 tests, 0 failures - -``` - -### 基准测试 -``` -$ cd benchmarks/ -$ sh xdb-benchmark.sh -===> Verifying dependencies... -===> Analyzing applications... -===> Compiling ip2region -Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit] - -Eshell V12.3.2.2 (abort with ^G) -1> =INFO REPORT==== 17-Jan-2023::11:37:35.631095 === -XdbFile:/home/admin/erl-workspace/ip2region/binding/erlang/_build/default/lib/ip2region/priv/ip2region.xdb - -===> Booted ip2region -===> Evaluating: "xdb_benchmark:main(\"../../data/ip.merge.txt\"), init:stop()." -CPU info: -model name : AMD EPYC 7K62 48-Core Processor -cache size : 512 KB -cpu MHz : 2595.124 -bogomips : 5190.24 -cores/threads : 2 - -Erlang info: -system_version:Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit] -load test data use 4.835593s - -start run benchmark tests - -search from file: -ip count:683844, -total time: 28.201699s, -search 24248.326315375536 times per second, -use 41.23995969841075 micro second per search - -search from cache: -ip count:683844, -total time: 0.671801s, -search 1017926.4395259906 times per second, -use 0.9823892583688677 micro second per search - -benchmark test finish - -``` diff --git a/binding/erlang/benchmarks/xdb-benchmark.sh b/binding/erlang/benchmarks/xdb-benchmark.sh deleted file mode 100755 index 40d13f3..0000000 --- a/binding/erlang/benchmarks/xdb-benchmark.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -cd .. - -rebar3 shell --eval="xdb_benchmark:main(\"../../data/ip.merge.txt\"), init:stop()." diff --git a/binding/erlang/include/ip2region.hrl b/binding/erlang/include/ip2region.hrl deleted file mode 100644 index a0e91e7..0000000 --- a/binding/erlang/include/ip2region.hrl +++ /dev/null @@ -1,26 +0,0 @@ --ifndef(IP2REGION_HRL). --define(IP2REGION_HRL, true). - --define(NONE, none). --define(APP_NAME, ip2region). - --define(XDB_VECTOR_INDEX, ets_xdb_vector_index). --define(XDB_SEGMENT_INDEX, ets_xdb_segement_index). --define(IP2REGION_CACHE, ets_ip2region_cache). - - --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 - --define(XDB_SEGMENT_INDEX_SIZE, 14). - --define(IP2REGION_POOL, ip2region_pool). - --ifndef(IF). --define(IF(C, T, F), case (C) of true -> (T); false -> (F) end). --define(IF(C, T), ?IF(C, T, skip)). --endif. - --endif. \ No newline at end of file diff --git a/binding/erlang/priv/dummy b/binding/erlang/priv/dummy deleted file mode 100644 index e69de29..0000000 diff --git a/binding/erlang/rebar.config b/binding/erlang/rebar.config deleted file mode 100644 index 076f916..0000000 --- a/binding/erlang/rebar.config +++ /dev/null @@ -1,24 +0,0 @@ -{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/leihua996/ip2region/tree/master/binding/erlang"} -]}. - -{hex, [{doc, ex_doc}]}. diff --git a/binding/erlang/rebar.lock b/binding/erlang/rebar.lock deleted file mode 100644 index 1ab8da9..0000000 --- a/binding/erlang/rebar.lock +++ /dev/null @@ -1,8 +0,0 @@ -{"1.2.0", -[{<<"poolboy">>,{pkg,<<"poolboy">>,<<"1.5.2">>},0}]}. -[ -{pkg_hash,[ - {<<"poolboy">>, <<"392B007A1693A64540CEAD79830443ABF5762F5D30CF50BC95CB2C1AAAFA006B">>}]}, -{pkg_hash_ext,[ - {<<"poolboy">>, <<"DAD79704CE5440F3D5A3681C8590B9DC25D1A561E8F5A9C995281012860901E3">>}]} -]. diff --git a/binding/erlang/src/ip2region.app.src b/binding/erlang/src/ip2region.app.src deleted file mode 100644 index 4de369c..0000000 --- a/binding/erlang/src/ip2region.app.src +++ /dev/null @@ -1,20 +0,0 @@ -{application, ip2region, - [{description, "ip2region xdb client application"}, - {vsn, "0.1.0"}, - {registered, []}, - {mod, {ip2region_app, []}}, - {applications, - [kernel, - stdlib - ]}, - {env,[ - {poolargs, [ - {size, 1}, - {max_overflow, 5} - ]} - ]}, - {modules, []}, - - {licenses, ["Apache-2.0"]}, - {links, [{"Github", "https://github.com/leihua996/ip2region/tree/master/binding/erlang"}]} - ]}. diff --git a/binding/erlang/src/ip2region_app.erl b/binding/erlang/src/ip2region_app.erl deleted file mode 100644 index 20bb1cb..0000000 --- a/binding/erlang/src/ip2region_app.erl +++ /dev/null @@ -1,22 +0,0 @@ -%%%------------------------------------------------------------------- -%% 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 diff --git a/binding/erlang/src/ip2region_sup.erl b/binding/erlang/src/ip2region_sup.erl deleted file mode 100644 index 304bc98..0000000 --- a/binding/erlang/src/ip2region_sup.erl +++ /dev/null @@ -1,55 +0,0 @@ -%%%------------------------------------------------------------------- -%% 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, _PoolPid} = start_ip2region_pool(SupPid), - {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 = [], - {ok, {SupFlags, ChildSpecs}}. - -%% internal functions -%% -create_table() -> - Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}], - ets:new(?XDB_VECTOR_INDEX, Opts), - ets:new(?XDB_SEGMENT_INDEX, Opts), - ets:new(?IP2REGION_CACHE, Opts). - -start_ip2region_pool(Sup) -> - {ok, PoolArgsCfg} = application:get_env(poolargs), - PoolName = ?IP2REGION_POOL, - PoolArgs = [{strategy, fifo}, {name, {local, PoolName}}, {worker_module, ip2region_worker} | PoolArgsCfg], - WorkerArgs = [], - ChildSpecs = poolboy:child_spec(PoolName, PoolArgs, WorkerArgs), - supervisor:start_child(Sup, ChildSpecs). \ No newline at end of file diff --git a/binding/erlang/src/ip2region_util.erl b/binding/erlang/src/ip2region_util.erl deleted file mode 100644 index 0359b85..0000000 --- a/binding/erlang/src/ip2region_util.erl +++ /dev/null @@ -1,26 +0,0 @@ -%%%------------------------------------------------------------------- -%% 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 -%% @end -%%%------------------------------------------------------------------- --module(ip2region_util). --export([ipv4_to_n/1]). - - -ipv4_to_n(IntIp) when is_integer(IntIp) -> IntIp; -ipv4_to_n({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 inet_parse:address(Ip) of - {ok, Addr} -> - ipv4_to_n(Addr); - _ -> - {error, bad_ip_format} - end. \ No newline at end of file diff --git a/binding/erlang/src/ip2region_worker.erl b/binding/erlang/src/ip2region_worker.erl deleted file mode 100644 index 64d039c..0000000 --- a/binding/erlang/src/ip2region_worker.erl +++ /dev/null @@ -1,183 +0,0 @@ -%%%------------------------------------------------------------------- -%% 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 -%% @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}). - -%%========================================== -%% 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 = filename:join([PrivDir, "ip2region.xdb"]), - error_logger:info_report(io_lib:format("XdbFile:~s~n", [XdbFileName])), - {ok, IoDevice} = file:open(XdbFileName, [read, binary]), - load_vector_index(IoDevice), - {ok, #state{xdb_fd = IoDevice}}. - -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), - {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])), - {noreply, 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}. - - -load_vector_index(IoDevice) -> - Key = ip2region_header_loaded, - case persistent_term:get(Key, false) of - true -> ok; - _ -> - {ok, <<_Header:?XDB_HEADER_SIZE/binary, VectorIndexBin/binary>> } = - file:read(IoDevice, ?XDB_HEADER_SIZE + ?XDB_VECTOR_INDEX_COUNT*8), - load_vector_index_aux(VectorIndexBin, 0), - persistent_term:put(Key, true) - end. - -load_vector_index_aux(<<>>, _Index) -> ok; -load_vector_index_aux(<>, Index) -> - Term = {Index, SPtr, EPtr}, - ets:insert(?XDB_VECTOR_INDEX, Term), - load_vector_index_aux(VectorIndexBin, Index + 1). - - -search_ip(IoDevice, Ip) -> - IntIp = ip2region_util:ipv4_to_n(Ip), - case ets:lookup(?IP2REGION_CACHE, IntIp) of - [{_IntIp, RegionInfo}] -> - RegionInfo; - _ -> - <> = <>, - VectorIdx = A * ?XDB_VECTOR_COLS + B, - [{_, SPtr, EPtr}] = ets:lookup(?XDB_VECTOR_INDEX, VectorIdx), - RegionInfo = search_ip(IoDevice, IntIp, SPtr, EPtr, 0, (EPtr - SPtr) div ?XDB_SEGMENT_INDEX_SIZE), - ets:insert_new(?IP2REGION_CACHE, {IntIp, RegionInfo}), - RegionInfo - end. - -search_ip(IoDevice, IntIp, SPtr, EPtr, Low, High) when Low =< High -> - Middle = (Low + High) bsr 1, - SPtr2 = SPtr + Middle * ?XDB_SEGMENT_INDEX_SIZE, - {SIp, EIp, DataLen, DataPtr} = read_segement_index(IoDevice, SPtr2), - if - IntIp < SIp -> - search_ip(IoDevice, IntIp, SPtr, EPtr, Low, Middle - 1); - IntIp > EIp -> - search_ip(IoDevice, IntIp, SPtr, EPtr, Middle + 1, High); - true -> - {ok, DataBin} = read_file(IoDevice, DataPtr, DataLen), - unicode:characters_to_nfc_list(DataBin) - end; -search_ip(_IoDevice, _IntIp, _SPtr, _EPtr, _Low, _High) -> - {error, unknown}. - -read_file(IoDevice, Position, DataLength) -> - file:position(IoDevice, {bof, Position}), - file:read(IoDevice, DataLength). - -read_segement_index(IoDevice, SPtr) -> - case ets:lookup(?XDB_SEGMENT_INDEX, SPtr) of - [{_SPtr, SIp, EIp, DataLen, DataPtr}] -> - {SIp, EIp, DataLen, DataPtr}; - _ -> - {ok, <>} = - read_file(IoDevice, SPtr, ?XDB_SEGMENT_INDEX_SIZE), - ets:insert_new(?XDB_SEGMENT_INDEX, {SPtr, SIp, EIp, DataLen, DataPtr}), - {SIp, EIp, DataLen, DataPtr} - end. \ No newline at end of file diff --git a/binding/erlang/src/xdb.erl b/binding/erlang/src/xdb.erl deleted file mode 100644 index 2b98ac6..0000000 --- a/binding/erlang/src/xdb.erl +++ /dev/null @@ -1,32 +0,0 @@ - -%%%------------------------------------------------------------------- -%% 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 -%% @end -%%%------------------------------------------------------------------- --module(xdb). --include("ip2region.hrl"). - --export([search/1]). - --spec search(Ip :: tuple() | list() | binary()) -> Result :: binary | {error, Reason::atom()}. -search(Ip) when is_integer(Ip); is_list(Ip); is_tuple(Ip); is_binary(Ip) -> - case ip2region_util:ipv4_to_n(Ip) of - IntIp when is_integer(IntIp) -> - case ets:lookup(?IP2REGION_CACHE, IntIp) of - [{_IntIp, Region}] -> Region; - _ -> - Worker = poolboy:checkout(?IP2REGION_POOL, true, infinity), - try - ip2region_worker:search(Worker, IntIp) - after - poolboy:checkin(?IP2REGION_POOL, Worker) - end - end; - Ret -> - Ret - end. diff --git a/binding/erlang/src/xdb_benchmark.erl b/binding/erlang/src/xdb_benchmark.erl deleted file mode 100644 index e52801c..0000000 --- a/binding/erlang/src/xdb_benchmark.erl +++ /dev/null @@ -1,75 +0,0 @@ -%%%------------------------------------------------------------------- -%% 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 benchmark test -%% @end -%%%------------------------------------------------------------------- --module(xdb_benchmark). --export([main/1]). - -main(DataFile) -> - application:ensure_started(ip2region), - show_hw_sw_info(), - IpList = load_test_data(DataFile), - run(IpList). - -show_hw_sw_info() -> - io:format("CPU info:~n", []), - io:format("~s", [os:cmd("egrep '^model name' /proc/cpuinfo | head -1")]), - io:format("~s", [os:cmd("egrep '^cache' /proc/cpuinfo | head -1")]), - io:format("~s", [os:cmd("egrep '^cpu MHz' /proc/cpuinfo | head -1")]), - io:format("~s", [os:cmd("egrep '^bogomips' /proc/cpuinfo | head -1")]), - io:format("cores/threads : ~s~n", [os:cmd("egrep -c '^processor' /proc/cpuinfo")]), - io:format("Erlang info:~n", []), - io:format("system_version:~s", [erlang:system_info(system_version)]), - ok. - -load_test_data(DataFile) -> - {ok, Fd} = file:open(DataFile, [read]), - T0 = os:timestamp(), - IpList = load_test_data(Fd, []), - T1 = os:timestamp(), - Sec = timer:now_diff(T1, T0) / 1000000, - io:format("load test data use ~ps~n", [Sec]), - IpList. - -load_test_data(Fd, IpList) -> - case file:read_line(Fd) of - {ok, Ip} -> - case string:tokens(unicode:characters_to_list(Ip), "|") of - [SIp | _Tail] -> - load_test_data(Fd, [string:trim(SIp)| IpList]); - _ -> - load_test_data(Fd, IpList) - end; - _ -> - file:close(Fd), - IpList - end. - -run(IpList) -> - garbage_collect(), - io:format("~nstart run benchmark tests~n", []), - io:format("~nsearch from file:~n", []), - run_test(IpList), - io:format("~nsearch from cache:~n", []), - run_test(IpList), - io:format("~nbenchmark test finish~n", []). - -run_test(IpList) -> - T0 = os:timestamp(), - run_test_aux(IpList), - T1 = os:timestamp(), - Sec = timer:now_diff(T1, T0) / 1000000, - IpCount = length(IpList), - io:format("ip count:~p,~ntotal time: ~ps,~nsearch ~p times per second,~nuse ~p micro second per search~n", - [IpCount, Sec, IpCount / Sec, Sec * 1000000/IpCount]). - -run_test_aux([]) -> ok; -run_test_aux([Ip | Tail]) -> - xdb:search(Ip), - run_test_aux(Tail). - diff --git a/binding/erlang/test/xdb_test.erl b/binding/erlang/test/xdb_test.erl deleted file mode 100644 index 85adcac..0000000 --- a/binding/erlang/test/xdb_test.erl +++ /dev/null @@ -1,18 +0,0 @@ --module(xdb_test). - --include_lib("eunit/include/eunit.hrl"). - -search_test_() -> - application:ensure_started(ip2region), - A = "中国|0|广东省|广州市|电信", - Region0 = xdb:search("1.0.8.0"), - Region1 = xdb:search(<<"1.0.8.0">>), - Region2 = xdb:search({1,0,8,0}), - Region3 = xdb:search("xxx.0.8.0"), - [ - ?_assert(A =:= Region0), - ?_assert(A =:= Region1), - ?_assert(A =:= Region2), - ?_assert({error, bad_ip_format} =:= Region3) - - ]. \ No newline at end of file diff --git a/binding/golang/Makefile b/binding/golang/Makefile deleted file mode 100644 index 4900d01..0000000 --- a/binding/golang/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# ip2region golang binding makefile -all: build -.PHONY: all - -build: - go build -o xdb_searcher -test: - go test -v ./... -clean: - find ./ -name xdb_searcher | xargs rm -f \ No newline at end of file diff --git a/binding/golang/ReadMe.md b/binding/golang/ReadMe.md deleted file mode 100644 index 68f055f..0000000 --- a/binding/golang/ReadMe.md +++ /dev/null @@ -1,190 +0,0 @@ -# ip2region xdb golang 查询客户端实现 - -# 使用方式 - -### package 获取 -```bash -go get github.com/lionsoul2014/ip2region/binding/golang -``` - -### 关于 IPv4 / IPv6 -该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下: -```golang -// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 xdb.IPv4 -dbPath := "../../data/ip2region_v4.xdb" // 或者你的 ipv4 xdb 的路径 -version := xdb.IPv4 - -// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 xdb.IPv6 -dbPath = "../../data/ip2region_v6.xdb" // 或者你的 ipv6 xdb 路径 -version = xdb.IPv6 - -// dbPath 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错 -// 备注:以下演示直接使用 dbPath 和 version 变量 -``` - -### 文件验证 -建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 -你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。 -```golang -err := xdb.VerifyFromFile(dbPath) -if err != nil { - // err 包含的验证的错误 - return fmt.Errorf("xdb file verify: %w", err) -} - -// 当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作 -``` - -### 完全基于文件的查询 - -```golang -import ( - "fmt" - "github.com/lionsoul2014/ip2region/binding/golang/xdb" - "time" -) - -func main() { - // 通过 version 和 dbPath 创建完全基于文件的查询对象 - searcher, err := xdb.NewWithFileOnly(version, dbPath) - if err != nil { - fmt.Printf("failed to create searcher: %s\n", err.Error()) - return - } - - defer searcher.Close() - - // 定位信息查询:IPv4 或者 IPv6 的地址都支持 - var ip = "1.2.3.4" // IPv4 - // ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 - var tStart = time.Now() - region, err := searcher.SearchByStr(ip) - if err != nil { - fmt.Printf("failed to SearchIP(%s): %s\n", ip, err) - return - } - - // IPv4 或者 IPv6 的定位信息 - fmt.Printf("{region: %s, took: %s}\n", region, time.Since(tStart)) - - // 备注:并发使用,每个 goroutine 需要创建一个独立的 searcher 对象。 -} -``` - -### 缓存 `VectorIndex` 索引 - -可以预先加载 `vectorIndex` 缓存,然后做成全局变量,每次创建 searcher 的时候使用全局的 `vectorIndex`,可以减少一次固定的 IO 操作从而加速查询,减少系统 io 压力。 -```golang -// 1、从 dbPath 加载 VectorIndex 缓存,把下述 vIndex 变量全局到内存里面。 -vIndex, err := xdb.LoadVectorIndexFromFile(dbPath) -if err != nil { - fmt.Printf("failed to load vector index from `%s`: %s\n", dbPath, err) - return -} - -// 2、用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 -searcher, err := xdb.NewWithVectorIndex(version, dbPath, vIndex) -if err != nil { - fmt.Printf("failed to create searcher with vector index: %s\n", err) - return -} - -// 备注:并发使用,全部 goroutine 共享全局的只读 vIndex 缓存,每个 goroutine 创建一个独立的 searcher 对象 -``` - -### 缓存整个 `xdb` 数据 - -可以预先加载整个 ip2region.xdb 到内存,完全基于内存查询,类似于之前的 memory search 查询。 -```golang -// 1、从 dbPath 加载整个 xdb 到内存 -cBuff, err := xdb.LoadContentFromFile(dbPath) -if err != nil { - fmt.Printf("failed to load content from `%s`: %s\n", dbPath, err) - return -} - -// 2、用全局的 cBuff 创建完全基于内存的查询对象。 -searcher, err := xdb.NewWithBuffer(version, cBuff) -if err != nil { - fmt.Printf("failed to create searcher with content: %s\n", err) - return -} - -// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。 -``` - - - -# 编译测试程序 - -通过如下方式编译得到 xdb_searcher 可执行程序: -```bash -# 切换到 golang binding 根目录 -make -``` - - -# 查询测试 - -通过 `xdb_searcher search` 命令来测试 xdb 的查询: -```bash -➜ golang git:(fr_xdb_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 -➜ golang git:(master) ./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>> 219.133.111.87 -{region: 中国|广东省|深圳市|电信, ioCount: 2, took: 19.005µs} -ip2region>> -``` - -例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 的查询: -```bash -➜ golang git:(master) ✗ ./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>> 240e:87c:892:ffff:ffff:ffff:ffff:ffff -{region: 中国|广东省|深圳市|专线用户, ioCount: 12, took: 130.294µs} -ip2region>> -``` - -输入对应版本的 ip 地址进行查询即可,输入 quit 退出测试程序。可以设置 `cache-policy` 为 file/vectorIndex/content 来测试不同的查询缓存机制。 - - -# bench 测试 - -通过 `xdb_searcher bench` 命令来进行自动 bench 测试,一方面确保程序和 `xdb` 文件都没有错误,另一方面通过大量的查询得到平均查询性能: -```bash -➜ golang git:(fr_xdb_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 -./xdb_searcher bench --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt -``` - -例如:通过 data/ip2region_v6.xdb 和 data/ipv6_source.txt 进行 ipv6 的 bench 测试: -```bash -./xdb_searcher bench --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt -``` - - -可以设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 不同缓存实现机制的效率。 - -*请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件的相同的源文件*。 - -bench 程序会逐行读取 `src` 指定的源IP文件,然后每个 IP 段选取 5 个固定位置的 IP 进行测试,以确保查询的 region 信息和原始的 region 信息是相同。测试途中没有调试信息的输出,有错误会打印错误信息并且终止运行,所以看到 `Bench finished` 就表示 bench 成功了,cost 是表示每次查询操作的平均时间(ns)。 diff --git a/binding/golang/go.mod b/binding/golang/go.mod deleted file mode 100644 index 130ba4c..0000000 --- a/binding/golang/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/lionsoul2014/ip2region/binding/golang - -go 1.17 - -require github.com/mitchellh/go-homedir v1.1.0 \ No newline at end of file diff --git a/binding/golang/go.sum b/binding/golang/go.sum deleted file mode 100644 index ae38d14..0000000 --- a/binding/golang/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= diff --git a/binding/golang/main.go b/binding/golang/main.go deleted file mode 100644 index 2bb0288..0000000 --- a/binding/golang/main.go +++ /dev/null @@ -1,298 +0,0 @@ -// 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 -// @Date 2022/06/16 - -package main - -import ( - "bufio" - "fmt" - "log" - "os" - "strings" - "time" - - "github.com/lionsoul2014/ip2region/binding/golang/xdb" - "github.com/mitchellh/go-homedir" -) - -func printHelp() { - fmt.Printf("ip2region xdb searcher\n") - fmt.Printf("%s [command] [command options]\n", os.Args[0]) - fmt.Printf("Command: \n") - fmt.Printf(" search search input test\n") - fmt.Printf(" bench search bench test\n") -} - -func testSearch() { - var err error - var dbFile, cachePolicy = "", "vectorIndex" - for i := 2; i < len(os.Args); i++ { - r := os.Args[i] - if len(r) < 5 { - continue - } - - if strings.Index(r, "--") != 0 { - continue - } - - var sIdx = strings.Index(r, "=") - if sIdx < 0 { - fmt.Printf("missing = for args pair '%s'\n", r) - return - } - - switch r[2:sIdx] { - case "db": - dbFile = r[sIdx+1:] - case "cache-policy": - cachePolicy = r[sIdx+1:] - default: - fmt.Printf("undefined option `%s`\n", r) - return - } - } - - if dbFile == "" { - fmt.Printf("%s search [command options]\n", os.Args[0]) - fmt.Printf("options:\n") - fmt.Printf(" --db string ip2region binary xdb file path\n") - fmt.Printf(" --cache-policy string cache policy: file/vectorIndex/content\n") - return - } - - dbPath, err := homedir.Expand(dbFile) - if err != nil { - fmt.Printf("invalid xdb file path `%s`: %s", dbFile, err) - return - } - - // create the searcher with the cache policy setting - searcher, err := createSearcher(dbPath, cachePolicy) - if err != nil { - fmt.Printf("failed to create searcher: %s\n", err.Error()) - return - } - defer func() { - searcher.Close() - fmt.Printf("searcher test program exited, thanks for trying\n") - }() - - fmt.Printf(`ip2region xdb searcher test program -source xdb: %s (%s, %s) -type 'quit' to exit -`, dbPath, searcher.IPVersion().Name, cachePolicy) - reader := bufio.NewReader(os.Stdin) - for { - fmt.Print("ip2region>> ") - str, err := reader.ReadString('\n') - if err != nil { - log.Fatalf("failed to read string: %s", err) - } - - line := strings.TrimSpace(strings.TrimSuffix(str, "\n")) - if len(line) == 0 { - continue - } - - if line == "quit" { - break - } - - tStart := time.Now() - region, err := searcher.SearchByStr(line) - if err != nil { - fmt.Printf("\x1b[0;31m{err: %s, ioCount: %d}\x1b[0m\n", err.Error(), searcher.GetIOCount()) - } else { - fmt.Printf("\x1b[0;32m{region: %s, ioCount: %d, took: %s}\x1b[0m\n", region, searcher.GetIOCount(), time.Since(tStart)) - } - } -} - -func testBench() { - var err error - var dbFile, srcFile, cachePolicy = "", "", "vectorIndex" - for i := 2; i < len(os.Args); i++ { - r := os.Args[i] - if len(r) < 5 { - continue - } - - if strings.Index(r, "--") != 0 { - continue - } - - var sIdx = strings.Index(r, "=") - if sIdx < 0 { - fmt.Printf("missing = for args pair '%s'\n", r) - return - } - - switch r[2:sIdx] { - case "db": - dbFile = r[sIdx+1:] - case "src": - srcFile = r[sIdx+1:] - case "cache-policy": - cachePolicy = r[sIdx+1:] - default: - fmt.Printf("undefined option `%s`\n", r) - return - } - } - - if dbFile == "" || srcFile == "" { - fmt.Printf("%s bench [command options]\n", os.Args[0]) - fmt.Printf("options:\n") - fmt.Printf(" --db string ip2region binary xdb file path\n") - fmt.Printf(" --src string source ip text file path\n") - fmt.Printf(" --cache-policy string cache policy: file/vectorIndex/content\n") - return - } - - dbPath, err := homedir.Expand(dbFile) - if err != nil { - fmt.Printf("invalid xdb file path `%s`: %s", dbFile, err) - return - } - - searcher, err := createSearcher(dbPath, cachePolicy) - if err != nil { - fmt.Printf("failed to create searcher: %s\n", err.Error()) - return - } - defer func() { - searcher.Close() - }() - - handle, err := os.OpenFile(srcFile, os.O_RDONLY, 0600) - if err != nil { - fmt.Printf("failed to open source text file: %s\n", err) - return - } - defer handle.Close() - - var count, tStart, costs = int64(0), time.Now(), int64(0) - var scanner = bufio.NewScanner(handle) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - var l = strings.TrimSpace(strings.TrimSuffix(scanner.Text(), "\n")) - var ps = strings.SplitN(l, "|", 3) - if len(ps) != 3 { - fmt.Printf("invalid ip segment line `%s`\n", l) - return - } - - sip, err := xdb.ParseIP(ps[0]) - if err != nil { - fmt.Printf("check start ip `%s`: %s\n", ps[0], err) - return - } - - eip, err := xdb.ParseIP(ps[1]) - if err != nil { - fmt.Printf("check end ip `%s`: %s\n", ps[1], err) - return - } - - if xdb.IPCompare(sip, eip) > 0 { - fmt.Printf("start ip(%s) should not be greater than end ip(%s)\n", ps[0], ps[1]) - return - } - - for _, ip := range [][]byte{sip, eip} { - sTime := time.Now() - region, err := searcher.Search(ip) - if err != nil { - fmt.Printf("failed to search ip '%s': %s\n", xdb.IP2String(ip), err) - return - } - - costs += time.Since(sTime).Nanoseconds() - - // check the region info - if region != ps[2] { - fmt.Printf("failed Search(%s) with (%s != %s)\n", xdb.IP2String(ip), region, ps[2]) - return - } - - count++ - } - } - - cost := time.Since(tStart) - fmt.Printf("Bench finished, {cachePolicy: %s, total: %d, took: %s, cost: %d μs/op}\n", - cachePolicy, count, cost, costs/count/1000) -} - -func createSearcher(dbPath string, cachePolicy string) (*xdb.Searcher, error) { - handle, err := os.OpenFile(dbPath, os.O_RDONLY, 0600) - if err != nil { - return nil, fmt.Errorf("open xdb file `%s`: %w", dbPath, err) - } - - defer handle.Close() - - // verify the xdb file - // @Note: do NOT call it every time you create a searcher since this will slow down the search response. - // @see the util.Verify function for details. - err = xdb.Verify(handle) - if err != nil { - return nil, fmt.Errorf("xdb verify: %w", err) - } - - // auto-detect the ip version from the xdb header - header, err := xdb.LoadHeader(handle) - if err != nil { - return nil, fmt.Errorf("failed to load header from `%s`: %s", dbPath, err) - } - - version, err := xdb.VersionFromHeader(header) - if err != nil { - return nil, fmt.Errorf("failed to detect IP version from `%s`: %s", dbPath, err) - } - - switch cachePolicy { - case "nil", "file": - return xdb.NewWithFileOnly(version, dbPath) - case "vectorIndex": - vIndex, err := xdb.LoadVectorIndexFromFile(dbPath) - if err != nil { - return nil, fmt.Errorf("failed to load vector index from `%s`: %w", dbPath, err) - } - - return xdb.NewWithVectorIndex(version, dbPath, vIndex) - case "content": - cBuff, err := xdb.LoadContentFromFile(dbPath) - if err != nil { - return nil, fmt.Errorf("failed to load content from '%s': %w", dbPath, err) - } - - return xdb.NewWithBuffer(version, cBuff) - default: - return nil, fmt.Errorf("invalid cache policy `%s`, options: file/vectorIndex/content", cachePolicy) - } -} - -func main() { - if len(os.Args) < 2 { - printHelp() - return - } - - // set the log flag - log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) - switch strings.ToLower(os.Args[1]) { - case "search": - testSearch() - case "bench": - testBench() - default: - printHelp() - } -} diff --git a/binding/golang/xdb/searcher.go b/binding/golang/xdb/searcher.go deleted file mode 100644 index b93a829..0000000 --- a/binding/golang/xdb/searcher.go +++ /dev/null @@ -1,267 +0,0 @@ -// 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. - -// --- -// Ip2Region database v2.0 searcher. -// @Note this is a Not thread safe implementation. -// -// @Author Lion -// @Date 2022/06/16 - -package xdb - -import ( - "encoding/binary" - "fmt" - "os" -) - -const ( - Structure20 = 2 - Structure30 = 3 - HeaderInfoLength = 256 - VectorIndexRows = 256 - VectorIndexCols = 256 - VectorIndexSize = 8 -) - -// --- Index policy define - -type IndexPolicy int - -const ( - VectorIndexPolicy IndexPolicy = 1 - BTreeIndexPolicy IndexPolicy = 2 -) - -func (i IndexPolicy) String() string { - switch i { - case VectorIndexPolicy: - return "VectorIndex" - case BTreeIndexPolicy: - return "BtreeIndex" - default: - return "unknown" - } -} - -// --- Header define - -type Header struct { - // data []byte - Version uint16 - IndexPolicy IndexPolicy - CreatedAt uint32 - StartIndexPtr uint32 - EndIndexPtr uint32 - - // since IPv6 supporting - IPVersion int - RuntimePtrBytes int -} - -func NewHeader(input []byte) (*Header, error) { - if len(input) < 16 { - return nil, fmt.Errorf("invalid input buffer") - } - - return &Header{ - Version: binary.LittleEndian.Uint16(input[0:]), - IndexPolicy: IndexPolicy(binary.LittleEndian.Uint16(input[2:])), - CreatedAt: binary.LittleEndian.Uint32(input[4:]), - StartIndexPtr: binary.LittleEndian.Uint32(input[8:]), - EndIndexPtr: binary.LittleEndian.Uint32(input[12:]), - - IPVersion: int(binary.LittleEndian.Uint16(input[16:])), - RuntimePtrBytes: int(binary.LittleEndian.Uint16(input[18:])), - }, nil -} - -// --- searcher implementation - -type Searcher struct { - version *Version - handle *os.File - - ioCount int - - // use it only when this feature enabled. - // Preload the vector index will reduce the number of IO operations - // thus speedup the search process - vectorIndex []byte - - // content buffer. - // running with the whole xdb file cached - contentBuff []byte -} - -func baseNew(version *Version, dbFile string, vIndex []byte, cBuff []byte) (*Searcher, error) { - var err error - - // content buff first - if cBuff != nil { - return &Searcher{ - version: version, - vectorIndex: nil, - contentBuff: cBuff, - }, nil - } - - // open the xdb binary file - handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600) - if err != nil { - return nil, err - } - - return &Searcher{ - version: version, - handle: handle, - vectorIndex: vIndex, - }, nil -} - -func NewWithFileOnly(version *Version, dbFile string) (*Searcher, error) { - return baseNew(version, dbFile, nil, nil) -} - -func NewWithVectorIndex(version *Version, dbFile string, vIndex []byte) (*Searcher, error) { - return baseNew(version, dbFile, vIndex, nil) -} - -func NewWithBuffer(version *Version, cBuff []byte) (*Searcher, error) { - return baseNew(version, "", nil, cBuff) -} - -func (s *Searcher) Close() { - if s.handle != nil { - err := s.handle.Close() - if err != nil { - return - } - } -} - -// IPVersion return the ip version -func (s *Searcher) IPVersion() *Version { - return s.version -} - -// GetIOCount return the global io count for the last search -func (s *Searcher) GetIOCount() int { - return s.ioCount -} - -// SearchByStr find the region for the specified ip string -func (s *Searcher) SearchByStr(str string) (string, error) { - ip, err := ParseIP(str) - if err != nil { - return "", err - } - - return s.Search(ip) -} - -// Search find the region for the specified long ip -func (s *Searcher) Search(ip []byte) (string, error) { - // ip version check - if len(ip) != s.version.Bytes { - return "", fmt.Errorf("invalid ip address(%s expected)", s.version.Name) - } - - // reset the global ioCount - s.ioCount = 0 - - // locate the segment index block based on the vector index - var il0, il1 = int(ip[0]), int(ip[1]) - var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize - var sPtr, ePtr = uint32(0), uint32(0) - if s.vectorIndex != nil { - sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:]) - ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:]) - } else if s.contentBuff != nil { - sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:]) - ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:]) - } else { - // read the vector index block - var buff = make([]byte, VectorIndexSize) - err := s.read(int64(HeaderInfoLength+idx), buff) - if err != nil { - return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err) - } - - sPtr = binary.LittleEndian.Uint32(buff) - ePtr = binary.LittleEndian.Uint32(buff[4:]) - } - - // fmt.Printf("sPtr=%d, ePtr=%d\n", sPtr, ePtr) - - // binary search the segment index to get the region - var bytes, dBytes = len(ip), len(ip) << 1 - var segIndexSize = uint32(s.version.SegmentIndexSize) - var dataLen, dataPtr = 0, uint32(0) - var buff = make([]byte, segIndexSize) - var l, h = 0, int((ePtr - sPtr) / segIndexSize) - for l <= h { - m := (l + h) >> 1 - p := sPtr + uint32(m)*segIndexSize - err := s.read(int64(p), buff) - if err != nil { - return "", fmt.Errorf("read segment index at %d: %w", p, err) - } - - // decode the data step by step to reduce the unnecessary operations - if s.version.IPCompare(ip, buff[0:bytes]) < 0 { - h = m - 1 - } else if s.version.IPCompare(ip, buff[bytes:dBytes]) > 0 { - l = m + 1 - } else { - dataLen = int(binary.LittleEndian.Uint16(buff[dBytes:])) - dataPtr = binary.LittleEndian.Uint32(buff[dBytes+2:]) - break - } - } - - // fmt.Printf("dataLen: %d, dataPtr: %d\n", dataLen, dataPtr) - if dataLen == 0 { - return "", nil - } - - // load and return the region data - var regionBuff = make([]byte, dataLen) - err := s.read(int64(dataPtr), regionBuff) - if err != nil { - return "", fmt.Errorf("read region at %d: %w", dataPtr, err) - } - - return string(regionBuff), nil -} - -// do the data read operation based on the setting. -// content buffer first or will read from the file. -// this operation will invoke the Seek for file based read. -func (s *Searcher) read(offset int64, buff []byte) error { - if s.contentBuff != nil { - cLen := copy(buff, s.contentBuff[offset:]) - if cLen != len(buff) { - return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff)) - } - } else { - _, err := s.handle.Seek(offset, 0) - if err != nil { - return fmt.Errorf("seek to %d: %w", offset, err) - } - - s.ioCount++ - rLen, err := s.handle.Read(buff) - if err != nil { - return fmt.Errorf("handle read: %w", err) - } - - if rLen != len(buff) { - return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff)) - } - } - - return nil -} diff --git a/binding/golang/xdb/util.go b/binding/golang/xdb/util.go deleted file mode 100644 index 50eaf92..0000000 --- a/binding/golang/xdb/util.go +++ /dev/null @@ -1,245 +0,0 @@ -// 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 -// @Date 2022/06/16 - -package xdb - -import ( - "bytes" - "embed" - "fmt" - "io" - "net" - "os" -) - -func ParseIP(ip string) ([]byte, error) { - parsedIP := net.ParseIP(ip) - if parsedIP == nil { - return nil, fmt.Errorf("invalid ip address: %s", ip) - } - - v4 := parsedIP.To4() - if v4 != nil { - return v4, nil - } - - v6 := parsedIP.To16() - if v6 != nil { - return v6, nil - } - - return nil, fmt.Errorf("invalid ip address: %s", ip) -} - -func IP2String(ip []byte) string { - return net.IP(ip[:]).String() -} - -// IPCompare compares two IP addresses -// Returns: -1 if ip1 < ip2, 0 if ip1 == ip2, 1 if ip1 > ip2 -func IPCompare(ip1, ip2 []byte) int { - // for i := 0; i < len(ip1); i++ { - // if ip1[i] < ip2[i] { - // return -1 - // } - // if ip1[i] > ip2[i] { - // return 1 - // } - // } - // return 0 - return bytes.Compare(ip1, ip2) -} - -// 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. -func Verify(handle *os.File) error { - header, err := LoadHeader(handle) - if err != nil { - return fmt.Errorf("loading header: %w", err) - } - - // get the runtime ptr bytes - runtimePtrBytes := 0 - switch header.Version { - case Structure20: - runtimePtrBytes = 4 - case Structure30: - runtimePtrBytes = header.RuntimePtrBytes - default: - return fmt.Errorf("invalid version: %d", header.Version) - } - - // 1, confirm the xdb file size. - // to sure that the MaxFilePointer does no overflow - stat, err := handle.Stat() - if err != nil { - return fmt.Errorf("file stat: %w", err) - } - - maxFilePtr := int64(1<<(runtimePtrBytes*8) - 1) - if stat.Size() > maxFilePtr { - return fmt.Errorf("xdb file exceeds the maximum supported bytes: %d", maxFilePtr) - } - - return nil -} - -// VerifyFromFile check Verify for details -func VerifyFromFile(dbFile string) error { - handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600) - if err != nil { - return fmt.Errorf("open xdb file `%s`: %w", dbFile, err) - } - defer handle.Close() - - return Verify(handle) -} - -// LoadHeader load the header info from the specified handle -func LoadHeader(handle *os.File) (*Header, error) { - _, err := handle.Seek(0, 0) - if err != nil { - return nil, fmt.Errorf("seek to the header: %w", err) - } - - var buff = make([]byte, HeaderInfoLength) - rLen, err := handle.Read(buff) - if err != nil { - return nil, err - } - - if rLen != len(buff) { - return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff)) - } - - return NewHeader(buff) -} - -// LoadHeaderFromFile load header info from the specified db file path -func LoadHeaderFromFile(dbFile string) (*Header, error) { - handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600) - if err != nil { - return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err) - } - defer handle.Close() - - header, err := LoadHeader(handle) - if err != nil { - return nil, err - } - - return header, nil -} - -// LoadHeaderFromBuff wrap the header info from the content buffer -func LoadHeaderFromBuff(cBuff []byte) (*Header, error) { - return NewHeader(cBuff[0:HeaderInfoLength]) -} - -// LoadVectorIndex util function to load the vector index from the specified file handle -func LoadVectorIndex(handle *os.File) ([]byte, error) { - // load all the vector index block - _, err := handle.Seek(HeaderInfoLength, 0) - if err != nil { - return nil, fmt.Errorf("seek to vector index: %w", err) - } - - var buff = make([]byte, VectorIndexRows*VectorIndexCols*VectorIndexSize) - rLen, err := handle.Read(buff) - if err != nil { - return nil, err - } - - if rLen != len(buff) { - return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff)) - } - - return buff, nil -} - -// LoadVectorIndexFromFile load vector index from a specified file path -func LoadVectorIndexFromFile(dbFile string) ([]byte, error) { - handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600) - if err != nil { - return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err) - } - defer handle.Close() - - vIndex, err := LoadVectorIndex(handle) - if err != nil { - return nil, err - } - - return vIndex, nil -} - -// LoadContent load the whole xdb content from the specified file handle -func LoadContent(handle *os.File) ([]byte, error) { - // get file size - fi, err := handle.Stat() - if err != nil { - return nil, fmt.Errorf("stat: %w", err) - } - - size := fi.Size() - - // seek to the head of the file - _, err = handle.Seek(0, 0) - if err != nil { - return nil, fmt.Errorf("seek to get xdb file length: %w", err) - } - - var buff = make([]byte, size) - rLen, err := handle.Read(buff) - if err != nil { - return nil, err - } - - if rLen != len(buff) { - return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff)) - } - - return buff, nil -} - -// LoadContentFromFile load the whole xdb content from the specified db file path -func LoadContentFromFile(dbFile string) ([]byte, error) { - handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600) - if err != nil { - return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err) - } - defer handle.Close() - - cBuff, err := LoadContent(handle) - if err != nil { - return nil, err - } - - return cBuff, nil -} - -// LoadContentFromFS load the whole xdb binary from embed.FS -func LoadContentFromFS(fs embed.FS, filePath string) ([]byte, error) { - file, err := fs.Open(filePath) - if err != nil { - return nil, fmt.Errorf("failed to open embedded file `%s`: %w", filePath, err) - } - defer file.Close() - - var cBuff []byte - cBuff, err = io.ReadAll(file) - if err != nil { - return nil, fmt.Errorf("failed to read embedded file `%s`: %w", filePath, err) - } - - return cBuff, nil -} diff --git a/binding/golang/xdb/util_test.go b/binding/golang/xdb/util_test.go deleted file mode 100644 index 75696a7..0000000 --- a/binding/golang/xdb/util_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// 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 -// @Date 2022/06/16 - -package xdb - -import ( - "fmt" - "testing" - "time" -) - -func TestParseIP(t *testing.T) { - var ips = []string{"29.34.191.255", "2c0f:fff0::", "2fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"} - for _, ip := range ips { - bytes, err := ParseIP(ip) - if err != nil { - t.Errorf("check ip `%s`: %s\n", IP2String(bytes), err) - } - - nip := IP2String(bytes) - fmt.Printf("checkip: (%s / %s), isEqual: %v\n", ip, nip, ip == nip) - } -} - -func TestIPCompare(t *testing.T) { - var ipPairs = [][]string{ - {"1.2.3.4", "1.2.3.5"}, - {"58.250.36.41", "58.250.30.41"}, - {"2c10::", "2e00::"}, - {"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, - {"fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "fe00::"}, - } - - for _, pairs := range ipPairs { - fmt.Printf("IPCompare(%s, %s): %d\n", pairs[0], pairs[1], IPCompare([]byte(pairs[0]), []byte(pairs[1]))) - } -} - -func TestLoadVectorIndex(t *testing.T) { - vIndex, err := LoadVectorIndexFromFile("../../../data/ip2region_v4.xdb") - if err != nil { - fmt.Printf("failed to load vector index: %s\n", err) - return - } - - fmt.Printf("vIndex length: %d\n", len(vIndex)) -} - -func TestLoadContent(t *testing.T) { - buff, err := LoadContentFromFile("../../../data/ip2region_v4.xdb") - if err != nil { - fmt.Printf("failed to load xdb content: %s\n", err) - return - } - - fmt.Printf("buff length: %d\n", len(buff)) -} - -func TestLoadHeader(t *testing.T) { - header, err := LoadHeaderFromFile("../../../data/ip2region_v4.xdb") - if err != nil { - fmt.Printf("failed to load xdb header info: %s\n", err) - return - } - - fmt.Printf("Version : %d\n", header.Version) - fmt.Printf("IndexPolicy : %s\n", header.IndexPolicy.String()) - fmt.Printf("CreatedAt : %d(%s)\n", header.CreatedAt, time.Unix(int64(header.CreatedAt), 0).Format(time.RFC3339)) - fmt.Printf("StartIndexPtr : %d\n", header.StartIndexPtr) - fmt.Printf("EndIndexPtr : %d\n", header.EndIndexPtr) - fmt.Printf("IPVersion : %d\n", header.IPVersion) - fmt.Printf("RuntimePtrBytes : %d\n", header.RuntimePtrBytes) -} diff --git a/binding/golang/xdb/version.go b/binding/golang/xdb/version.go deleted file mode 100644 index 972dad2..0000000 --- a/binding/golang/xdb/version.go +++ /dev/null @@ -1,97 +0,0 @@ -// 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. - -package xdb - -import ( - "bytes" - "fmt" - "strings" -) - -type Version struct { - Id int - Name string - Bytes int - SegmentIndexSize int - - // function to compare two ips - IPCompare func([]byte, []byte) int -} - -const ( - IPv4VersionNo = 4 - IPv6VersionNo = 6 -) - -var ( - IPvx = &Version{} - IPv4 = &Version{ - Id: IPv4VersionNo, - Name: "IPv4", - Bytes: 4, - SegmentIndexSize: 14, // 4 + 4 + 2 + 4, - IPCompare: func(ip1, ip2 []byte) int { - // ip1 - with Big endian byte order parsed from an input - // ip2 - with Little endian byte order read from the xdb index - ip2[0], ip2[3] = ip2[3], ip2[0] - ip2[1], ip2[2] = ip2[2], ip2[1] - return bytes.Compare(ip1, ip2) - }, - } - IPv6 = &Version{ - Id: IPv6VersionNo, - Name: "IPv6", - Bytes: 16, - SegmentIndexSize: 38, // 16 + 16 + 2 + 4, - IPCompare: func(ip1, ip2 []byte) int { - return bytes.Compare(ip1, ip2) - }, - } -) - -func VersionFromIP(ip string) (*Version, error) { - r, err := ParseIP(ip) - if err != nil { - return IPvx, fmt.Errorf("parse ip fail: %w", err) - } - - if len(r) == 4 { - return IPv4, nil - } - - return IPv6, nil -} - -func VersionFromName(name string) (*Version, error) { - switch strings.ToUpper(name) { - case "V4", "IPV4": - return IPv4, nil - case "V6", "IPV6": - return IPv6, nil - default: - return IPvx, fmt.Errorf("invalid version name `%s`", name) - } -} - -func VersionFromHeader(header *Header) (*Version, error) { - // old structure with IPv4 supports ONLY - if header.Version == Structure20 { - return IPv4, nil - } - - // structure 3.0 after IPv6 supporting - if header.Version != Structure30 { - return IPvx, fmt.Errorf("invalid version `%d`", header.IPVersion) - } - - switch header.IPVersion { - case IPv4VersionNo: - return IPv4, nil - case IPv6VersionNo: - return IPv6, nil - default: - return IPvx, fmt.Errorf("invalid version `%d`", header.Version) - } -} diff --git a/binding/java/ReadMe.md b/binding/java/ReadMe.md deleted file mode 100644 index e490a38..0000000 --- a/binding/java/ReadMe.md +++ /dev/null @@ -1,256 +0,0 @@ -# ip2region xdb java 查询客户端实现 - -# 使用方式 - -### maven 仓库: -```xml - - org.lionsoul - ip2region - 3.1.0 - -``` - -### 关于 IPv4 和 IPv6 -该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下: -```java -import org.lionsoul.ip2region.xdb.Version; - -// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 Version.IPv4 -final String dbPath = "../../data/ip2region_v4.xdb"; // 或者你的 ipv4 xdb 的路径 -final Version version = Version.IPv4; - -// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 Version.IPv6 -final String dbPath = "../../data/ip2region_v6.xdb"; // 或者你的 ipv6 xdb 路径 -final Version version = Version.IPv6; - -// dbPath 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错 -// 备注:以下演示直接使用 dbPath 和 version 变量 -``` - -### 文件验证 -建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。 -```java -try { - Searcher.verifyFromFile(dbPath); -} catch (Exception e) { - // 适用性验证失败!!! - // 当前查询客户端实现不适用于 dbPath 指定的 xdb 文件的查询. - // 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 dbPath 的 Searcher 实现。 - return; -} - -// 验证通过,当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作 -``` - -### 完全基于文件的查询 - -```java -import org.lionsoul.ip2region.xdb.Searcher; -import java.io.*; -import java.util.concurrent.TimeUnit; - -public class SearcherTest { - public static void main(String[] args) { - // 1、使用上述的 version 和 dbPath 创建 searcher 对象 - Searcher searcher = null; - try { - searcher = Searcher.newWithFileOnly(version, dbPath); - } catch (IOException e) { - System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e); - return; - } - - // 2、查询,IPv4 或者 IPv6 的地址都支持 - try { - String ip = "1.2.3.4"; - // ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - long sTime = System.nanoTime(); - String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); - System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); - } catch (Exception e) { - System.out.printf("failed to search(%s): %s\n", ip, e); - } - - // 3、关闭资源 - searcher.close(); - - // 备注:并发使用,每个线程需要创建一个独立的 searcher 对象单独使用。 - } -} -``` - -### 缓存 `VectorIndex` 索引 - -我们可以提前从 `xdb` 文件中加载出来 `VectorIndex` 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。 -```java -import org.lionsoul.ip2region.xdb.Searcher; -import java.io.*; -import java.util.concurrent.TimeUnit; - -public class SearcherTest { - public static void main(String[] args) { - // 备注:version 和 dbPath 来源,请看上面的版本描述 - - // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。 - byte[] vIndex; - try { - vIndex = Searcher.loadVectorIndexFromFile(dbPath); - } catch (Exception e) { - System.out.printf("failed to load vector index from `%s`: %s\n", dbPath, e); - return; - } - - // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 - Searcher searcher; - try { - searcher = Searcher.newWithVectorIndex(version, dbPath, vIndex); - } catch (Exception e) { - System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e); - return; - } - - // 3、查询,IPv4 或者 IPv6 地址都支持 - try { - String ip = "1.2.3.4"; - // ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - long sTime = System.nanoTime(); - String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); - System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); - } catch (Exception e) { - System.out.printf("failed to search(%s): %s\n", ip, e); - } - - // 4、关闭资源 - searcher.close(); - - // 备注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。 - } -} -``` - -### 缓存整个 `xdb` 数据 - -我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。 -```java -import org.lionsoul.ip2region.xdb.Searcher; -import java.io.*; -import java.util.concurrent.TimeUnit; - -public class SearcherTest { - public static void main(String[] args) { - // 备注:version 和 dbPath 来源,请看上面的版本描述 - - // 1、从 dbPath 加载整个 xdb 到内存。 - // 从这个 release 版本开始,xdb 的 buffer 使用 LongByteArray 来存储,避免 xdb 文件过大的时候 int 类型的溢出 - LongByteArray cBuff; - try { - cBuff = Searcher.loadContentFromFile(dbPath); - } catch (Exception e) { - System.out.printf("failed to load content from `%s`: %s\n", dbPath, e); - return; - } - - // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 - Searcher searcher; - try { - searcher = Searcher.newWithBuffer(version, cBuff); - } catch (Exception e) { - System.out.printf("failed to create content cached searcher: %s\n", e); - return; - } - - // 3、查询,IPv4 和 IPv6 都支持 - try { - String ip = "1.2.3.4"; - // ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - long sTime = System.nanoTime(); - String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); - System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); - } catch (Exception e) { - System.out.printf("failed to search(%s): %s\n", ip, e); - } - - // 4、关闭资源 - 该 searcher 对象可以安全用于并发,等整个服务关闭的时候再关闭 searcher - // searcher.close(); - - // 备注:并发使用,用整个 xdb 数据缓存创建的查询对象可以安全的用于并发,也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。 - } -} -``` - - -# 编译测试程序 - -通过 maven 来编译测试程序。 -```bash -# cd 到 java binding 的根目录 -cd binding/java/ -mvn compile package -``` - -然后会在当前目录的 target 目录下得到一个 ip2region-{version}.jar 的打包文件。 - - - -# 查询测试 - -可以通过 `java -jar ip2region-{version}.jar search` 命令来测试查询: -```bash -➜ java git:(fr_java_ipv6) ✗ java -jar target/ip2region-3.1.0.jar search -java -jar ip2region-{version}.jar 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 -➜ java git:(fr_java_ipv6) ✗ java -jar target/ip2region-3.1.0.jar search --db=../../data/ip2region_v4.xdb -ip2region xdb searcher test program -source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex) -type 'quit' to exit -ip2region>> 1.2.3.4 -{region: 美国|华盛顿|0|谷歌, ioCount: 7, took: 82 μs} -``` - -例如:使用默认的 data/ip2region_v6.xdb 文件进行 IPv6 的查询测试: -```bash -➜ java git:(fr_java_ipv6) ✗ java -jar target/ip2region-3.1.0.jar search --db=../../data/ip2region_v6.xdb -ip2region xdb searcher test program -source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) -type 'quit' to exit -ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e -{region: 中国|广东省|深圳市|家庭宽带, ioCount: 14, took: 424 μs} -``` - -输入 ip 即可进行查询测试,也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的查询效果。 - - -# bench 测试 - -可以通过 `java -jar ip2region-{version}.jar bench` 命令来进行 bench 测试,一方面确保 `xdb` 文件没有错误,一方面可以评估查询性能: -```bash -➜ java git:(fr_java_ipv6) ✗ java -jar target/ip2region-3.1.0.jar bench -java -jar ip2region-{version}.jar 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 -java -jar target/ip2region-3.1.0.jar bench --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt -``` - -例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 文件进行 IPv6 的 bench 测试: -```bash -java -jar target/ip2region-3.1.0.jar bench --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt -``` - -可以通过分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效果。 -@Note: 注意 bench 使用的 src 文件要是生成对应 xdb 文件相同的源文件。 diff --git a/binding/java/pom.xml b/binding/java/pom.xml deleted file mode 100644 index dea3fc7..0000000 --- a/binding/java/pom.xml +++ /dev/null @@ -1,204 +0,0 @@ - - - 4.0.0 - - org.lionsoul - ip2region - 3.1.0 - jar - - ip2region - https://github.com/lionsoul2014/ip2region - An open source offline IP address data manager framework and locator with both IPv4 and IPv6 suppported - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - git@github.com:lionsoul2014/ip2region.git - scm:git:git@github.com:lionsoul2014/ip2region.git - scm:git:git@github.com:lionsoul2014/ip2region.git - - - - - lionsoul - chenxin - chenxin619315@gmail.com - - - - - - lionsoul - https://oss.sonatype.org/content/repositories/snapshots/ - - - lionsoul - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - https://github.com/lionsoul2014/ip2region/issues - Github issues - - - - UTF-8 - UTF-8 - 1.8 - 1.8 - - - - - junit - junit - 4.13.1 - test - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.1.2 - - - attach-sources - package - - jar - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - - attach-javadocs - package - - jar - - - ${javadoc.opts} - - - - - false - - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - package - - shade - - - - - org.lionsoul.ip2region.SearchApp - - - - - - - - - - - - java8-doclint-disabled - - [1.8,) - - - -Xdoclint:none - - - - release - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - package - - jar - - - ${javadoc.opts} - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - verify - - sign - - - - - - - org.sonatype.central - central-publishing-maven-plugin - 0.7.0 - true - - lionsoul - true - - - - - - - - diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/SearchApp.java b/binding/java/src/main/java/org/lionsoul/ip2region/SearchApp.java deleted file mode 100644 index 64fa6da..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/SearchApp.java +++ /dev/null @@ -1,251 +0,0 @@ -// 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 -// @Date 2022/06/23 - -package org.lionsoul.ip2region; - -import org.lionsoul.ip2region.xdb.InetAddressException; -import org.lionsoul.ip2region.xdb.XdbException; -import org.lionsoul.ip2region.xdb.LongByteArray; -import org.lionsoul.ip2region.xdb.Searcher; -import org.lionsoul.ip2region.xdb.Util; -import org.lionsoul.ip2region.xdb.Version; - -import java.io.*; -import java.nio.charset.Charset; -import java.util.concurrent.TimeUnit; - -public class SearchApp { - - public static void printHelp(String[] args) { - System.out.print("ip2region xdb searcher\n"); - System.out.print("java -jar ip2region-{version}.jar [command] [command options]\n"); - System.out.print("Command: \n"); - System.out.print(" search search input test\n"); - System.out.print(" bench search bench test\n"); - } - - public static Searcher createSearcher(String dbPath, String cachePolicy) throws IOException, XdbException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - - // verify the xdb file - // @Note: do NOT call it every time you create a searcher since this will slow - // down the search response. - // @see the util.Verify function for details. - Searcher.verify(handle); - - // get the ip version from header - final Version version = Version.fromHeader(Searcher.loadHeader(handle)); - - // create the final searcher - if ("file".equals(cachePolicy)) { - return Searcher.newWithFileOnly(version, dbPath); - } else if ("vectorIndex".equals(cachePolicy)) { - byte[] vIndex = Searcher.loadVectorIndexFromFile(dbPath); - return Searcher.newWithVectorIndex(version, dbPath, vIndex); - } else if ("content".equals(cachePolicy)) { - LongByteArray cBuff = Searcher.loadContentFromFile(dbPath); - return Searcher.newWithBuffer(version, cBuff); - } else { - throw new IOException("invalid cache policy `" + cachePolicy + "`, options: file/vectorIndex/content"); - } - } - - public static void searchTest(String[] args) throws IOException, XdbException { - String dbPath = "", cachePolicy = "vectorIndex"; - for (final String r : args) { - if (r.length() < 5) { - continue; - } - - if (r.indexOf("--") != 0) { - continue; - } - - int sIdx = r.indexOf('='); - if (sIdx < 0) { - System.out.printf("missing = for args pair `%s`\n", r); - return; - } - - String key = r.substring(2, sIdx); - String val = r.substring(sIdx + 1); - // System.out.printf("key=%s, val=%s\n", key, val); - if ("db".equals(key)) { - dbPath = val; - } else if ("cache-policy".equals(key)) { - cachePolicy = val; - } else { - System.out.printf("undefined option `%s`\n", r); - return; - } - } - - if (dbPath.isEmpty()) { - System.out.print("java -jar ip2region-{version}.jar search [command options]\n"); - System.out.print("options:\n"); - System.out.print(" --db string ip2region binary xdb file path\n"); - System.out.print(" --cache-policy string cache policy: file/vectorIndex/content\n"); - return; - } - - Searcher searcher = createSearcher(dbPath, cachePolicy); - final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - System.out.printf("ip2region xdb searcher test program\n" -+ "source xdb: %s (%s, %s)\n" -+ "type 'quit' to exit\n", dbPath, searcher.getIPVersion().name, cachePolicy); - while ( true ) { - System.out.print("ip2region>> "); - String line = reader.readLine().trim(); - if ( line.length() < 2 ) { - continue; - } - - if ( line.equalsIgnoreCase("quit") ) { - break; - } - - try { - double sTime = System.nanoTime(); - String region = searcher.search(line); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); - System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); - } catch (Exception e) { - System.out.printf("{err: %s, ioCount: %d}\n", e, searcher.getIOCount()); - } - } - - reader.close(); - searcher.close(); - System.out.println("searcher test program exited, thanks for trying"); - } - - public static void benchTest(String[] args) throws IOException, XdbException, InetAddressException { - String dbPath = "", srcPath = "", cachePolicy = "vectorIndex"; - for (final String r : args) { - if (r.length() < 5) { - continue; - } - - if (r.indexOf("--") != 0) { - continue; - } - - int sIdx = r.indexOf('='); - if (sIdx < 0) { - System.out.printf("missing = for args pair `%s`\n", r); - return; - } - - String key = r.substring(2, sIdx); - String val = r.substring(sIdx + 1); - if ("db".equals(key)) { - dbPath = val; - } else if ("src".equals(key)) { - srcPath = val; - } else if ("cache-policy".equals(key)) { - cachePolicy = val; - } else { - System.out.printf("undefined option `%s`\n", r); - return; - } - } - - if (dbPath.length() < 1 || srcPath.length() < 1) { - System.out.print("java -jar ip2region-{version}.jar bench [command options]\n"); - System.out.print("options:\n"); - System.out.print(" --db string ip2region binary xdb file path\n"); - System.out.print(" --src string source ip text file path\n"); - System.out.print(" --cache-policy string cache policy: file/vectorIndex/content\n"); - return; - } - - Searcher searcher = createSearcher(dbPath, cachePolicy); - long count = 0, costs = 0, tStart = System.nanoTime(); - String line; - final Charset charset = Charset.forName("utf-8"); - final FileInputStream fis = new FileInputStream(srcPath); - final BufferedReader reader = new BufferedReader(new InputStreamReader(fis, charset)); - while ((line = reader.readLine()) != null) { - String l = line.trim(); - String[] ps = l.split("\\|", 3); - if (ps.length != 3) { - reader.close(); - System.out.printf("invalid ip segment `%s`\n", l); - return; - } - - byte[] sip; - try { - sip = Util.parseIP(ps[0]); - } catch (Exception e) { - reader.close(); - System.out.printf("check start ip `%s`: %s\n", ps[0], e); - return; - } - - byte[] eip; - try { - eip = Util.parseIP(ps[1]); - } catch (Exception e) { - reader.close(); - System.out.printf("check end ip `%s`: %s\n", ps[1], e); - return; - } - - if (Util.ipCompare(sip, eip) > 0) { - reader.close(); - System.out.printf("start ip(%s) should not be greater than end ip(%s)\n", ps[0], ps[1]); - return; - } - - for (final byte[] ip : new byte[][]{sip, eip}) { - long sTime = System.nanoTime(); - String region = searcher.search(ip); - costs += System.nanoTime() - sTime; - - // check the region info - if (!ps[2].equals(region)) { - System.out.printf("failed search(%s) with (%s != %s)\n", Util.ipToString(ip), region, ps[2]); - reader.close(); - return; - } - - count++; - } - } - - reader.close(); - searcher.close(); - long took = System.nanoTime() - tStart; - System.out.printf("Bench finished, {cachePolicy: %s, total: %d, took: %ds, cost: %d μs/op}\n", - cachePolicy, count, TimeUnit.NANOSECONDS.toSeconds(took), - count == 0 ? 0 : TimeUnit.NANOSECONDS.toMicros(costs/count)); - } - - public static void main(String[] args) { - if (args.length < 1) { - printHelp(args); - return; - } - - if ("search".equals(args[0])) { - try { - searchTest(args); - } catch (Exception e) { - System.out.printf("failed running search test: %s\n", e); - } - } else if ("bench".equals(args[0])) { - try { - benchTest(args); - } catch (Exception e) { - System.out.printf("fwailed running bench test: %s\n", e); - } - } else { - printHelp(args); - } - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java deleted file mode 100644 index a6305f1..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java +++ /dev/null @@ -1,45 +0,0 @@ -// 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 -// @Date 2022/06/23 - -package org.lionsoul.ip2region.xdb; - -public class Header { - public final int version; - public final int indexPolicy; - public final long createdAt; - public final long startIndexPtr; - public final long endIndexPtr; - - // since xdb 3.0 with IPv6 supporting - public final int ipVersion; - public final int runtimePtrBytes; - - public final byte[] buffer; - - public Header(byte[] buff) { - assert buff.length >= 16; - version = LittleEndian.getInt2(buff, 0); - indexPolicy = LittleEndian.getInt2(buff, 2); - createdAt = LittleEndian.getUint32(buff, 4); - startIndexPtr = LittleEndian.getUint32(buff, 8); - endIndexPtr = LittleEndian.getUint32(buff, 12); - ipVersion = LittleEndian.getInt2(buff, 16); - runtimePtrBytes = LittleEndian.getInt2(buff, 18); - buffer = buff; - } - - @Override public String toString() { - return "{" + - "Version: " + version + ',' + - "IndexPolicy: " + indexPolicy + ',' + - "CreatedAt: " + createdAt + ',' + - "StartIndexPtr: " + startIndexPtr + ',' + - "EndIndexPtr: " + endIndexPtr + ',' + - "IPVersion: " + ipVersion + ',' + - "RuntimePtrBytes: " + runtimePtrBytes + - '}'; - } -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java deleted file mode 100644 index bd07013..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// IPv4 version implementation -// @Author Lion -// @Date 2025/09/10 - -public class IPv4 extends Version { - public IPv4() { - // segmentIndex: 4 + 4 + 2 + 4 - super(4, "IPv4", 4, 14); - } - - @Override - public int putBytes(byte[] buff, int offset, byte[] ip) { - // use the Little endian byte order to compatible with the old searcher implementation - buff[offset++] = ip[3]; - buff[offset++] = ip[2]; - buff[offset++] = ip[1]; - buff[offset ] = ip[0]; - return ip.length; - } - - @Override - public int ipSubCompare(byte[] ip1, byte[] buff, int offset) { - // ip1: Big endian byte order parsed from input - // ip2: Little endian byte order read from xdb index. - // @Note: to compatible with the old Litten endian index encode implementation. - int j = offset + ip1.length - 1; - for (int i = 0; i < ip1.length; i++, j--) { - final int i1 = (int) (ip1[i] & 0xFF); - final int i2 = (int) (buff[j] & 0xFF); - if (i1 < i2) { - return -1; - } - - if (i1 > i2) { - return 1; - } - } - - return 0; - } -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java deleted file mode 100644 index af7349d..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// IPv4 version implementation -// @Author Lion -// @Date 2025/09/10 - -public class IPv6 extends Version { - - public IPv6() { - // segmentIndex: 16 + 16 + 2 + 4 - super(6, "IPv6", 16, 38); - } - - @Override - public int putBytes(byte[] buff, int offset, byte[] ip) { - System.arraycopy(ip, 0, buff, offset, ip.length); - return ip.length; - } - - @Override - public int ipSubCompare(byte[] ip1, byte[] buff, int offset) { - return Util.ipSubCompare(ip1, buff, offset); - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java deleted file mode 100644 index 369c2b8..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java +++ /dev/null @@ -1,13 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -public class InetAddressException extends Exception { - - public InetAddressException(String str) { - super(str); - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java deleted file mode 100644 index 2a8b7b0..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java +++ /dev/null @@ -1,58 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// Little Endian basic data type decode and encode. -// @Author Lion -// @Date 2025/09/10 - -public class LittleEndian { - - public final static int[] shiftIndex = {0, 8, 16, 24, 32, 40, 48, 56}; - - // put specified bytes to the buffer started from the offset - public static void put(final byte[] buff, int offset, long value, int bytes) { - if (bytes > 8) { - throw new IndexOutOfBoundsException("bytes should be <= 8"); - } - - for (int i = 0; i < bytes; i++) { - buff[offset++] = (byte)((value >>> shiftIndex[i]) & 0xFF); - } - } - - // put an uint32 (4 bytes long) to the buffer from the offset - public static void putUint32(final byte[] buff, int offset, long value) { - buff[offset++] = (byte) (value & 0xFF); - buff[offset++] = (byte) ((value >> 8) & 0xFF); - buff[offset++] = (byte) ((value >> 16) & 0xFF); - buff[offset ] = (byte) ((value >> 24) & 0xFF); - } - - // put a 2-bytes int to the buffer from the specified offset - public static void putInt2(final byte[] buff, int offset, int value) { - buff[offset++] = (byte) (value & 0xFF); - buff[offset ] = (byte) ((value >> 8) & 0xFF); - } - - // get an uint32 from a byte array from the specified offset - public static long getUint32(final byte[] buff, int offset) { - return ( - ((buff[offset++] & 0x000000FFL)) | - ((buff[offset++] << 8) & 0x0000FF00L) | - ((buff[offset++] << 16) & 0x00FF0000L) | - ((buff[offset ] << 24) & 0xFF000000L) - ); - } - - // get an 2 bytes int from a byte array from the specified offset - public static int getInt2(final byte[] buff, int offset) { - return ( - ((buff[offset++]) & 0x000000FF) | - ((buff[offset ] << 8) & 0x0000FF00) - ); - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java deleted file mode 100644 index a5297fe..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java +++ /dev/null @@ -1,119 +0,0 @@ -// 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 -// @Date 2022/07/14 - -package org.lionsoul.ip2region.xdb; - -import java.text.SimpleDateFormat; -import java.util.Date; - -// simple log implementation -public class Log { - - /* Log level constants define */ - public static final int DEBUG = 0; - public static final int INFO = 1; - public static final int WARN = 2; - public static final int ERROR = 3; - - // level name - public static final String[] level_string = new String[] { - "DEBUG", - "INFO", - "WARN", - "ERROR" - }; - - public final Class baseClass; - private int level = INFO; - - public Log(Class baseClass) { - this.baseClass = baseClass; - } - - public static Log getLogger(Class baseClass) { - return new Log(baseClass); - } - - public String format(int level, String format, Object... args) { - // append the datetime - final StringBuilder sb = new StringBuilder(); - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - sb.append(String.format("%s %-5s ", sdf.format(new Date()), level_string[level])); - - // append the class name - sb.append(baseClass.getName()).append(' '); - sb.append(String.format(format, args)); - return sb.toString(); - } - - public void printf(int level, String format, Object... args) { - if (level < DEBUG || level > ERROR) { - throw new IndexOutOfBoundsException("invalid level index " + level); - } - - // level filter - if (level < this.level) { - return; - } - - System.out.println(format(level, format, args)); - System.out.flush(); - } - - public String getDebugf(String format, Object... args) { - return format(DEBUG, format, args); - } - - public void debugf(String format, Object... args) { - printf(DEBUG, format, args); - } - - public String getInfof(String format, Object... args) { - return format(INFO, format, args); - } - - public void infof(String format, Object... args) { - printf(INFO, format, args); - } - - public String getWarnf(String format, Object... args) { - return format(WARN, format, args); - } - - public void warnf(String format, Object... args) { - printf(WARN, format, args); - } - - public String getErrorf(String format, Object... args) { - return format(ERROR, format, args); - } - - public void errorf(String format, Object... args) { - printf(ERROR, format, args); - } - - public Log setLevel(int level) { - this.level = level; - return this; - } - - public Log setLevel(String level) { - String v = level.toLowerCase(); - if ("debug".equals(v)) { - this.level = DEBUG; - } else if ("info".equals(v)) { - this.level = INFO; - } else if ("warn".equals(v)) { - this.level = WARN; - } else if ("error".equals(v)) { - this.level = ERROR; - } - - return this; - } - -} \ No newline at end of file diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java deleted file mode 100644 index 3fb2a07..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java +++ /dev/null @@ -1,130 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// xdb byte buffer which used to instead of the byte array -// when the size of the xdb file is greater than 2^32 << 2; -// xdb file v4 is designed to be a maximum of 2^32 bytes in size. -// @Author Leon -// @Date 2025/08/22 - -import java.util.ArrayList; -import java.util.List; - -public class LongByteArray { - // byte buffer list - private final List buffs = new ArrayList(); - private long length; - - public LongByteArray() { - this.length = 0; - } - - public LongByteArray(byte[] buff) { - buffs.add(buff); - length = buff.length; - } - - // append new buffer - public void append(final byte[] buffer) { - buffs.add(buffer); - length += buffer.length; - } - - public long length() { - return length; - } - - // internal method to determine the position of the specified offset - private Position determinate(final long offset) { - int index = 0, position = 0, buffLen = buffs.size(); - long curIndex = 0; - for (index = 0; index < buffLen; index++) { - final byte[] buff = buffs.get(index); - if (curIndex + buff.length < offset) { - curIndex += buff.length; - continue; - } - - // matched and calc the position - position = (int) (offset - curIndex); - break; - } - - return new Position(index, position); - } - - // Copy from the current buffers to a specified buffer - // from the specified offset with a specified length - public byte[] copy(final long srcPos, final byte[] dest, final int destPos, final int length) { - if (srcPos >= this.length) { - throw new IndexOutOfBoundsException("srcPos exceed the maximum array length `" + this.length + "`"); - } - - if (destPos + length > dest.length) { - throw new IndexOutOfBoundsException("destPost+length exceed the maximum dest buffer length `" + dest.length + "`"); - } - - final Position pos = determinate(srcPos); - - // copy from the current buffer - final byte[] hBuff = buffs.get(pos.index++); - final int copyLen = Math.min(hBuff.length - pos.offset, length); - System.arraycopy(hBuff, pos.offset, dest, destPos, copyLen); - - // check and copy from the rest buffer? - int sPos = destPos + copyLen; - int left = length - copyLen; - while (left > 0) { - final byte[] tBuff = buffs.get(pos.index++); - final int buffLen = tBuff.length; - if (left <= buffLen) { - System.arraycopy(tBuff, 0, dest, sPos, left); - break; - } - - System.arraycopy(tBuff, 0, dest, sPos, buffLen); - sPos += buffLen; - left -= buffLen; - } - - return dest; - } - - // get a byte-buffer from the specified index with a specified length. - // this method will allocate a new byte buffer with length = $length. - public byte[] slice(long offset, int length) { - if (offset + length > this.length) { - throw new IndexOutOfBoundsException("offset+length exceed the maximum array length `" + this.length + "`"); - } - - final byte[] buffer = new byte[length]; - return copy(offset, buffer, 0, length); - } - - // get a 4-bytes uint32 integer from the specified index - public long getUint32(long offset) { - final byte[] b = new byte[4]; - copy(offset, b, 0, 4); - return LittleEndian.getUint32(b, 0); - } - - public int getInt2(long offset) { - final byte[] b = new byte[4]; - copy(offset, b, 0, 4); - return LittleEndian.getInt2(b, 0); - } - - // position entry class - public static class Position { - public int index; - public int offset; - public Position(int index, int offset) { - this.index = index; - this.offset = offset; - } - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java deleted file mode 100644 index 974da13..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ /dev/null @@ -1,272 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// xdb searcher (Not thread safe implementation) -// @Author Lion -// @Date 2022/06/23 - -import java.io.IOException; -import java.io.RandomAccessFile; - -public class Searcher { - // xdb structure version no - public static final int STRUCTURE_20 = 2; - public static final int STRUCTURE_30 = 3; - - // constant defined copied from the xdb maker - public static final int HeaderInfoLength = 256; - public static final int VectorIndexRows = 256; - public static final int VectorIndexCols = 256; - public static final int VectorIndexSize = 8; - - // Linux max write / read bytes - public static final int MAX_WRITE_BYTES = 0x7ffff000; - - // ip version - private final Version version; - - // random access file handle for file-based search - private final RandomAccessFile handle; - - private int ioCount = 0; - - // vector index. - // use the byte[] instead of VectorIndex entry array to keep - // the minimal memory allocation. - private final byte[] vectorIndex; - - // xdb content buffer, used for in-memory search. - // @Note: use the LongByteArray instead since 2025/08/22 - // private final byte[] contentBuff; - private final LongByteArray contentBuff; - - // --- static method to create searchers - - public static Searcher newWithFileOnly(Version version, String dbPath) throws IOException { - return new Searcher(version, dbPath, null, null); - } - - public static Searcher newWithVectorIndex(Version version, String dbPath, byte[] vectorIndex) throws IOException { - return new Searcher(version, dbPath, vectorIndex, null); - } - - public static Searcher newWithBuffer(Version version, LongByteArray cBuff) throws IOException { - return new Searcher(version, null, null, cBuff); - } - - // --- End of creator - - public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException { - this.version = version; - if (cBuff != null) { - this.handle = null; - this.vectorIndex = null; - this.contentBuff = cBuff; - } else { - this.handle = new RandomAccessFile(dbFile, "r"); - this.vectorIndex = vectorIndex; - this.contentBuff = null; - } - } - - public void close() throws IOException { - if (this.handle != null) { - this.handle.close(); - } - } - - public Version getIPVersion() { - return version; - } - - public int getIOCount() { - return ioCount; - } - - public String search(String ipStr) throws Exception { - return search(Util.parseIP(ipStr)); - } - - public String search(byte[] ip) throws IOException, InetAddressException { - // ip version check - if (ip.length != version.bytes) { - throw new InetAddressException("invalid ip address ("+version.name+" expected)"); - } - - // reset the global counter - this.ioCount = 0; - - // locate the segment index block based on the vector index - long sPtr = 0, ePtr = 0; - int il0 = (int) (ip[0] & 0xFF); - int il1 = (int) (ip[1] & 0xFF); - int idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; - // System.out.printf("il0: %d, il1: %d, idx: %d\n", il0, il1, idx); - if (vectorIndex != null) { - sPtr = LittleEndian.getUint32(vectorIndex, idx); - ePtr = LittleEndian.getUint32(vectorIndex, idx + 4); - } else if (contentBuff != null) { - sPtr = contentBuff.getUint32(HeaderInfoLength + idx); - ePtr = contentBuff.getUint32(HeaderInfoLength + idx + 4); - } else { - final byte[] buff = new byte[VectorIndexSize]; - read(HeaderInfoLength + idx, buff); - sPtr = LittleEndian.getUint32(buff, 0); - ePtr = LittleEndian.getUint32(buff, 4); - } - - // System.out.printf("sPtr: %d, ePtr: %d\n", sPtr, ePtr); - - // binary search the segment index block to get the region info - final int bytes = ip.length, dBytes = ip.length << 1; - final int segIndexSize = version.segmentIndexSize; - final byte[] buff = new byte[segIndexSize]; - int dataLen = -1; - long dataPtr = -1, l = 0, h = (ePtr - sPtr) / segIndexSize; - while (l <= h) { - long m = (l + h) >> 1; - long p = sPtr + m * segIndexSize; - - // read the segment index - read(p, buff); - if (version.ipSubCompare(ip, buff, 0) < 0) { - h = m - 1; - } else if (version.ipSubCompare(ip, buff, bytes) > 0) { - l = m + 1; - } else { - dataLen = LittleEndian.getInt2(buff, dBytes); - dataPtr = LittleEndian.getUint32(buff, dBytes + 2); - break; - } - } - - // empty match interception - // System.out.printf("dataLen: %d, dataPtr: %d\n", dataLen, dataPtr); - if (dataPtr < 0) { - return null; - } - - // load and return the region data - final byte[] regionBuff = new byte[dataLen]; - read(dataPtr, regionBuff); - return new String(regionBuff, "utf-8"); - } - - protected void read(long offset, byte[] buffer) throws IOException { - // check the in-memory buffer first - if (contentBuff != null) { - contentBuff.copy(offset, buffer, 0, buffer.length); - return; - } - - // read from the file handle - assert handle != null; - handle.seek(offset); - - this.ioCount++; - int rLen = handle.read(buffer); - if (rLen != buffer.length) { - throw new IOException("incomplete read: read bytes should be " + buffer.length); - } - } - - // --- static util function - - public static Header loadHeader(RandomAccessFile handle) throws IOException { - handle.seek(0); - final byte[] buff = new byte[HeaderInfoLength]; - handle.read(buff); - return new Header(buff); - } - - public static Header loadHeaderFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final Header header = loadHeader(handle); - handle.close(); - return header; - } - - public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException { - handle.seek(HeaderInfoLength); - int len = VectorIndexRows * VectorIndexCols * VectorIndexSize; - final byte[] buff = new byte[len]; - int rLen = handle.read(buff); - if (rLen != len) { - throw new IOException("incomplete read: read bytes should be " + len); - } - - return buff; - } - - public static byte[] loadVectorIndexFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final byte[] vIndex = loadVectorIndex(handle); - handle.close(); - return vIndex; - } - - public static LongByteArray loadContent(RandomAccessFile handle) throws IOException { - handle.seek(0); - // check the length and do the buff load - long toRead = handle.length(); - final LongByteArray byteArray = new LongByteArray(); - while (toRead > 0) { - final byte[] buff = new byte[(int) Math.min(toRead, MAX_WRITE_BYTES)]; - final int rLen = handle.read(buff); - if (rLen != buff.length) { - throw new IOException("incomplete read: read bytes should be " + buff.length + ", got `" + rLen + "`"); - } - - byteArray.append(buff); - toRead -= rLen; - } - - return byteArray; - } - - public static LongByteArray loadContentFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final LongByteArray content = loadContent(handle); - handle.close(); - return content; - } - - // --- verify util function - - // 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. - public static void verify(RandomAccessFile handle) throws IOException, XdbException { - final Header header = loadHeader(handle); - - // get the runtime ptr bytes - int runtimePtrBytes = 0; - if (header.version == STRUCTURE_20) { - runtimePtrBytes = 4; - } else if (header.version == STRUCTURE_30) { - runtimePtrBytes = header.runtimePtrBytes; - } else { - throw new XdbException("invalid structure version `" + header.version + "`"); - } - - // 1, confirm the xdb file size - // to ensure that the maximum file pointer does not overflow - final long maxFilePtr = (1L << (runtimePtrBytes * 8)) - 1; - if (handle.length() > maxFilePtr) { - throw new XdbException("xdb file exceeds the maximum supported bytes: "+maxFilePtr+""); - } - } - - public static void verifyFromFile(String dbFile) throws IOException, XdbException { - final RandomAccessFile handle = new RandomAccessFile(dbFile, "r"); - verify(handle); - handle.close(); - } - -} \ No newline at end of file diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java deleted file mode 100644 index 461d24c..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java +++ /dev/null @@ -1,82 +0,0 @@ -// 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 -// @Date 2022/07/14 - -package org.lionsoul.ip2region.xdb; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -public class Util -{ - - // parse the specified IP address and return its bytes. - // returns: byte[4] for IPv4 and byte[16] for IPv6 and the bytes should be in Big endian order. - public static byte[] parseIP(String ip) throws InetAddressException { - try { - return InetAddress.getByName(ip).getAddress(); - } catch (UnknownHostException e) { - throw new InetAddressException("invalid ip address `"+ip+"`"); - } - } - - // convert the byte[] ip to string ip address - public static String ipToString(final byte[] ip) { - if (ip.length != 4 && ip.length != 16) { - return String.format("invalid-ip-address-length: %d", ip.length); - } - - try { - return InetAddress.getByAddress(ip).getHostAddress(); - } catch (UnknownHostException e) { - return String.format("invalid-ip-address `%s`", ipJoin(ip)); - } - } - - // implode the byte[] ip with its byte value. - public static String ipJoin(byte[] ip) { - return bytesToString(ip, 0, ip.length); - } - - public static String bytesToString(byte[] buff, int offset, int length) { - final StringBuffer sb = new StringBuffer(); - sb.append("["); - for (int i = 0; i < length; i++) { - if (i > 0) { - sb.append(','); - } - sb.append((buff[offset+i] & 0xFF)); - } - sb.append("]"); - return sb.toString(); - } - - // compare two byte ip - // Returns: -1 if ip1 < ip2, 0 if ip1 == ip2, 1 if ip1 > ip2 - public static int ipCompare(byte[] ip1, byte[] ip2) { - return ipSubCompare(ip1, ip2, 0); - } - - // compare the ip with the ip in the buffer start from offset - // Returns: -1 if ip < buff[offset], 0 if ip == buff[offset], 1 if ip > buff[offset] - public static int ipSubCompare(byte[] ip, byte[] buff, int offset) { - for (int i = 0; i < ip.length; i++) { - // covert the byte to int to sure the uint8 attribute - final int i1 = (int)(ip[i] & 0xFF); - final int i2 = (int)(buff[offset+i] & 0xFF); - if (i1 < i2) { - return -1; - } - - if (i1 > i2) { - return 1; - } - } - - return 0; - } - -} diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java deleted file mode 100644 index e062a0d..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java +++ /dev/null @@ -1,83 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -// IP version abstract manager (IPv4 & IPv6) -// @Author Lion -// @Date 2025/09/10 - -public abstract class Version { - public static final int IPv4VersionNo = 4; - public static final int IPv6VersionNo = 6; - - public static final IPv4 IPv4 = new IPv4(); - public static final IPv6 IPv6 = new IPv6(); - - // version id and name - public final int id; - public final String name; - - // the numbers of bytes for one IP - public final int bytes; - - // segment index size (bytes) - public final int segmentIndexSize; - - public Version(int id, String name, int bytes, int segmentIndexSize) { - this.id = id; - this.name = name; - this.bytes = bytes; - this.segmentIndexSize = segmentIndexSize; - } - - // encode the specified IP bytes to the specified buffer - public abstract int putBytes(byte[] buff, int offset, byte[] ip); - - // compare the two IPs with the current version. - // Returns: -1 if ip1 < ip2, 0 if ip1 == ip2, 1 if ip1 > ip2 - public int ipCompare(byte[] ip1, byte[] ip2) { - return ipSubCompare(ip1, ip2, 0); - } - - // @see ipCompare - public abstract int ipSubCompare(byte[] ip1, byte[] buff, int offset); - - // parse the version from an name - public static final Version fromName(String name) throws Exception { - final String n = name.toUpperCase(); - if (n.equals("V4") || n.equals("IPV4")) { - return IPv4; - } else if (n.equals("V6") || n.equals("IPV6")) { - return IPv6; - } else { - throw new Exception("invalid version name `"+name+"`"); - } - } - - // parse the version from header - public static final Version fromHeader(Header header) throws XdbException { - // Old 2.0 structure with IPv4 supports ONLY. - if (header.version == Searcher.STRUCTURE_20) { - return IPv4; - } - - // structure 3.0 after IPv6 supporting - if (header.version != Searcher.STRUCTURE_30) { - throw new XdbException("invalid xdb structure version `"+header.version+"`"); - } - - if (header.ipVersion == IPv4VersionNo) { - return IPv4; - } else if (header.ipVersion == IPv6VersionNo) { - return IPv6; - } else { - throw new XdbException("invalid ip version number `" + header.ipVersion + "`"); - } - } - - @Override public String toString() { - return String.format("{Id:%d, Name:%s, Bytes:%d, IndexSize: %d}", id, name, bytes, segmentIndexSize); - } -} \ No newline at end of file diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java deleted file mode 100644 index afdded2..0000000 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java +++ /dev/null @@ -1,13 +0,0 @@ -// 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. - -package org.lionsoul.ip2region.xdb; - -public class XdbException extends Exception { - - public XdbException(String str) { - super(str); - } - -} diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java b/binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java deleted file mode 100644 index ef34917..0000000 --- a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.lionsoul.ip2region.xdb; - -import org.junit.Test; - -public class IPv4Test { - - private static final Log log = Log.getLogger(IPv4Test.class); - - @Test - public void testIpSubCompare() throws InetAddressException { - final byte[] sip = Util.parseIP("0.255.255.255"); - final byte[] eip = Util.parseIP("1.0.0.2"); - final byte[] buff = new byte[Version.IPv4.segmentIndexSize]; - Version.IPv4.putBytes(buff, 0, sip); - Version.IPv4.putBytes(buff, 4, eip); - log.infof("bytesToString(buff): %s", Util.bytesToString(buff, 0, 8)); - - final byte[] ip = Util.parseIP("1.0.0.0"); - - // compare the sip - log.infof("ipSubCompare(%s, %s): %d", - Util.ipToString(ip), - Util.bytesToString(buff, 0, 4), - Util.ipSubCompare(ip, buff, 0) - ); - log.infof("IPv4.ipSubCompare(%s, %s): %d", - Util.ipToString(ip), - Util.bytesToString(buff, 0, 4), - Version.IPv4.ipSubCompare(ip, buff, 0) - ); - - // compare the eip - log.infof("ipSubCompare(%s, %s): %d", - Util.ipToString(ip), - Util.bytesToString(buff, 4, 4), - Util.ipSubCompare(ip, buff, 4) - ); - log.infof("IPv4.ipSubCompare(%s, %s): %d", - Util.ipToString(ip), - Util.bytesToString(buff, 4, 4), - Version.IPv4.ipSubCompare(ip, buff, 4) - ); - } -} diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java deleted file mode 100644 index 9b216a3..0000000 --- a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.lionsoul.ip2region.xdb; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class LittleEndianTest { - - private static final Log log = Log.getLogger(LittleEndianTest.class).setLevel(Log.DEBUG); - - @Test - public void testAll() { - final byte[] buff = new byte[14]; - - // encode - // do the put - LittleEndian.put(buff, 0, 1L, 4); - LittleEndian.put(buff, 4, 2L, 4); - - // putUint32 - LittleEndian.putInt2(buff, 8, 24); - LittleEndian.putUint32(buff, 10, 1024L); - - // decode - assertEquals(LittleEndian.getUint32(buff, 0), 1); - assertEquals(LittleEndian.getUint32(buff, 4), 2); - assertEquals(LittleEndian.getInt2(buff, 8), 24); - assertEquals(LittleEndian.getUint32(buff, 10), 1024); - - log.debugf("uint32(buff, 0): %d", LittleEndian.getUint32(buff, 0)); - log.debugf("uint32(buff, 4): %d", LittleEndian.getUint32(buff, 4)); - log.debugf("int2(buff, 8): %d", LittleEndian.getInt2(buff, 8)); - log.debugf("uint32(buff, 10): %d", LittleEndian.getUint32(buff, 10)); - } - -} diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java deleted file mode 100644 index 5fa36d1..0000000 --- a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.lionsoul.ip2region.xdb; - -import org.junit.Test; - -public class UtilTest { - - private static final Log log = Log.getLogger(UtilTest.class).setLevel(Log.DEBUG); - - @Test - public void testCheckIP() throws InetAddressException { - final String[] ips = new String[]{ - "192.168.1.102", - "219.133.111.87", - "::", - "3000::", - "::1001:ffff", - "2001:2:0:ffff:ffff:ffff:ffff:ffff", - "::ffff:114.114.114.114" - }; - - for (String ip : ips) { - final byte[] ipBytes = Util.parseIP(ip); - log.debugf("%s(v=%s) => %s", ip, Util.ipJoin(ipBytes), Util.ipToString(ipBytes)); - } - } - - @Test - public void testIpCompare() throws InetAddressException { - final String[][] ipPairs = new String[][]{ - {"1.0.0.0", "1.0.0.1"}, - {"192.168.1.101", "192.168.1.90"}, - {"219.133.111.87", "114.114.114.114"}, - {"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"} - }; - - for (String[] ips : ipPairs) { - final byte[] ip1 = Util.parseIP(ips[0]); - final byte[] ip2 = Util.parseIP(ips[1]); - log.debugf("compare(%s, %s): %d", ips[0], ips[1], Util.ipCompare(ip1, ip2)); - } - } - - @Test - public void testIpSubCompare() throws InetAddressException { - final String[][] ipPairs = new String[][] { - {"1.0.0.0", "1.0.0.1"}, - {"192.168.1.100", "192.168.2.100"}, - {"10.100.1.10", "11.100.2.10"}, - {"11.100.1.10", "10.100.2.10"} - }; - - for (final String[] ips : ipPairs) { - final byte[] ip1 = Util.parseIP(ips[0]); - final byte[] ip2 = Util.parseIP(ips[1]); - log.debugf("ipSubCompare(%s, %s): %d", Util.ipToString(ip1), Util.ipToString(ip2), Util.ipSubCompare(ip1, ip2, 0)); - } - } - -} diff --git a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java b/binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java deleted file mode 100644 index 42f4bc7..0000000 --- a/binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.lionsoul.ip2region.xdb; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class VersionTest { - - private static final Log log = Log.getLogger(VersionTest.class).setLevel(Log.DEBUG); - - @Test - public void testFromName() throws Exception { - final String[] vers = new String[]{"IPv4", "IPv6"}; - final Version v4 = Version.fromName(vers[0]); - assertEquals(v4.name, vers[0]); - final Version v6 = Version.fromName(vers[1]); - assertEquals(v6.name, vers[1]); - - log.debugf("v4: %s", v4); - log.debugf("v6: %s", v6); - } - - @Test - public void testFromHeader() throws XdbException { - // create the header buffer - final byte[] buff1 = new byte[Searcher.HeaderInfoLength]; - // structure version - LittleEndian.put(buff1, 0, 2, 2); - LittleEndian.put(buff1, 16, Version.IPv4VersionNo, 2); - - final byte[] buff2 = new byte[Searcher.HeaderInfoLength]; - LittleEndian.put(buff2, 0, 3, 2); - LittleEndian.put(buff2, 16, Version.IPv6VersionNo, 2); - - final Version ver1 = Version.fromHeader(new Header(buff1)); - final Version ver2 = Version.fromHeader(new Header(buff2)); - log.debugf("ver1: %s", ver1.toString()); - log.debugf("ver2: %s", ver2.toString()); - } - -} diff --git a/binding/lua/ReadMe.md b/binding/lua/ReadMe.md deleted file mode 100644 index c3a97a3..0000000 --- a/binding/lua/ReadMe.md +++ /dev/null @@ -1,153 +0,0 @@ -# ip2region xdb lua 查询客户端实现 - -#### 备注:请优先使用 lua_c 扩展 xdb 查询客户端,性能比纯 lua 实现的要快很多!!! - - -# 使用方式 - -### 完全基于文件的查询 -```lua -local xdb = require("xdb_searcher") - --- 1、从 db_path 创建基于文件的 xdb 查询对象 -local db_path = "ip2region.xdb file path" -local searcher, err = xdb.new_with_file_only(db_path) -if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return -end - --- 2、调用查询 API 进行查询 -local ip_str = "1.2.3.4" -local s_time = xdb.now() -region, err = searcher:search(ip_str) -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - --- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象 - -print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -``` - -### 缓存 `VectorIndex` 索引 - -如果你的 `lua` 母环境支持,可以预先加载 vectorIndex 缓存,然后做成全局变量,每次创建 Searcher 的时候使用全局的 vectorIndex,可以减少一次固定的 IO 操作从而加速查询,减少 io 压力。 -```lua -local xdb = require("xdb_searcher") - -local db_path = "ip2region.xdb file path" - --- 1、从指定的 db_path 加载 VectorIndex 缓存,把下述的 v_index 对象做成全局变量。 --- vectorIndex 加载一次即可,建议在服务启动的时候加载为全局对象。 -v_index, err = xdb.load_vector_index(db_path) -if err ~= nil then - print(string.format("failed to load vector index from '%s'", db_path)) - return -end - --- 2、使用全局的 v_index 创建带 VectorIndex 缓存的查询对象。 -searcher, err = xdb.new_with_vector_index(db_path, v_index) -if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return -end - --- 3、调用查询 API -local ip_str = "1.2.3.4" -local s_time = xdb.now() -region, err = searcher:search(ip_str) -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - --- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象 - -print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -``` - -### 缓存整个 `xdb` 数据 - -如果你的 `lua` 母环境支持,可以预先加载整个 xdb 的数据到内存,这样可以实现完全基于内存的查询,类似之前的 memory search 查询。 -```lua -local xdb = require("xdb_searcher") - -local db_path = "ip2region.xdb file path" - --- 1、从指定的 db_path 加载整个 xdb 到内存。 --- xdb内容加载一次即可,建议在服务启动的时候加载为全局对象。 -content = xdb.load_content(db_path) -if content == nil then - print(string.format("failed to load xdb content from '%s'", db_path)) - return -end - --- 2、使用全局的 content 创建带完全基于内存的查询对象。 -searcher, err = xdb.new_with_buffer(content) -if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return -end - --- 3、调用查询 API -local ip_str = "1.2.3.4" -local s_time = xdb.now() -region, err = searcher:search(ip_str) -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - --- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。 --- 建议在服务启动的时候创建好全局的 searcher 对象,然后全局并发使用。 - -print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -``` - - -# 查询测试 - -通过 `search_test.lua` 脚本来进行查询测试: -```bash -➜ lua git:(lua_binding) ✗ lua search_test.lua -lua search_test.lua [command options] -options: - --db string ip2region binary xdb file path - --cache-policy string cache policy: file/vectorIndex/content -``` - -例如:使用默认的 data/ip2region.xdb 进行查询测试: -```bash -➜ lua git:(lua_binding) ✗ lua search_test.lua --db=../../data/ip2region.xdb --cache-policy=vectorIndex -ip2region xdb searcher test program, cachePolicy: vectorIndex -type 'quit' to exit -ip2region>> 1.2.3.4 -{region: 美国|0|华盛顿|0|谷歌, io_count: 7, took: 0μs} -ip2region>> -``` - -输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。 - - -# bench 测试 - -通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: -```bash -➜ lua git:(lua_binding) ✗ lua bench_test.lua -lua bench_test.lua [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.xdb 和 data/ip.merge.txt 来进行 bench 测试: -```bash -➜ lua git:(lua_binding) ✗ lua bench_test.lua --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=vectorIndex -Bench finished, {cachePolicy: vectorIndex, total: 3417955, took: 29.000 s, cost: 7.899 μs/op} -``` - -可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。 -@Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件的相同的源文件。 diff --git a/binding/lua/bench_test.lua b/binding/lua/bench_test.lua deleted file mode 100644 index 91c9010..0000000 --- a/binding/lua/bench_test.lua +++ /dev/null @@ -1,175 +0,0 @@ --- 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 --- @Date 2022/06/30 - --- set the package to load the current xdb_searcher.so -package.path = "./?.lua" .. package.path -package.cpath = "./?.so" .. package.cpath -local xdb = require("xdb_searcher") - -function printHelp() - print("lua bench_test.lua [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --src string source ip text file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") -end - -if #arg < 2 then - printHelp(arg) - return -end - --- parser the command line args -local dbFile, srcFile = "", "" -local cachePolicy = "vectorIndex" -for _, r in ipairs(arg) do - if string.len(r) < 5 then - goto continue - end - - if string.sub(r, 1, 2) ~= "--" then - goto continue - end - - for k, v in string.gmatch(string.sub(r, 3), "([^=]+)=([^%s]+)") do - if k == "db" then - dbFile = v - elseif k == "src" then - srcFile = v - elseif k == "cache-policy" then - cachePolicy = v - else - print(string.format("undefined option `%s`", r)) - return - end - - -- break the match iterate - break - end - - -- continue this loop - ::continue:: -end - --- print(string.format("dbFile=%s, srcFile=%s, cachePolicy=%s", dbFile, srcFile, cachePolicy)) -if string.len(dbFile) < 2 or string.len(srcFile) < 2 then - printHelp() - return -end - --- create the searcher based on the cache-policy -local searcher, v_index, content -if cachePolicy == "file" then - searcher, err = xdb.new_with_file_only(dbFile) - if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return - end -elseif cachePolicy == "vectorIndex" then - v_index, err = xdb.load_vector_index(dbFile) - if err ~= nil then - print(string.format("failed to load vector index: %s", err)) - return - end - - searcher, err = xdb.new_with_vector_index(dbFile, v_index) - if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return - end -elseif cachePolicy == "content" then - content, err = xdb.load_content(dbFile) - if err ~= nil then - print(string.format("failed to load xdb content: %s", err)) - return - end - - searcher, err = xdb.new_with_buffer(content) - if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return - end -else - print(string.format("undefined cache-policy `%s`", cachePolicy)) - return -end - --- do the bench test -local handle = io.open(srcFile, "r") -if handle == nil then - print(string.format("failed to open src text file `%s`", handle)) - return -end - -local lines = handle:lines() -local sip_str, eip_str, s_region, region = "", "", "", "" -local sip, mip, eip, err = 0, 0, 0, 0 -local count, t_time, c_time = 0, 0, 0 -local s_time = xdb.now() -for l in lines do - if string.len(l) < 1 then - goto continue - end - - for v1, v2, v3 in string.gmatch(l, "([%d%.]+)|([%d%.]+)|([^\n]+)") do - -- print(sip_str, eip_str, region) - sip_str = v1 - eip_str = v2 - s_region = v3 - break - end - - sip, err = xdb.check_ip(sip_str) - if err ~= nil then - print(string.format("invalid start ip `%s`", sip_str)) - return - end - - eip, err = xdb.check_ip(eip_str) - if err ~= nil then - print(string.format("invalid end ip `%s`", sip_str)) - return - end - - if sip > eip then - print(string.format("start ip(%s) should not be greater than end ip(%s)\n", sip_str, eip_str)) - return - end - - mip = (sip + eip) >> 1 - for _, ip in ipairs({sip, (sip + mip) >> 1, mip, (mip + eip) >> 1, eip}) do - t_time = xdb.now() - region, err = searcher:search(ip) - c_time = c_time + xdb.now() - t_time - if err ~= nil then - print(string.format("failed to search ip `%s`", xdb.long2ip(ip))) - return - end - - -- check the region - if region ~= s_region then - print(string.format("failed search(%s) with (%s != %s)\n", xdb.long2ip(ip), region, s_region)) - return - end - - count = count + 1 - end - - ::continue:: -end - --- resource cleanup -searcher:close() - --- print the stats -local avg_costs = 0 -if count > 0 then - avg_costs = c_time / count -end -print(string.format("Bench finished, {cachePolicy: %s, total: %d, took: %.3f s, cost: %.3f μs/op}", - cachePolicy, count, (xdb.now() - s_time)/1e6, c_time / count)) diff --git a/binding/lua/search_test.lua b/binding/lua/search_test.lua deleted file mode 100644 index 24363a5..0000000 --- a/binding/lua/search_test.lua +++ /dev/null @@ -1,137 +0,0 @@ --- 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 --- @Date 2022/06/30 - --- set the package to load the current xdb_searcher.so -package.path = "./?.lua" .. package.path -package.cpath = "./?.so" .. package.cpath -local xdb = require("xdb_searcher") - -function printHelp() - print("lua search_test.lua [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") -end - -if #arg < 2 then - printHelp(arg) - return -end - --- parser the command line args -local dbFile = "" -local cachePolicy = "vectorIndex" -for _, r in ipairs(arg) do - if string.len(r) < 5 then - goto continue - end - - if string.sub(r, 1, 2) ~= "--" then - goto continue - end - - for k, v in string.gmatch(string.sub(r, 3), "([^=]+)=([^%s]+)") do - if k == "db" then - dbFile = v - elseif k == "cache-policy" then - cachePolicy = v - else - print(string.format("undefined option `%s`", r)) - return - end - - -- break the match iterate - break - end - - -- continue this loop - ::continue:: -end - --- print(string.format("dbFile=%s, cachePolicy=%s", dbFile, cachePolicy)) -if string.len(dbFile) < 2 then - printHelp() - return -end - --- create the searcher based on the cache-policy -local searcher, v_index, content -if cachePolicy == "file" then - searcher, err = xdb.new_with_file_only(dbFile) - if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return - end -elseif cachePolicy == "vectorIndex" then - v_index, err = xdb.load_vector_index(dbFile) - if err ~= nil then - print(string.format("failed to load vector index: %s", err)) - return - end - - searcher, err = xdb.new_with_vector_index(dbFile, v_index) - if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return - end -elseif cachePolicy == "content" then - content, err = xdb.load_content(dbFile) - if err ~= nil then - print(string.format("failed to load xdb content: %s", err)) - return - end - - searcher, err = xdb.new_with_buffer(content) - if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return - end -else - print(string.format("undefined cache-policy `%s`", cachePolicy)) - return -end - --- do the search -print(string.format([[ -ip2region xdb searcher test program, cachePolicy: %s -type 'quit' to exit]], cachePolicy)) -local region, err = "", nil -local ip_int, s_time, c_time = 0, 0, 0 -while ( true ) do - io.write("ip2region>> "); - io.input(io.stdin); - local line = io.read(); - if (line == nil) then - break - end - - if ( line == "quit" ) then - break - end - - ip_int, err = xdb.check_ip(line) - if err ~= nil then - print(string.format("invalid ip address `%s`", line)) - goto continue - end - - -- do the search - s_time = xdb.now() - region, err = searcher:search(line) - if err ~= nil then - print(string.format("{err: %s, io_count: %d}", err, searcher:get_io_count())) - else - c_time = xdb.now() - s_time - print(string.format("{region: %s, io_count: %d, took: %dμs}", region, searcher:get_io_count(), c_time)) - end - - ::continue:: -end - --- resource cleanup -searcher:close() \ No newline at end of file diff --git a/binding/lua/util_test.lua b/binding/lua/util_test.lua deleted file mode 100644 index a3df35e..0000000 --- a/binding/lua/util_test.lua +++ /dev/null @@ -1,98 +0,0 @@ --- 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 --- @Date 2022/07/05 - --- set the package path -package.path = "./?.lua" -package.cpath = "./?.so" - -local xdb = require("xdb_searcher") - ----- ip checking testing -print("--- testing check_ip and long2ip ... ") -local ip_list = { - "1.2.3.4", "192.168.2.3", "120.24.78.129", "255.255.255.0", - "256.7.12.9", "12.56.78.320", "32.12.45.192", "222.221.220.219", - "192.168.1.101 ", "132.96.12.98a", "x23.12.2.12" -} - -local s_time = xdb.now() -for _, ip_src in ipairs(ip_list) do - ip, err = xdb.check_ip(ip_src) - if err ~= nil then - print(string.format("invalid ip address `%s`: %s", ip_src, err)) - else - ip_dst = xdb.long2ip(ip) - io.write(string.format("long(%-15s)=%10d, long2ip(%-10d)=%-15s", ip_src, ip, ip, ip_dst)) - if ip_src ~= ip_dst then - print(" --[Failed]") - else - print(" --[Ok]") - end - end -end - ----- buffer loading test -print("\n--- testing load header ... ") -header, err = xdb.load_header("../../data/ip2region.xdb") -if err ~= nil then - print("failed to load header: ", err) -else - print("xdb header buffer loaded") - - local tpl = [[ -header: { - version: %d - index_policy: %d - created_at: %d - start_index_ptr: %d - end_index_ptr: %d -}]] - - print(string.format(tpl, - header["version"], header["index_policy"], - header["created_at"], header["start_index_ptr"], header["end_index_ptr"]) - ) -end - - -print("\n--- testing load vector index ... ") -v_index, err = xdb.load_vector_index("../../data/ip2region.xdb") -if err ~= nil then - print("failed to load vector index: ", err) -else - print("xdb vector index buffer loaded") -end - - -print("\n--- testing load content buffer ... ") -c_buffer, err = xdb.load_content("../../data/ip2region.xdb") -if err ~= nil then - print("failed to load content: ", err) -else - print("xdb content buffer loaded") -end - - -print("\n--- testing search ... ") -local ip_str = "1.2.3.4" -searcher, err = xdb.new_with_file_only("../../data/ip2region.xdb") -local t_start = xdb.now() -region, err = searcher:search(ip_str) -if err ~= nil then - print(string.format("search(%s) failed: %s", ip_str, err)) -else - local c_time = xdb.now() - t_start - print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}", - ip_str, region, searcher:get_io_count(), c_time, err)) - print(string.format("searcher.tostring=%s", searcher)) -end -searcher:close() - - -print("") -print(string.format("all tests done, elapsed %d μs", xdb.now() - s_time)) \ No newline at end of file diff --git a/binding/lua/xdb_searcher.lua b/binding/lua/xdb_searcher.lua deleted file mode 100644 index 1805819..0000000 --- a/binding/lua/xdb_searcher.lua +++ /dev/null @@ -1,334 +0,0 @@ --- 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 --- @Date 2022/07/05 - --- constants define -local HeaderInfoLength = 256 -local VectorIndexRows = 256 -local VectorIndexCols = 256 -local VectorIndexSize = 8 -local SegmentIndexSize = 14 -local VectorIndexLength = 524288 - -local _M = { - -- xdb file handle - handle = nil, - - -- header info - header = nil, - io_count = 0, - - -- vector index - vector_index = nil, - - -- xdb content buffer - content_buff = nil -} - --- index and to string attribute set -_M.__index = _M -_M.__tostring = function(self) - return "xdb searcher object (lua)" -end - - --- construct functions - -function newBase(dbPath, vIndex, cBuffer) - local obj = setmetatable({}, _M) - if cBuffer ~= nil then - obj.io_count = 0 - obj.vector_index = nil - obj.content_buff = cBuffer - else - obj.io_count = 0 - obj.vector_index = vIndex - obj.handle = io.open(dbPath, "r") - if obj.handle == nil then - return nil, string.format("failed to open xdb file `%s`", dbPath) - end - end - - return obj, nil -end - -function _M.new_with_file_only(dbPath) - return newBase(dbPath, nil, nil) -end - -function _M.new_with_vector_index(dbPath, vIndex) - return newBase(dbPath, vIndex, nil) -end - -function _M.new_with_buffer(cBuffer) - return newBase(nil, nil, cBuffer) -end - --- End of constructors - --- object api impl, must call via ':' - -function _M:search(ip_src) - -- check and convert string ip to long ip - local t, ip = type(ip_src), 0 - if t == nil then - return "", string.format("invalid ip address `%s`", ip_src) - elseif t == "string" then - ip, err = self.check_ip(ip_src) - if err ~= nil then - return "", string.format("check ip `%s`: %s", ip_src, err) - end - elseif t ~= "number" then - return "", "invalid number or string ip" - else - -- use the original value - ip = ip_src - end - - -- reset the global counter - -- and global resource local cache - self.io_count = 0 - local vector_index = self.vector_index - local content_buff = self.content_buff - local read_data = self.read - - -- locate the segment index based on the vector index - local il0 = (ip >> 24) & 0xFF - local il1 = (ip >> 16) & 0xFF - local idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize - local s_ptr, e_ptr = 0, 0 - if vector_index ~= nil then - s_ptr = getLong(vector_index, idx + 1) - e_ptr = getLong(vector_index, idx + 5) - elseif content_buff ~= nil then - s_ptr = getLong(content_buff, HeaderInfoLength + idx + 1) - e_ptr = getLong(content_buff, HeaderInfoLength + idx + 5) - else - -- load from the file - buff, err = read_data(self, HeaderInfoLength + idx, SegmentIndexSize) - if err ~= nil then - return "", string.format("read buffer: %s", err) - end - - s_ptr = getLong(buff, 1) - e_ptr = getLong(buff, 5) - end - - -- print(string.format("s_ptr: %d, e_ptr: %d", s_ptr, e_ptr)) - -- binary search to get the data - local data_ptr, data_len, p = 0, 0, 0 - local sip, eip, err, buff = 0, 0, "" - local l, m, h = 0, 0, (e_ptr - s_ptr) / SegmentIndexSize - while l <= h do - m = (l + h) >> 1 - p = s_ptr + m * SegmentIndexSize - - -- read the segment index - buff, err = read_data(self, p, SegmentIndexSize) - if err ~= nil then - return "", string.format("read segment index at %d", p) - end - - sip = getLong(buff, 1) - if ip < sip then - h = m - 1 - else - eip = getLong(buff, 5) - if ip > eip then - l = m + 1 - else - data_len = getShort(buff, 9) - data_ptr = getLong(buff, 11) - break - end - end - end - - -- matching nothing interception - -- print(string.format("data_len=%d, data_ptr=%d", data_len, data_ptr)) - if data_len == 0 then - return "", nil - end - - -- load and return the region data - buff, err = read_data(self, data_ptr, data_len) - if err ~= nil then - return "", string.format("read data at %d:%d", data_ptr, data_len) - end - - return buff, nil -end - - --- read specified bytes from the specified index - -function _M:read(offset, length) - -- local cache - local content_buff = self.content_buff - local handle = self.handle - - -- check the in-memory buffer first - if content_buff ~= nil then - return string.sub(content_buff, offset + 1, offset + length), nil - end - - -- read from the file - local r = handle:seek("set", offset) - if r == nil then - return nil, string.format("seek to offset %d", offset) - end - - self.io_count = self.io_count + 1 - local buff = handle:read(length) - if buff == nil then - return nil, string.format("read %d bytes", length) - end - - return buff, nil -end - -function _M:get_io_count() - return self.io_count -end - -function _M:close() - if self.handle ~= nil then - self.handle:close() - end -end - --- End of search api - - --- static util functions - -function _M.load_header(dbPath) - local handle = io.open(dbPath, "r") - if handle == nil then - return nil, string.format("failed to open xdb file `%s`", dbPath) - end - - local r = handle:seek("set", 0) - if r == nil then - handle:close() - return nil, "failed to seek to 0" - end - - local c = handle:read(HeaderInfoLength) - if c == nil then - handle:close() - return nil, string.format("failed to read %d bytes", HeaderInfoLength) - end - - handle:close() - return { - ["version"] = getShort(c, 1), - ["index_policy"] = getShort(c, 3), - ["created_at"] = getLong(c, 5), - ["start_index_ptr"] = getLong(c, 9), - ["end_index_ptr"] = getLong(c, 13), - ["raw_data"] = c - }, nil -end - -function _M.load_vector_index(dbPath) - local handle = io.open(dbPath, "r") - if handle == nil then - return nil, string.format("failed to open xdb file `%s`", dbPath) - end - - local r = handle:seek("set", HeaderInfoLength) - if r == nil then - handle:close() - return nil, string.format("failed to seek to %d", HeaderInfoLength) - end - - local c = handle:read(VectorIndexLength) - if c == nil then - handle:close() - return nil, string.format("failed to read %d bytes", VectorIndexLength) - end - - handle:close() - return c, nil -end - -function _M.load_content(dbPath) - local handle = io.open(dbPath, "r") - if handle == nil then - return nil, string.format("failed to open xdb file `%s`", dbPath) - end - - local c = handle:read("*a") - if c == nil then - handle:close() - return nil, string.format("failed to read xdb content") - end - - handle:close() - return c, nil -end - -function _M.check_ip(ip_str) - local ip, id, v = 0, 1, 0 - local offset_arr = {24, 16, 8, 0} - for p in string.gmatch(ip_str..".", "([%d]+)%.") do - -- match pattern checking - if p == nil then - return 0, "err=1" - end - - -- count checking - if id > 4 then - return 0, "err=1" - end - - -- value checking - v = tonumber(p) - if v > 255 then - return 0, "err=2" - end - - ip = ip | (v << offset_arr[id]) - id = id + 1 - end - - if id ~= 5 then - return 0, "err=1" - end - - return ip, nil -end - -function _M.long2ip(ip) - return string.format("%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8 ) & 0xFF, ip & 0xFF) -end - --- this is a bit weird, but we have to better choice for now -function _M.now() - return os.time() * 1e6 -end - --- End of util functions - ---internal function to get a integer from a binary string - -function getLong(buff, idx) - local i1 = (string.byte(string.sub(buff, idx, idx))) - local i2 = (string.byte(string.sub(buff, idx+1, idx+1)) << 8) - local i3 = (string.byte(string.sub(buff, idx+2, idx+2)) << 16) - local i4 = (string.byte(string.sub(buff, idx+3, idx+3)) << 24) - return (i1 | i2 | i3 | i4) -end - -function getShort(buff, idx) - local i1 = (string.byte(string.sub(buff, idx, idx))) - local i2 = (string.byte(string.sub(buff, idx+1, idx+1)) << 8) - return (i1 | i2) -end - -return _M \ No newline at end of file diff --git a/binding/lua_c/Makefile b/binding/lua_c/Makefile deleted file mode 100644 index b28f059..0000000 --- a/binding/lua_c/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -LuaVersion = 5.4 -LIB_DIR = /usr/local/share/lua/$(LuaVersion) - -all: ../c/xdb_api.h ../c/xdb_util.c ../c/xdb_searcher.c xdb_searcher.c - gcc -g -O2 -I../c/ -I/usr/include/lua$(LuaVersion) ../c/xdb_util.c ../c/xdb_searcher.c xdb_searcher.c -fPIC -shared -o xdb_searcher.so - -install: - sudo mkdir -p $(LIB_DIR); \ - sudo cp xdb_searcher.so $(LIB_DIR);\ - echo "install xdb searcher to $(LIB_DIR) successfully.";\ - -clean: - find . -name \*.so | xargs rm -f - find . -name \*.o | xargs rm -f - -.PHONY: clean diff --git a/binding/lua_c/ReadMe.md b/binding/lua_c/ReadMe.md deleted file mode 100644 index 962e913..0000000 --- a/binding/lua_c/ReadMe.md +++ /dev/null @@ -1,235 +0,0 @@ -# ip2region xdb lua c 扩展查询客户端实现 - -# 编译安装 - -通过如下方式来编译安装该扩展: -```bash -# cd 到 lua_c binding 的根目录 -make -sudo make install -``` - -备注:Makefile 里面的 `LuaVersion` 变量代表你本地环境的 lua 的版本,默认为 5.4,如果你的版本不是 5.4,记得先修改为和你本地 lua 版本一致的版本号。 - - -# 使用方式 - -### 关于 IPv4 和 IPv6 -该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下: -```lua --- 引入 xdb searcher 扩展 -local xdb = require("xdb_searcher") - --- 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 IPv4 -local db_path = "../../data/ip2region_v4.xdb" -- 或者你的 ipv4 xdb 的路径 -local version = xdb.IPv4 - --- 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 IPv6 -local db_path = "../../data/ip2region_v6.xdb"; -- 或者你的 ipv6 xdb 路径 -local version = xdb.IPv6 - --- db_path 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错 --- 备注:以下演示直接使用 db_path 和 version 变量 -``` - -### XDB 文件验证 -建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。 -```lua -local xdb = require("xdb_searcher") - --- verify the xdb -if xdb.verify(db_path) == false then - -- 适用性验证失败!!! - -- 当前查询客户端实现不适用于 db_path 指定的 xdb 文件的查询. - -- 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 db_path 的 Searcher 实现。 - print(string.format("failed to verify the xdb file: %s", db_path)) - return -end - --- 验证通过,当前使用的 Searcher 可以安全的用于对 db_path 指向的 xdb 的查询操作 -``` - -### 完全基于文件的查询 -```lua -local xdb = require("xdb_searcher") - --- 1、使用 version 从 db_path 创建基于文件的 xdb 查询对象 -local searcher, err = xdb.new_with_file_only(version, db_path) -if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return -end - --- 2、调用查询 API 进行查询,IPv4 和 IPv6 都支持 -local ip_str = "1.2.3.4" --- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 -local s_time = xdb.now() -region, err = searcher:search(ip_str) -local c_time = xdb.now() - s_time -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - -print(string.format("{region: %s, took: %.5f μs}", region, c_time)) - --- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象 - --- 3,关闭 xdb 查询器 -searcher:close() - --- --- 4,模块资源清理,仅在需要将整个服务完全关闭前调用 -xdb.cleanup() -``` - -### 缓存 `VectorIndex` 索引 - -如果你的 `lua` 母环境支持,可以预先加载 vectorIndex 缓存,然后做成全局变量,每次创建 Searcher 的时候使用全局的 vectorIndex,可以减少一次固定的 IO 操作从而加速查询,减少 io 压力。 -```lua -local xdb = require("xdb_searcher") - --- 1、从指定的 db_path 加载 VectorIndex 缓存,把下述的 v_index 对象做成全局变量。 --- vectorIndex 加载一次即可,建议在服务启动的时候加载为全局对象。 -v_index, err = xdb.load_vector_index(db_path) -if err ~= nil then - print(string.format("failed to load vector index from '%s'", db_path)) - return -end - --- 2、使用全局的 v_index 创建带 VectorIndex 缓存的查询对象。 -searcher, err = xdb.new_with_vector_index(version, db_path, v_index) -if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return -end - --- 3、调用查询 API ,IPv4 和 IPv6 都支持 -local ip_str = "1.2.3.4" --- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 -local s_time = xdb.now() -region, err = searcher:search(ip_str) -local c_time = xdb.now() = s_time -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - -print(string.format("{region: %s, took: %.5f μs}", region, c_time)) - --- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象 - --- 4,关闭 xdb 查询器 -searcher:close() - --- --- 5,模块资源清理,仅在需要将整个服务完全关闭前调用 -xdb.cleanup() -``` - -### 缓存整个 `xdb` 数据 - -如果你的 `lua` 母环境支持,可以预先加载整个 xdb 的数据到内存,这样可以实现完全基于内存的查询,类似之前的 memory search 查询。 -```lua -local xdb = require("xdb_searcher") - --- 1、从指定的 db_path 加载整个 xdb 到内存。 --- xdb内容加载一次即可,建议在服务启动的时候加载为全局对象。 -local content = xdb.load_content(db_path) -if content == nil then - print(string.format("failed to load xdb content from '%s'", db_path)) - return -end - --- 2、使用全局的 content 创建带完全基于内存的查询对象。 -searcher, err = xdb.new_with_buffer(version, content) -if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return -end - --- 3、调用查询 API ,IPv4 和 IPv6 都支持 -local ip_str = "1.2.3.4" --- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 -local s_time = xdb.now() -region, err = searcher:search(ip_str) -local c_time = xdb.now() - s_time -if err ~= nil then - print(string.format("failed to search(%s): %s", ip_str, err)) - return -end - -print(string.format("{region: %s, took: %.5f μs}", region, c_time)) - --- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。 --- 建议在服务启动的时候创建好全局的 searcher 对象,然后全局并发使用。 - --- 4,关闭 xdb 查询器 -searcher:close() - --- --- 5,模块资源清理,仅在需要将整个服务完全关闭前调用 -xdb.cleanup() -``` - - -# 查询测试 - -通过 `search_test.lua` 脚本来进行查询测试: -```bash -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./search_test.lua -lua search_test.lua [command options] -options: - --db string ip2region binary xdb file path - --cache-policy string cache policy: file/vectorIndex/content -``` - -例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 查询测试: -```bash -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./search_test.lua --db=../../data/ip2region_v4.xdb -ip2region xdb searcher test program -source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex) -type 'quit' to exit -ip2region>> 120.229.45.2 -{region: 中国|广东省|深圳市|移动, io_count: 3, took: 34μs} -``` - -例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 查询测试: -```bash -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./search_test.lua --db=../../data/ip2region_v6.xdb -ip2region xdb searcher test program -source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) -type 'quit' to exit -ip2region>> 240e:3b7:3276:33b0:958f:f34c:d04f:f6a -{region: 中国|广东省|深圳市|家庭宽带, io_count: 14, took: 79μs} -``` - -输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。 - - -# bench 测试 - -通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: -```bash -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua -lua bench_test.lua [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 -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt -Bench finished, {cachePolicy: vectorIndex, total: 1367686, took: 8.593 s, cost: 5.433 μs/op} -``` - -例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 来进行 IPv6 的 bench 测试: -```bash -➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt -Bench finished, {cachePolicy: vectorIndex, total: 34159862, took: 829.008 s, cost: 23.176 μs/op} -``` - -可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。 -@Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件的相同的源文件。 diff --git a/binding/lua_c/bench_test.lua b/binding/lua_c/bench_test.lua deleted file mode 100644 index 6534c65..0000000 --- a/binding/lua_c/bench_test.lua +++ /dev/null @@ -1,201 +0,0 @@ --- 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 --- @Date 2022/06/30 - --- set the package to load the current xdb_searcher.so -package.path = "./?.lua" .. package.path -package.cpath = "./?.so" .. package.cpath -local xdb = require("xdb_searcher") - -function printHelp() - print("lua bench_test.lua [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --src string source ip text file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") -end - -if #arg < 2 then - printHelp(arg) - return -end - --- parser the command line args -local dbFile, srcFile = "", "" -local cachePolicy = "vectorIndex" -for _, r in ipairs(arg) do - if string.len(r) < 5 then - goto continue - end - - if string.sub(r, 1, 2) ~= "--" then - goto continue - end - - for k, v in string.gmatch(string.sub(r, 3), "([^=]+)=([^%s]+)") do - if k == "db" then - dbFile = v - elseif k == "src" then - srcFile = v - elseif k == "cache-policy" then - cachePolicy = v - else - print(string.format("undefined option `%s`", r)) - return - end - - -- break the match iterate - break - end - - -- continue this loop - ::continue:: -end - --- print(string.format("dbFile=%s, srcFile=%s, cachePolicy=%s", dbFile, srcFile, cachePolicy)) -if string.len(dbFile) < 2 or string.len(srcFile) < 2 then - printHelp() - return -end - --- verify the xdb from header -if xdb.verify(dbFile) == false then - print(string.format("failed to verify the xdb file: %s", dbFile)) - return -end - --- detect the version from the xdb header -header, err = xdb.load_header(dbFile) -if err ~= nil then - print(string.format("failed to load header: %s", err)) - return -end - -version, err = xdb.version_from_header(header); -if err ~= nil then - print(string.format("failed to detect version from header: %s", err)) - return -end - --- create the searcher based on the cache-policy -local searcher, v_index, content -if cachePolicy == "file" then - searcher, err = xdb.new_with_file_only(version, dbFile) - if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return - end -elseif cachePolicy == "vectorIndex" then - v_index, err = xdb.load_vector_index(dbFile) - if err ~= nil then - print(string.format("failed to load vector index: %s", err)) - return - end - - searcher, err = xdb.new_with_vector_index(version, dbFile, v_index) - if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return - end -elseif cachePolicy == "content" then - content, err = xdb.load_content(dbFile) - if err ~= nil then - print(string.format("failed to load xdb content from '%s'", dbFile)) - return - end - - searcher, err = xdb.new_with_buffer(version, content) - if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return - end -else - print(string.format("undefined cache-policy `%s`", cachePolicy)) - return -end - --- do the bench test -local handle = io.open(srcFile, "r") -if handle == nil then - print(string.format("failed to open src text file `%s`", handle)) - return -end - -local lines = handle:lines() -local sip_str, eip_str, s_region, region = "", "", "", "" -local sip, mip, eip, err = 0, 0, 0, 0 -local count, t_time, c_time = 0, 0, 0 -local s_time = xdb.now() -for l in lines do - if string.len(l) < 1 then - goto continue - end - - for v1, v2, v3 in string.gmatch(l, "([^|]+)|([^|]+)|([^\n]+)") do - -- print(sip_str, eip_str, region) - sip_str = v1 - eip_str = v2 - s_region = v3 - break - end - - sip_bytes, err = xdb.parse_ip(sip_str) - if err ~= nil then - print(string.format("invalid start ip `%s`", sip_str)) - return - end - - eip_bytes, err = xdb.parse_ip(eip_str) - if err ~= nil then - print(string.format("invalid end ip `%s`", sip_str)) - return - end - - if xdb.ip_compare(sip_bytes, eip_bytes) > 0 then - print(string.format("start ip(%s) should not be greater than end ip(%s)\n", sip_str, eip_str)) - return - end - - for _, ip_bytes in ipairs({sip_bytes, eip_bytes}) do - t_time = xdb.now() - region, err = searcher:search(ip_bytes) - c_time = c_time + xdb.now() - t_time - if err ~= nil then - print(string.format("failed to search ip `%s`", xdb.ip_to_string(ip_bytes))) - return - end - - -- check the region - if region ~= s_region then - printf(string.format("failed search(%s) with (%s != %s)\n", xdb.ip_to_string(ip_bytes), region, s_region)) - return - end - - count = count + 1 - end - - ::continue:: -end - --- resource cleanup -searcher:close() -if v_index ~= nil then - v_index:close() -end -if content ~= nil then - content:close() -end - -xdb.cleanup(); - --- print the stats -local avg_costs = 0 -if count > 0 then - avg_costs = c_time / count -end -print(string.format("Bench finished, {cachePolicy: %s, total: %d, took: %.3f s, cost: %.3f μs/op}", - cachePolicy, count, (xdb.now() - s_time)/1e6, c_time / count)) \ No newline at end of file diff --git a/binding/lua_c/search_test.lua b/binding/lua_c/search_test.lua deleted file mode 100644 index a4db546..0000000 --- a/binding/lua_c/search_test.lua +++ /dev/null @@ -1,172 +0,0 @@ --- 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 --- @Date 2022/06/30 - --- set the package to load the current xdb_searcher.so -package.path = "./?.lua" .. package.path -package.cpath = "./?.so" .. package.cpath -local xdb = require("xdb_searcher") - -function printHelp() - print("lua search_test.lua [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") -end - -if #arg < 1 then - printHelp(arg) - return -end - --- parser the command line args -local dbFile = "" -local cachePolicy = "vectorIndex" -for _, r in ipairs(arg) do - if string.len(r) < 5 then - goto continue - end - - if string.sub(r, 1, 2) ~= "--" then - goto continue - end - - for k, v in string.gmatch(string.sub(r, 3), "([^=]+)=([^%s]+)") do - if k == "db" then - dbFile = v - elseif k == "cache-policy" then - cachePolicy = v - else - print(string.format("undefined option `%s`", r)) - return - end - - -- break the match iterate - break - end - - -- continue this loop - ::continue:: -end - --- print(string.format("dbFile=%s, cachePolicy=%s", dbFile, cachePolicy)) -if string.len(dbFile) < 2 then - printHelp() - return -end - --- verify the xdb -if xdb.verify(dbFile) == false then - print(string.format("failed to verify the xdb file: %s", dbFile)) - return -end - --- detect the version from the xdb header -header, err = xdb.load_header(dbFile) -if err ~= nil then - print(string.format("failed to load header: %s", err)) - return -end - -version, err = xdb.version_from_header(header); -if err ~= nil then - print(string.format("failed to detect version from header: %s", err)) - return -end - --- create the searcher based on the cache-policy -local searcher, v_index, content -if cachePolicy == "file" then - searcher, err = xdb.new_with_file_only(version, dbFile) - if err ~= nil then - print(string.format("failed to create searcher: %s", err)) - return - end -elseif cachePolicy == "vectorIndex" then - v_index, err = xdb.load_vector_index(dbFile) - if err ~= nil then - print(string.format("failed to load vector index: %s", err)) - return - end - - searcher, err = xdb.new_with_vector_index(version, dbFile, v_index) - if err ~= nil then - print(string.format("failed to create vector index searcher: %s", err)) - return - end -elseif cachePolicy == "content" then - content, err = xdb.load_content(dbFile) - if err ~= nil then - print(string.format("failed to load xdb content from '%s'", dbFile)) - return - end - - searcher, err = xdb.new_with_buffer(version, content) - if err ~= nil then - print(string.format("failed to create content buffer searcher: %s", err)) - return - end -else - print(string.format("undefined cache-policy `%s`", cachePolicy)) - return -end - --- do the search -print(string.format([[ -ip2region xdb searcher test program -source xdb: %s (%s, %s) -type 'quit' to exit]], dbFile, xdb.version_info(version).name, cachePolicy)) -local region, err = "", nil -local ip_int, s_time, c_time = 0, 0, 0 -while ( true ) do - io.write("ip2region>> "); - io.input(io.stdin); - local line = io.read(); - if (line == nil) then - break - end - - if ( line == "quit" ) then - break - end - - -- empty string ignore - line = line:gsub("^%s*(.-)%s*$", "%1") - if string.len(line) < 1 then - goto continue - end - - ip_bytes, err = xdb.parse_ip(line) - -- print(string.format("parse(%s): %s, err: %s", line, xdb.ip_to_string(ip_bytes), err)) - if err ~= nil then - print(string.format("invalid ip address `%s`", line)) - goto continue - end - - -- do the search - s_time = xdb.now() - region, err = searcher:search(ip_bytes) - c_time = xdb.now() - s_time - if err ~= nil then - print(string.format("{err: %s, io_count: %d}", err, searcher:get_io_count())) - else - print(string.format("{region: %s, io_count: %d, took: %dμs}", region, searcher:get_io_count(), c_time)) - end - - ::continue:: -end - --- resource cleanup -searcher:close() -if v_index ~= nil then - v_index:close() -end -if content ~= nil then - content:close() -end - -xdb.cleanup(); \ No newline at end of file diff --git a/binding/lua_c/util_test.lua b/binding/lua_c/util_test.lua deleted file mode 100644 index 66fcf53..0000000 --- a/binding/lua_c/util_test.lua +++ /dev/null @@ -1,140 +0,0 @@ --- 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 --- @Date 2022/06/30 - --- set the package to load the current xdb_searcher.so -package.path = "./?.lua" .. package.path -package.cpath = "./?.so" .. package.cpath -local xdb = require("xdb_searcher") - ----- ip checking testing -function test_parse_ip() - local ip_list = { - "1.2.3.4", "192.168.2.3", "120.24.78.129", "255.255.255.0", "invalid-ipv.4", - "::", "3000::", "240e:3b7:3276:33b0:4844:6f28:f69c:1eee", "2001:4:112::", "invalid-ipv::6" - } - - local s_time = xdb.now() - for _, ip_src in ipairs(ip_list) do - ip_bytes, err = xdb.parse_ip(ip_src) - if err ~= nil then - print(string.format("invalid ip address `%s`: %s", ip_src, err)) - else - local ip_string = xdb.ip_to_string(ip_bytes); - print(string.format("parse_ip(%s)->%s ? %s", ip_src, ip_string, ip_src==ip_string)) - end - end -end - -function test_print_const() - print("ipv4: ", xdb.IPv4); - print("ipv6: ", xdb.IPv6); - print("header_buffer: ", xdb.header_buffer); - print("v_index_buffer: ", xdb.v_index_buffer); - print("content_buffer: ", xdb.content_buffer); -end - ----- buffer loading test -function test_load_header() - header, err = xdb.load_header("../../data/ip2region_v4.xdb") - if err ~= nil then - print("failed to load header: ", err) - else - print(string.format("xdb header buffer `%s` loaded", header)) - - local tpl = [[ - header: { - version: %d - index_policy: %d - created_at: %d - start_index_ptr: %d - end_index_ptr: %d - ip_version: %d - runtime_ptr_bytes: %d - }]] - - local t = header:to_table() - print(string.format(tpl, - t["version"], t["index_policy"], t["created_at"], - t["start_index_ptr"], t["end_index_ptr"], t["ip_version"], t["runtime_ptr_bytes"]) - ) - end -end - -function test_version_info() - local v4 = xdb.version_info(xdb.IPv4) - print(string.format("{id:%d, name: %s, bytes: %d, segment_index_size: %d}", v4.id, v4.name, v4.bytes, v4.segment_index_size)) - local v6 = xdb.version_info(xdb.IPv6) - print(string.format("{id:%d, name: %s, bytes: %d, segment_index_size: %d}", v6.id, v6.name, v6.bytes, v6.segment_index_size)) - local vx = xdb.version_info(3) -end - -function test_load_vector_index() - v_index, err = xdb.load_vector_index("../../data/ip2region_v4.xdb") - if err ~= nil then - print("failed to load vector index: ", err) - else - print(string.format("xdb vector index buffer `%s` loaded, info={name=%s, type=%d, length=%d}", - v_index, v_index:name(), v_index:type(), v_index:length())) - v_index:close() - end -end - - -function test_load_content() - c_buffer, err = xdb.load_content("../../data/ip2region_v4.xdb") - if err ~= nil then - print("failed to load content: ", err) - else - print(string.format("xdb content buffer `%s` loaded, info={name=%s, type=%d, length=%d}", - c_buffer, c_buffer:name(), c_buffer:type(), c_buffer:length())) - c_buffer:close(); - end -end - - -function test_search() - -- ipv4 - local ip_str = "1.2.3.4" - searcher, err = xdb.new_with_file_only(xdb.IPv4, "../../data/ip2region_v4.xdb") - print(string.format("searcher.tostring=%s", searcher)) - local t_start = xdb.now() - region, err = searcher:search(ip_str) - local c_time = xdb.now() - t_start - print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}", - ip_str, region, searcher:get_io_count(), c_time, err)) - searcher:close() - - -- IPv6 - ip_str = "240e:3b7:3276:33b0:958f:f34c:d04f:f6a" - searcher, err = xdb.new_with_file_only(xdb.IPv6, "../../data/ip2region_v6.xdb") - print(string.format("searcher.tostring=%s", searcher)) - t_start = xdb.now() - region, err = searcher:search(ip_str) - c_time = xdb.now() - t_start - print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}", - ip_str, region, searcher:get_io_count(), c_time, err)) - searcher:close() -end - -local func_name = arg[1] -if func_name == nil then - print("please specified the function to test") - return -end - -if (_G[func_name] == nil) then - print(string.format("undefined function `%s` to call", func_name)) - return -end - -local s_time = xdb.now(); -print(string.format("+---calling test function %s ...", func_name)) -_G[func_name](); -local cost_time = xdb.now() - s_time -xdb.cleanup(); -print(string.format("|---done, took: %.3fμs", cost_time)) \ No newline at end of file diff --git a/binding/lua_c/xdb_searcher.c b/binding/lua_c/xdb_searcher.c deleted file mode 100644 index c342a09..0000000 --- a/binding/lua_c/xdb_searcher.c +++ /dev/null @@ -1,761 +0,0 @@ -// 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 -// @Date 2022/06/30 - - -#include "stdio.h" -#include "lua.h" -#include "lauxlib.h" -#include "../c/xdb_api.h" - -#define XDB_BUFFER_METATABLE_NAME "xdb_buffer_mt" -#define XDB_METATABLE_NAME "xdb_metatable_name" - -#define xdb_header_buffer 1 -#define xdb_vector_index_buffer 2 -#define xdb_content_buffer 3 - - -// --- xdb buffer interface impl - -struct xdb_buffer_entry { - int type; // buffer type - char *name; // buffer name - void *ptr; // buffer ptr - void (*closer) (void *); -}; -typedef struct xdb_buffer_entry xdb_buffer_t; - -static int lua_xdb_buffer_name(lua_State *L) { - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - lua_pushstring(L, buffer->name); - return 1; -} - -static int lua_xdb_buffer_type(lua_State *L) { - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - lua_pushinteger(L, buffer->type); - return 1; -} - -static int lua_xdb_buffer_to_table(lua_State *L) { - xdb_buffer_t *buffer; - xdb_header_t *header; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - lua_newtable(L); - if (buffer->type == xdb_header_buffer) { - header = (xdb_header_t *) buffer->ptr; - lua_pushinteger(L, header->version); - lua_setfield(L, -2, "version"); - - lua_pushinteger(L, header->index_policy); - lua_setfield(L, -2, "index_policy"); - - lua_pushinteger(L, header->created_at); - lua_setfield(L, -2, "created_at"); - - lua_pushinteger(L, header->start_index_ptr); - lua_setfield(L, -2, "start_index_ptr"); - - lua_pushinteger(L, header->end_index_ptr); - lua_setfield(L, -2, "end_index_ptr"); - - lua_pushinteger(L, header->ip_version); - lua_setfield(L, -2, "ip_version"); - - lua_pushinteger(L, header->runtime_ptr_bytes); - lua_setfield(L, -2, "runtime_ptr_bytes"); - } else { - // do nothing for now - } - - return 1; -} - -static int lua_xdb_buffer_length(lua_State *L) { - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - if (buffer->type == xdb_header_buffer) { - lua_pushinteger(L, ((xdb_header_t *) buffer->ptr)->length); - } else if (buffer->type == xdb_vector_index_buffer) { - lua_pushinteger(L, ((xdb_vector_index_t *) buffer->ptr)->length); - } else if (buffer->type == xdb_content_buffer) { - lua_pushinteger(L, ((xdb_content_t *) buffer->ptr)->length); - } else { - lua_pushinteger(L, -1); - } - - return 1; -} - -static int lua_xdb_buffer_tostring(lua_State *L) { - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - lua_pushfstring(L, "xdb %s buffer object {name: %s, type: %d}", buffer->name, buffer->name, buffer->type); - return 1; -} - -static int lua_xdb_buffer_close(lua_State *L) { - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - buffer = (xdb_buffer_t *) luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - - // check and call the closer - if (buffer->closer != NULL) { - buffer->closer(buffer->ptr); - buffer->closer = NULL; - } - - return 0; -} - -// module method define, should be access via ':' -static const struct luaL_Reg xdb_buffer_methods[] = { - {"name", lua_xdb_buffer_name}, - {"type", lua_xdb_buffer_type}, - {"length", lua_xdb_buffer_length}, - {"to_table", lua_xdb_buffer_to_table}, - {"close", lua_xdb_buffer_close}, - {"__gc", lua_xdb_buffer_close}, - {"__tostring", lua_xdb_buffer_tostring}, - {NULL, NULL} -}; - -// --- End of xdb buffer - - -// --- xdb util function - -static int lua_xdb_load_header_from_file(lua_State *L) { - const char *db_path; - xdb_header_t *header; - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and the xdb file path expected"); - db_path = luaL_checkstring(L, 1); - header = xdb_load_header_from_file(db_path); - if (header == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "load header from `%s`", db_path); - return 2; - } - - // alloc the buffer. - buffer = (xdb_buffer_t *) lua_newuserdata(L, sizeof(xdb_buffer_t)); - if (buffer == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "failed to alloc xdb buffer entry"); - return 2; - } - - // init the buffer - buffer->type = xdb_header_buffer; - buffer->name = "header"; - buffer->ptr = header; - buffer->closer = xdb_free_header; - - // set the metatable of the header buffer object and push onto the stack - luaL_getmetatable(L, XDB_BUFFER_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_load_vector_index_from_file(lua_State *L) { - const char *db_path; - xdb_vector_index_t *v_index; - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and the xdb path expected"); - db_path = luaL_checkstring(L, 1); - v_index = xdb_load_vector_index_from_file(db_path); - if (v_index == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "load vector index from `%s`", db_path); - return 2; - } - - // alloc the buffer. - buffer = (xdb_buffer_t *) lua_newuserdata(L, sizeof(xdb_buffer_t)); - if (buffer == NULL) { - lua_pushnil(L); - lua_pushstring(L, "failed to alloc xdb buffer entry"); - return 2; - } - - // init the buffer - buffer->type = xdb_vector_index_buffer; - buffer->name = "v_index"; - buffer->ptr = v_index; - buffer->closer = xdb_free_vector_index; - - // set the metatable of the header buffer object and push onto the stack - luaL_getmetatable(L, XDB_BUFFER_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_load_content_from_file(lua_State *L) { - const char *db_path; - xdb_content_t *content; - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and xdb path expected"); - db_path = luaL_checkstring(L, 1); - content = xdb_load_content_from_file(db_path); - if (content == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "load xdb content from `%s`", db_path); - return 2; - } - - // alloc the buffer. - buffer = (xdb_buffer_t *) lua_newuserdata(L, sizeof(xdb_buffer_t)); - if (buffer == NULL) { - lua_pushnil(L); - lua_pushstring(L, "failed to alloc xdb buffer entry"); - return 2; - } - - // init the buffer - buffer->type = xdb_content_buffer; - buffer->name = "content"; - buffer->ptr = content; - buffer->closer = xdb_free_content; - - // set the metatable of the header buffer object and push onto the stack - luaL_getmetatable(L, XDB_BUFFER_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_verify_from_file(lua_State *L) { - const char *db_path; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and the xdb file path expected"); - db_path = luaL_checkstring(L, 1); - lua_pushboolean(L, xdb_verify_from_file(db_path) == 0 ? 1 : 0); - return 1; -} - -static int lua_xdb_version_from_header(lua_State *L) { - xdb_version_t *version; - xdb_buffer_t *buffer; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and xdb header expected"); - // header buffer checking - buffer = luaL_checkudata(L, 1, XDB_BUFFER_METATABLE_NAME); - if (buffer->type != xdb_header_buffer) { - return luaL_error(L, "invalid xdb header buffer"); - } - - version = xdb_version_from_header((xdb_header_t *) buffer->ptr); - if (version == NULL) { - lua_pushnil(L); - lua_pushstring(L, "failed to detect version from header"); - } else { - lua_pushinteger(L, version->id); - lua_pushnil(L); - } - - return 2; -} - -static xdb_version_t *_get_version(lua_State *L, int arg) { - int vid = luaL_checkinteger(L, arg); - if (vid == xdb_ipv4_id) { - return XDB_IPv4; - } else if (vid == xdb_ipv6_id) { - return XDB_IPv6; - } else { - return NULL; - } -} - -static int lua_xdb_version_info(lua_State *L) { - xdb_version_t *version; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and version id expected"); - // check the ip version - version = _get_version(L, 1); - if (version == NULL) { - return luaL_error(L, "invalid verison id specified"); - } - - lua_newtable(L); - lua_pushinteger(L, version->id); - lua_setfield(L, -2, "id"); - - lua_pushstring(L, version->name); - lua_setfield(L, -2, "name"); - - lua_pushinteger(L, version->bytes); - lua_setfield(L, -2, "bytes"); - - lua_pushinteger(L, version->segment_index_size); - lua_setfield(L, -2, "segment_index_size"); - return 1; -} - -static int lua_xdb_parse_ip(lua_State *L) { - int err; - const char *ip_str; - bytes_ip_t ip_bytes[19] = {'\0'}; - xdb_version_t *version; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and string ip expected, eg: 1.2.3.4 / 3000::"); - ip_str = luaL_checkstring(L, 1); - - version = xdb_parse_ip(ip_str, ip_bytes + 2, sizeof(ip_bytes) - 2); - if (version == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "failed to parse the `%s`", ip_str); - return 2; - } - - // append the magic char for later analysis - // printf("ip:%s, version->id: %d\n", ip_str, version->id); - ip_bytes[0] = '&'; - ip_bytes[1] = (bytes_ip_t) version->id; - lua_pushlstring(L, ip_bytes, version->bytes + 2); - lua_pushnil(L); - return 2; -} - -static int lua_xdb_ip_to_string(lua_State *L) { - int err, vid, bytes; - const bytes_ip_t *ip_bytes; - char ip_string[INET6_ADDRSTRLEN + 1] = {'\0'}; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and bytes ip expected"); - ip_bytes = luaL_checkstring(L, 1); - if (strlen(ip_bytes) < 2) { - lua_pushnil(L); - lua_pushstring(L, "invalid binary ip bytes specified"); - return 2; - } - - if (ip_bytes[0] != '&') { - lua_pushnil(L); - lua_pushstring(L, "invalid binary ip bytes specified"); - return 2; - } - - vid = ip_bytes[1] & 0xFF; - if (vid == xdb_ipv4_id) { - // IPv4 - bytes = xdb_ipv4_bytes; - } else if (vid == xdb_ipv6_id) { - // IPv6 - bytes = xdb_ipv6_bytes; - } else { - lua_pushnil(L); - lua_pushstring(L, "invalid binary ip bytes specified"); - return 2; - } - - err = xdb_ip_to_string(ip_bytes + 2, bytes, ip_string, sizeof(ip_string)); - if (err != 0) { - lua_pushnil(L); - lua_pushstring(L, "failed to conver the ip bytes to string"); - return 2; - } - - lua_pushstring(L, ip_string); - lua_pushnil(L); - return 2; -} - -static int _validate_bytes_ip(const bytes_ip_t *ip_bytes) { - if (strlen(ip_bytes) < 2) { - return 1; - } - - if (ip_bytes[0] != '&') { - return 2; - } - - int vid = ip_bytes[1] & 0xFF; - if (vid != xdb_ipv4_id && vid != xdb_ipv6_id) { - return 3; - } - - return 0; -} - -static int lua_xdb_ip_compare(lua_State *L) { - int err, vid, bytes; - const bytes_ip_t *ip1_bytes, *ip2_bytes; - - luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' bytes ip1 and ip2 expected"); - ip1_bytes = luaL_checkstring(L, 1); - ip2_bytes = luaL_checkstring(L, 2); - - // validate the ip1 - err = _validate_bytes_ip(ip1_bytes); - if (err != 0) { - lua_pushnil(L); - lua_pushfstring(L, "failed to validate ip1 with errcode=%d", err); - return 2; - } - - // validate the ip2 - err = _validate_bytes_ip(ip2_bytes); - if (err != 0) { - lua_pushnil(L); - lua_pushfstring(L, "failed to validate ip2 with errcode=%d", err); - return 2; - } - - if (ip1_bytes[1] != ip2_bytes[1]) { - lua_pushnil(L); - lua_pushstring(L, "ip version of ip1 and ip2 are not the same"); - return 2; - } - - err = xdb_ip_sub_compare(ip1_bytes + 2, (ip1_bytes[1] & 0xFF), ip2_bytes, 2); - lua_pushinteger(L, err); - lua_pushnil(L); - return 2; -} - -static int lua_xdb_now(lua_State *L) { - lua_pushinteger(L, xdb_now()); - return 1; -} - - -// --- End of xdb util api - - -// --- xdb searcher api - -static int lua_xdb_new_with_file_only(lua_State *L) { - int err; - xdb_version_t *version; - xdb_searcher_t *searcher; - const char *db_path = NULL; - - luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb file path expected"); - - // check the ip version - version = _get_version(L, 1); - if (version == NULL) { - return luaL_error(L, "invalid verison id specified"); - } - - // check the db path - db_path = luaL_checkstring(L, 2); - - // alloc for the searcher - searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); - if (searcher == NULL) { - return luaL_error(L, "failed to alloc xdb searcher entry"); - } - - // init the xdb searcher - err = xdb_new_with_file_only(version, searcher, db_path); - if (err != 0) { - lua_pushnil(L); - lua_pushfstring(L, "init xdb searcher on `%s`: errcode=%d", db_path, err); - return 2; - } - - // push the metatable onto the stack and - // set it as the metatable of the current searcher - luaL_getmetatable(L, XDB_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_new_with_vector_index(lua_State *L) { - xdb_version_t *version; - xdb_searcher_t *searcher; - xdb_buffer_t *xBuffer; - const char *db_path; - int err; - - luaL_argcheck(L, lua_gettop(L) == 3, 1, "call via '.', ip version / xdb file path / vector index buffer expected"); - - // check the ip version - version = _get_version(L, 1); - if (version == NULL) { - return luaL_error(L, "invalid verison id specified"); - } - - // db_path checking - db_path = luaL_checkstring(L, 2); - - // vector index buffer checking - xBuffer = luaL_checkudata(L, 3, XDB_BUFFER_METATABLE_NAME); - if (xBuffer->type != xdb_vector_index_buffer) { - return luaL_error(L, "invalid vector index buffer"); - } - - // alloc the searcher - searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); - if (searcher == NULL) { - return luaL_error(L, "failed to alloc xdb searcher entry"); - } - - // init the xdb searcher - err = xdb_new_with_vector_index(version, searcher, db_path, (xdb_vector_index_t *) xBuffer->ptr); - if (err != 0) { - lua_pushnil(L); - lua_pushfstring(L, "init vector index cached xdb searcher on `%s` with errcode=%d", db_path, err); - return 2; - } - - // push the metatable onto the stack and - // set it as the metatable of the current searcher - luaL_getmetatable(L, XDB_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_new_with_buffer(lua_State *L) { - xdb_version_t *version; - xdb_searcher_t *searcher; - xdb_buffer_t *xBuffer; - int err; - - luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb content buffer expected"); - - // check the ip version - version = _get_version(L, 1); - if (version == NULL) { - return luaL_error(L, "invalid verison id specified"); - } - - // content buffer checking - xBuffer = (xdb_buffer_t *) luaL_checkudata(L, 2, XDB_BUFFER_METATABLE_NAME); - if (xBuffer->type != xdb_content_buffer) { - return luaL_error(L, "invalid xdb content buffer"); - } - - // alloc the searcher - searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); - if (searcher == NULL) { - return luaL_error(L, "failed to alloc xdb searcher entry"); - } - - // init the xdb searcher - err = xdb_new_with_buffer(version, searcher, (xdb_content_t *) xBuffer->ptr); - if (err != 0) { - lua_pushnil(L); - lua_pushfstring(L, "init content cached xdb searcher with errcode=%d", err); - return 2; - } - - // push the metatable onto the stack and - // set it as the metatable of the current searcher - luaL_getmetatable(L, XDB_METATABLE_NAME); - lua_setmetatable(L, -2); - lua_pushnil(L); - - return 2; -} - -static int lua_xdb_close(lua_State *L) { - xdb_searcher_t *searcher; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':'"); - searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); - if (searcher == NULL) { - return luaL_error(L, "broken xdb searcher instance"); - } - - xdb_close(searcher); - return 0; -} - -static int lua_xdb_search(lua_State *L) { - int err, vid, ip_len; - const char *ip_string; - bytes_ip_t ip_buffer[INET6_ADDRSTRLEN] = {'\0'}; - const bytes_ip_t *ip_bytes; - - xdb_version_t *version; - xdb_region_buffer_t region; - xdb_searcher_t *searcher; - - luaL_argcheck(L, lua_gettop(L) == 2, 2, "call via ':' and string ip address expected"); - - // get the searcher - searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); - ip_string = luaL_checkstring(L, 2); - - // ip string type checking - if (strlen(ip_string) < 2) { - lua_pushnil(L); - lua_pushfstring(L, "invalid ip address `%s`", ip_string); - return 2; - } - - if (ip_string[0] == '&') { - vid = ip_string[1] & 0xFF; - if (vid == xdb_ipv4_id) { - ip_len = xdb_ipv4_bytes; - } else if (vid == xdb_ipv6_id) { - ip_len = xdb_ipv6_bytes; - } else { - lua_pushnil(L); - lua_pushstring(L, "invalid binary ip bytes specified"); - return 2; - } - - ip_bytes = ip_string + 2; - // printf("ip_len: %d, vid: %d\n", ip_len, vid); - } else { - version = xdb_parse_ip(ip_string, ip_buffer, sizeof(ip_buffer)); - if (version == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "failed to parse string ip `%s`", ip_string); - return 2; - } - - ip_len = version->bytes; - ip_bytes = ip_buffer; - } - - // init the region buffer - err = xdb_region_buffer_init(®ion, NULL, 0); - if (err != 0) { - return luaL_error(L, "failed to init the region buffer with errcode=%d", err); - } - - // do the search - err = xdb_search(searcher, ip_bytes, ip_len, ®ion); - if (err != 0) { - lua_pushinteger(L, err); - lua_pushfstring(L, "err=%d", err); - } else { - lua_pushstring(L, region.value); - lua_pushnil(L); - } - - // clean up the region buffer - xdb_region_buffer_free(®ion); - return 2; -} - -static int lua_xdb_get_io_count(lua_State *L) { - xdb_searcher_t *searcher; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':' or xdb searcher was broken"); - searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); - lua_pushinteger(L, xdb_get_io_count(searcher)); - return 1; -} - -static int lua_xdb_tostring(lua_State *L) { - xdb_searcher_t *searcher; - - luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':' or xdb searcher was broken"); - searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); - lua_pushfstring(L, "xdb %s searcher object", xdb_get_version(searcher)->name); - return 1; -} - -// cleanup the current module -static int lua_xdb_cleanup(lua_State *L) { - xdb_clean_winsock(); - return 0; -} - -// module method define, should be access via ':' -static const struct luaL_Reg xdb_searcher_methods[] = { - {"search", lua_xdb_search}, - {"get_io_count",lua_xdb_get_io_count}, - {"close", lua_xdb_close}, - {"__gc", lua_xdb_close}, - {"__tostring", lua_xdb_tostring}, - {NULL, NULL}, -}; - -// module function define, should be access via '.' -static const struct luaL_Reg xdb_searcher_functions[] = { - {"new_with_file_only", lua_xdb_new_with_file_only}, - {"new_with_vector_index", lua_xdb_new_with_vector_index}, - {"new_with_buffer", lua_xdb_new_with_buffer}, - {"load_header", lua_xdb_load_header_from_file}, - {"load_vector_index", lua_xdb_load_vector_index_from_file}, - {"load_content", lua_xdb_load_content_from_file}, - {"verify", lua_xdb_verify_from_file}, - {"version_from_header", lua_xdb_version_from_header}, - {"version_info", lua_xdb_version_info}, - {"cleanup", lua_xdb_cleanup}, - {"parse_ip", lua_xdb_parse_ip}, - {"ip_to_string", lua_xdb_ip_to_string}, - {"ip_compare", lua_xdb_ip_compare}, - {"now", lua_xdb_now}, - {NULL, NULL} -}; - -// module register function -int luaopen_xdb_searcher(lua_State *L) -{ - int err = xdb_init_winsock(); - if (err != 0) { - luaL_error(L, "failed to init the winsock with errno=%d\n", err); - return 1; - } - - // create a metatable for xdb buffer object - luaL_newmetatable(L, XDB_BUFFER_METATABLE_NAME); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - luaL_setfuncs(L, xdb_buffer_methods, 0); - - // create a metatable for xdb searcher object - luaL_newmetatable(L, XDB_METATABLE_NAME); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - luaL_setfuncs(L, xdb_searcher_methods, 0); - luaL_setfuncs(L, xdb_searcher_functions, 0); - - // register the constants attributes - lua_pushinteger(L, xdb_ipv4_id); - lua_setfield(L, -2, "IPv4"); - lua_pushinteger(L, xdb_ipv6_id); - lua_setfield(L, -2, "IPv6"); - lua_pushinteger(L, xdb_header_buffer); - lua_setfield(L, -2, "header_buffer"); - lua_pushinteger(L, xdb_vector_index_buffer); - lua_setfield(L, -2, "v_index_buffer"); - lua_pushinteger(L, xdb_content_buffer); - lua_setfield(L, -2, "content_buffer"); - - return 1; -} \ No newline at end of file diff --git a/binding/nginx/README.md b/binding/nginx/README.md deleted file mode 100644 index e71f9c0..0000000 --- a/binding/nginx/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# nginx-ip2region - -## build - -```shell -$ mkdir -p workspace -$ cd workspace -$ wget https://nginx.org/download/nginx-1.23.4.tar.gz -$ tar -zxf nginx-1.23.4.tar.gz && rm -rf nginx-1.23.4.tar.gz -$ git clone https://github.com/lionsoul2014/ip2region.git -$ cd ip2region/binding/c -$ make xdb_searcher_lib -$ cd ../../../nginx-1.23.4 -$ ./configure \ - --add-module=$(PWD)/../ip2region/binding/nginx \ - --with-cc-opt="-I $(PWD)/../ip2region/binding/c/build/include" \ - --with-ld-opt="-L$(PWD)/../ip2region/binding/c/build/lib" -$ make -$ make install -``` - -## nginx conf - -> Syntax: `ip2region_db xdb_file_path [cache_policy Optional]`; -> Context: http - -cache_policy: `file` or `vectorIndex` or `content`, default: `content` - -Edit `nginx.conf` add `ip2region_db` directive - -```nginx -... -http { - - log_format main escape=json '{' - '"remote_addr": "$remote_addr", ' - '"region": "$ip2region", ' - '"http_x_forwarded_for": "$http_x_forwarded_for"' - '}'; - - access_log logs/access.log main; - - # set xdb file path - ip2region_db ip2region.xdb; - # ip2region_db ip2region.xdb vectorIndex; - # ip2region_db ip2region.xdb file; - # ip2region_db ip2region.xdb content; - - server { - listen 80; - server_name localhost; - - location / { - root html; - index index.html index.htm; - } - } -} - -``` - -Copy `ip2region.xdb` to `nginx/config` folder, then restart nginx, the `region` data stored in `ip2region` variable - -nginx access log sample - -```log -{"remote_addr": "127.0.0.1", "region": "0|0|0|内网IP|内网IP", "http_x_forwarded_for": ""} -{"remote_addr": "127.0.0.1", "region": "0|0|0|内网IP|内网IP", "http_x_forwarded_for": ""} - -``` - -Made with ♥ by Wu Jian Ping diff --git a/binding/nginx/config b/binding/nginx/config deleted file mode 100644 index 1da0253..0000000 --- a/binding/nginx/config +++ /dev/null @@ -1,22 +0,0 @@ -ngx_addon_name=ngx_http_ip2region_module - -NGX_HTTP_IP2REGION_SRCS=" \ - $ngx_addon_dir/src/ngx_http_ip2region_module.c \ - " - -NGX_HTTP_IP2REGION_DEPS=" \ - " - -if test -n "$ngx_module_link"; then - ngx_module_type=HTTP - ngx_module_name=$ngx_addon_name - ngx_module_deps="$NGX_HTTP_IP2REGION_DEPS" - ngx_module_srcs="$NGX_HTTP_IP2REGION_SRCS" - ngx_module_libs="-lxdb_searcher" - - . auto/module -else - HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" - NGX_ADDON_DEPS="$NGX_ADDON_DEPS $NGX_HTTP_IP2REGION_DEPS" - NGX_ADDON_SRCS="$NGX_ADDON_SRCS $NGX_HTTP_IP2REGION_SRCS" -fi \ No newline at end of file diff --git a/binding/nginx/src/ngx_http_ip2region_module.c b/binding/nginx/src/ngx_http_ip2region_module.c deleted file mode 100644 index aa3e3e7..0000000 --- a/binding/nginx/src/ngx_http_ip2region_module.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2023/03/30. - */ - -#include "ngx_http_ip2region_module.h" - -static ngx_int_t ngx_http_ua_parser_test_full_name(char *name); - -static ngx_int_t ngx_http_ip2region_add_variables(ngx_conf_t *cf); - -static void *ngx_http_ip2region_create_conf(ngx_conf_t *cf); -static void ngx_http_ip2region_cleanup(void *data); -static char *ngx_http_ip2region_init(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); - -static ngx_int_t ngx_http_ip2region_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); - -static ngx_http_module_t ngx_http_ip2region_ctx = { - ngx_http_ip2region_add_variables, /* pre configuration */ - NULL, /* post configuration */ - ngx_http_ip2region_create_conf, /* create main configuration */ - NULL, /* init main configuration */ - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - NULL, /* create location configuration */ - NULL /* merge location configuration */ -}; - - -static ngx_command_t ngx_http_ip2region_commands[] = { - { ngx_string("ip2region_db"), - NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE12, - ngx_http_ip2region_init, - NGX_HTTP_MAIN_CONF_OFFSET, - 0, - NULL }, - - ngx_null_command -}; - - -/* ngx_module_t is required, otherwise failed at complie time */ -ngx_module_t ngx_http_ip2region_module = { - NGX_MODULE_V1, - &ngx_http_ip2region_ctx, /* module context */ - ngx_http_ip2region_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; - - -static ngx_http_variable_t ngx_http_ip2region_vars[] = { - { ngx_string("ip2region"), NULL, - ngx_http_ip2region_variable, - 0, 0, 0 }, - - ngx_http_null_variable -}; - - -static char * -ngx_http_ip2region_init(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) -{ - ngx_http_ip2region_conf_t *ip2region_cf; - ip2region_searcher_t *ip2region_searcher; - char *db_name, *cache_policy; - ngx_str_t *value; - int err; - char *db_path; - size_t len; - ip2region_cf = conf; - - if (ip2region_cf->ip2region_searcher) { - return "is duplicate"; - } - - value = cf->args->elts; - - db_name = (char *)value[1].data; - - // default cache_policy: content - if(cf->args->nelts == 2) { - cache_policy = "content"; - } else { - cache_policy = (char *)value[2].data; - } - - if(ngx_http_ua_parser_test_full_name(db_name) == NGX_OK) { - db_path = db_name; - } else { // relative path to conf directory - len = ngx_cycle->conf_prefix.len + strlen(db_name) + 1; - db_path = malloc(len); - memset(db_path, '\0', len); - memcpy(db_path, ngx_cycle->conf_prefix.data, ngx_cycle->conf_prefix.len); - strcat(db_path, db_name); - } - - ip2region_searcher = ngx_palloc(cf->pool, sizeof(ip2region_searcher_t)); - - if(ip2region_searcher == NULL) { - return NGX_CONF_ERROR; - } - - ip2region_searcher->v_index = NULL; - ip2region_searcher->c_buffer = NULL; - - if (strcmp(cache_policy, "file") == 0) { - err = xdb_new_with_file_only(&ip2region_searcher->searcher, db_path); - if (err != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "failed to create searcher: %s", db_path); - return NGX_CONF_ERROR; - } - } else if (strcmp(cache_policy, "vectorIndex") == 0) { - ip2region_searcher->v_index = xdb_load_vector_index_from_file(db_path); - if (ip2region_searcher->v_index == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "failed to load vector index from: %s", db_path); - return NGX_CONF_ERROR; - } - - err = xdb_new_with_vector_index(&ip2region_searcher->searcher, db_path, ip2region_searcher->v_index); - if (err != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "failed to create vector index cached searcher: %s", db_path); - return NGX_CONF_ERROR; - } - } else if (strcmp(cache_policy, "content") == 0) { - ip2region_searcher->c_buffer = xdb_load_content_from_file(db_path); - if (ip2region_searcher->c_buffer == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "failed to load xdb content: %s", db_path); - return NGX_CONF_ERROR; - } - - err = xdb_new_with_buffer(&ip2region_searcher->searcher, ip2region_searcher->c_buffer); - if (err != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "failed to create content cached searcher: %s", db_path); - return NGX_CONF_ERROR; - } - } else { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid cache policy `%V`, options: file/vectorIndex/content", &value[2]); - return NGX_CONF_ERROR; - } - - ip2region_cf->ip2region_searcher = ip2region_searcher; - - if(ngx_http_ua_parser_test_full_name(db_name) != NGX_OK) { - free(db_path); - } - - return NGX_CONF_OK; -} - - -static void * -ngx_http_ip2region_create_conf(ngx_conf_t *cf) -{ - ngx_pool_cleanup_t *cln; - ngx_http_ip2region_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ip2region_conf_t)); - if (conf == NULL) { - return NULL; - } - - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - return NULL; - } - - cln->handler = ngx_http_ip2region_cleanup; - cln->data = conf; - - return conf; -} - - -static ngx_int_t -ngx_http_ip2region_add_variables(ngx_conf_t *cf) -{ - ngx_http_variable_t *var; - ngx_http_variable_t *v; - - for (v = ngx_http_ip2region_vars; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - return NGX_OK; -} - - -static ngx_int_t -ngx_http_ip2region_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data) -{ - ngx_http_ip2region_conf_t *ip2region_conf; - struct sockaddr_in *sin; - char region[512] = {'\0'}; - int err; - unsigned int ip; - -#if (NGX_HAVE_INET6) - u_char *p; - in_addr_t addr; - struct sockaddr_in6 *sin6; -#endif - - ip2region_conf = ngx_http_get_module_main_conf(r, ngx_http_ip2region_module); - - if (ip2region_conf->ip2region_searcher != NULL) { - - switch (r->connection->sockaddr->sa_family) { - case AF_INET: - sin = (struct sockaddr_in *) r->connection->sockaddr; - ip = htonl(sin->sin_addr.s_addr); - err = xdb_search(&ip2region_conf->ip2region_searcher->searcher, ip, region, sizeof(region)); - if (err == 0) { - v->data = (unsigned char *)region; - v->len = strlen(region); - return NGX_OK; - } - break; - -#if (NGX_HAVE_INET6) - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; - p = sin6->sin6_addr.s6_addr; - - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - addr = p[12] << 24; - addr += p[13] << 16; - addr += p[14] << 8; - addr += p[15]; - - ip = htonl(addr); - err = xdb_search(&ip2region_conf->ip2region_searcher->searcher, ip, region, sizeof(region)); - if (err == 0) { - v->data = (unsigned char *)region; - v->len = strlen(region); - return NGX_OK; - } - } - break; - -#endif - - } - } - - v->not_found = 1; - return NGX_OK; -} - - -static void -ngx_http_ip2region_cleanup(void *data) -{ - ngx_http_ip2region_conf_t *ip2region_conf = data; - - if(ip2region_conf->ip2region_searcher != NULL) { - xdb_close(&ip2region_conf->ip2region_searcher->searcher); - - // check and free the vector index - if (ip2region_conf->ip2region_searcher->v_index != NULL) { - xdb_close_vector_index(ip2region_conf->ip2region_searcher->v_index); - ip2region_conf->ip2region_searcher->v_index = NULL; - } - - // check and free the content buffer - if (ip2region_conf->ip2region_searcher->c_buffer != NULL) { - xdb_close_content(ip2region_conf->ip2region_searcher->c_buffer); - ip2region_conf->ip2region_searcher->c_buffer = NULL; - } - - ip2region_conf->ip2region_searcher = NULL; - } -} - - -static ngx_int_t -ngx_http_ua_parser_test_full_name(char *name) -{ -#if (NGX_WIN32) - u_char c0, c1; - - c0 = name[0]; - - if (strlen(name) < 2) { - if (c0 == '/') { - return 2; - } - - return NGX_DECLINED; - } - - c1 = name[1]; - - if (c1 == ':') { - c0 |= 0x20; - - if ((c0 >= 'a' && c0 <= 'z')) { - return NGX_OK; - } - - return NGX_DECLINED; - } - - if (c1 == '/') { - return NGX_OK; - } - - if (c0 == '/') { - return 2; - } - - return NGX_DECLINED; - -#else - - if (name[0] == '/') { - return NGX_OK; - } - - return NGX_DECLINED; - -#endif -} diff --git a/binding/nginx/src/ngx_http_ip2region_module.h b/binding/nginx/src/ngx_http_ip2region_module.h deleted file mode 100644 index 8861cee..0000000 --- a/binding/nginx/src/ngx_http_ip2region_module.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2023/03/30. - */ - -#ifndef __NGX_HTTP_IP2REGION_MODULE_H_INCLUDED__ -#define __NGX_HTTP_IP2REGION_MODULE_H_INCLUDED__ - -#include -#include -#include -#include - -typedef struct { - xdb_searcher_t searcher; - xdb_vector_index_t *v_index; - xdb_content_t *c_buffer; -} ip2region_searcher_t; - -typedef struct { - ip2region_searcher_t *ip2region_searcher; -} ngx_http_ip2region_conf_t; - -#endif diff --git a/binding/nginx/t/http_ip2region.t b/binding/nginx/t/http_ip2region.t deleted file mode 100644 index 7b16856..0000000 --- a/binding/nginx/t/http_ip2region.t +++ /dev/null @@ -1,27 +0,0 @@ -use lib 'lib'; -use Test::Nginx::Socket; # 'no_plan'; - -repeat_each(2); - -plan tests => repeat_each() * 124; - -no_long_string(); -#no_diff; - -run_tests(); - -__DATA__ - -=== TEST 1: set request header at client side ---- config - location /foo { - echo $http_x_foo; - } ---- request - GET /foo ---- more_headers -X-Foo: blah ---- response_headers -! X-Foo ---- response_body -blah diff --git a/binding/nodejs/.eslintrc.js b/binding/nodejs/.eslintrc.js deleted file mode 100644 index 11a8bcc..0000000 --- a/binding/nodejs/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - env: { - browser: true, - commonjs: true, - es2021: true - }, - parserOptions: { - ecmaVersion: 'latest' - }, - extends: [ - 'standard' - ], - globals: { - describe: true, - it: true - } -} diff --git a/binding/nodejs/.mocharc.json b/binding/nodejs/.mocharc.json deleted file mode 100644 index bf668ea..0000000 --- a/binding/nodejs/.mocharc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "reporter": "spec", - "timeout": 60000, - "exit": true -} \ No newline at end of file diff --git a/binding/nodejs/.nycrc.json b/binding/nodejs/.nycrc.json deleted file mode 100644 index 8fb5c3d..0000000 --- a/binding/nodejs/.nycrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "all": true, - "report-dir": "./coverage/", - "reporter": ["text", "html"] -} \ No newline at end of file diff --git a/binding/nodejs/ReadMe.md b/binding/nodejs/ReadMe.md deleted file mode 100644 index 97f97ec..0000000 --- a/binding/nodejs/ReadMe.md +++ /dev/null @@ -1,155 +0,0 @@ -# ip2region nodejs 查询客户端实现 - -## 使用方式 - -### 完全基于文件的查询 - -```javascript -// 导入包 -const Searcher = require('.') -// 指定ip2region数据文件路径 -const dbPath = 'ip2region.xdb file path' - -try { - // 创建searcher对象 - const searcher = Searcher.newWithFileOnly(dbPath) - // 查询 - const data = await searcher.search('218.4.167.70') - // data: {region: '中国|0|江苏省|苏州市|电信', ioCount: 3, took: 1.342389} -} catch(e) { - console.log(e) -} - -``` - -### 缓存 `VectorIndex` 索引 - -```javascript -// 导入包 -const Searcher = require('.') -// 指定ip2region数据文件路径 -const dbPath = 'ip2region.xdb file path' - -try { - // 同步读取vectorIndex - const vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) - // 创建searcher对象 - const searcher = Searcher.newWithVectorIndex(dbPath, vectorIndex) - // 查询 await 或 promise均可 - const data = await searcher.search('218.4.167.70') - // data: {region: '中国|0|江苏省|苏州市|电信', ioCount: 2, took: 0.402874} -} catch(e) { - console.log(e) -} -``` - -### 缓存整个 `xdb` 数据 - -```javascript -// 导入包 -const Searcher = require('.') -// 指定ip2region数据文件路径 -const dbPath = 'ip2region.xdb file path' - -try { - // 同步读取buffer - const buffer = Searcher.loadContentFromFile(dbPath) - // 创建searcher对象 - const searcher = Searcher.newWithBuffer(buffer) - // 查询 await 或 promise均可 - const data = await searcher.search('218.4.167.70') - // data: {region:'中国|0|江苏省|苏州市|电信', ioCount: 0, took: 0.063833} -} catch(e) { - console.log(e) -} -``` - -## 查询测试 - -可以通过 `node ./tests/test.app.js` 命令来测试查询: - -```shell -➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/test.app.js --help -usage: Usage node test.app.js - -ip2region test app - -optional arguments: - -h, --help show this help message and exit - -d DB, --db DB ip2region binary xdb file path, default: ../../data/ip2region.xdb - -c CACHE_POLICY, --cache-policy CACHE_POLICY - cache policy: file/vectorIndex/content, default: content -``` - -例如:使用默认的 data/ip2region.xdb 文件进行查询测试: - -```shell -➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/test.app.js -parameters: - dbPath: ../../data/ip2region.xdb - cache-policy: content - -type 'quit' to exit -ip2region>> 1.2.3.4 -{ region: '美国|0|华盛顿|0|谷歌', ioCount: 0, took: 54.606261 } -ip2region>> -``` - -输入 ip 即可进行查询测试,也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的查询效果。 - -## bench 测试 - -```shell -➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/bench.app.js --help -usage: Usage node test.app.js [command options] - -ip2region benchmark app - -optional arguments: - -h, --help show this help message and exit - --db DB ip2region binary xdb file path, default: ../../data/ip2region.xdb - --src SRC source ip text file path, default: ../../data/ip.merge.txt - --cache-policy CACHE_POLICY - cache policy: file/vectorIndex/content, default: content - -``` - -例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 文件进行 bench 测试: - -```shell -➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/bench.app.js -options: - dbPath: ../../data/ip2region.xdb - src: ../../data/ip2region.xdb - cache-policy: content - -Bench finished, {cachePolicy: content, total: 3417955, took: 20.591887765s, cost: 6.02462225658325μs/op} -``` - -可以通过分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效果。 ->Note: 注意 bench 使用的 src 文件要是生成对应 xdb 文件相同的源文件。 - -## 单元测试及覆盖率结果 - -```shell -➜ nodejs git:(v2.0-for-nodejs) ✗ npm run coverage - -... - - ip2region - ✔ #newWithFileOnly and search - ✔ #newWithVectorIndex and search - ✔ #newWithBuffer and search - - - 3 passing (6ms) - -----------|---------|----------|---------|---------|---------------------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s -----------|---------|----------|---------|---------|---------------------------------- -All files | 91.58 | 60.71 | 100 | 91.58 | - index.js | 91.58 | 60.71 | 100 | 91.58 | 61,75,90,146,152,187,193,207,215 -----------|---------|----------|---------|---------|---------------------------------- -``` - -Made with ♥ by Wu Jian Ping diff --git a/binding/nodejs/index.js b/binding/nodejs/index.js deleted file mode 100644 index 87d0275..0000000 --- a/binding/nodejs/index.js +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2022/07/22. - */ - -const fs = require('fs') - -const VectorIndexSize = 8 -const VectorIndexCols = 256 -const VectorIndexLength = 256 * 256 * (4 + 4) -const SegmentIndexSize = 14 -const IP_REGEX = /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/ - -const getStartEndPtr = Symbol('#getStartEndPtr') -const getBuffer = Symbol('#getBuffer') -const openFilePromise = Symbol('#openFilePromise') - -class Searcher { - constructor (dbFile, vectorIndex, buffer) { - this._dbFile = dbFile - this._vectorIndex = vectorIndex - this._buffer = buffer - - if (this._buffer) { - this._vectorIndex = this._buffer.subarray(256, 256 + VectorIndexLength) - } - } - - async [getStartEndPtr] (idx, fd, ioStatus) { - if (this._vectorIndex) { - const sPtr = this._vectorIndex.readUInt32LE(idx) - const ePtr = this._vectorIndex.readUInt32LE(idx + 4) - return { sPtr, ePtr } - } else { - const buf = await this[getBuffer](256 + idx, 8, fd, ioStatus) - const sPtr = buf.readUInt32LE() - const ePtr = buf.readUInt32LE(4) - return { sPtr, ePtr } - } - } - - async [getBuffer] (offset, length, fd, ioStatus) { - if (this._buffer) { - return this._buffer.subarray(offset, offset + length) - } else { - const buf = Buffer.alloc(length) - return new Promise((resolve, reject) => { - ioStatus.ioCount += 1 - fs.read(fd, buf, 0, length, offset, (err) => { - if (err) { - reject(err) - } else { - resolve(buf) - } - }) - }) - } - } - - [openFilePromise] (fileName) { - return new Promise((resolve, reject) => { - fs.open(fileName, 'r', (err, fd) => { - if (err) { - reject(err) - } else { - resolve(fd) - } - }) - }) - } - - async search (ip) { - const startTime = process.hrtime() - const ioStatus = { - ioCount: 0 - } - - if (!isValidIp(ip)) { - throw new Error(`IP: ${ip} is invalid`) - } - - let fd = null - - if (!this._buffer) { - fd = await this[openFilePromise](this._dbFile) - } - - const ps = ip.split('.') - const i0 = parseInt(ps[0]) - const i1 = parseInt(ps[1]) - const i2 = parseInt(ps[2]) - const i3 = parseInt(ps[3]) - - const ipInt = i0 * 256 * 256 * 256 + i1 * 256 * 256 + i2 * 256 + i3 - const idx = i0 * VectorIndexCols * VectorIndexSize + i1 * VectorIndexSize - const { sPtr, ePtr } = await this[getStartEndPtr](idx, fd, ioStatus) - let l = 0 - let h = (ePtr - sPtr) / SegmentIndexSize - let result = null - - while (l <= h) { - const m = (l + h) >> 1 - - const p = sPtr + m * SegmentIndexSize - - const buff = await this[getBuffer](p, SegmentIndexSize, fd, ioStatus) - - const sip = buff.readUInt32LE(0) - - if (ipInt < sip) { - h = m - 1 - } else { - const eip = buff.readUInt32LE(4) - if (ipInt > eip) { - l = m + 1 - } else { - const dataLen = buff.readUInt16LE(8) - const dataPtr = buff.readUInt32LE(10) - const data = await this[getBuffer](dataPtr, dataLen, fd, ioStatus) - result = data.toString('utf-8') - break - } - } - } - if (fd) { - fs.close(fd,function(){}) - } - - const diff = process.hrtime(startTime) - - const took = (diff[0] * 1e9 + diff[1]) / 1e3 - return { region: result, ioCount: ioStatus.ioCount, took } - } -} - -const _checkFile = dbPath => { - try { - fs.accessSync(dbPath, fs.constants.F_OK) - } catch (err) { - throw new Error(`${dbPath} ${err ? 'does not exist' : 'exists'}`) - } - - try { - fs.accessSync(dbPath, fs.constants.R_OK) - } catch (err) { - throw new Error(`${dbPath} ${err ? 'is not readable' : 'is readable'}`) - } -} - -const isValidIp = ip => { - return IP_REGEX.test(ip) -} - -const newWithFileOnly = dbPath => { - _checkFile(dbPath) - - return new Searcher(dbPath, null, null) -} - -const newWithVectorIndex = (dbPath, vectorIndex) => { - _checkFile(dbPath) - - if (!Buffer.isBuffer(vectorIndex)) { - throw new Error('vectorIndex is invalid') - } - - return new Searcher(dbPath, vectorIndex, null) -} - -const newWithBuffer = buffer => { - if (!Buffer.isBuffer(buffer)) { - throw new Error('buffer is invalid') - } - - return new Searcher(null, null, buffer) -} - -const loadVectorIndexFromFile = dbPath => { - const fd = fs.openSync(dbPath, 'r') - const buffer = Buffer.alloc(VectorIndexLength) - fs.readSync(fd, buffer, 0, VectorIndexLength, 256) - fs.close(fd,function(){}) - return buffer -} - -const loadContentFromFile = dbPath => { - const stats = fs.statSync(dbPath) - const buffer = Buffer.alloc(stats.size) - const fd = fs.openSync(dbPath, 'r') - fs.readSync(fd, buffer, 0, stats.size, 0) - fs.close(fd,function(){}) - return buffer -} - -module.exports = { - isValidIp, - loadVectorIndexFromFile, - loadContentFromFile, - newWithFileOnly, - newWithVectorIndex, - newWithBuffer -} diff --git a/binding/nodejs/package.json b/binding/nodejs/package.json deleted file mode 100644 index 7228876..0000000 --- a/binding/nodejs/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "node-ip2region", - "version": "2.0.0", - "description": "official nodejs client of ip2region", - "main": "index.js", - "scripts": { - "lint": "eslint ./index.js", - "test": "mocha ./tests/function.test.js", - "coverage": "nyc npm run test", - "benchmark": "node ./tests/benchmark.js" - }, - "author": "Wu Jian Ping", - "license": "ISC", - "devDependencies": { - "@types/chai": "^4.3.1", - "@types/node": "^18.0.6", - "argparse": "^2.0.1", - "benchmark": "^2.1.4", - "chai": "^4.3.6", - "eslint": "^8.20.0", - "eslint-config-standard": "^17.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-n": "^15.2.4", - "eslint-plugin-promise": "^6.0.0", - "linebyline": "^1.3.0", - "mocha": "^10.0.0", - "nyc": "^15.1.0" - }, - "engines": { - "node": ">=8.0.0" - } -} \ No newline at end of file diff --git a/binding/nodejs/tests/bench.app.js b/binding/nodejs/tests/bench.app.js deleted file mode 100644 index b1b1aeb..0000000 --- a/binding/nodejs/tests/bench.app.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2022/07/22. - */ - -const Searcher = require('..') -const { ArgumentParser } = require('argparse') -const readline = require('linebyline') - -// 处理输入参数 -const parser = new ArgumentParser({ - add_help: true, - description: 'ip2region benchmark app', - prog: 'node test.app.js', - usage: 'Usage %(prog)s [command options]' -}) - -parser.add_argument('--db', { help: 'ip2region binary xdb file path, default: ../../data/ip2region.xdb' }) -parser.add_argument('--src', { help: 'source ip text file path, default: ../../data/ip.merge.txt' }) -parser.add_argument('--cache-policy', { help: 'cache policy: file/vectorIndex/content, default: content' }) - -const args = parser.parse_args() -const dbPath = args.db || '../../data/ip2region.xdb' -const src = args.src || '../../data/ip.merge.txt' -const cachePolicy = args.cache_policy || 'content' - -// 创建searcher对象 -const createSearcher = () => { - let searcher = null - let vectorIndex = null - let buffer = null - - switch (cachePolicy) { - case 'file': - searcher = Searcher.newWithFileOnly(dbPath) - break - case 'vectorIndex': - vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) - searcher = Searcher.newWithVectorIndex(dbPath, vectorIndex) - break - default: - buffer = Searcher.loadContentFromFile(dbPath) - searcher = Searcher.newWithBuffer(buffer) - } - console.log('options: ') - console.log(` dbPath: ${dbPath}`) - console.log(` src: ${dbPath}`) - console.log(` cache-policy: ${cachePolicy}`) - console.log('') - return searcher -} - -const ipToInt = ip => { - // 切割IP - const ps = ip.split('.') - // 将各段转成int - const i0 = parseInt(ps[0]) - const i1 = parseInt(ps[1]) - const i2 = parseInt(ps[2]) - const i3 = parseInt(ps[3]) - - // 假如使用移位操作的话,这边可能产生负数 - return i0 * 256 * 256 * 256 + i1 * 256 * 256 + i2 * 256 + i3 -} - -const intToIp = ip => { - const i0 = Math.floor(ip / (256 * 256 * 256)) - const i1 = Math.floor((ip % (256 * 256 * 256)) / (256 * 256)) - const i2 = Math.floor((ip % (256 * 256)) / 256) - const i3 = ip % 256 - - return `${i0}.${i1}.${i2}.${i3}` -} - -const searcher = createSearcher() - -// 开始时间 -const startTime = process.hrtime() -let total = 0 - -// 程序主入口 -const main = async () => { - const rl = readline(src) - rl - .on('line', async (line, lineCount, byteCount) => { - try { - const list = line.split('|') - const sip = list[0] - const eip = list[1] - - if (!Searcher.isValidIp(sip)) { - throw new Error(`IP: ${sip} is invalid`) - } - if (!Searcher.isValidIp(eip)) { - throw new Error(`IP: ${eip} is invalid`) - } - - const sipInt = ipToInt(sip) - const eipInt = ipToInt(eip) - - if (sipInt > eipInt) { - throw new Error(`start ip(${sip}) should not be greater than end ip(${eip})`) - } - - const mipInt = Math.floor((sipInt + eipInt) / 2) - const mip = intToIp(mipInt) - - const mipLeftInt = Math.floor((sipInt + mipInt) / 2) - const mipLeft = intToIp(mipLeftInt) - - const mipRightInt = Math.floor((mipInt + eipInt) / 2) - const mipRight = intToIp(mipRightInt) - - const arr = [sip, mipLeft, mip, mipRight, eip] - - for (let i = 0; i < arr.length; ++i) { - const target = arr[i] - const info = await searcher.search(target) - - const region = list.slice(2, list.length).join('|') - // check the region info - if (region !== info.region) { - throw new Error(`failed search(${mip}) with (${region} != ${info.region})`) - } - total++ - } - } catch (err) { - console.log(err) - process.exit(1) - } - }) - .on('error', err => { - console.log(err) - process.exit(1) - }) -} - -process.on('exit', code => { - if (code === 0) { - // 这边只算个总时间就够了 - const diff = process.hrtime(startTime) - const totalInNS = diff[0] * 1e9 + diff[1] - console.log(`Bench finished, {cachePolicy: ${cachePolicy}, total: ${total}, took: ${totalInNS / 1e9}s, cost: ${total === 0 ? 0 : (totalInNS / 1e3) / total}μs/op}`) - } -}) - -main() diff --git a/binding/nodejs/tests/benchmark.js b/binding/nodejs/tests/benchmark.js deleted file mode 100644 index fef7ea7..0000000 --- a/binding/nodejs/tests/benchmark.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2022/07/22. - */ - -const Benchmark = require('benchmark') -const path = require('path') -const Searcher = require('..') - -const dbPath = path.join(__dirname, '..', '..', '..', 'data', 'ip2region.xdb') -const buffer = Searcher.loadContentFromFile(dbPath) -const searcher1 = Searcher.newWithBuffer(buffer) - -const vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) -const searcher2 = Searcher.newWithVectorIndex(dbPath, vectorIndex) - -const searcher3 = Searcher.newWithFileOnly(dbPath) - -const suite = new Benchmark.Suite() -suite - .add('#缓存整个xdb数据【搜索218.4.167.70】', async () => { - const ip = '218.4.167.70' - return searcher1.search(ip) - }) - .add('#缓存VectorIndex索引【搜索218.4.167.70】', async () => { - const ip = '218.4.167.70' - return searcher2.search(ip) - }) - .add('#完全基于文件的查询【搜索218.4.167.70】', async () => { - const ip = '218.4.167.70' - return searcher3.search(ip) - }) - .on('cycle', function (event) { - console.log(String(event.target)) // eslint-disable-line - }) - .on('complete', function () { - console.log('Fastest is ' + this.filter('fastest').map('name')) // eslint-disable-line - }) - .run({ async: true }) diff --git a/binding/nodejs/tests/function.test.js b/binding/nodejs/tests/function.test.js deleted file mode 100644 index bdfd524..0000000 --- a/binding/nodejs/tests/function.test.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2022/07/22. - */ - -const { expect } = require('chai') -const path = require('path') -const Searcher = require('..') - -const dbPath = path.join(__dirname, '..', '..', '..', 'data', 'ip2region.xdb') -const buffer = Searcher.loadContentFromFile(dbPath) -const searcher1 = Searcher.newWithBuffer(buffer) - -const vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) -const searcher2 = Searcher.newWithVectorIndex(dbPath, vectorIndex) - -const searcher3 = Searcher.newWithFileOnly(dbPath) - -describe('ip2region', () => { - it('#newWithFileOnly and search', async () => { - const d = await searcher3.search('218.4.167.70') - expect(d.region).equal('中国|0|江苏省|苏州市|电信') - }) - - it('#newWithVectorIndex and search', async () => { - const d = await searcher2.search('218.4.167.70') - expect(d.region).equal('中国|0|江苏省|苏州市|电信') - }) - - it('#newWithBuffer and search', async () => { - const d = await searcher1.search('218.4.167.70') - expect(d.region).equal('中国|0|江苏省|苏州市|电信') - }) -}) diff --git a/binding/nodejs/tests/test.app.js b/binding/nodejs/tests/test.app.js deleted file mode 100644 index 26abbd0..0000000 --- a/binding/nodejs/tests/test.app.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Created by Wu Jian Ping on - 2022/07/22. - */ - -const Searcher = require('../') -const { ArgumentParser } = require('argparse') - -// 处理输入参数 -const parser = new ArgumentParser({ - add_help: true, - description: 'ip2region test app', - prog: 'node test.app.js', - usage: 'Usage %(prog)s [command options]' -}) - -parser.add_argument('--db', { help: 'ip2region binary xdb file path, default: ../../data/ip2region.xdb' }) -parser.add_argument('--cache-policy', { help: 'cache policy: file/vectorIndex/content, default: content' }) - -const args = parser.parse_args() -const dbPath = args.db || '../../data/ip2region.xdb' -const cachePolicy = args.cache_policy || 'content' - -// 创建searcher对象 -const createSearcher = () => { - let searcher = null - let vectorIndex = null - let buffer = null - - switch (cachePolicy) { - case 'file': - searcher = Searcher.newWithFileOnly(dbPath) - break - case 'vectorIndex': - vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) - searcher = Searcher.newWithVectorIndex(dbPath, vectorIndex) - break - default: - buffer = Searcher.loadContentFromFile(dbPath) - searcher = Searcher.newWithBuffer(buffer) - } - console.log('options: ') - console.log(` dbPath: ${dbPath}`) - console.log(` cache-policy: ${cachePolicy}`) - console.log('') - return searcher -} - -// 从控制台读取用户一行输入 -const readlineSync = () => { - return new Promise((resolve, reject) => { - process.stdin.resume() - process.stdin.on('data', data => { - process.stdin.pause() - resolve(data.toString('utf-8')) - }) - }) -} - -const searcher = createSearcher() - -const main = async () => { - console.log('type \'quit\' to exit') - while (true) { - process.stdout.write('ip2region>> ') - const ip = (await readlineSync()).trim() - if (ip === 'quit') { - process.exit(0) - } else { - try { - const response = await searcher.search(ip) - console.log(response) - } catch (err) { - console.log(err) - } - } - } -} - -main() diff --git a/binding/php/ReadMe.md b/binding/php/ReadMe.md deleted file mode 100644 index b9227c4..0000000 --- a/binding/php/ReadMe.md +++ /dev/null @@ -1,221 +0,0 @@ -# ip2region xdb php 查询客户端实现 - -# 使用方式 - -第三方 composer 地址: -1. [https://github.com/zoujingli/ip2region](https://github.com/zoujingli/ip2region) - 已提供 IPv6 支持。 -2. [https://github.com/chinayin/ip2region-core-php](https://github.com/chinayin/ip2region-core-php) - - -### 关于 IPv4 和 IPv6 -该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下: -```php -use \ip2region\xdb\{IPv4, IPv6}; - -// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 IPv4 -$dbFile = "../../data/ip2region_v4.xdb"; // 或者你的 ipv4 xdb 的路径 -$version = IPv4::default(); - -// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 IPv6 -$dbFile = "../../data/ip2region_v6.xdb"; // 或者你的 ipv6 xdb 路径 -$version = IPv6::default(); - -// dbPath 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错 -// 备注:以下演示直接使用 $dbFile 和 $version 变量 -``` - -### XDB 文件验证 -建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。 -```php -use \ip2region\xdb\Util; - -$err = Util::verify($dbFile); -if ($err != null) { - // 适用性验证失败!!! - // 当前查询客户端实现不适用于 dbPath 指定的 xdb 文件的查询. - // 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 dbPath 的 Searcher 实现。 - printf("failed to verify xdb file `%s`: %s\n", $dbFile, $err); - return; -} - -// 验证通过,当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作 -``` - -### 完全基于文件的查询 -```php -// require or autoload the xdb\Searcher -require 'xdb\Searcher.php'; -use \ip2region\xdb\Util; -use \ip2region\xdb\Searcher; - -// 1, 使用上述的 $version 和 $dbFile 创建 Searcher 对象 -try { - $searcher = Searcher::newWithFileOnly($version, $dbFile); -} catch (Exception $e) { - printf("failed to create searcher with '%s': %s\n", $dbFile, $e->getMessage()); - return; -} - -// 2, 查询,IPv4 或者 IPv6 的地址都支持 -try { - $ip = '1.2.3.4'; - // $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - $sTime = Util::now(); - $region = $searcher->search($ip); - $costMs = Util::now() - $sTime; - printf("{region: %s, took: %.5f ms}\n", $region, $costMs); -} catch (Exception $e) { - printf("failed to search(%s): %s", $ip, $e->getMessage()); -} - -// 3,关闭资源 -$searcher->close(); - -// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象。 -``` - -### 缓存 `VectorIndex` 索引 - -如果你的 php 母环境支持,可以预先加载 vectorIndex 缓存,然后做成全局变量,每次创建 Searcher 的时候使用全局的 vectorIndex,可以减少一次固定的 IO 操作从而加速查询,减少 io 压力。 -```php -// require or autoload the xdb\Searcher -require 'xdb\Searcher.php'; -use \ip2region\xdb\Util; -use \ip2region\xdb\Searcher; - - -// 1、从 $dbFile 加载 VectorIndex 缓存,把下述的 vIndex 变量缓存到内存里面。 -$vIndex = Util::loadVectorIndexFromFile($dbFile); -if ($vIndex === null) { - printf("failed to load vector index from '%s'\n", $dbFile); - return; -} - -// 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 -try { - $searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex); -} catch (Exception $e) { - printf("failed to create vectorIndex searcher with '%s': %s\n", $dbFile, $e->getMessage()); - return; -} - -// 3、查询,IPv4 或者 IPv6 都支持 -try { - $ip = '1.2.3.4'; - // $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - $sTime = Util::now(); - $region = $searcher->search($ip); - $costMs = Util::now() - $sTime; - printf("{region: %s, took: %.5f ms}\n", $region, $costMs); -} catch (Exception $e) { - printf("failed to search(%s): %s", $ip, $e->getMessage()); -} - -// 4, 关闭资源 -$searcher->close(); - -// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象,但是都共享统一的只读全局 vectorIndex。。 -``` - -### 缓存整个 `xdb` 数据 - -如果你的 PHP 母环境支持,可以预先加载整个 `xdb` 的数据到内存,这样可以实现完全基于内存的查询,类似之前的 memory search 查询。 -```php -// require or autoload the xdb\Searcher -require 'xdb\Searcher.php'; -use \ip2region\xdb\Util; -use \ip2region\xdb\Searcher; - -// 1、从 $dbFile 加载整个 xdb 到内存。 -$cBuff = Util::loadContentFromFile($dbFile); -if ($cBuff === null) { - printf("failed to load content buffer from '%s'\n", $dbFile); - return; -} - -// 2、使用全局的 cBuff 创建带完全基于内存的查询对象。 -try { - $searcher = Searcher::newWithBuffer($version, $cBuff); -} catch (Exception $e) { - printf("failed to create buffer cached searcher: %s\n", $dbFile, $e->getMessage()); - return; -} - -// 3、查询,IPv4 或者 IPv6 都支持 -try { - $ip = '1.2.3.4'; - // $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6 - $sTime = Util::now(); - $region = $searcher->search($ip); - $costMs = Util::now() - $sTime; - printf("{region: %s, took: %.5f ms}\n", $region, $costMs); -} catch (Exception $e) { - printf("failed to search(%s): %s", $ip, $e->getMessage()); -} - -// 4,关闭资源 -// 该 searcher 对象可以安全的用于并发,等整个服务都关闭的时候再关闭 searcher -// $searcher->close(); - -// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。 -``` - -# 查询测试 - -通过 `search_test.php` 脚本来进行查询测试: -```bash -➜ php git:(fr_php_ipv6) ✗ php search_test.php -php search_test.php [command options] -options: - --db string ip2region binary xdb file path - --cache-policy string cache policy: file/vectorIndex/content -``` - -例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 的查询测试: -```bash -➜ php git:(fr_php_ipv6) ✗ php search_test.php --db=../../data/ip2region_v4.xdb -ip2region xdb searcher test program -source xdb file: ../../data/ip2region_v4.xdb (IPv4, vectorIndex) -type 'quit' to exit -ip2region>> 120.229.45.2 -{region: 中国|广东省|深圳市|移动, ioCount: 3, took: 0.02783 ms} -``` - -例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 的查询测试: -```bash -➜ php git:(fr_php_ipv6) ✗ php search_test.php --db=../../data/ip2region_v6.xdb -ip2region xdb searcher test program -source xdb file: ../../data/ip2region_v6.xdb (IPv6, vectorIndex) -type 'quit' to exit -ip2region>> :: -{region: |||, ioCount: 2, took: 0.06982 ms} -ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff -{region: 中国|广东省|深圳市|数据中心, ioCount: 13, took: 0.19409 ms} -``` - -输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。 - -# bench 测试 - -通过 `bench_test.php` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: -```bash -➜ php git:(fr_php_ipv6) ✗ php bench_test.php -php bench_test.php [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 -php bench_test.php --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt -``` - -例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 文件进行 IPv6 的 bench 测试: -```bash -php bench_test.php --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt -``` - -可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。 -@Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件的相同的源文件。 diff --git a/binding/php/batch_test.php b/binding/php/batch_test.php deleted file mode 100644 index 85e61bc..0000000 --- a/binding/php/batch_test.php +++ /dev/null @@ -1,161 +0,0 @@ - -// @Date 2022/06/22 - -require dirname(__FILE__) . '/xdb/Searcher.class.php'; - -use \ip2region\xdb\Util; -use \ip2region\xdb\{IPv4, IPv6}; -use \ip2region\xdb\Searcher; - -function printHelp($argv) { - printf("php %s [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"); -} - -if($argc < 2) { - printHelp($argv); - return; -} - -$dbFile = ""; -$srcFile = ""; -$cachePolicy = 'vectorIndex'; -array_shift($argv); -foreach ($argv as $r) { - if (strlen($r) < 5) { - continue; - } - - if (strpos($r, '--') != 0) { - continue; - } - - $sIdx = strpos($r, "="); - if ($sIdx < 0) { - printf("missing = for args pair %s\n", $r); - return; - } - - $key = substr($r, 2, $sIdx - 2); - $val = substr($r, $sIdx + 1); - if ($key == 'db') { - $dbFile = $val; - } else if ($key == 'src') { - $srcFile = $val; - } else if ($key == 'cache-policy') { - $cachePolicy = $val; - } else { - printf("undefined option `%s`\n", $r); - return; - } -} - -if (strlen($dbFile) < 1 || strlen($srcFile) < 1) { - printHelp($argv); - return; -} - -// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy); -$handle = fopen($dbFile, 'r'); -if ($handle === false) { - printf("failed to open the xdb file `{$dbFile}`\n"); - return; -} - -// load header -$header = Util::loadHeader($handle); -if ($header == null) { - printf("failed to load the header\n"); - return; -} - -// get the version number from the xdb header -try { - $version = Util::versionFromHeader($header); -} catch (Exception $e) { - printf("failed to detect version from header: {$e->getMessage()}\n"); - return; -} - -// create the xdb searcher by the cache-policy -switch ( $cachePolicy ) { - case 'file': - try { - $searcher = Searcher::newWithFileOnly($version, $dbFile); - } catch (Exception $e) { - printf("failed to create searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; - case 'vectorIndex': - $vIndex = Util::loadVectorIndex($handle); - if ($vIndex == null) { - printf("failed to load vector index from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex); - } catch (Exception $e) { - printf("failed to create vector index cached searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; - case 'content': - $cBuff = Util::loadContent($handle); - if ($cBuff == null) { - printf("failed to load xdb content from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithBuffer($version, $cBuff); - } catch (Exception $e) { - printf("failed to create content cached searcher: %s", $e); - return; - } - break; - default: - printf("undefined cache-policy `%s`\n", $cachePolicy); - return; -} - - -// do the bench test -$handle = fopen($srcFile, "r"); -if ($handle === false) { - printf("failed to open source text file `%s`\n", $srcFile); - return null; -} - -$count = 0; -$qx_count = 0; -while (!feof($handle)) { - $line = trim(fgets($handle, 1024)); - if (strlen($line) < 1) { - continue; - } - - try { - $ipBytes = Util::parseIP($line); - } catch (Exception $e) { - printf("failed to parse ip `%s`: %s\n", $line, $e->getMessage()); - continue; - } - - $count++; - $region = $searcher->searchByBytes($ipBytes); - $ss = explode('|', $region); - echo $line, ",", str_replace('|', ',', $region), "\n"; -} - -fclose($handle); -echo "Done, with {$count} IPs\n"; diff --git a/binding/php/bench_test.php b/binding/php/bench_test.php deleted file mode 100644 index 20e5309..0000000 --- a/binding/php/bench_test.php +++ /dev/null @@ -1,218 +0,0 @@ - -// @Date 2022/06/22 - -require dirname(__FILE__) . '/xdb/Searcher.class.php'; - -use \ip2region\xdb\Util; -use \ip2region\xdb\{IPv4, IPv6}; -use \ip2region\xdb\Searcher; - -function printHelp($argv) { - printf("php %s [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"); -} - -if($argc < 2) { - printHelp($argv); - return; -} - -$dbFile = ""; -$srcFile = ""; -$cachePolicy = 'vectorIndex'; -array_shift($argv); -foreach ($argv as $r) { - if (strlen($r) < 5) { - continue; - } - - if (strpos($r, '--') != 0) { - continue; - } - - $sIdx = strpos($r, "="); - if ($sIdx < 0) { - printf("missing = for args pair %s\n", $r); - return; - } - - $key = substr($r, 2, $sIdx - 2); - $val = substr($r, $sIdx + 1); - if ($key == 'db') { - $dbFile = $val; - } else if ($key == 'src') { - $srcFile = $val; - } else if ($key == 'cache-policy') { - $cachePolicy = $val; - } else { - printf("undefined option `%s`\n", $r); - return; - } -} - -if (strlen($dbFile) < 1 || strlen($srcFile) < 1) { - printHelp($argv); - return; -} - -// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy); -$handle = fopen($dbFile, 'r'); -if ($handle === false) { - printf("failed to open the xdb file `{$dbFile}`\n"); - return; -} - -// verify the xdb file -// @Note: do NOT call it every time you create a searcher since this will slow -// down the search response. -// @see the Util.verify function for details. -$err = Util::verify($handle); -if ($err != null) { - printf("failed to verify xdb file `%s`: %s\n", $dbFile, $err); - return; -} - -// load header -$header = Util::loadHeader($handle); -if ($header == null) { - printf("failed to load the header\n"); - return; -} - -// get the version number from the xdb header -try { - $version = Util::versionFromHeader($header); -} catch (Exception $e) { - printf("failed to detect version from header: {$e->getMessage()}\n"); - return; -} - -// create the xdb searcher by the cache-policy -switch ( $cachePolicy ) { - case 'file': - try { - $searcher = Searcher::newWithFileOnly($version, $dbFile); - } catch (Exception $e) { - printf("failed to create searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; - case 'vectorIndex': - $vIndex = Util::loadVectorIndex($handle); - if ($vIndex == null) { - printf("failed to load vector index from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex); - } catch (Exception $e) { - printf("failed to create vector index cached searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; - case 'content': - $cBuff = Util::loadContent($handle); - if ($cBuff == null) { - printf("failed to load xdb content from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithBuffer($version, $cBuff); - } catch (Exception $e) { - printf("failed to create content cached searcher: %s", $e); - return; - } - break; - default: - printf("undefined cache-policy `%s`\n", $cachePolicy); - return; -} - - -// do the bench test -$handle = fopen($srcFile, "r"); -if ($handle === false) { - printf("failed to open source text file `%s`\n", $srcFile); - return null; -} - -$count = 0; -$costs = 0; -$sTime = Util::now(); -while (!feof($handle)) { - $line = trim(fgets($handle, 1024)); - if (strlen($line) < 1) { - continue; - } - - // ignore the comment - if ($line[0] == '#') { - continue; - } - - $ps = explode('|', $line, 3); - if (count($ps) != 3) { - printf("invalid ip segment line `${line}`\n"); - return; - } - - $sip = Util::parseIP($ps[0]); - if ($sip === null) { - printf("invalid start ip `%s`\n", $ps[0]); - return; - } - - $eip = Util::parseIP($ps[1]); - if ($eip === null) { - printf("invalid end ip `%s`\n", $ps[1]); - return; - } - - if (Util::ipCompare($sip, $eip) > 0) { - printf( - "start ip(%s) should not be greater than end ip(%s)\n", - Util::ipToString($ps[0]), Util::ipToString($ps[1]) - ); - return; - } - - foreach ([$sip, $eip] as $ip) { - try { - $cTime = Util::now(); - $region = $searcher->searchByBytes($ip); - $costs += Util::now() - $cTime; - } catch (Exception $e) { - printf("failed to search ip `%s`: %s\n", Util::ipToString($ip), $e->getMessage()); - return; - } - - if ($region == null) { - printf("failed to search ip `%s`: empty region info\n", Util::ipToString($ip)); - return; - } - - // check the region info - if ($region != $ps[2]) { - printf("failed search(%s) with (%s != %s)\n", Util::ipToString($ip), $region, $ps[2]); - return; - } - - $count++; - } -} - -// close the searcher at last -fclose($handle); -$searcher->close(); -printf("Bench finished, {cachePolicy: %s, total: %d, took: %ds, cost: %.3f ms/op}\n", - $cachePolicy, $count, (Util::now() - $sTime)/1000, $count == 0 ? 0 : $costs/$count); diff --git a/binding/php/search_test.php b/binding/php/search_test.php deleted file mode 100644 index 6d73322..0000000 --- a/binding/php/search_test.php +++ /dev/null @@ -1,171 +0,0 @@ - -// @Date 2022/06/21 - -require dirname(__FILE__) . '/xdb/Searcher.class.php'; - -use \ip2region\xdb\Util; -use \ip2region\xdb\{IPv4, IPv6}; -use \ip2region\xdb\Searcher; - -function printHelp($argv) { - printf("php %s [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"); -} - -if($argc < 2) { - printHelp($argv); - return; -} - -$dbFile = ""; -$cachePolicy = 'vectorIndex'; -array_shift($argv); -foreach ($argv as $r) { - if (strlen($r) < 5) { - continue; - } - - if (strpos($r, '--') != 0) { - continue; - } - - $sIdx = strpos($r, "="); - if ($sIdx < 0) { - printf("missing = for args pair %s\n", $r); - return; - } - - $key = substr($r, 2, $sIdx - 2); - $val = substr($r, $sIdx + 1); - if ($key == 'db') { - $dbFile = $val; - } else if ($key == 'cache-policy') { - $cachePolicy = $val; - } else { - printf("undefined option `%s`\n", $r); - return; - } -} - -if (strlen($dbFile) < 1) { - printHelp($argv); - return; -} - -// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy); -$handle = fopen($dbFile, 'r'); -if ($handle === false) { - printf("failed to open the xdb file `{$dbFile}`\n"); - return; -} - -// verify the xdb file -// @Note: do NOT call it every time you create a searcher since this will slow -// down the search response. -// @see the Util.verify function for details. -$err = Util::verify($handle); -if ($err != null) { - printf("failed to verify xdb file `%s`: %s\n", $dbFile, $err); - return; -} - -// load header -$header = Util::loadHeader($handle); -if ($header == null) { - printf("failed to load the header\n"); - return; -} - -// get the version number from the xdb header -try { - $version = Util::versionFromHeader($header); -} catch (Exception $e) { - printf("failed to detect version from header: {$e->getMessage()}\n"); - return; -} - -// create the xdb searcher by the cache-policy -switch ( $cachePolicy ) { -case 'file': - try { - $searcher = Searcher::newWithFileOnly($version, $dbFile); - } catch (Exception $e) { - printf("failed to create searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; -case 'vectorIndex': - $vIndex = Util::loadVectorIndex($handle); - if ($vIndex == null) { - printf("failed to load vector index from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex); - } catch (Exception $e) { - printf("failed to create vector index cached searcher with '%s': %s\n", $dbFile, $e); - return; - } - break; -case 'content': - $cBuff = Util::loadContent($handle); - if ($cBuff == null) { - printf("failed to load xdb content from '%s'\n", $dbFile); - return; - } - - try { - $searcher = Searcher::newWithBuffer($version, $cBuff); - } catch (Exception $e) { - printf("failed to create content cached searcher: %s", $e); - return; - } - break; -default: - printf("undefined cache-policy `%s`\n", $cachePolicy); - return; -} - -printf(<<name}, ${cachePolicy}) -type 'quit' to exit\n -EOF); -while ( true ) { - echo "ip2region>> "; - $line = trim(fgets(STDIN)); - if (strlen($line) < 2) { - continue; - } - - if ($line == 'quit') { - break; - } - - $cost = -1; - try { - $sTime = Util::now(); - $region = $searcher->search($line); - $cost = Util::now() - $sTime; - } catch (Exception $e) { - printf("search call failed: %s\n", $e->getMessage()); - continue; - } - - printf( - "{region: %s, ioCount: %d, took: %.5f ms}\n", - $region, $searcher->getIOCount(), $cost - ); -} - -// close the searcher at last -$searcher->close(); -printf("searcher test program exited, thanks for trying\n"); \ No newline at end of file diff --git a/binding/php/xdb/Searcher.class.php b/binding/php/xdb/Searcher.class.php deleted file mode 100644 index 7b598f1..0000000 --- a/binding/php/xdb/Searcher.class.php +++ /dev/null @@ -1,580 +0,0 @@ - -// @Date 2022/06/21 - -namespace ip2region\xdb; -use \Exception; - -// global constants -const Structure_20 = 2; -const Structure_30 = 3; -const IPv4VersionNo = 4; -const IPv6VersionNo = 6; -const HeaderInfoLength = 256; -const VectorIndexRows = 256; -const VectorIndexCols = 256; -const VectorIndexSize = 8; - - -// Util class -class Util { - // parse the specified IP address and return its bytes. - // returns: NULL for failed or the packed bytes - public static function parseIP($ipString) { - $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6; - if (!filter_var($ipString, FILTER_VALIDATE_IP, $flag)) { - return null; - } - - return inet_pton($ipString); - } - - // IP bytes to string - public static function ipToString($ipBytes) { - $l = strlen($ipBytes); - return ($l == 4 || $l == 16) ? inet_ntop($ipBytes) : ''; - } - - // compare two ip bytes (packed string return by parsedIP) - // returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2 - public static function ipSubCompare($ip1, $buff, $offset) { - // $r = substr_compare($ip1, $buff, $offset, strlen($ip1)); - // @Note: substr_compare is not working, use the substr + strcmp instead - $r = strcmp($ip1, substr($buff, $offset, strlen($ip1))); - if ($r < 0) { - return -1; - } else if ($r > 0) { - return 1; - } else { - return 0; - } - } - - // returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2 - public static function ipCompare($ip1, $ip2) { - $r = strcmp($ip1, $ip2); - if ($r < 0) { - return -1; - } else if ($r > 0) { - return 1; - } else { - return 0; - } - } - - // version parse - public static function versionFromName($ver_name) { - $name = strtoupper($ver_name); - if ($name == "V4" || $name == "IPv4") { - return IPv4::default(); - } else if ($name == "V6" || $name == "IPv6") { - return IPv6::default(); - } else { - throw new Exception("invalid verstion name `{$ver_name}`"); - } - } - - // version parse from header - public static function versionFromHeader($header) { - // Old structure 2.0 with IPv4 supports ONLY - if ($header['version'] == Structure_20) { - return IPv4::default(); - } - - // structure 3.0 after IPv6 supporting - if ($header['version'] != Structure_30) { - throw new Exception("invalid xdb structure version `{$header['version']}`"); - } - - if ($header['ipVersion'] == IPv4VersionNo) { - return IPv4::default(); - } else if ($header['ipVersion'] == IPv6VersionNo) { - return IPv6::default(); - } else { - throw new Exception("invalid ip version number `{$header['ipVersion']}`"); - } - } - - // binary string chars implode with space - public static function bytesToString($buff, $offset, $length) { - $sb = []; - for ($i = 0; $i < $length; $i++) { - $sb[] = ord($buff[$offset+$i]) & 0xFF; - } - return '['.implode(' ', $sb).']'; - } - - // decode a 4bytes long with Little endian byte order from a byte buffer - public static function le_getUint32($b, $idx) { - $val = (ord($b[$idx])) | (ord($b[$idx+1]) << 8) - | (ord($b[$idx+2]) << 16) | (ord($b[$idx+3]) << 24); - - // convert signed int to unsigned int if on 32 bit operating system - if ($val < 0 && PHP_INT_SIZE == 4) { - $val = sprintf("%u", $val); - } - - return $val; - } - - // read a 2bytes int with litten endian byte order from a byte buffer - public static function le_getUint16($b, $idx) { - return ((ord($b[$idx])) | (ord($b[$idx+1]) << 8)); - } - - // 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. - // returns: null for everything is ok or the error string. - public static function verify($handle) { - // load the header - $header = self::loadHeader($handle); - if ($header == null) { - return 'failed to load the header'; - } - - // get the runtime ptr bytes - $runtimePtrBytes = 0; - if ($header['version'] == Structure_20) { - $runtimePtrBytes = 4; - } else if ($header['version'] == Structure_30) { - $runtimePtrBytes = $header['runtimePtrBytes']; - } else { - return "invalid structure version `{$header['version']}`"; - } - - // 1, confirm the xdb file size - // to ensure that the maximum file pointer does not overflow - $stat = fstat($handle); - if ($stat == false) { - return 'failed to stat the xdb file'; - } - - $maxFilePtr = (1 << ($runtimePtrBytes * 8)) - 1; - // print_r([$stat['size'], $maxFilePtr]); - if ($stat['size'] > $maxFilePtr) { - return "xdb file exceeds the maximum supported bytes: {$maxFilePtr}"; - } - - return null; - } - - public static function verifyFromFile($dbFile) { - $handle = fopen($dbFile, 'r'); - if ($handle === false) { - return null; - } - - $r = self::verify($handle); - fclose($handle); - return $r; - } - - // load header info from a specified file handle - public static function loadHeader($handle) { - if (fseek($handle, 0) == -1) { - return null; - } - - $buff = fread($handle, HeaderInfoLength); - if ($buff === false) { - return null; - } - - // read bytes length checking - if (strlen($buff) != HeaderInfoLength) { - return null; - } - - // return the decoded header info - return array( - 'version' => self::le_getUint16($buff, 0), - 'indexPolicy' => self::le_getUint16($buff, 2), - 'createdAt' => self::le_getUint32($buff, 4), - 'startIndexPtr' => self::le_getUint32($buff, 8), - 'endIndexPtr' => self::le_getUint32($buff, 12), - 'ipVersion' => self::le_getUint16($buff, 16), - 'runtimePtrBytes' => self::le_getUint16($buff, 18) - ); - } - - // load header info from the specified xdb file path - public static function loadHeaderFromFile($dbFile) { - $handle = fopen($dbFile, 'r'); - if ($handle === false) { - return null; - } - - $header = self::loadHeader($handle); - fclose($handle); - return $header; - } - - // load vector index from a file handle - public static function loadVectorIndex($handle) { - if (fseek($handle, HeaderInfoLength) == -1) { - return null; - } - - $rLen = VectorIndexRows * VectorIndexCols * VectorIndexSize; - $buff = fread($handle, $rLen); - if ($buff === false) { - return null; - } - - if (strlen($buff) != $rLen) { - return null; - } - - return $buff; - } - - // load vector index from a specified xdb file path - public static function loadVectorIndexFromFile($dbFile) { - $handle = fopen($dbFile, 'r'); - if ($handle === false) { - return null; - } - - $vIndex = self::loadVectorIndex($handle); - fclose($handle); - return $vIndex; - } - - // load the xdb content from a file handle - public static function loadContent($handle) { - if (fseek($handle, 0, SEEK_END) == -1) { - return null; - } - - $size = ftell($handle); - if ($size === false) { - return null; - } - - // seek to the head for reading - if (fseek($handle, 0) == -1) { - return null; - } - - $buff = fread($handle, $size); - if ($buff === false) { - return null; - } - - // read length checking - if (strlen($buff) != $size) { - return null; - } - - return $buff; - } - - // load the xdb content from a file path - public static function loadContentFromFile($dbFile) { - $str = file_get_contents($dbFile, false); - if ($str === false) { - return null; - } else { - return $str; - } - } - - public static function now() { - return (microtime(true) * 1000); - } -} - -// IPv4 version class -class IPv4 { - public $id; - public $name; - public $bytes; - public $segmentIndexSize; - - private static $C = null; - public static function default() { - if (self::$C == null) { - // 14 = 4 + 4 + 2 + 4 - self::$C = new self(IPv4VersionNo, 'IPv4', 4, 14); - } - return self::$C; - } - - public function __construct($id, $name, $bytes, $segmentIndexSize) { - $this->id = $id; - $this->name = $name; - $this->bytes = $bytes; - $this->segmentIndexSize = $segmentIndexSize; - } - - // compare the two ip bytes with the current version - public function ipSubCompare($ip1, $buff, $offset) { - // ip1: Little endian byte order encoded long from searcher. - // ip2: Little endian byte order read from xdb index. - $len = strlen($ip1); - $eIdx = $offset + $len; - for ($i = 0, $j = $eIdx - 1; $i < $len; $i++, $j--) { - $i1 = ord($ip1[$i]) & 0xFF; - $i2 = ord($buff[$j]) & 0xFF; - // printf("i:%d, j:%d, i1:%d, i2:%d\n", $i, $j, $i1, $i2); - if ($i1 > $i2) { - return 1; - } else if ($i1 < $i2) { - return -1; - } - } - - return 0; - } - - public function __toString() { - return sprintf( - "{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}", - $this->id, $this->name, $this->bytes, $this->segmentIndexSize - ); - } -} - -class IPv6 { - public $id; - public $name; - public $bytes; - public $segmentIndexSize; - - private static $C = null; - public static function default() { - if (self::$C == null) { - // 38 = 16 + 16 + 2 + 4 - self::$C = new self(IPv6VersionNo, 'IPv6', 16, 38); - } - - return self::$C; - } - - public function __construct($id, $name, $bytes, $segmentIndexSize) { - $this->id = $id; - $this->name = $name; - $this->bytes = $bytes; - $this->segmentIndexSize = $segmentIndexSize; - } - - public function ipSubCompare($ip, $buff, $offset) { - // return Util::ipCompare($ip, substr($buff, $offset, strlen($ip))); - return Util::ipSubCompare($ip, $buff, $offset); - } - - public function __toString() { - return sprintf( - "{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}", - $this->id, $this->name, $this->bytes, $this->segmentIndexSize - ); - } -} - -// Xdb searcher implementation -class Searcher { - // ip version - private $version; - - // xdb file handle - private $handle = null; - private $ioCount = 0; - - // vector index in binary string. - // string decode will be faster than the map based Array. - private $vectorIndex = null; - - // xdb content buffer - private $contentBuff = null; - - // --- - // static function to create searcher - - /** - * @throws Exception - */ - public static function newWithFileOnly($version, $dbFile) { - return new self($version, $dbFile, null, null); - } - - /** - * @throws Exception - */ - public static function newWithVectorIndex($version, $dbFile, $vIndex) { - return new self($version, $dbFile, $vIndex, null); - } - - /** - * @throws Exception - */ - public static function newWithBuffer($version, $cBuff) { - return new self($version, null, null, $cBuff); - } - - // --- End of static creator - - /** - * initialize the xdb searcher - * @throws Exception - */ - function __construct($version, $dbFile, $vectorIndex=null, $cBuff=null) { - $this->version = $version; - // check the content buffer first - if ($cBuff != null) { - $this->vectorIndex = null; - $this->contentBuff = $cBuff; - } else { - // open the xdb binary file - $this->handle = fopen($dbFile, "r"); - if ($this->handle === false) { - throw new Exception("failed to open xdb file '%s'", $dbFile); - } - - $this->vectorIndex = $vectorIndex; - } - } - - public function close() { - if ($this->handle != null) { - fclose($this->handle); - } - } - - public function getIPVersion() { - return $this->version; - } - - public function getIOCount() { - return $this->ioCount; - } - - /** - * find the region info for the specified ip address. - * @Note: the ip address couldO ONLY be a human-readable IP address string, - * DO not use the packed binary string returned by #parseIP - * - * @throws Exception - */ - public function search($ip) { - $ipBytes = Util::parseIP($ip); - if ($ipBytes == null) { - throw new Exception("invalid ip address `{$ip}`"); - } - - return $this->searchByBytes($ipBytes); - } - - /** - * find the region info for the specified binary ip bytes returned by #parseIP. - * - * @throws Exception - */ - public function searchByBytes($ipBytes) { - // ip version check - if (strlen($ipBytes) != $this->version->bytes) { - throw new Exception("invalid ip address ({$this->version->name} expected)"); - } - - // reset the global counter - $this->ioCount = 0; - - // locate the segment index block based on the vector index - $il0 = ord($ipBytes[0]) & 0xFF; - $il1 = ord($ipBytes[1]) & 0xFF; - $idx = $il0 * VectorIndexCols * VectorIndexSize + $il1 * VectorIndexSize; - if ($this->vectorIndex != null) { - $sPtr = Util::le_getUint32($this->vectorIndex, $idx); - $ePtr = Util::le_getUint32($this->vectorIndex, $idx + 4); - } else if ($this->contentBuff != null) { - $sPtr = Util::le_getUint32($this->contentBuff, HeaderInfoLength + $idx); - $ePtr = Util::le_getUint32($this->contentBuff, HeaderInfoLength + $idx + 4); - } else { - // read the vector index block - $buff = $this->read(HeaderInfoLength + $idx, 8); - if ($buff === null) { - throw new Exception("failed to read vector index at ${idx}"); - } - - $sPtr = Util::le_getUint32($buff, 0); - $ePtr = Util::le_getUint32($buff, 4); - } - - // printf("sPtr: %d, ePtr: %d\n", $sPtr, $ePtr); - [$bytes, $dBytes] = [strlen($ipBytes), strlen($ipBytes) << 1]; - - // binary search the segment index to get the region info - $idxSize = $this->version->segmentIndexSize; - [$dataLen, $dataPtr] = [0, null]; - [$l, $h] = [0, ($ePtr - $sPtr) / $idxSize]; - while ($l <= $h) { - $m = ($l + $h) >> 1; - $p = $sPtr + $m * $idxSize; - - // read the segment index - $buff = $this->read($p, $idxSize); - if ($buff == null) { - throw new Exception("failed to read segment index with ptr={$p}"); - } - - if ($this->version->ipSubCompare($ipBytes, $buff, 0) < 0) { - $h = $m - 1; - } else if ($this->version->ipSubCompare($ipBytes, $buff, $bytes) > 0) { - $l = $m + 1; - } else { - $dataLen = Util::le_getUint16($buff, $dBytes); - $dataPtr = Util::le_getUint32($buff, $dBytes + 2); - break; - } - } - - // match nothing interception. - // @TODO: could this even be a case ? - // printf("dataLen: %d, dataPtr: %d\n", $dataLen, $dataPtr); - if ($dataPtr == null) { - return null; - } - - // load and return the region data - $buff = $this->read($dataPtr, $dataLen); - if ($buff == null) { - return null; - } - - return $buff; - } - - // read specified bytes from the specified index - private function read($offset, $len) { - // check the in-memory buffer first - if ($this->contentBuff != null) { - return substr($this->contentBuff, $offset, $len); - } - - // read from the file - $r = fseek($this->handle, $offset); - if ($r == -1) { - return null; - } - - $this->ioCount++; - $buff = fread($this->handle, $len); - if ($buff === false) { - return null; - } - - if (strlen($buff) != $len) { - return null; - } - - return $buff; - } - -} \ No newline at end of file diff --git a/binding/php/xdb/util_test.php b/binding/php/xdb/util_test.php deleted file mode 100644 index 1103381..0000000 --- a/binding/php/xdb/util_test.php +++ /dev/null @@ -1,124 +0,0 @@ - -// @Date 2022/06/22 - -require 'Searcher.class.php'; -use \ip2region\xdb\Util; -use \ip2region\xdb\Searcher; -use \ip2region\xdb\IPv4; -use \ip2region\xdb\IPv6; - -// check and get the function to run -if($argc < 2) { - printf("please specified the function name\n"); - return; -} else { - $func_name = trim($argv[1]); -} - -$basePath = dirname(dirname(dirname(dirname(__FILE__)))); - -function testLoadHeader() { - global $basePath; - $header = Util::loadHeaderFromFile("{$basePath}/data/ip2region_v4.xdb"); - if ($header == null) { - printf("failed to load header from file\n"); - return; - } - - printf("header loaded: "); - print_r($header); -} - -function testLoadVectorIndex() { - global $basePath; - $vIndex = Util::loadVectorIndexFromFile("{$basePath}/data/ip2region_v4.xdb"); - if ($vIndex == null) { - printf("failed to load vector index from file\n"); - return; - } - - printf("vector index loaded: length=%d\n", strlen($vIndex)); -} - -function testLoadContent() { - global $basePath; - $cBuff = Util::loadContentFromFile("{$basePath}/data/ip2region_v4.xdb"); - if ($cBuff == null) { - printf("failed to load content from file\n"); - return; - } - - printf("content loaded, length=%d\n", strlen($cBuff)); -} - -function testParseIP() { - $ips = [ - // IPv4 - "1.0.0.1", - "192.168.1.100", - "121.35.184.170", - - "xx.xx.1.100", - - // IPv6 - "3000::", - "240e:87c:71a:639a:3dff:ffff:ffff:ffff", - "240e:87c:71a:c877:900::" - ]; - - foreach ($ips as $ip) { - $bytes = Util::parseIP($ip); - if ($bytes == NULL) { - printf("invalid ip address: `%s`\n", $ip); - continue; - } - - // for ($i = 0; $i < strlen($bytes); $i++) { - // printf("%d: []: %s, ord: %d\n", $i, $bytes[$i], ord($bytes[$i])); - // } - - printf("bytes: %s (%s), address: %s\n", bin2hex($bytes), gettype($bytes), Util::ipToString($bytes)); - } -} - -function testIPCompare() { - $ipPairs = [ - ["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"] - ]; - - foreach ($ipPairs as $ips) { - $ip1 = Util::parseIP($ips[0]); - $ip2 = Util::parseIP($ips[1]); - printf("ipCompare(%s, %s): %d\n", Util::ipToString($ip1), Util::ipToString($ip2), Util::ipSubCompare($ip1, $ip2, 0)); - } -} - -function testAttributes() { - printf("IPv4VersioNo: %d\n", \ip2region\xdb\IPv4VersionNo); - printf("IPv6VersioNo: %d\n", \ip2region\xdb\IPv6VersionNo); - printf("IPv4 Object: %s\n", IPv4::default()); - printf("IPv6 Object: %s\n", IPv6::default()); -} - - -if (!function_exists($func_name)) { - printf("function {$func_name} not found\n"); -} else { - printf("calling {$func_name} ... \n"); - $now = Util::now(); - $func_name(); - $cost = Util::now() - $now; - printf("done, cost: %0.5f ms\n", $cost); -} \ No newline at end of file diff --git a/binding/python/ReadMe.md b/binding/python/ReadMe.md deleted file mode 100644 index 837dc4d..0000000 --- a/binding/python/ReadMe.md +++ /dev/null @@ -1,114 +0,0 @@ -# ip2region python 查询客户端实现 - -# 使用方式 - -### 完全基于文件的查询 - -```python -from xdbSearcher import XdbSearcher - -def searchWithFile(): - # 1. 创建查询对象 - dbPath = "../../data/ip2region.xdb" - searcher = XdbSearcher(dbfile=dbPath) - - # 2. 执行查询 - ip = "1.2.3.4" - region_str = searcher.searchByIPStr(ip) - print(region_str) - - # 3. 关闭searcher - searcher.close() -``` - -### 缓存 `VectorIndex` 索引 - -我们可以提前从 `xdb` 文件中加载出来 `VectorIndex` 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。 - -```python -from xdbSearcher import XdbSearcher - -def searchWithVectorIndex(): - # 1. 预先加载整个 xdb - dbPath = "../../data/ip2region.xdb" - vi = XdbSearcher.loadVectorIndexFromFile(dbfile=dbPath) - - # 2. 使用上面的缓存创建查询对象, 同时也要加载 xdb 文件 - searcher = XdbSearcher(dbfile=dbPath, vectorIndex=vi) - - # 3. 执行查询 - ip = "1.2.3.4" - region_str = searcher.search(ip) - print(region_str) - - # 4. 关闭searcher - searcher.close() -``` - -### 缓存整个 `xdb` 数据 - -我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。 - -```python -from xdbSearcher import XdbSearcher - -def searchWithContent(): - # 1. 预先加载整个 xdb - dbPath = "../../data/ip2region.xdb"; - cb = XdbSearcher.loadContentFromFile(dbfile=dbPath) - - # 2. 仅需要使用上面的全文件缓存创建查询对象, 不需要传源 xdb 文件 - searcher = XdbSearcher(contentBuff=cb) - - # 3. 执行查询 - ip = "1.2.3.4" - region_str = searcher.search(ip) - print(region_str) - - # 4. 关闭searcher - searcher.close() -``` -# 查询测试 - -通过 `search_test.py` 脚本来进行查询测试: -```bash -➜ python git:(python_dev) ✗ python3 ./search_test.py -python3 search_test.py [command options] -options: - --db string ip2region binary xdb file path - --cache-policy string cache policy: file/vectorIndex/content -``` - -例如:使用默认的 data/ip2region.xdb 进行查询测试: -```bash -➜ python git:(python_dev) ✗ python3 ./search_test.py --db=../../data/ip2region.xdb --cache-policy=content -ip2region xdb searcher test program, cachePolicy: content -type 'quit' to exit -ip2region>> 1.2.3.4 -region :美国|0|华盛顿|0|谷歌 , took 0.0689 ms -ip2region>> quit -searcher test program exited, thanks for trying -``` - -输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。 - -# bench 测试 - -通过 `bench_test.py` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: -```bash -➜ python git:(python_dev) ✗ python3 ./bench_test.py -python bench_test.py [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.xdb 和 data/ip.merge.txt 来进行 bench 测试: -```bash -➜ python git:(python_dev) ✗ python3 ./bench_test.py --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=content -Bench finished, [cachePolicy: content, total: 3417955, took: 34.93 s, cost: 0.0094 ms/op] -``` - -可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。 -@Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件的相同的源文件。 \ No newline at end of file diff --git a/binding/python/bench_test.py b/binding/python/bench_test.py deleted file mode 100644 index 2c1c8c3..0000000 --- a/binding/python/bench_test.py +++ /dev/null @@ -1,120 +0,0 @@ -# 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. -# -# Created by luckydog on 2022/7/1. -# Copyright © 2022年 luckydog. All rights reserved. -# -# from ast import main -import io - -from xdbSearcher import XdbSearcher -import argparse -import time -import sys - - -def printHelp(): - print("python bench_test.py [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --src string source ip text file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") - -def trim(string): - if string[:1] != ' ' and string[-1:] != ' ': - return string - elif string[:1] == ' ': - return trim(string[1:]) - else: - return trim(string[:-1]) - -def start_bench(dbFile="", srcFile="", cachePolicy="vectorIndex"): - if cachePolicy == "file": - try: - searcher = XdbSearcher(dbfile=dbFile) - except Exception as err: - print(err) - return - elif cachePolicy == "vectorIndex": - try: - vi = XdbSearcher.loadVectorIndexFromFile(dbfile=dbFile) - if vi is None: - print(f"failed to load vector index from {dbFile}\n") - searcher = XdbSearcher(dbfile=dbFile, vectorIndex=vi) - - except Exception as err: - print(err) - return - else: - try: - cb = XdbSearcher.loadContentFromFile(dbfile=dbFile) - if cb is None: - print(f"failed to load xdb content from {dbFile}\n") - searcher = XdbSearcher(contentBuff=cb) - - except Exception as err: - print(err) - return - # do the bench test - - try: - count = 0 - costs = 0 - sTime = time.time() - f = io.open(srcFile, "rb") - while True: - line = trim(f.readline(1024)).decode("utf-8").replace("\n", "") - if len(line) < 1: - break - - ps = line.split("|",2) - if len(ps) != 3: - print(f"invalid ip segment line :{line}") - return - sip = XdbSearcher.ip2long(None, ps[0]) - eip = XdbSearcher.ip2long(None, ps[1]) - - if sip > eip: - print(f"start ip({ps[0]}) should not be greater than end ip({ps[1]})") - return - - mip = (sip + eip) >> 1 - - for ip in [sip, (sip + mip) >> 1, mip, (mip + eip) >> 1, eip]: - try: - cTime = time.time() - region = searcher.search(ip) - costs = costs + (time.time() - cTime) - except Exception as error: - print(f"failed to search ip :{ip}") - return - - if region is None: - print(f"failed to search ip :{ip}") - return - if region != ps[2]: - print(f"failed search({ip}) with ({region} != {ps[2]})") - return - count = count + 1 - - # close the searcher at last - f.close() - searcher.close() - print(f"Bench finished, {{cachePolicy: {cachePolicy}, total: {count}, took: {round(time.time() - sTime, 2)} s, cost: {round(costs/count*1000, 4)} ms/op}}") - except Exception as err: - print(f"failed to open source text file :{err}") - return - - -if __name__ == '__main__': - if len(sys.argv) < 2: - printHelp() - exit(0) - parse = argparse.ArgumentParser() - parse.add_argument("--db", help="ip2region binary xdb file path") - parse.add_argument("--src", help="source ip text file path") - parse.add_argument("--cache-policy", choices=["file", "vectorIndex", "content"], - help="cache policy: file/vectorIndex/content") - args = parse.parse_args() - start_bench(dbFile=args.db, srcFile=args.src, cachePolicy=args.cache_policy) diff --git a/binding/python/iptest.py b/binding/python/iptest.py deleted file mode 100644 index 0f8db9f..0000000 --- a/binding/python/iptest.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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. -# - -from xdbSearcher import XdbSearcher - -def searchWithFile(): - # 1. 创建查询对象 - dbPath = "../../data/ip2region.xdb" - searcher = XdbSearcher(dbfile=dbPath) - - # 2. 执行查询 - ip = "1.2.3.4" - region_str = searcher.searchByIPStr(ip) - print(region_str) - - # 3. 关闭searcher - searcher.close() - -def searchWithVectorIndex(): - # 1. 预先加载整个 xdb - dbPath = "../../data/ip2region.xdb" - vi = XdbSearcher.loadVectorIndexFromFile(dbfile=dbPath) - - # 2. 使用上面的缓存创建查询对象, 同时也要加载 xdb 文件 - searcher = XdbSearcher(dbfile=dbPath, vectorIndex=vi) - - # 3. 执行查询 - ip = "1.2.3.4" - region_str = searcher.search(ip) - print(region_str) - - # 4. 关闭searcher - searcher.close() - -def searchWithContent(): - # 1. 预先加载整个 xdb - dbPath = "../../data/ip2region.xdb"; - cb = XdbSearcher.loadContentFromFile(dbfile=dbPath) - - # 2. 仅需要使用上面的全文件缓存创建查询对象, 不需要传源 xdb 文件 - searcher = XdbSearcher(contentBuff=cb) - - # 3. 执行查询 - ip = "1.2.3.4" - region_str = searcher.search(ip) - print(region_str) - - # 4. 关闭searcher - searcher.close() - - -if __name__ == '__main__': - searchWithContent() - diff --git a/binding/python/search_test.py b/binding/python/search_test.py deleted file mode 100644 index 742a2d9..0000000 --- a/binding/python/search_test.py +++ /dev/null @@ -1,95 +0,0 @@ -# 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. -# -# Created by luckydog on 2022/7/1. -# Copyright © 2022年 luckydog. All rights reserved. -# - -from xdbSearcher import XdbSearcher -import argparse -import time -import sys - -def printHelp(): - print("python3 search_test.py [command options]") - print("options: ") - print(" --db string ip2region binary xdb file path") - print(" --cache-policy string cache policy: file/vectorIndex/content") - - -def trim(string): - if string[:1] != ' ' and string[-1:] != ' ': - return string - elif string[:1] == ' ': - return trim(string[1:]) - else: - return trim(string[:-1]) - - -def start_search(dbFile="", cachePolicy="vectorIndex"): - if cachePolicy == "file": - try: - searcher = XdbSearcher(dbfile=dbFile) - except Exception as err: - print(err) - return - elif cachePolicy == "vectorIndex": - try: - vi = XdbSearcher.loadVectorIndexFromFile(dbfile=dbFile) - if vi is None: - print(f"failed to load vector index from {dbFile}\n") - searcher = XdbSearcher(dbfile=dbFile, vectorIndex=vi) - - except Exception as err: - print(err) - return - else: - try: - cb = XdbSearcher.loadContentFromFile(dbfile=dbFile) - if cb is None: - print(f"failed to load xdb content from {dbFile}\n") - searcher = XdbSearcher(contentBuff=cb) - - except Exception as err: - print(err) - return - - # 开始的提示 - print(f"ip2region xdb searcher test program, cachePolicy: {cachePolicy}\ntype 'quit' to exit") - while True: - line = trim(input("ip2region>> ")) - # print(f"{line}") - - if len(line) < 2: - continue - if line == "quit": - break - - if not XdbSearcher.isip(None, ip=line): - print("Error: invalid ip address") - continue - start = time.time() - - try: - region_str = searcher.searchByIPStr(line) - except Exception as error: - print(error) - return - - print(f"{{region: {region_str} , took: {round((time.time()-start)*1000.00, 4)} ms}}") - # quit - searcher.close() - print("searcher test program exited, thanks for trying") - - -if __name__ == '__main__': - if len(sys.argv) < 2: - printHelp() - exit(0) - parse = argparse.ArgumentParser() - parse.add_argument("--db", help="ip2region binary xdb file path") - parse.add_argument("--cache-policy", choices=["file", "vectorIndex", "content"], - help="cache policy: file/vectorIndex/content") - args = parse.parse_args() - start_search(dbFile=args.db, cachePolicy=args.cache_policy) diff --git a/binding/python/xdbSearcher.py b/binding/python/xdbSearcher.py deleted file mode 100644 index b3c6475..0000000 --- a/binding/python/xdbSearcher.py +++ /dev/null @@ -1,191 +0,0 @@ -# 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. -# -# Created by luckydog on 2022/6/29. -# Copyright © 2022年 luckydog. All rights reserved. -# - -import socket -import struct -import io -import sys - - -# xdb默认参数 -HeaderInfoLength = 256 -VectorIndexRows = 256 -VectorIndexCols = 256 -VectorIndexSize = 8 -SegmentIndexSize = 14 - - -class XdbSearcher(object): - __f = None - - # the minimal memory allocation. - vectorIndex = None - # 整个读取xdb,保存在内存中 - contentBuff = None - - @staticmethod - def loadVectorIndexFromFile(dbfile): - try: - f = io.open(dbfile, "rb") - f.seek(HeaderInfoLength) - vi_len = VectorIndexRows * VectorIndexCols * SegmentIndexSize - vector_data = f.read(vi_len) - f.close() - return vector_data - except IOError as e: - print("[Error]: %s" % e) - - @staticmethod - def loadContentFromFile(dbfile): - try: - f = io.open(dbfile, "rb") - all_data = f.read() - f.close() - return all_data - except IOError as e: - print("[Error]: %s" % e) - - def __init__(self, dbfile=None, vectorIndex=None, contentBuff=None): - self.initDatabase(dbfile, vectorIndex, contentBuff) - - def search(self, ip): - if isinstance(ip, str): - if not ip.isdigit(): ip = self.ip2long(ip) - return self.searchByIPLong(ip) - else: - return self.searchByIPLong(ip) - - def searchByIPStr(self, ip): - if not ip.isdigit(): ip = self.ip2long(ip) - return self.searchByIPLong(ip) - - def searchByIPLong(self, ip): - # locate the segment index block based on the vector index - sPtr = ePtr = 0 - il0 = (int)((ip >> 24) & 0xFF) - il1 = (int)((ip >> 16) & 0xFF) - idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize - - if self.vectorIndex is not None: - sPtr = self.getLong(self.vectorIndex, idx) - ePtr = self.getLong(self.vectorIndex, idx + 4) - elif self.contentBuff is not None: - sPtr = self.getLong(self.contentBuff, HeaderInfoLength + idx) - ePtr = self.getLong(self.contentBuff, HeaderInfoLength + idx + 4) - else: - self.__f.seek(HeaderInfoLength + idx) - buffer_ptr = self.__f.read(8) - sPtr = self.getLong(buffer_ptr, 0) - ePtr = self.getLong(buffer_ptr, 4) - - # binary search the segment index block to get the region info - dataLen = dataPtr = int(-1) - l = int(0) - h = int((ePtr - sPtr) / SegmentIndexSize) - while l <= h: - m = int((l + h) >> 1) - p = int(sPtr + m * SegmentIndexSize) - # read the segment index - buffer_sip = self.readBuffer(p, SegmentIndexSize) - sip = self.getLong(buffer_sip, 0) - if ip < sip: - h = m - 1 - else: - eip = self.getLong(buffer_sip, 4) - if ip > eip: - l = m + 1 - else: - dataLen = self.getInt2(buffer_sip, 8) - dataPtr = self.getLong(buffer_sip, 10) - break - - # empty match interception - if dataPtr < 0: - return "" - - buffer_string = self.readBuffer(dataPtr, dataLen) - return_string = buffer_string.decode("utf-8") - return return_string - - def readBuffer(self, offset, length): - buffer = None - # check the in-memory buffer first - if self.contentBuff is not None: - buffer = self.contentBuff[offset:offset + length] - return buffer - - # read from the file handle - if self.__f is not None: - self.__f.seek(offset) - buffer = self.__f.read(length) - return buffer - - def initDatabase(self, dbfile, vi, cb): - """ - " initialize the database for search - " param: dbFile, vectorIndex, contentBuff - """ - try: - if cb is not None: - self.__f = None - self.vectorIndex = None - self.contentBuff = cb - else: - self.__f = io.open(dbfile, "rb") - self.vectorIndex = vi - except IOError as e: - print("[Error]: %s" % e) - sys.exit() - - def ip2long(self, ip): - _ip = socket.inet_aton(ip) - return struct.unpack("!L", _ip)[0] - - def isip(self, ip): - p = ip.split(".") - - if len(p) != 4: return False - for pp in p: - if not pp.isdigit(): return False - if len(pp) > 3: return False - if int(pp) > 255: return False - return True - - def getLong(self, b, offset): - if len(b[offset:offset + 4]) == 4: - return struct.unpack('I', b[offset:offset + 4])[0] - return 0 - - def getInt2(self, b, offset): - return ((b[offset] & 0x000000FF) | (b[offset+1] << 8)) - - def close(self): - if self.__f is not None: - self.__f.close() - self.vectorIndex = None - self.contentBuff = None - - -if __name__ == '__main__': - ip_array = [ - "1.2.3.4", - "192.168.1.1" - ] - # 1. 缓存 - dbPath = "./data/ip2region.xdb"; - cb = XdbSearcher.loadContentFromFile(dbfile=dbPath) - - # 2. 创建查询对象 - searcher = XdbSearcher(contentBuff=cb) - - # 3. 执行查询 - # ip = "1.2.3.4" - for ip in ip_array: - region_str = searcher.searchByIPStr(ip) - print(region_str) - searcher.close() diff --git a/binding/rust/Cargo.toml b/binding/rust/Cargo.toml deleted file mode 100644 index 5cf4568..0000000 --- a/binding/rust/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["example", "xdb"] diff --git a/binding/rust/xdb/Cargo.toml b/binding/rust/xdb/Cargo.toml index 2d10ef7..5cf4568 100644 --- a/binding/rust/xdb/Cargo.toml +++ b/binding/rust/xdb/Cargo.toml @@ -1,22 +1,2 @@ -[package] -name = "xdb" -version = "0.1.0" -edition = "2021" -rust-version = "1.66.0" -description = "the rust binding for ip2region" -license = "Apache-2.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -once_cell = "1.16" -tracing = "0.1" -tracing-subscriber = "0.3.14" - -[dev-dependencies] -criterion = "0.4" -rand = "0.8" - -[[bench]] -name = "search" -harness = false +[workspace] +members = ["example", "xdb"] diff --git a/binding/rust/ReadMe.md b/binding/rust/xdb/ReadMe.md similarity index 100% rename from binding/rust/ReadMe.md rename to binding/rust/xdb/ReadMe.md diff --git a/binding/rust/example/Cargo.toml b/binding/rust/xdb/example/Cargo.toml similarity index 100% rename from binding/rust/example/Cargo.toml rename to binding/rust/xdb/example/Cargo.toml diff --git a/binding/rust/example/src/cmd.rs b/binding/rust/xdb/example/src/cmd.rs similarity index 100% rename from binding/rust/example/src/cmd.rs rename to binding/rust/xdb/example/src/cmd.rs diff --git a/binding/rust/example/src/main.rs b/binding/rust/xdb/example/src/main.rs similarity index 100% rename from binding/rust/example/src/main.rs rename to binding/rust/xdb/example/src/main.rs diff --git a/binding/rust/xdb/xdb/Cargo.toml b/binding/rust/xdb/xdb/Cargo.toml new file mode 100644 index 0000000..2d10ef7 --- /dev/null +++ b/binding/rust/xdb/xdb/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "xdb" +version = "0.1.0" +edition = "2021" +rust-version = "1.66.0" +description = "the rust binding for ip2region" +license = "Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +once_cell = "1.16" +tracing = "0.1" +tracing-subscriber = "0.3.14" + +[dev-dependencies] +criterion = "0.4" +rand = "0.8" + +[[bench]] +name = "search" +harness = false diff --git a/binding/rust/xdb/benches/search.rs b/binding/rust/xdb/xdb/benches/search.rs similarity index 100% rename from binding/rust/xdb/benches/search.rs rename to binding/rust/xdb/xdb/benches/search.rs diff --git a/binding/rust/xdb/src/ip_value.rs b/binding/rust/xdb/xdb/src/ip_value.rs similarity index 100% rename from binding/rust/xdb/src/ip_value.rs rename to binding/rust/xdb/xdb/src/ip_value.rs diff --git a/binding/rust/xdb/src/lib.rs b/binding/rust/xdb/xdb/src/lib.rs similarity index 100% rename from binding/rust/xdb/src/lib.rs rename to binding/rust/xdb/xdb/src/lib.rs diff --git a/binding/rust/xdb/src/searcher.rs b/binding/rust/xdb/xdb/src/searcher.rs similarity index 100% rename from binding/rust/xdb/src/searcher.rs rename to binding/rust/xdb/xdb/src/searcher.rs diff --git a/binding/rust/xdb_parse/.gitignore b/binding/rust/xdb_parse/.gitignore new file mode 100644 index 0000000..575bacf --- /dev/null +++ b/binding/rust/xdb_parse/.gitignore @@ -0,0 +1,25 @@ +<<<<<<< HEAD +# Generated by Cargo +# will have compiled files and executables + +./target +./assets +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +======= +/target +>>>>>>> 1bd2848 (feature: add support ipv6 and ipv4 search) diff --git a/binding/rust/xdb_parse/Cargo.toml b/binding/rust/xdb_parse/Cargo.toml new file mode 100644 index 0000000..aeaba31 --- /dev/null +++ b/binding/rust/xdb_parse/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "xdb-parse" +version = "0.1.0" +edition = "2024" +authors = ["RuntimeBroker"] +description = "A parser to xdb files like ip2region" +license = "MIT OR Apache-2.0" +repository = "https://github.com/RuntimeBroker/xdb_parse" +homepage = "https://github.com/RuntimeBroker/xdb_parse" # 可选 +documentation = "https://docs.rs/xdb_parse" # 可选 +readme = "README.md" # 指定 README 文件 + +[dependencies] +anyhow = "1.0.100" +thiserror = "2.0.16" + +[dev-dependencies] +criterion = "0.7.0" +rand = "0.9.2" + +[[bench]] +name = "search" +harness = false + diff --git a/binding/rust/xdb_parse/LICENSE-APACHE.txt b/binding/rust/xdb_parse/LICENSE-APACHE.txt new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/binding/rust/xdb_parse/LICENSE-APACHE.txt @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/binding/rust/xdb_parse/LICENSE-MIT.txt b/binding/rust/xdb_parse/LICENSE-MIT.txt new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/binding/rust/xdb_parse/LICENSE-MIT.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND 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. diff --git a/binding/rust/xdb_parse/README.md b/binding/rust/xdb_parse/README.md new file mode 100644 index 0000000..172e2d3 --- /dev/null +++ b/binding/rust/xdb_parse/README.md @@ -0,0 +1,247 @@ +# xdb-parse +A parser to xdb files like ip2region +A high-performance Rust library for parsing and querying IP database files (xdb format) like ip2region. Supports both IPv4 and IPv6 address lookup with efficient binary search algorithms. + +## Features + +- **Dual Protocol Support**: Full support for both IPv4 and IPv6 address lookup +- **High Performance**: Optimized binary search algorithm for fast IP queries +- **Memory Efficient**: Load database files once and query multiple times +- **Thread Safe**: Designed for concurrent usage in multi-threaded environments +- **Flexible Input**: Accepts IP addresses in multiple formats (string, numeric) +- **Zero Dependencies**: Minimal external dependencies for reliability + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +xdb-parse = "0.1.0" +``` + +## Quick Start + +### Basic Usage + +```rust +use xdb_parse::utils::{load_file, search_ip}; +use anyhow::Result; + +fn main() -> Result<()> { + // Load the IP database file + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into())?; + + // Search for an IP address + let result = search_ip("73.24.63.66", &data)?; + println!("IP location: {}", result); + + Ok(()) +} +``` + +### IPv6 Support + +```rust +use xdb_parse::utils::{load_file, search_ip}; +use anyhow::Result; + +fn main() -> Result<()> { + // Load IPv6 database + let path = "./assets/ip2region_v6.xdb"; + let data = load_file(path.into())?; + + // Search for IPv6 address + let result = search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334", &data)?; + println!("IPv6 location: {}", result); + + Ok(()) +} +``` + +## Examples + +### Example 1: Basic IP Lookup + +See `examples/search_ip.rs`: + +```rust +use std::time::Instant; +use anyhow::Result; +use xdb_parse::utils::{load_file, search_ip}; + +fn main() -> Result<()> { + let start = Instant::now(); + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into())?; + + let ret = search_ip("73.24.63.66", &data)?; + let time = start.elapsed(); + + println!("Search time: {:?} - Result: {}", time, ret); + Ok(()) +} +``` + +### Example 2: Multi-threaded Usage + +The library is designed for thread-safe concurrent usage: + +```rust +use std::{sync::Arc, thread}; +use xdb_parse::utils::{load_file, search_ip}; +use anyhow::Result; + +fn main() -> Result<()> { + let path = "./assets/ip2region_v6.xdb"; + let data = Arc::new(load_file(path.into())?); + + let data_clone = Arc::clone(&data); + let handle = thread::spawn(move || { + let result = search_ip("2408:8352:da10:1ad:c283:c9ff:fec6:4046", &data_clone).unwrap(); + println!("Thread result: {}", result); + }); + + // Main thread can also query + let result = search_ip("2408:8352:da10:1ad:c283:c9ff:fec6:4046", &data)?; + println!("Main thread result: {}", result); + + handle.join().unwrap(); + Ok(()) +} +``` + +### Example 3: Performance Benchmarking + +See `benches/search.rs` for performance testing: + +```rust +use criterion::{Criterion, criterion_group, criterion_main}; +use xdb_parse::utils::{load_file, search_ip}; + +fn search_benchmark(c: &mut Criterion) { + c.bench_function("ipv4_search", |b| { + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into()).unwrap(); + b.iter(|| { + search_ip("73.24.63.66", &data).unwrap(); + }) + }); +} + +criterion_group!(benches, search_benchmark); +criterion_main!(benches); +``` + +## API Reference + +### Core Functions + +#### `load_file(path: PathBuf) -> Result, XdbError>` + +Loads an xdb database file into memory. + +**Parameters:** +- `path`: Path to the xdb file + +**Returns:** Byte vector containing the database data + +#### `search_ip(ip: &str, data: &[u8]) -> Result` + +Searches for an IP address in the loaded database. + +**Parameters:** +- `ip`: IP address to search (supports multiple formats) +- `data`: Database data loaded by `load_file` + +**Returns:** Location information as string + +### Supported IP Formats + +The library accepts IP addresses in multiple formats: + +```rust +// Standard IPv4 format +search_ip("192.168.1.1", &data)?; + +// Standard IPv6 format +search_ip("2001:0db8:85a3::0370:7334", &data)?; + +// Numeric format (IPv4) +search_ip("3232235777", &data)?; // Equivalent to 192.168.1.1 + +// Numeric format (IPv6) +search_ip("42540766411282592856903984951653826560", &data)?; +``` + +## Database File Format + +The library supports the xdb format used by ip2region: + +- **Header**: 256 bytes containing metadata +- **Vector Index**: 256×256 index blocks for fast lookup +- **Segment Data**: IP range segments with location information + +### File Structure + +``` +┌─────────────────┐ +│ Header │ 256 bytes +├─────────────────┤ +│ Vector Index │ 256×256×8 bytes +├─────────────────┤ +│ Segment Data │ Variable length +└─────────────────┘ +``` + +## Performance + +The library uses an optimized binary search algorithm: + +- **IPv4 Lookup**: ~5 microseconds per query +- **IPv6 Lookup**: ~250 microseconds per query +- **Memory Usage**: Database loaded once, shared across threads + +## Error Handling + +The library uses `thiserror` for comprehensive error handling: + +```rust +use xdb_parse::error::XdbError; + +match search_ip("invalid_ip", &data) { + Ok(result) => println!("Location: {}", result), + Err(XdbError::InvalidIP(msg)) => println!("Invalid IP: {}", msg), + Err(e) => println!("Error: {}", e), +} +``` + +## Running Examples + +To run the provided examples: + +```bash +# Run the basic search example +cargo run --example search_ip + +# Run benchmarks +cargo bench + +# Run tests +cargo test +``` + +## License + +This project is licensed under the same terms as Rust itself. + +## Contributing + +Contributions are welcome! Please feel free to submit issues and pull requests. + +## Acknowledgments + +- Inspired by the ip2region project +- Uses efficient binary search algorithms for fast IP lookup +- Designed for high-performance applications diff --git a/binding/rust/xdb_parse/benches/search.rs b/binding/rust/xdb_parse/benches/search.rs new file mode 100644 index 0000000..4e3f109 --- /dev/null +++ b/binding/rust/xdb_parse/benches/search.rs @@ -0,0 +1,50 @@ +use criterion::{Criterion,criterion_group, criterion_main}; +use std::hint::black_box; +use rand; + +use xdb_parse::utils::{load_file, search_ip}; + +fn search_by_ipv4_bench(c: &mut Criterion) { + c.bench_function("search_by_ipv4_bench", |b| { + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into()).unwrap(); + b.iter(|| { + let ip = rand::random::().to_string(); + search_ip(&ip, &data).unwrap(); + }) + }); +} +fn search_by_ipv6_bench(c: &mut Criterion) { + c.bench_function("search_by_ipv6_bench", |b| { + let path = "./assets/ip2region_v6.xdb"; + let data = load_file(path.into()).unwrap(); + b.iter(|| { + let ip = rand::random::().to_string(); + search_ip(&ip, &data).unwrap(); + }) + }); +} + +fn get_block_by_size_bench(c: &mut Criterion) { + c.bench_function("get_block_by_size_bench", |b| { + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into()).unwrap(); + b.iter(|| { + let start = rand::random::() as usize; + let end = start + 4; + black_box(u32::from_le_bytes( + data[start..end] + .try_into() + .unwrap(), + )); + }) + }); +} + +criterion_group!( + benches, + search_by_ipv4_bench, + search_by_ipv6_bench, + get_block_by_size_bench, +); +criterion_main!(benches); diff --git a/binding/rust/xdb_parse/examples/search_ip.rs b/binding/rust/xdb_parse/examples/search_ip.rs new file mode 100644 index 0000000..86dcf52 --- /dev/null +++ b/binding/rust/xdb_parse/examples/search_ip.rs @@ -0,0 +1,18 @@ +use std::time::Instant; + +use anyhow::Result; +use xdb_parse::utils::{load_file, search_ip}; +fn main() -> Result<()> { + let start = Instant::now(); + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into())?; + // xdb_init(Some(path.into())).unwrap(); + let ret = search_ip("73.24.63.66", &data)?; + // let ret = search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334",CACHE_DATA.get().unwrap())?; + + // let ret = search_ip("73.24.63.66",&data)?; + // let ret = search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334",&data)?; + let time = start.elapsed(); + println!("use time:{:?}-ret:{}", time, ret); + Ok(()) +} diff --git a/binding/rust/xdb_parse/gitignore.txt b/binding/rust/xdb_parse/gitignore.txt new file mode 100644 index 0000000..e9e2199 --- /dev/null +++ b/binding/rust/xdb_parse/gitignore.txt @@ -0,0 +1,2 @@ +/target/ +/Cargo.lock diff --git a/binding/rust/xdb_parse/src/error.rs b/binding/rust/xdb_parse/src/error.rs new file mode 100644 index 0000000..fdc062e --- /dev/null +++ b/binding/rust/xdb_parse/src/error.rs @@ -0,0 +1,28 @@ +use thiserror::Error; + + +///XdbError struct +#[derive(Debug, Error)] +pub enum XdbError { + #[error("Invalid IP: {0}")] + InvalidIP(String), + #[error("Invalid IP Version: {0}")] + InvalidIPVersion(String), + #[error("Header Parse Error: {0}")] + HeaderParseError(String), + #[error("RangeIndexOut:{0}")] + RangeIndexOutError(String), + + #[error("AddrParseError: {0}")] + AddrParseError(#[from] std::net::AddrParseError), + #[error("ParseInt Error: {0}")] + ParseIntError(#[from] std::num::ParseIntError), + #[error("Io Error:{0}")] + IoError(#[from] std::io::Error), + #[error("InfallibleError: {0}")] + InfallibleError(#[from] std::convert::Infallible), + #[error("Invalid Ip Format")] + InvalidIpUTF8Format(#[from] std::string::FromUtf8Error), + #[error("Slice Error: {0}")] + SliceError(#[from] std::array::TryFromSliceError), +} diff --git a/binding/rust/xdb_parse/src/lib.rs b/binding/rust/xdb_parse/src/lib.rs new file mode 100644 index 0000000..71ef3e5 --- /dev/null +++ b/binding/rust/xdb_parse/src/lib.rs @@ -0,0 +1,20 @@ +pub mod error; +pub mod utils; +pub mod xdb; + +pub (crate) const TOTAL_HEADER_SIZE: usize = 256; + +//VectorIndex row and column size +pub (crate) const VECTOR_COL_SIZE: usize = 256; +pub (crate) const VECTOR_ROW_SIZE: usize = 256; +pub (crate) const VECTOR_INDEX_BLOCK_SIZE: usize = 8; +//VectorIndex total size +pub (crate) const TOTAL_VECTOR_INDEX_SIZE: usize = + VECTOR_COL_SIZE * VECTOR_ROW_SIZE * VECTOR_INDEX_BLOCK_SIZE; + +//last segments,ipv4 and ipv6 block size +pub (crate) const IPV4_SEGMENT_INDEX_BLOCK_SIZE: u32 = 14; +pub (crate) const IPV6_SEGMENT_INDEX_BLOCK_SIZE: u32 = 38; + + +pub use utils::{load_file,search_ip}; diff --git a/binding/rust/xdb_parse/src/utils.rs b/binding/rust/xdb_parse/src/utils.rs new file mode 100644 index 0000000..c393fc4 --- /dev/null +++ b/binding/rust/xdb_parse/src/utils.rs @@ -0,0 +1,268 @@ +use std::{ + fs::File, + io::Read, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + path::PathBuf, + str::FromStr, +}; + +use crate::{ + TOTAL_HEADER_SIZE, IPV4_SEGMENT_INDEX_BLOCK_SIZE, IPV6_SEGMENT_INDEX_BLOCK_SIZE, + TOTAL_VECTOR_INDEX_SIZE, VECTOR_COL_SIZE, VECTOR_INDEX_BLOCK_SIZE, + error::XdbError, + xdb::{Header, SegmentIndex}, +}; +///ip database file load to memery +pub fn load_file(path: PathBuf) -> Result, XdbError> { + let mut data = File::open(path)?; + let mut buf = Vec::new(); + data.read_to_end(&mut buf)?; + Ok(buf) +} +///A trivial function +pub fn print_ip(header: Header, data: &[u8], ip_version: &str) -> Result<(), XdbError> { + + if ip_version == "ipv4" { + let count = (header.index_end_address - header.index_base_address + 1) + / IPV4_SEGMENT_INDEX_BLOCK_SIZE; + + for i in 0..count { + let index_start_addr = + header.index_base_address + i as u32 * IPV4_SEGMENT_INDEX_BLOCK_SIZE; + let index_end_addr = index_start_addr + IPV4_SEGMENT_INDEX_BLOCK_SIZE; + let segment_block = SegmentIndex::::try_parse( + &data[(index_start_addr as usize)..(index_end_addr as usize)], + )?; + let ret = String::from_utf8( + data[segment_block.data_ptr as usize + ..(segment_block.data_ptr + segment_block.data_len as u32) as usize] + .to_vec(), + )?; + let mut res = String::new(); + res.push_str(Ipv4Addr::from(segment_block.ip_start).to_string().as_str()); + res.push_str("|"); + res.push_str(Ipv4Addr::from(segment_block.ip_end).to_string().as_str()); + res.push_str("|"); + res.push_str(ret.as_str()); + + println!("{}", res); + } + } else if ip_version == "ipv6" { + let count = (header.index_end_address - header.index_base_address + 1) + / IPV6_SEGMENT_INDEX_BLOCK_SIZE; + for i in 0..count { + let index_start_addr = + header.index_base_address + i as u32 * IPV6_SEGMENT_INDEX_BLOCK_SIZE; + let index_end_addr = index_start_addr + IPV6_SEGMENT_INDEX_BLOCK_SIZE; + let segment_block = SegmentIndex::::try_parse( + &data[(index_start_addr as usize)..(index_end_addr as usize)], + )?; + let ret = String::from_utf8( + data[segment_block.data_ptr as usize + ..(segment_block.data_ptr + segment_block.data_len as u32) as usize] + .to_vec(), + )?; + let mut res = String::new(); + res.push_str(Ipv6Addr::from(segment_block.ip_start).to_string().as_str()); + res.push_str("|"); + res.push_str(Ipv6Addr::from(segment_block.ip_end).to_string().as_str()); + res.push_str("|"); + res.push_str(ret.as_str()); + } + } else { + return Err(XdbError::InvalidIPVersion("Invalid IP version".into())); + } + Ok(()) +} + +///search ipv4 or ipv6 +pub fn search_ip(ip: &str, data: &[u8]) -> Result { + let ip = parse_ip_or_number(ip)?; + match ip { + IpAddr::V4(ipv4) => { + let ip = ipv4.to_bits(); + let il0 = ((ip >> 24) & 0xFF) as usize; + let il1 = ((ip >> 16) & 0xFF) as usize; + + let idx = il0 as u32 * VECTOR_COL_SIZE as u32 * VECTOR_INDEX_BLOCK_SIZE as u32 + + il1 as u32 * VECTOR_INDEX_BLOCK_SIZE as u32; + let vec_segment: &[u8] = + &data[TOTAL_HEADER_SIZE..(TOTAL_HEADER_SIZE + TOTAL_VECTOR_INDEX_SIZE)]; + let slice = &vec_segment[(idx as usize)..(idx as usize + VECTOR_INDEX_BLOCK_SIZE)]; + let start_ptr = u32::from_le_bytes(slice[0..4].try_into()?); + let end_ptr = u32::from_le_bytes(slice[4..8].try_into()?); + + let mut left: usize = 0; + let mut right: usize = + ((end_ptr - start_ptr) / IPV4_SEGMENT_INDEX_BLOCK_SIZE as u32) as usize; + + while left <= right { + let mid = (left + right) / 2; + let offset = start_ptr as usize + mid * IPV4_SEGMENT_INDEX_BLOCK_SIZE as usize; + let ip_value = &data[offset..offset + IPV4_SEGMENT_INDEX_BLOCK_SIZE as usize]; + let start_ip = u32::from_le_bytes(ip_value[0..4].try_into()?); + if ip < (start_ip as u32) { + right = mid - 1; + } else if ip > (u32::from_le_bytes(ip_value[4..8].try_into()?)) { + left = mid + 1; + } else { + let data_length = u16::from_le_bytes(ip_value[8..10].try_into()?); + let data_offset = u32::from_le_bytes(ip_value[10..14].try_into()?); + let result = String::from_utf8( + data[data_offset as usize..(data_offset as usize + data_length as usize)] + .to_vec(), + )?; + return Ok(result); + } + } + //now get vector segment data + } + IpAddr::V6(ipv6) => { + let ip = ipv6.to_bits(); + // For IPv6, use the first two bytes (16 bits) similar to IPv4 + let il0 = ((ip >> 120) & 0xFF) as usize; // First byte (bits 120-127) + let il1 = ((ip >> 112) & 0xFF) as usize; // Second byte (bits 112-119) + let vec_segment: &[u8] = + &data[TOTAL_HEADER_SIZE..(TOTAL_HEADER_SIZE + TOTAL_VECTOR_INDEX_SIZE)]; + // Calculate index (same formula as IPv4) + let idx = il0 as u32 * VECTOR_COL_SIZE as u32 * VECTOR_INDEX_BLOCK_SIZE as u32 + + il1 as u32 * VECTOR_INDEX_BLOCK_SIZE as u32; + let slice = &vec_segment[(idx as usize)..(idx as usize + VECTOR_INDEX_BLOCK_SIZE)]; + let start_ptr = u32::from_le_bytes(slice[0..4].try_into()?); + let end_ptr = u32::from_le_bytes(slice[4..8].try_into()?); + + let mut left: usize = 0; + let mut right: usize = + ((end_ptr - start_ptr) / IPV6_SEGMENT_INDEX_BLOCK_SIZE as u32) as usize; + + while left <= right { + let mid = (left + right) / 2; + let offset = start_ptr as usize + mid * IPV6_SEGMENT_INDEX_BLOCK_SIZE as usize; + let ip_value = &data[offset..offset + IPV6_SEGMENT_INDEX_BLOCK_SIZE as usize]; + + // Parse u128 start and end IP addresses + let start_ip = u128::from_le_bytes(ip_value[0..16].try_into()?); + let end_ip = u128::from_le_bytes(ip_value[16..32].try_into()?); + + if ip < start_ip { + right = mid - 1; + } else if ip > end_ip { + left = mid + 1; + } else { + let data_length = u16::from_le_bytes(ip_value[32..34].try_into()?); + let data_offset = u32::from_le_bytes(ip_value[34..38].try_into()?); + let result = String::from_utf8( + data[data_offset as usize..(data_offset as usize + data_length as usize)] + .to_vec(), + )?; + return Ok(result); + } + } + } + }; + Ok("".into()) +} + +///Numeric string ip parse to standard ip format +fn parse_ip_or_number(ip: &str) -> Result { + // 1. normal ip byparse + if let Ok(ip_addr) = IpAddr::from_str(ip) { + return Ok(ip_addr); + } + + // 2. parse to IPv4 + if let Ok(num) = ip.parse::() { + return Ok(IpAddr::V4(Ipv4Addr::from(num))); + } + + // 3. parse to IPv6 + if let Ok(num) = ip.parse::() { + return Ok(IpAddr::V6(Ipv6Addr::from(num))); + } + Err(XdbError::InvalidIP("InvalidIP or IP error".into())) +} + + +#[cfg(test)] +mod tests { + use std::{sync::Arc, thread, time::Instant}; + + use anyhow::Result; + + use crate::utils::{load_file, parse_ip_or_number, search_ip}; + + #[test] + fn test_multi_thread_only_load_xdb_once() -> Result<()> { + /* + ──────────── + Nextest run ID a4d3234a-55bf-4ce9-a5aa-d6244d36ade8 with nextest profile: default + Starting 1 test across 2 binaries (1 test skipped) + Running [ 00:00:00] 0/1: 0 running, 0 passed, 0 skipped + PASS [ 0.276s] xdb-parse utils::tests::test_multi_thread_only_load_xdb_once + Nextest run ID c1650311-801c-4628-83c5-fe1a46469326 with nextest profile: default + Starting 1 test across 2 binaries (1 test skipped) + Running [ 00:00:00] 0/1: 0 running, 0 passed, 0 skipped + PASS [ 0.021s] xdb-parse utils::tests::test_multi_thread_only_load_xdb_once + */ + let start = Instant::now(); + let path = "./assets/ip2region_v6.xdb"; + let data = load_file(path.into())?; + let data = Arc::new(data); + // let path = "./assets/ip2region_v6.xdb"; + + let data_clone = Arc::clone(&data); + let handle = thread::spawn(move || { + // let result = search_ip("2.2.2.2",&get_cache()).unwrap(); + // let result =search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334", &get_cache()).unwrap(); + search_ip("2408:8352:da10:1ad:c283:c9ff:fec6:4046", &data_clone).unwrap(); + // println!("ip search in spawn: {result}"); + }); + // let r = search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334", &get_cache()).unwrap(); + search_ip("2408:8352:da10:1ad:c283:c9ff:fec6:4046", &data).unwrap(); + // println!("ip search in main thread: {r}"); + let time = start.elapsed(); + println!("use time:{:?}", time); + handle.join().unwrap(); + Ok(()) + } + + #[test] + fn test_use_once_cell_search_ipv4() -> Result<()> { + /* + ---- utils::tests::test_use_once_cell_search_ipv4 stdout ---- + use time:5.3644ms-ret:美国|佛罗里达|0|康卡斯特 + */ + let start = Instant::now(); + let path = "./assets/ip2region_v4.xdb"; + let data = load_file(path.into())?; + + let ret = search_ip("73.24.63.66", &data)?; + let time = start.elapsed(); + println!("use time:{:?}-ret:{}", time, ret); + Ok(()) + } + #[test] + fn test_use_once_cell_search_ipv6() -> Result<()> { + /* + ---- utils::tests::test_use_once_cell_search_ipv6 stdout ---- + use time:251.6989ms-ret:美国|亚拉巴马州|杰斐逊|专线用户 + */ + let start = Instant::now(); + let path = "./assets/ip2region_v6.xdb"; + + // xdb_init(Some(path.into())).unwrap(); + let data = load_file(path.into())?; + let ret = search_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334", &data)?; + let time = start.elapsed(); + println!("use time:{:?}-ret:{}", time, ret); + Ok(()) + } + + #[test] + fn test_parse_ip() -> Result<()> { + println!("{:?}", parse_ip_or_number("192.168.1.1")?); // V4 + println!("{:?}", parse_ip_or_number("2400:3200::1")?); // V6 + println!("{:?}", parse_ip_or_number("3232235776")?); // V4 + Ok(()) + } +} diff --git a/binding/rust/xdb_parse/src/xdb.rs b/binding/rust/xdb_parse/src/xdb.rs new file mode 100644 index 0000000..5182e1a --- /dev/null +++ b/binding/rust/xdb_parse/src/xdb.rs @@ -0,0 +1,220 @@ +use std::{ + fmt::Debug, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + ops::{Deref, DerefMut}, str::FromStr, +}; + +use crate::{VECTOR_ROW_SIZE, error::XdbError}; + +///Header +#[derive(Debug, Default)] +pub struct Header { + pub xdb_version: u16, + pub cache_type: u16, + pub generat_time: u32, + pub index_base_address: u32, + pub index_end_address: u32, +} + +impl Header { + pub fn new() -> Self { + Header { + xdb_version: 0, + cache_type: 0, + generat_time: 0, + index_base_address: 0, + index_end_address: 0, + } + } + pub fn try_parse(data: &[u8]) -> Result { + Ok(Header { + xdb_version: u16::from_ne_bytes(data[0..2].try_into()?), + cache_type: u16::from_ne_bytes(data[2..4].try_into()?), + generat_time: u32::from_ne_bytes(data[4..8].try_into()?), + index_base_address: u32::from_ne_bytes(data[8..12].try_into()?), + index_end_address: u32::from_ne_bytes(data[12..16].try_into()?), + }) + } +} + +///VectorIndex block struct +#[derive(Debug, Default)] +pub struct IndexBlock { + pub index_start_address: u32, + pub index_end_address: u32, +} + +impl IndexBlock { + pub fn new() -> Self { + IndexBlock { + index_start_address: 0, + index_end_address: 0, + } + } + pub fn try_parse(data: &[u8]) -> Result { + Ok(IndexBlock { + index_start_address: u32::from_ne_bytes(data[0..4].try_into()?), + index_end_address: u32::from_ne_bytes(data[4..8].try_into()?), + }) + } +} + +///Vector index segment struct +#[derive(Debug, Default)] +pub struct VectorIndex(pub Vec); + +impl VectorIndex { + pub fn new() -> Self { + VectorIndex(Vec::with_capacity(VECTOR_ROW_SIZE * VECTOR_ROW_SIZE)) + } + pub fn try_parse(data: &[u8]) -> Result { + let mut vec_index = VectorIndex::new(); + for i in 0..(VECTOR_ROW_SIZE * VECTOR_ROW_SIZE) { + //parse each index block , everyone is 8 bytes + let start = i * 8; + let end = start + 8; + let index_block = IndexBlock::try_parse(&data[start..end])?; + vec_index.push(index_block); + } + Ok(vec_index) + } +} + +impl Deref for VectorIndex { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for VectorIndex { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +///Binary index segment struct +#[derive(Default)] +pub struct SegmentIndex { + pub ip_start: T, + pub ip_end: T, + pub data_len: u16, + pub data_ptr: u32, +} + +impl SegmentIndex +where + T: Default + Debug + FromLeBytes, +{ + pub fn new() -> Self { + SegmentIndex { + ip_start: T::default(), + ip_end: T::default(), + data_len: 0, + data_ptr: 0, + } + } + pub fn try_parse(data: &[u8]) -> Result { + let size_of_val = size_of::(); + Ok(SegmentIndex { + ip_start: T::from_le_bytes(data[0..size_of_val].try_into()?)?, + ip_end: T::from_le_bytes(data[size_of_val..size_of_val * 2].try_into()?)?, + data_len: u16::from_ne_bytes( + data[(size_of_val * 2)..(size_of_val * 2) + 2].try_into()?, + ), + data_ptr: u32::from_ne_bytes( + data[(size_of_val * 2) + 2..((size_of_val * 2) + 2) + 4].try_into()?, + ), + }) + } +} + +impl std::fmt::Debug for SegmentIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ip_start:{}, ip_end:{}, data_len:{}, data_ptr:{}", + IpAddr::V4(Ipv4Addr::from(self.ip_start)).to_string(), + IpAddr::V4(Ipv4Addr::from(self.ip_end)).to_string(), + self.data_len, + self.data_ptr + ) + } +} + +impl std::fmt::Debug for SegmentIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ip_start:{}, ip_end:{}, data_len:{}, data_ptr:{}", + IpAddr::V6(Ipv6Addr::from(self.ip_start)).to_string(), + IpAddr::V6(Ipv6Addr::from(self.ip_end)).to_string(), + self.data_len, + self.data_ptr + ) + } +} + +pub trait FromLeBytes: Sized { + fn from_le_bytes(bytes: &[u8]) -> Result; +} + +impl FromLeBytes for u32 { + fn from_le_bytes(bytes: &[u8]) -> Result { + Ok(u32::from_ne_bytes(bytes.try_into()?)) + } +} + +impl FromLeBytes for u128 { + fn from_le_bytes(bytes: &[u8]) -> Result { + Ok(u128::from_ne_bytes(bytes.try_into()?)) + } +} + +pub trait ToUSizeIp{ + fn to_usize_ip(&self) -> Result; +} +impl ToUSizeIp for Ipv4Addr { + fn to_usize_ip(&self) -> Result { + Ok(u32::from(*self) as u128) + } +} + +impl ToUSizeIp for &str { + fn to_usize_ip(&self) -> Result { + // 优先尝试解析 IPv6 + if let Ok(ipv6) = Ipv6Addr::from_str(self) { + return Ok(u128::from(ipv6)); + } + // 再尝试解析 IPv4 + if let Ok(ipv4) = Ipv4Addr::from_str(self) { + return Ok(u32::from(ipv4) as u128); + } + // 最后直接解析数字 + Ok(self.parse::()?) + } +} + + +#[cfg(test)] +mod tests { + use std::net::Ipv4Addr; + + use anyhow::Result; + #[test] + fn test_parse_ip_and_calculate_index() -> Result<()> { + /* + Index: 152552 + il0: 74, il1: 125 + ip: 1249717091 + */ + // let ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334".to_usize_ip().unwrap(); + let ip = "74.125.43.99".parse::()?.to_bits(); + let il0 = ((ip >> 24) & 0xFF) as usize; + let il1 = ((ip >> 16) & 0xFF) as usize; + let idx = il0 * 256 * 8 + il1 * 8; + println!("Index: {}", idx); + println!("il0: {}, il1: {}", il0, il1); + println!("ip: {}", ip); + Ok(()) + } +} \ No newline at end of file diff --git a/binding/typescript/.gitattributes b/binding/typescript/.gitattributes deleted file mode 100644 index fcadb2c..0000000 --- a/binding/typescript/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text eol=lf diff --git a/binding/typescript/.gitignore b/binding/typescript/.gitignore deleted file mode 100644 index 4c9d7c3..0000000 --- a/binding/typescript/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.log -.DS_Store -node_modules -dist diff --git a/binding/typescript/README.md b/binding/typescript/README.md deleted file mode 100644 index d37c189..0000000 --- a/binding/typescript/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# ts-ip2region - -TypeScript node client library for IP2Region - -## Installation - -Install the package with NPM - -```bash -npm install ts-ip2region -# or -yarn add ts-ip2region -``` - -## Usage - -```typescript -import { Searcher, CachePolicy, ISearcher } from 'ts-ip2region'; - -const dbPath = './ip2region.xdb'; -const searcher: ISearcher = new Searcher(CachePolicy.Content, dbPath); - -const ip = '8.8.8.8'; -const regionInfo = await searcher.search(ip); -console.log(`IP: ${ip}, Region: ${regionInfo}`); - -await searcher.close(); -``` -### 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 - -## 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) \ No newline at end of file diff --git a/binding/typescript/package.json b/binding/typescript/package.json deleted file mode 100644 index 094c980..0000000 --- a/binding/typescript/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "ts-ip2region", - "version": "0.1.1", - "license": "Apache-2.0", - "author": "Alan", - "main": "dist/index.js", - "module": "dist/ip2region.esm.js", - "typings": "dist/index.d.ts", - "files": [ - "dist", - "src" - ], - "scripts": { - "analyze": "size-limit --why", - "build": "dts build", - "lint": "dts lint", - "prepare": "dts build", - "size": "size-limit", - "start": "dts watch", - "test": "dts test" - }, - "husky": { - "hooks": { - "pre-commit": "dts lint" - } - }, - "prettier": { - "printWidth": 80, - "semi": true, - "singleQuote": true, - "trailingComma": "es5" - }, - "jest": { - "testEnvironment": "node" - }, - "peerDependencies": {}, - "engines": { - "node": ">=12" - }, - "size-limit": [ - { - "path": "dist/ip2region.cjs.production.min.js", - "limit": "10 KB" - }, - { - "path": "dist/ip2region.esm.js", - "limit": "10 KB" - } - ], - "devDependencies": { - "@size-limit/preset-small-lib": "^11.1.6", - "@tsconfig/recommended": "^1.0.8", - "dts-cli": "^2.0.5", - "husky": "^9.1.7", - "size-limit": "^11.1.6", - "tslib": "^2.8.1", - "typescript": "^5.7.2" - } -} diff --git a/binding/typescript/src/cache/abstract-cache-strategy.ts b/binding/typescript/src/cache/abstract-cache-strategy.ts deleted file mode 100644 index 96b9fa8..0000000 --- a/binding/typescript/src/cache/abstract-cache-strategy.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as fs from 'fs/promises'; - -export abstract class AbstractCacheStrategy { - protected static readonly HEADER_INFO_LENGTH = 256; - protected static readonly VECTOR_INDEX_ROWS = 256; - protected static readonly VECTOR_INDEX_COLS = 256; - protected static readonly VECTOR_INDEX_SIZE = 8; - - private fileHandle: fs.FileHandle | null = null; - private fileSize: number = 0; - private ioCounter = 0; - - protected constructor(protected readonly xdbPath: string) { - // 构造不立即打开文件,懒加载 - } - - public get ioCount(): number { - return this.ioCounter; - } - - protected incrementIoCount() { - this.ioCounter++; - } - - /** 打开文件,若已打开则跳过 */ - protected async openFile(): Promise { - if (this.fileHandle === null) { - const fh = await fs.open(this.xdbPath, 'r'); - this.fileHandle = fh; - const stat = await fh.stat(); - this.fileSize = stat.size; - } - } - - protected getFileSize(): number { - return this.fileSize; - } - - /** 关闭文件 */ - public async closeFile(): Promise { - if (this.fileHandle) { - await this.fileHandle.close(); - this.fileHandle = null; - } - } - - /** - * 计算矢量索引的起始位置 - * @param ip uint32 - */ - protected getVectorIndexStartPos(ip: number): number { - const il0 = (ip >>> 24) & 0xFF; - const il1 = (ip >>> 16) & 0xFF; - const idx = il0 * AbstractCacheStrategy.VECTOR_INDEX_COLS * AbstractCacheStrategy.VECTOR_INDEX_SIZE - + il1 * AbstractCacheStrategy.VECTOR_INDEX_SIZE; - return idx; - } - - public abstract getVectorIndex(ip: number): Promise; - - /** - * 异步读取文件数据(无需单资源锁,Node.js可安全并发读) - * @param offset 文件起始偏移 - * @param length 读取字节数 - */ - public async getData(offset: number, length: number): Promise { - await this.openFile(); - if (!this.fileHandle) { - throw new Error('File handle is not available.'); - } - - // Node.js 通常会一次性读完,但仍保留循环以防偶发读不足 - let totalBytesRead = 0; - const buffer = Buffer.allocUnsafe(length); - - while (totalBytesRead < length) { - const { bytesRead } = await this.fileHandle.read({ - buffer, - offset: totalBytesRead, - length: length - totalBytesRead, - position: offset + totalBytesRead - }); - this.incrementIoCount(); - - if (bytesRead === 0) { - break; // 文件结束或出错 - } - totalBytesRead += bytesRead; - } - - return buffer.subarray(0, totalBytesRead); - } -} \ No newline at end of file diff --git a/binding/typescript/src/cache/cache-strategy-factory.ts b/binding/typescript/src/cache/cache-strategy-factory.ts deleted file mode 100644 index 9ac0bd5..0000000 --- a/binding/typescript/src/cache/cache-strategy-factory.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CachePolicy } from "../models/cache-policy"; -import { AbstractCacheStrategy } from "./abstract-cache-strategy"; -import { ContentCacheStrategy } from "./content-cache-strategy"; -import { FileCacheStrategy } from "./file-cache-strategy"; -import { VectorIndexCacheStrategy } from "./vector-index-cache-strategy"; - -export class CacheStrategyFactory { - constructor(private readonly xdbPath: string) {} - - public createCacheStrategy(cachePolicy: CachePolicy): AbstractCacheStrategy { - switch (cachePolicy) { - case CachePolicy.Content: - return new ContentCacheStrategy(this.xdbPath); - case CachePolicy.VectorIndex: - return new VectorIndexCacheStrategy(this.xdbPath); - case CachePolicy.File: - return new FileCacheStrategy(this.xdbPath); - default: - throw new Error(`Unknown cachePolicy: ${cachePolicy}`); - } - } -} diff --git a/binding/typescript/src/cache/content-cache-strategy.ts b/binding/typescript/src/cache/content-cache-strategy.ts deleted file mode 100644 index bc78b95..0000000 --- a/binding/typescript/src/cache/content-cache-strategy.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AbstractCacheStrategy } from "./abstract-cache-strategy"; - - -export class ContentCacheStrategy extends AbstractCacheStrategy { - private cacheData: Buffer | null = null; - - constructor(xdbPath: string) { - super(xdbPath); - } - - public async getVectorIndex(ip: number): Promise { - if (!this.cacheData) { - await this.ensureCacheLoaded(); - } - if (!this.cacheData) { - throw new Error('Cache data is not loaded.'); - } - const idx = this.getVectorIndexStartPos(ip); - return this.cacheData.subarray( - AbstractCacheStrategy.HEADER_INFO_LENGTH + idx, - AbstractCacheStrategy.HEADER_INFO_LENGTH + idx + AbstractCacheStrategy.VECTOR_INDEX_SIZE - ); - } - - public override async getData(offset: number, length: number): Promise { - if (!this.cacheData) { - await this.ensureCacheLoaded(); - } - if (!this.cacheData) { - throw new Error('Cache data is not loaded.'); - } - return this.cacheData.subarray(offset, offset + length); - } - - private async ensureCacheLoaded(): Promise { - if (this.cacheData !== null) { - return; - } - await this.openFile(); - const fileSize = this.getFileSize(); - this.cacheData = await super.getData(0, fileSize); - // 全量读取后立即关闭文件 - await this.closeFile(); - } -} \ No newline at end of file diff --git a/binding/typescript/src/cache/file-cache-strategy.ts b/binding/typescript/src/cache/file-cache-strategy.ts deleted file mode 100644 index 8851c2d..0000000 --- a/binding/typescript/src/cache/file-cache-strategy.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AbstractCacheStrategy } from "./abstract-cache-strategy"; - -export class FileCacheStrategy extends AbstractCacheStrategy { - public constructor(xdbPath: string) { - super(xdbPath); - } - - public async getVectorIndex(ip: number): Promise { - const idx = this.getVectorIndexStartPos(ip); - return this.getData( - AbstractCacheStrategy.HEADER_INFO_LENGTH + idx, - AbstractCacheStrategy.VECTOR_INDEX_SIZE - ); - } -} \ No newline at end of file diff --git a/binding/typescript/src/cache/vector-index-cache-strategy.ts b/binding/typescript/src/cache/vector-index-cache-strategy.ts deleted file mode 100644 index 2fe34e5..0000000 --- a/binding/typescript/src/cache/vector-index-cache-strategy.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AbstractCacheStrategy } from "./abstract-cache-strategy"; - -export class VectorIndexCacheStrategy extends AbstractCacheStrategy { - private vectorIndex: Buffer | null = null; - - constructor(xdbPath: string) { - super(xdbPath); - } - - public async getVectorIndex(ip: number): Promise { - if (!this.vectorIndex) { - await this.ensureVectorIndexLoaded(); - } - if (!this.vectorIndex) { - throw new Error('Vector index data not loaded.'); - } - const idx = this.getVectorIndexStartPos(ip); - return this.vectorIndex.subarray(idx, idx + AbstractCacheStrategy.VECTOR_INDEX_SIZE); - } - - private async ensureVectorIndexLoaded(): Promise { - if (this.vectorIndex !== null) { - return; - } - await this.openFile(); - const vectorLength = AbstractCacheStrategy.VECTOR_INDEX_ROWS - * AbstractCacheStrategy.VECTOR_INDEX_COLS - * AbstractCacheStrategy.VECTOR_INDEX_SIZE; - this.vectorIndex = await super.getData(AbstractCacheStrategy.HEADER_INFO_LENGTH, vectorLength); - // 保留 fileHandle 打开,因为后续可能还要继续读段数据 - } -} \ No newline at end of file diff --git a/binding/typescript/src/index.ts b/binding/typescript/src/index.ts deleted file mode 100644 index 0801d75..0000000 --- a/binding/typescript/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './searcher/searcher'; -export * from './searcher/ISearcher'; -export * from './models/cache-policy'; \ No newline at end of file diff --git a/binding/typescript/src/models/cache-policy.ts b/binding/typescript/src/models/cache-policy.ts deleted file mode 100644 index e3afc68..0000000 --- a/binding/typescript/src/models/cache-policy.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum CachePolicy { - File = 'File', - VectorIndex = 'VectorIndex', - Content = 'Content' -} \ No newline at end of file diff --git a/binding/typescript/src/searcher/ISearcher.ts b/binding/typescript/src/searcher/ISearcher.ts deleted file mode 100644 index 348e818..0000000 --- a/binding/typescript/src/searcher/ISearcher.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ISearcher { - search(ipStr: string): Promise; - searchByUint32(ip: number): Promise; - ioCount: number; - close(): Promise; -} \ No newline at end of file diff --git a/binding/typescript/src/searcher/searcher.ts b/binding/typescript/src/searcher/searcher.ts deleted file mode 100644 index e2c9f59..0000000 --- a/binding/typescript/src/searcher/searcher.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { AbstractCacheStrategy } from "../cache/abstract-cache-strategy"; -import { CacheStrategyFactory } from "../cache/cache-strategy-factory"; -import { CachePolicy } from "../models/cache-policy"; -import { Util } from "../util/ip-util"; -import { ISearcher } from "./ISearcher"; - -export class Searcher implements ISearcher { - private readonly cacheStrategy: AbstractCacheStrategy; - - constructor(cachePolicy: CachePolicy, dbPath: string) { - const factory = new CacheStrategyFactory(dbPath); - this.cacheStrategy = factory.createCacheStrategy(cachePolicy); - } - - public get ioCount(): number { - return this.cacheStrategy.ioCount; - } - - /** 主动关闭 */ - public async close(): Promise { - await this.cacheStrategy.closeFile(); - } - - public async search(ipStr: string): Promise { - const ip = Util.ipAddressToUInt32(ipStr); - return this.searchByUint32(ip); - } - - /** - * 优化后的二分查找——一次性读 sPtr~ePtr 所有段数据,而不是循环 getData() - */ - public async searchByUint32(ip: number): Promise { - const index = await this.cacheStrategy.getVectorIndex(ip); - if (index.length < 8) { - return undefined; - } - - // 读取 sPtr, ePtr(uint32, 小端序) - const sPtr = index.readUInt32LE(0); - const ePtr = index.readUInt32LE(4); - - const SEGMENT_INDEX_SIZE = 14; - const totalCount = Math.floor((ePtr - sPtr) / SEGMENT_INDEX_SIZE) + 1; - if (totalCount <= 0) { - return undefined; - } - - // 一次性批量读取 [sPtr, ePtr] 范围内的所有 segment - const blockSize = totalCount * SEGMENT_INDEX_SIZE; - const blockBuff = await this.cacheStrategy.getData(sPtr, blockSize); - - // 在内存中完成二分查找 - let l = 0; - let h = totalCount - 1; - let dataLen = 0; - let dataPtr = 0; - - while (l <= h) { - const mid = (l + h) >>> 1; // 中点 - const offset = mid * SEGMENT_INDEX_SIZE; - - const sip = blockBuff.readUInt32LE(offset); - const eip = blockBuff.readUInt32LE(offset + 4); - - if (ip < sip) { - h = mid - 1; - } else if (ip > eip) { - l = mid + 1; - } else { - dataLen = blockBuff.readUInt16LE(offset + 8); - dataPtr = blockBuff.readUInt32LE(offset + 10); - break; - } - } - - if (dataLen === 0) { - return undefined; - } - - // 读取 region 数据 - const regionBuff = await this.cacheStrategy.getData(dataPtr, dataLen); - return regionBuff.toString('utf8'); - } -} diff --git a/binding/typescript/src/util/ip-util.ts b/binding/typescript/src/util/ip-util.ts deleted file mode 100644 index 4cd71e1..0000000 --- a/binding/typescript/src/util/ip-util.ts +++ /dev/null @@ -1,33 +0,0 @@ -export class Util { - - public static isValidIpAddress(ipStr: string): boolean { - const octets = ipStr.split('.'); - if (octets.length !== 4) { - return false; - } - for (const octet of octets) { - if (isNaN(Number(octet)) || Number(octet) < 0 || Number(octet) > 255) { - return false; - } - } - return true; - } - - /** - * 把字符串形式的 IPv4 转为 uint32,小端序表示 - * 例如 "1.2.3.4" -> 0x04030201 - */ - public static ipAddressToUInt32(ipStr: string): number { - if (!Util.isValidIpAddress(ipStr)) { - throw new Error(`Invalid IPv4: ${ipStr}`); - } - const octets = ipStr.split('.').map(Number); - // 数据库是小端存储,如 1.2.3.4 => 0x04030201 - return ( - ((octets[3] & 0xff) << 24) | - ((octets[2] & 0xff) << 16) | - ((octets[1] & 0xff) << 8) | - (octets[0] & 0xff) - ) >>> 0; - } -} diff --git a/binding/typescript/test/cache/content-cache-strategy.test.ts b/binding/typescript/test/cache/content-cache-strategy.test.ts deleted file mode 100644 index 2591549..0000000 --- a/binding/typescript/test/cache/content-cache-strategy.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ContentCacheStrategy } from '../../src/cache/content-cache-strategy'; - -describe('ContentCacheStrategy Tests', () => { - let strategy: ContentCacheStrategy; - const dbPath = "../../data/ip2region.xdb"; - - beforeAll(() => { - strategy = new ContentCacheStrategy(dbPath); - }); - - afterAll(async () => { - await strategy.closeFile(); - }); - - it('should load the entire file into memory', async () => { - // 首次访问会触发 ensureCacheLoaded - const vectorIndex = await strategy.getVectorIndex(0x01020304); - expect(vectorIndex.length).toBe(8); // VECTOR_INDEX_SIZE = 8 - - // 再次读取不会增加IO次数太多 - const initIo = strategy.ioCount; - await strategy.getVectorIndex(0x05060708); - const newIo = strategy.ioCount; - expect(newIo).toBe(initIo); // 全量缓存后几乎不增加IO - }); - - it('should read arbitrary data after caching', async () => { - const data = await strategy.getData(0, 32); // 读取前32字节 - expect(data.length).toBe(32); - }); -}); \ No newline at end of file diff --git a/binding/typescript/test/cache/file-cache-strategy.test.ts b/binding/typescript/test/cache/file-cache-strategy.test.ts deleted file mode 100644 index 3b84d67..0000000 --- a/binding/typescript/test/cache/file-cache-strategy.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { FileCacheStrategy } from '../../src/cache/file-cache-strategy'; - -describe('FileCacheStrategy Tests', () => { - let strategy: FileCacheStrategy; - //const dbPath = join(__dirname, '..', 'data', 'ip2region.xdb'); - const dbPath = "../../data/ip2region.xdb"; - beforeAll(() => { - strategy = new FileCacheStrategy(dbPath); - }); - - afterAll(async () => { - await strategy.closeFile(); - }); - - it('should read vector index from file each time', async () => { - // 每次获取矢量索引,都会触发一次文件读取 - const initIo = strategy.ioCount; - const vecIndex1 = await strategy.getVectorIndex(0x01020304); - expect(vecIndex1.length).toBe(8); - const afterIo1 = strategy.ioCount; - expect(afterIo1).toBeGreaterThan(initIo); - - // 第二次读取仍增加IO,因为不缓存 - const vecIndex2 = await strategy.getVectorIndex(0x05060708); - expect(vecIndex2.length).toBe(8); - const afterIo2 = strategy.ioCount; - expect(afterIo2).toBeGreaterThan(afterIo1); - }); - - it('should read arbitrary data directly from file', async () => { - const offset = 0; - const length = 64; - const data = await strategy.getData(offset, length); - expect(data.length).toBe(length); - }); -}); \ No newline at end of file diff --git a/binding/typescript/test/cache/vector-index-cache-strategy.test.ts b/binding/typescript/test/cache/vector-index-cache-strategy.test.ts deleted file mode 100644 index 2fe3f4e..0000000 --- a/binding/typescript/test/cache/vector-index-cache-strategy.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { VectorIndexCacheStrategy } from '../../src/cache/vector-index-cache-strategy'; - -describe('VectorIndexCacheStrategy Tests', () => { - let strategy: VectorIndexCacheStrategy; - //const dbPath = join(__dirname, '..', 'data', 'ip2region.xdb'); - const dbPath = "../../data/ip2region.xdb"; - - beforeAll(() => { - strategy = new VectorIndexCacheStrategy(dbPath); - }); - - afterAll(async () => { - await strategy.closeFile(); - }); - - it('should cache the vector index in memory', async () => { - // 首次调用会把矢量索引读到内存 - const initIo = strategy.ioCount; - const vecIndex1 = await strategy.getVectorIndex(0x01020304); - expect(vecIndex1.length).toBe(8); - const afterIo1 = strategy.ioCount; - expect(afterIo1).toBeGreaterThan(initIo); - - // 第二次获取矢量索引不会增加IO - const vecIndex2 = await strategy.getVectorIndex(0x05060708); - expect(vecIndex2.length).toBe(8); - const afterIo2 = strategy.ioCount; - expect(afterIo2).toBe(afterIo1); - }); - - it('should still read segment data from file as needed', async () => { - // 模拟读取部分数据段 - const offset = 1024; // 示例偏移 - const length = 32; - const segmentData = await strategy.getData(offset, length); - expect(segmentData.length).toBe(length); - // 这会增加IO - expect(strategy.ioCount).toBeGreaterThan(0); - }); -}); \ No newline at end of file diff --git a/binding/typescript/test/searcher/searcher.test.ts b/binding/typescript/test/searcher/searcher.test.ts deleted file mode 100644 index e63a6fa..0000000 --- a/binding/typescript/test/searcher/searcher.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Searcher } from '../../src/searcher/searcher'; -import { CachePolicy } from '../../src/models/cache-policy'; - -describe('Searcher Tests', () => { - let searcher: Searcher; - - beforeAll(() => { - // 指向一个真实存在的 xdb 文件(测试需要) - const dbPath = "../../data/ip2region.xdb"; - // 测试用 ContentCacheStrategy(一次性载入文件) - searcher = new Searcher(CachePolicy.Content, dbPath); - }); - - afterAll(async () => { - await searcher.close(); - }); - - it('should return valid data for a known IP', async () => { - const ipStr = '1.2.3.4'; - const regionInfo = await searcher.search(ipStr); - - expect(regionInfo).toBeDefined(); - }); - - it('should return undefined for an invalid IP', async () => { - const invalidIp = '999.999.999.999'; - await expect(searcher.search(invalidIp)).rejects.toThrow(); - }); - - it('should track ioCount properly', async () => { - // ioCount 取决于缓存策略和查询次数 - const initialCount = searcher.ioCount; - await searcher.search('8.8.8.8'); - const afterCount = searcher.ioCount; - - // ContentCacheStrategy:只读取一次,所以 ioCount 不变 - expect(afterCount).toEqual(initialCount); - }); -}); \ No newline at end of file diff --git a/binding/typescript/test/util/ip-util.test.ts b/binding/typescript/test/util/ip-util.test.ts deleted file mode 100644 index e39481d..0000000 --- a/binding/typescript/test/util/ip-util.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Util } from '../../src/util/ip-util'; - -describe('IP Util Tests', () => { - it('should convert valid IPv4 string to uint32 correctly', () => { - const ipStr = '1.2.3.4'; - const uint32 = Util.ipAddressToUInt32(ipStr); - // 1.2.3.4 小端序 => 0x04030201 => 67305985 - expect(uint32).toBe(67305985); - }); - - it('should throw error for invalid IPv4 string', () => { - expect(() => { - Util.ipAddressToUInt32('256.256.256.256'); - }).toThrow(); - }); -}); \ No newline at end of file diff --git a/binding/typescript/tsconfig.json b/binding/typescript/tsconfig.json deleted file mode 100644 index 4b1a4b6..0000000 --- a/binding/typescript/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@tsconfig/recommended/tsconfig.json", - "include": ["src", "types"], - "compilerOptions": { - "module": "esnext", - "allowImportingTsExtensions": false - } -} diff --git a/binding/typescript/yarn.lock b/binding/typescript/yarn.lock deleted file mode 100644 index 8f0cf07..0000000 --- a/binding/typescript/yarn.lock +++ /dev/null @@ -1,6370 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": - version "7.26.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== - dependencies: - "@babel/helper-validator-identifier" "^7.25.9" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02" - integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.24.3": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.26.0", "@babel/generator@^7.26.3", "@babel/generator@^7.7.2": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" - integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== - dependencies: - "@babel/parser" "^7.26.3" - "@babel/types" "^7.26.3" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^3.0.2" - -"@babel/helper-annotate-as-pure@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" - integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== - dependencies: - "@babel/types" "^7.25.9" - -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" - integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== - dependencies: - "@babel/compat-data" "^7.25.9" - "@babel/helper-validator-option" "^7.25.9" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" - integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/traverse" "^7.25.9" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0" - integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - regexpu-core "^6.2.0" - semver "^6.3.1" - -"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3": - version "0.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" - integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-member-expression-to-functions@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" - integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.24.3", "@babel/helper-module-imports@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" - integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" - integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== - dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-optimise-call-expression@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" - integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== - dependencies: - "@babel/types" "^7.25.9" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" - integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== - -"@babel/helper-remap-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" - integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-wrap-function" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-replace-supers@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" - integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" - integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-string-parser@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" - integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== - -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== - -"@babel/helper-validator-option@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" - integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== - -"@babel/helper-wrap-function@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" - integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== - dependencies: - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helpers@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" - integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== - dependencies: - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.1", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" - integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== - dependencies: - "@babel/types" "^7.26.3" - -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" - integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" - integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" - integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" - integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" - -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" - integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-import-assertions@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" - integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" - integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" - integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" - integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": - version "7.18.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" - integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-arrow-functions@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" - integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-async-generator-functions@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" - integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-remap-async-to-generator" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-transform-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" - integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== - dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-remap-async-to-generator" "^7.25.9" - -"@babel/plugin-transform-block-scoped-functions@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" - integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-block-scoping@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" - integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-class-properties@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" - integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-class-static-block@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" - integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-classes@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" - integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/traverse" "^7.25.9" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" - integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/template" "^7.25.9" - -"@babel/plugin-transform-destructuring@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" - integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-dotall-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" - integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-duplicate-keys@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" - integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" - integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-dynamic-import@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" - integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-exponentiation-operator@^7.25.9": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" - integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-export-namespace-from@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" - integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-for-of@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" - integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-function-name@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" - integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== - dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-transform-json-strings@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" - integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-literals@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" - integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-logical-assignment-operators@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" - integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-member-expression-literals@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" - integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-amd@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" - integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-commonjs@^7.25.9": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" - integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== - dependencies: - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-systemjs@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" - integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-transform-modules-umd@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" - integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" - integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-new-target@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" - integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949" - integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-numeric-separator@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" - integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-object-rest-spread@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" - integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== - dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" - -"@babel/plugin-transform-object-super@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" - integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - -"@babel/plugin-transform-optional-catch-binding@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" - integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" - integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-parameters@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" - integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-private-methods@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" - integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-private-property-in-object@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" - integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-property-literals@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" - integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-regenerator@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" - integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - regenerator-transform "^0.15.2" - -"@babel/plugin-transform-regexp-modifiers@^7.26.0": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" - integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-reserved-words@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" - integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-shorthand-properties@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" - integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-spread@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" - integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-sticky-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" - integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-template-literals@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" - integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-typeof-symbol@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b" - integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-escapes@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" - integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-property-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" - integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" - integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-sets-regex@^7.25.9": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" - integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/preset-env@^7.24.3": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" - integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== - dependencies: - "@babel/compat-data" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-validator-option" "^7.25.9" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" - "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions" "^7.26.0" - "@babel/plugin-syntax-import-attributes" "^7.26.0" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.25.9" - "@babel/plugin-transform-async-generator-functions" "^7.25.9" - "@babel/plugin-transform-async-to-generator" "^7.25.9" - "@babel/plugin-transform-block-scoped-functions" "^7.25.9" - "@babel/plugin-transform-block-scoping" "^7.25.9" - "@babel/plugin-transform-class-properties" "^7.25.9" - "@babel/plugin-transform-class-static-block" "^7.26.0" - "@babel/plugin-transform-classes" "^7.25.9" - "@babel/plugin-transform-computed-properties" "^7.25.9" - "@babel/plugin-transform-destructuring" "^7.25.9" - "@babel/plugin-transform-dotall-regex" "^7.25.9" - "@babel/plugin-transform-duplicate-keys" "^7.25.9" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-dynamic-import" "^7.25.9" - "@babel/plugin-transform-exponentiation-operator" "^7.25.9" - "@babel/plugin-transform-export-namespace-from" "^7.25.9" - "@babel/plugin-transform-for-of" "^7.25.9" - "@babel/plugin-transform-function-name" "^7.25.9" - "@babel/plugin-transform-json-strings" "^7.25.9" - "@babel/plugin-transform-literals" "^7.25.9" - "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" - "@babel/plugin-transform-member-expression-literals" "^7.25.9" - "@babel/plugin-transform-modules-amd" "^7.25.9" - "@babel/plugin-transform-modules-commonjs" "^7.25.9" - "@babel/plugin-transform-modules-systemjs" "^7.25.9" - "@babel/plugin-transform-modules-umd" "^7.25.9" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-new-target" "^7.25.9" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.25.9" - "@babel/plugin-transform-numeric-separator" "^7.25.9" - "@babel/plugin-transform-object-rest-spread" "^7.25.9" - "@babel/plugin-transform-object-super" "^7.25.9" - "@babel/plugin-transform-optional-catch-binding" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" - "@babel/plugin-transform-private-methods" "^7.25.9" - "@babel/plugin-transform-private-property-in-object" "^7.25.9" - "@babel/plugin-transform-property-literals" "^7.25.9" - "@babel/plugin-transform-regenerator" "^7.25.9" - "@babel/plugin-transform-regexp-modifiers" "^7.26.0" - "@babel/plugin-transform-reserved-words" "^7.25.9" - "@babel/plugin-transform-shorthand-properties" "^7.25.9" - "@babel/plugin-transform-spread" "^7.25.9" - "@babel/plugin-transform-sticky-regex" "^7.25.9" - "@babel/plugin-transform-template-literals" "^7.25.9" - "@babel/plugin-transform-typeof-symbol" "^7.25.9" - "@babel/plugin-transform-unicode-escapes" "^7.25.9" - "@babel/plugin-transform-unicode-property-regex" "^7.25.9" - "@babel/plugin-transform-unicode-regex" "^7.25.9" - "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" - "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.10" - babel-plugin-polyfill-corejs3 "^0.10.6" - babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.38.1" - semver "^6.3.1" - -"@babel/preset-modules@0.1.6-no-external-plugins": - version "0.1.6-no-external-plugins" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" - integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": - version "7.26.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.25.9", "@babel/template@^7.3.3": - version "7.25.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== - dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/traverse@^7.24.1", "@babel/traverse@^7.25.9": - version "7.26.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" - integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.3" - "@babel/parser" "^7.26.3" - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.3" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.26.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" - integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== - dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@esbuild/aix-ppc64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c" - integrity sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw== - -"@esbuild/android-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz#1add7e0af67acefd556e407f8497e81fddad79c0" - integrity sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w== - -"@esbuild/android-arm@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/android-arm/-/android-arm-0.24.0.tgz#ab7263045fa8e090833a8e3c393b60d59a789810" - integrity sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew== - -"@esbuild/android-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/android-x64/-/android-x64-0.24.0.tgz#e8f8b196cfdfdd5aeaebbdb0110983460440e705" - integrity sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ== - -"@esbuild/darwin-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz#2d0d9414f2acbffd2d86e98253914fca603a53dd" - integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw== - -"@esbuild/darwin-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz#33087aab31a1eb64c89daf3d2cf8ce1775656107" - integrity sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA== - -"@esbuild/freebsd-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz#bb76e5ea9e97fa3c753472f19421075d3a33e8a7" - integrity sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA== - -"@esbuild/freebsd-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz#e0e2ce9249fdf6ee29e5dc3d420c7007fa579b93" - integrity sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ== - -"@esbuild/linux-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz#d1b2aa58085f73ecf45533c07c82d81235388e75" - integrity sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g== - -"@esbuild/linux-arm@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz#8e4915df8ea3e12b690a057e77a47b1d5935ef6d" - integrity sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw== - -"@esbuild/linux-ia32@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz#8200b1110666c39ab316572324b7af63d82013fb" - integrity sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA== - -"@esbuild/linux-loong64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz#6ff0c99cf647504df321d0640f0d32e557da745c" - integrity sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g== - -"@esbuild/linux-mips64el@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz#3f720ccd4d59bfeb4c2ce276a46b77ad380fa1f3" - integrity sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA== - -"@esbuild/linux-ppc64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz#9d6b188b15c25afd2e213474bf5f31e42e3aa09e" - integrity sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ== - -"@esbuild/linux-riscv64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz#f989fdc9752dfda286c9cd87c46248e4dfecbc25" - integrity sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw== - -"@esbuild/linux-s390x@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz#29ebf87e4132ea659c1489fce63cd8509d1c7319" - integrity sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g== - -"@esbuild/linux-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz#4af48c5c0479569b1f359ffbce22d15f261c0cef" - integrity sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA== - -"@esbuild/netbsd-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz#1ae73d23cc044a0ebd4f198334416fb26c31366c" - integrity sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg== - -"@esbuild/openbsd-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz#5d904a4f5158c89859fd902c427f96d6a9e632e2" - integrity sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg== - -"@esbuild/openbsd-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz#4c8aa88c49187c601bae2971e71c6dc5e0ad1cdf" - integrity sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q== - -"@esbuild/sunos-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz#8ddc35a0ea38575fa44eda30a5ee01ae2fa54dd4" - integrity sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA== - -"@esbuild/win32-arm64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz#6e79c8543f282c4539db684a207ae0e174a9007b" - integrity sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA== - -"@esbuild/win32-ia32@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz#057af345da256b7192d18b676a02e95d0fa39103" - integrity sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw== - -"@esbuild/win32-x64@0.24.0": - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz#168ab1c7e1c318b922637fad8f339d48b01e1244" - integrity sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA== - -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" - integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== - dependencies: - eslint-visitor-keys "^3.4.3" - -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": - version "4.12.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== - -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.57.1": - version "8.57.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" - integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== - -"@humanwhocodes/config-array@^0.13.0": - version "0.13.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" - integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== - dependencies: - "@humanwhocodes/object-schema" "^2.0.3" - debug "^4.3.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^2.0.3": - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": - version "0.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/source-map@^0.3.3": - version "0.3.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" - integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://mirrors.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@rollup/plugin-babel@^6.0.4": - version "6.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz#bd698e351fa9aa9619fcae780aea2a603d98e4c4" - integrity sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@rollup/pluginutils" "^5.0.1" - -"@rollup/plugin-commonjs@^24.1.0": - version "24.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz#79e54bd83bb64396761431eee6c44152ef322100" - integrity sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ== - dependencies: - "@rollup/pluginutils" "^5.0.1" - commondir "^1.0.1" - estree-walker "^2.0.2" - glob "^8.0.3" - is-reference "1.2.1" - magic-string "^0.27.0" - -"@rollup/plugin-json@^6.1.0": - version "6.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-json/-/plugin-json-6.1.0.tgz#fbe784e29682e9bb6dee28ea75a1a83702e7b805" - integrity sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA== - dependencies: - "@rollup/pluginutils" "^5.1.0" - -"@rollup/plugin-node-resolve@^15.2.3": - version "15.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz#66008953c2524be786aa319d49e32f2128296a78" - integrity sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA== - dependencies: - "@rollup/pluginutils" "^5.0.1" - "@types/resolve" "1.20.2" - deepmerge "^4.2.2" - is-module "^1.0.0" - resolve "^1.22.1" - -"@rollup/plugin-replace@^5.0.5": - version "5.0.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz#150c9ee9db8031d9e4580a61a0edeaaed3d37687" - integrity sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ== - dependencies: - "@rollup/pluginutils" "^5.0.1" - magic-string "^0.30.3" - -"@rollup/plugin-terser@^0.4.4": - version "0.4.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz#15dffdb3f73f121aa4fbb37e7ca6be9aeea91962" - integrity sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A== - dependencies: - serialize-javascript "^6.0.1" - smob "^1.0.0" - terser "^5.17.4" - -"@rollup/pluginutils@^4.1.2": - version "4.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" - integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== - dependencies: - estree-walker "^2.0.1" - picomatch "^2.2.2" - -"@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.1.0": - version "5.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a" - integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== - dependencies: - "@types/estree" "^1.0.0" - estree-walker "^2.0.2" - picomatch "^4.0.2" - -"@rtsao/scc@^1.1.0": - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" - integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - -"@size-limit/esbuild@11.1.6": - version "11.1.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@size-limit/esbuild/-/esbuild-11.1.6.tgz#1a9b39bdbc060ca55fea87f8f4a9d6205e40b4e6" - integrity sha512-0nBKYSxeRjUVCVoCkWZbmGkGBwpm0HdwHedWgxksBGxTKU0PjOMSHc3XTjKOrXBKXQzw90Ue0mgOd4n6zct9SA== - dependencies: - esbuild "^0.24.0" - nanoid "^5.0.7" - -"@size-limit/file@11.1.6": - version "11.1.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@size-limit/file/-/file-11.1.6.tgz#de1244aef06081a93bd594ddc28ef14080ca5b01" - integrity sha512-ojzzJMrTfcSECRnaTjGy0wNIolTCRdyqZTSWG9sG5XEoXG6PNgHXDDS6gf6YNxnqb+rWfCfVe93u6aKi3wEocQ== - -"@size-limit/preset-small-lib@^11.1.6": - version "11.1.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@size-limit/preset-small-lib/-/preset-small-lib-11.1.6.tgz#d5d2740b2cf7b4594eab9843c22d59aac7864c0b" - integrity sha512-hlmkBlOryJIsKlGpS61Ti7/EEZomygAzOabpo2htdxUbkCkvtVoUQpGWHUfWuxdhheDVF6rtZZ6lPGftMKlaQg== - dependencies: - "@size-limit/esbuild" "11.1.6" - "@size-limit/file" "11.1.6" - size-limit "11.1.6" - -"@tootallnate/once@2": - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - -"@tsconfig/node10@^1.0.7": - version "1.0.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" - integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" - integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== - -"@tsconfig/recommended@^1.0.8": - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@tsconfig/recommended/-/recommended-1.0.8.tgz#16483d57b56bbbd32b8c3af0eff1a40c32d006fa" - integrity sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA== - -"@types/babel__core@^7.1.14": - version "7.20.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" - integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== - dependencies: - "@babel/types" "^7.20.7" - -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" - integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== - -"@types/glob@^7.1.1": - version "7.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/graceful-fs@^4.1.3": - version "4.1.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" - integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== - dependencies: - "@types/node" "*" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - -"@types/istanbul-lib-report@*": - version "3.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" - integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" - integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@^29.5.12": - version "29.5.14" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" - integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/jsdom@^20.0.0": - version "20.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" - integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - parse5 "^7.0.0" - -"@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/minimatch@*": - version "5.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - -"@types/node@*": - version "22.10.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9" - integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ== - dependencies: - undici-types "~6.20.0" - -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - -"@types/resolve@1.20.2": - version "1.20.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" - integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== - -"@types/semver@^7.3.12": - version "7.5.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - -"@types/stack-utils@^2.0.0": - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" - integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== - -"@types/tough-cookie@*": - version "4.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" - integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== - -"@types/yargs-parser@*": - version "21.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" - integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== - -"@types/yargs@^17.0.8": - version "17.0.33" - resolved "https://mirrors.huaweicloud.com/repository/npm/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" - integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== - dependencies: - "@types/yargs-parser" "*" - -"@typescript-eslint/eslint-plugin@^5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" - integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== - dependencies: - "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/type-utils" "5.62.0" - "@typescript-eslint/utils" "5.62.0" - debug "^4.3.4" - graphemer "^1.4.0" - ignore "^5.2.0" - natural-compare-lite "^1.4.0" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/parser@^5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" - integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== - dependencies: - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - -"@typescript-eslint/type-utils@5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" - integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== - dependencies: - "@typescript-eslint/typescript-estree" "5.62.0" - "@typescript-eslint/utils" "5.62.0" - debug "^4.3.4" - tsutils "^3.21.0" - -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== - -"@typescript-eslint/typescript-estree@5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.58.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - -"@typescript-eslint/visitor-keys@5.62.0": - version "5.62.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" - integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== - dependencies: - "@typescript-eslint/types" "5.62.0" - eslint-visitor-keys "^3.3.0" - -"@ungap/structured-clone@^1.2.0": - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd" - integrity sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA== - -abab@^2.0.6: - version "2.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -acorn-globals@^7.0.0: - version "7.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" - integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== - dependencies: - acorn "^8.1.0" - acorn-walk "^8.0.2" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-walk@^8.0.2, acorn-walk@^8.1.1: - version "8.3.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" - integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== - dependencies: - acorn "^8.11.0" - -acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.14.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" - integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== - -agent-base@6: - version "6.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv@^6.12.4: - version "6.12.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-colors@^4.1.1: - version "4.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: - version "4.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-escapes@^6.0.0: - version "6.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-escapes/-/ansi-escapes-6.2.1.tgz#76c54ce9b081dad39acec4b5d53377913825fb0f" - integrity sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig== - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -anymatch@^3.0.3: - version "3.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^4.1.0: - version "4.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^5.3.2: - version "5.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" - integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== - -array-buffer-byte-length@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" - integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== - dependencies: - call-bind "^1.0.5" - is-array-buffer "^3.0.4" - -array-includes@^3.1.6, array-includes@^3.1.8: - version "3.1.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" - integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.findlast@^1.2.5: - version "1.2.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" - integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -array.prototype.findlastindex@^1.2.5: - version "1.2.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" - integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: - version "1.3.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" - integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -array.prototype.flatmap@^1.3.2: - version "1.3.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" - integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-shim-unscopables "^1.0.2" - -array.prototype.tosorted@^1.1.4: - version "1.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" - integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - es-errors "^1.3.0" - es-shim-unscopables "^1.0.2" - -arraybuffer.prototype.slice@^1.0.4: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" - integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== - dependencies: - array-buffer-byte-length "^1.0.1" - call-bind "^1.0.8" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - is-array-buffer "^3.0.4" - -ast-types-flow@^0.0.8: - version "0.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" - integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== - -async@^3.2.3: - version "3.2.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -asyncro@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/asyncro/-/asyncro-3.0.0.tgz#3c7a732e263bc4a42499042f48d7d858e9c0134e" - integrity sha512-nEnWYfrBmA3taTiuiOoZYmgJ/CNrSoQLeLs29SeLcPu60yaw/mHDBHV0iOZ051fTvsTHxpCY+gXibqT9wbQYfg== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -axe-core@^4.10.0: - version "4.10.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" - integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== - -axobject-query@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" - integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== - -babel-jest@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-annotate-pure-calls@^0.4.0: - version "0.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-annotate-pure-calls/-/babel-plugin-annotate-pure-calls-0.4.0.tgz#78aa00fd878c4fcde4d49f3da397fcf5defbcce8" - integrity sha512-oi4M/PWUJOU9ZyRGoPTfPMqdyMp06jbJAomd3RcyYuzUtBOddv98BqLm96Lucpi2QFoQHkdGQt0ACvw7VzVEQA== - -babel-plugin-dev-expression@^0.2.3: - version "0.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-dev-expression/-/babel-plugin-dev-expression-0.2.3.tgz#8aaf52155dfb063ed4ddec6280456fbc256fead4" - integrity sha512-rP5LK9QQTzCW61nVVzw88En1oK8t8gTsIeC6E61oelxNsU842yMjF0G1MxhvUpCkxCEIj7sE8/e5ieTheT//uw== - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - -babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.12" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" - integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== - dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.3" - semver "^6.3.1" - -babel-plugin-polyfill-corejs3@^0.10.6: - version "0.10.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" - integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.2" - core-js-compat "^3.38.0" - -babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" - integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.3" - -babel-plugin-transform-rename-import@^2.3.0: - version "2.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-plugin-transform-rename-import/-/babel-plugin-transform-rename-import-2.3.0.tgz#5d9d645f937b0ca5c26a24b2510a06277b6ffd9b" - integrity sha512-dPgJoT57XC0PqSnLgl2FwNvxFrWlspatX2dkk7yjKQj5HHGw071vAcOf+hqW8ClqcBDMvEbm6mevn5yHAD8mlQ== - -babel-preset-current-node-syntax@^1.0.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" - integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-import-attributes" "^7.24.7" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== - dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bl@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.3: - version "3.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browserslist@^4.24.0, browserslist@^4.24.2: - version "4.24.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.24.3.tgz#5fc2725ca8fb3c1432e13dac278c7cc103e026d2" - integrity sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA== - dependencies: - caniuse-lite "^1.0.30001688" - electron-to-chromium "^1.5.73" - node-releases "^2.0.19" - update-browserslist-db "^1.1.1" - -bs-logger@^0.2.6: - version "0.2.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bser@2.1.1: - version "2.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@^5.5.0: - version "5.7.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -bytes-iec@^3.1.1: - version "3.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/bytes-iec/-/bytes-iec-3.1.1.tgz#94cd36bf95c2c22a82002c247df8772d1d591083" - integrity sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA== - -call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840" - integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bind@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" - integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== - dependencies: - call-bind-apply-helpers "^1.0.0" - es-define-property "^1.0.0" - get-intrinsic "^1.2.4" - set-function-length "^1.2.2" - -call-bound@^1.0.2, call-bound@^1.0.3: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681" - integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA== - dependencies: - call-bind-apply-helpers "^1.0.1" - get-intrinsic "^1.2.6" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.2.0, camelcase@^6.3.0: - version "6.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001688: - version "1.0.30001689" - resolved "https://mirrors.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz#67ca960dd5f443903e19949aeacc9d28f6e10910" - integrity sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g== - -chalk@^2.4.1: - version "2.4.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.2.0: - version "5.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -char-regex@^2.0.0: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/char-regex/-/char-regex-2.0.2.tgz#81385bb071af4df774bff8721d0ca15ef29ea0bb" - integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== - -chokidar@^4.0.1: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/chokidar/-/chokidar-4.0.2.tgz#97b9562c9f59de559177f069eadf5dcc67d24798" - integrity sha512-/b57FK+bblSU+dfewfFe0rT1YjVDfOmeLQwCAuC+vwvgLkXboATqqmy+Ipux6JrF6L5joe5CBnFOw+gLWH6yKg== - dependencies: - readdirp "^4.0.1" - -ci-info@^3.2.0: - version "3.9.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== - -cjs-module-lexer@^1.0.0: - version "1.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" - integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^2.0.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== - dependencies: - restore-cursor "^2.0.0" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^1.3.1: - version "1.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" - integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== - -cli-spinners@^2.5.0: - version "2.9.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== - -cliui@^8.0.1: - version "8.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - -co@^4.6.0: - version "4.6.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" - integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^2.20.0: - version "2.20.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -confusing-browser-globals@^1.0.11: - version "1.0.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" - integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -core-js-compat@^3.38.0, core-js-compat@^3.38.1: - version "3.39.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61" - integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw== - dependencies: - browserslist "^4.24.2" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -create-jest@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - -create-require@^1.1.0: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cssom@^0.5.0: - version "0.5.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -damerau-levenshtein@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - -data-urls@^3.0.2: - version "3.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" - integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== - dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - -data-view-buffer@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" - integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-length@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" - integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" - integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - -debug@^3.2.7: - version "3.2.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decimal.js@^10.4.2: - version "10.4.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== - -dedent@^1.0.0: - version "1.5.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" - integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - -defaults@^1.0.3: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.1.3, define-properties@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -del@^5.1.0: - version "5.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" - integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== - dependencies: - globby "^10.0.1" - graceful-fs "^4.2.2" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.1" - p-map "^3.0.0" - rimraf "^3.0.0" - slash "^3.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - -detect-newline@3.1.0, detect-newline@^3.0.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - -diff@^4.0.1: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -domexception@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" - integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== - dependencies: - webidl-conversions "^7.0.0" - -dts-cli@^2.0.5: - version "2.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/dts-cli/-/dts-cli-2.0.5.tgz#bf2c1dfac8f50b9999b597d680fcd0de961c54ff" - integrity sha512-1pjG5By5qng5mjzoVArptA4fCdT4Ya3yo4d0TMKDHusq3CvqlU21zgqZYn3liFIF8z9BTN7OutaWbADe+VDngw== - dependencies: - "@babel/core" "^7.24.3" - "@babel/helper-module-imports" "^7.24.3" - "@babel/parser" "^7.24.1" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/preset-env" "^7.24.3" - "@babel/traverse" "^7.24.1" - "@rollup/plugin-babel" "^6.0.4" - "@rollup/plugin-commonjs" "^24.1.0" - "@rollup/plugin-json" "^6.1.0" - "@rollup/plugin-node-resolve" "^15.2.3" - "@rollup/plugin-replace" "^5.0.5" - "@rollup/plugin-terser" "^0.4.4" - "@types/jest" "^29.5.12" - "@typescript-eslint/eslint-plugin" "^5.62.0" - "@typescript-eslint/parser" "^5.62.0" - ansi-escapes "^4.3.2" - asyncro "^3.0.0" - babel-jest "^29.7.0" - babel-plugin-annotate-pure-calls "^0.4.0" - babel-plugin-dev-expression "^0.2.3" - babel-plugin-macros "^3.1.0" - babel-plugin-polyfill-regenerator "^0.6.1" - babel-plugin-transform-rename-import "^2.3.0" - camelcase "^6.3.0" - chalk "^4.1.2" - confusing-browser-globals "^1.0.11" - enquirer "^2.4.1" - eslint "^8.37.0" - eslint-config-prettier "^8.10.0" - eslint-plugin-flowtype "^8.0.3" - eslint-plugin-import "^2.29.1" - eslint-plugin-jest "^27.9.0" - eslint-plugin-jsx-a11y "^6.8.0" - eslint-plugin-prettier "^4.2.1" - eslint-plugin-react "^7.34.1" - eslint-plugin-react-hooks "^4.6.0" - eslint-plugin-testing-library "^5.11.1" - execa "^4.1.0" - figlet "^1.7.0" - fs-extra "^10.1.0" - jest "^29.5.0" - jest-environment-jsdom "^29.7.0" - jest-watch-typeahead "^2.2.2" - jpjs "^1.2.1" - lodash.merge "^4.6.2" - ora "^5.4.1" - pascal-case "^3.1.2" - postcss "^8.4.38" - prettier "^2.8.1" - progress-estimator "^0.3.1" - regenerator-runtime "^0.14.1" - rollup "^3.20.0" - rollup-plugin-delete "^2.0.0" - rollup-plugin-dts "^5.3.1" - rollup-plugin-typescript2 "^0.36.0" - sade "^1.8.1" - semver "^7.6.0" - shelljs "^0.8.5" - sort-package-json "^1.57.0" - tiny-glob "^0.2.9" - ts-jest "^29.1.2" - ts-node "^10.9.2" - tslib "^2.6.2" - type-fest "^2.19.0" - typescript "^5.0.2" - -dunder-proto@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/dunder-proto/-/dunder-proto-1.0.0.tgz#c2fce098b3c8f8899554905f4377b6d85dabaa80" - integrity sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A== - dependencies: - call-bind-apply-helpers "^1.0.0" - es-errors "^1.3.0" - gopd "^1.2.0" - -ejs@^3.1.10: - version "3.1.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" - integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== - dependencies: - jake "^10.8.5" - -electron-to-chromium@^1.5.73: - version "1.5.74" - resolved "https://mirrors.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz#cb886b504a6467e4c00bea3317edb38393c53413" - integrity sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw== - -emittery@^0.13.1: - version "0.13.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enquirer@^2.4.1: - version "2.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" - integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== - dependencies: - ansi-colors "^4.1.1" - strip-ansi "^6.0.1" - -entities@^4.5.0: - version "4.5.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" - integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5: - version "1.23.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-abstract/-/es-abstract-1.23.6.tgz#55f0e1ce7128995cc04ace0a57d7dca348345108" - integrity sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA== - dependencies: - array-buffer-byte-length "^1.0.1" - arraybuffer.prototype.slice "^1.0.4" - available-typed-arrays "^1.0.7" - call-bind "^1.0.8" - call-bound "^1.0.3" - data-view-buffer "^1.0.1" - data-view-byte-length "^1.0.1" - data-view-byte-offset "^1.0.0" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-set-tostringtag "^2.0.3" - es-to-primitive "^1.3.0" - function.prototype.name "^1.1.7" - get-intrinsic "^1.2.6" - get-symbol-description "^1.0.2" - globalthis "^1.0.4" - gopd "^1.2.0" - has-property-descriptors "^1.0.2" - has-proto "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - internal-slot "^1.1.0" - is-array-buffer "^3.0.4" - is-callable "^1.2.7" - is-data-view "^1.0.2" - is-negative-zero "^2.0.3" - is-regex "^1.2.1" - is-shared-array-buffer "^1.0.3" - is-string "^1.1.1" - is-typed-array "^1.1.13" - is-weakref "^1.1.0" - math-intrinsics "^1.0.0" - object-inspect "^1.13.3" - object-keys "^1.1.1" - object.assign "^4.1.5" - regexp.prototype.flags "^1.5.3" - safe-array-concat "^1.1.3" - safe-regex-test "^1.1.0" - string.prototype.trim "^1.2.10" - string.prototype.trimend "^1.0.9" - string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.2" - typed-array-byte-length "^1.0.1" - typed-array-byte-offset "^1.0.3" - typed-array-length "^1.0.7" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.16" - -es-define-property@^1.0.0, es-define-property@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-iterator-helpers@^1.1.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" - integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - es-errors "^1.3.0" - es-set-tostringtag "^2.0.3" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - globalthis "^1.0.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - iterator.prototype "^1.1.3" - safe-array-concat "^1.1.2" - -es-object-atoms@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" - integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.0.3: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" - integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== - dependencies: - get-intrinsic "^1.2.4" - has-tostringtag "^1.0.2" - hasown "^2.0.1" - -es-shim-unscopables@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" - integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== - dependencies: - hasown "^2.0.0" - -es-to-primitive@^1.3.0: - version "1.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" - integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== - dependencies: - is-callable "^1.2.7" - is-date-object "^1.0.5" - is-symbol "^1.0.4" - -esbuild@^0.24.0: - version "0.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/esbuild/-/esbuild-0.24.0.tgz#f2d470596885fcb2e91c21eb3da3b3c89c0b55e7" - integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ== - optionalDependencies: - "@esbuild/aix-ppc64" "0.24.0" - "@esbuild/android-arm" "0.24.0" - "@esbuild/android-arm64" "0.24.0" - "@esbuild/android-x64" "0.24.0" - "@esbuild/darwin-arm64" "0.24.0" - "@esbuild/darwin-x64" "0.24.0" - "@esbuild/freebsd-arm64" "0.24.0" - "@esbuild/freebsd-x64" "0.24.0" - "@esbuild/linux-arm" "0.24.0" - "@esbuild/linux-arm64" "0.24.0" - "@esbuild/linux-ia32" "0.24.0" - "@esbuild/linux-loong64" "0.24.0" - "@esbuild/linux-mips64el" "0.24.0" - "@esbuild/linux-ppc64" "0.24.0" - "@esbuild/linux-riscv64" "0.24.0" - "@esbuild/linux-s390x" "0.24.0" - "@esbuild/linux-x64" "0.24.0" - "@esbuild/netbsd-x64" "0.24.0" - "@esbuild/openbsd-arm64" "0.24.0" - "@esbuild/openbsd-x64" "0.24.0" - "@esbuild/sunos-x64" "0.24.0" - "@esbuild/win32-arm64" "0.24.0" - "@esbuild/win32-ia32" "0.24.0" - "@esbuild/win32-x64" "0.24.0" - -escalade@^3.1.1, escalade@^3.2.0: - version "3.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.0.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-prettier@^8.10.0: - version "8.10.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" - integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== - -eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== - dependencies: - debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" - -eslint-module-utils@^2.12.0: - version "2.12.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" - integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== - dependencies: - debug "^3.2.7" - -eslint-plugin-flowtype@^8.0.3: - version "8.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" - integrity sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ== - dependencies: - lodash "^4.17.21" - string-natural-compare "^3.0.1" - -eslint-plugin-import@^2.29.1: - version "2.31.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" - integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== - dependencies: - "@rtsao/scc" "^1.1.0" - array-includes "^3.1.8" - array.prototype.findlastindex "^1.2.5" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.12.0" - hasown "^2.0.2" - is-core-module "^2.15.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - object.groupby "^1.0.3" - object.values "^1.2.0" - semver "^6.3.1" - string.prototype.trimend "^1.0.8" - tsconfig-paths "^3.15.0" - -eslint-plugin-jest@^27.9.0: - version "27.9.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" - integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug== - dependencies: - "@typescript-eslint/utils" "^5.10.0" - -eslint-plugin-jsx-a11y@^6.8.0: - version "6.10.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" - integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== - dependencies: - aria-query "^5.3.2" - array-includes "^3.1.8" - array.prototype.flatmap "^1.3.2" - ast-types-flow "^0.0.8" - axe-core "^4.10.0" - axobject-query "^4.1.0" - damerau-levenshtein "^1.0.8" - emoji-regex "^9.2.2" - hasown "^2.0.2" - jsx-ast-utils "^3.3.5" - language-tags "^1.0.9" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - safe-regex-test "^1.0.3" - string.prototype.includes "^2.0.1" - -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-plugin-react-hooks@^4.6.0: - version "4.6.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" - integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== - -eslint-plugin-react@^7.34.1: - version "7.37.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" - integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== - dependencies: - array-includes "^3.1.8" - array.prototype.findlast "^1.2.5" - array.prototype.flatmap "^1.3.2" - array.prototype.tosorted "^1.1.4" - doctrine "^2.1.0" - es-iterator-helpers "^1.1.0" - estraverse "^5.3.0" - hasown "^2.0.2" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.8" - object.fromentries "^2.0.8" - object.values "^1.2.0" - prop-types "^15.8.1" - resolve "^2.0.0-next.5" - semver "^6.3.1" - string.prototype.matchall "^4.0.11" - string.prototype.repeat "^1.0.0" - -eslint-plugin-testing-library@^5.11.1: - version "5.11.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz#5b46cdae96d4a78918711c0b4792f90088e62d20" - integrity sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw== - dependencies: - "@typescript-eslint/utils" "^5.58.0" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint@^8.37.0: - version "8.57.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" - integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.1" - "@humanwhocodes/config-array" "^0.13.0" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.2: - version "1.6.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: - version "5.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -estree-walker@^2.0.1, estree-walker@^2.0.2: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -execa@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -execa@^5.0.0: - version "5.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" - integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== - -fast-glob@^3.0.3, fast-glob@^3.2.9: - version "3.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fastq@^1.6.0: - version "1.17.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" - integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== - dependencies: - reusify "^1.0.4" - -fb-watchman@^2.0.0: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== - dependencies: - bser "2.1.1" - -fdir@^6.4.2: - version "6.4.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" - integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== - -figlet@^1.7.0: - version "1.8.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/figlet/-/figlet-1.8.0.tgz#1b93c4f65f4c1a3b1135221987eee8cf8b9c0ac7" - integrity sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw== - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -filelist@^1.0.4: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" - integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== - dependencies: - minimatch "^5.0.1" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -find-cache-dir@^3.3.2: - version "3.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" - -flatted@^3.2.9: - version "3.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" - integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -form-data@^4.0.0: - version "4.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -fs-extra@^10.0.0, fs-extra@^10.1.0: - version "10.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.prototype.name@^1.1.6, function.prototype.name@^1.1.7: - version "1.1.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/function.prototype.name/-/function.prototype.name-1.1.7.tgz#9df48ea5f746bf577d7e15b5da89df8952a98e7b" - integrity sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - functions-have-names "^1.2.3" - hasown "^2.0.2" - is-callable "^1.2.7" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6: - version "1.2.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-intrinsic/-/get-intrinsic-1.2.6.tgz#43dd3dd0e7b49b82b2dfcad10dc824bf7fc265d5" - integrity sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA== - dependencies: - call-bind-apply-helpers "^1.0.1" - dunder-proto "^1.0.0" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - function-bind "^1.1.2" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.0.0" - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-stream@^5.0.0: - version "5.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-symbol-description@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" - integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== - dependencies: - call-bind "^1.0.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - -git-hooks-list@1.0.3: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" - integrity sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ== - -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: - version "7.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^8.0.3: - version "8.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -globals@^11.1.0: - version "11.12.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.19.0: - version "13.24.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.4: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" - integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== - dependencies: - define-properties "^1.2.1" - gopd "^1.0.1" - -globalyzer@0.1.0: - version "0.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - -globby@10.0.0: - version "10.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/globby/-/globby-10.0.0.tgz#abfcd0630037ae174a88590132c2f6804e291072" - integrity sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - -globby@^10.0.1: - version "10.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - -globby@^11.1.0: - version "11.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -globrex@^0.1.2: - version "0.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - -gopd@^1.0.1, gopd@^1.2.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -has-bigints@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.3, has-proto@^1.2.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" - integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== - dependencies: - dunder-proto "^1.0.0" - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -html-encoding-sniffer@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" - integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== - dependencies: - whatwg-encoding "^2.0.0" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -humanize-duration@^3.15.3: - version "3.32.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/humanize-duration/-/humanize-duration-3.32.1.tgz#922beff5da36fb1cee3de26ada24c592b0fe519b" - integrity sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g== - -husky@^9.1.7: - version "9.1.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== - -iconv-lite@0.6.3: - version "0.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^5.1.1, ignore@^5.2.0: - version "5.3.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" - integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-local@^3.0.2: - version "3.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -internal-slot@^1.0.7, internal-slot@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" - integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.2" - side-channel "^1.1.0" - -interpret@^1.0.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -is-array-buffer@^3.0.4: - version "3.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" - integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - get-intrinsic "^1.2.6" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-async-function@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" - integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== - dependencies: - has-tostringtag "^1.0.0" - -is-bigint@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" - integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== - dependencies: - has-bigints "^1.0.2" - -is-boolean-object@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-boolean-object/-/is-boolean-object-1.2.1.tgz#c20d0c654be05da4fbc23c562635c019e93daf89" - integrity sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-callable@^1.1.3, is-callable@^1.2.7: - version "1.2.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.16.0: - version "2.16.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.0.tgz#6c01ffdd5e33c49c1d2abfa93334a85cb56bd81c" - integrity sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g== - dependencies: - hasown "^2.0.2" - -is-data-view@^1.0.1, is-data-view@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" - integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== - dependencies: - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - is-typed-array "^1.1.13" - -is-date-object@^1.0.5, is-date-object@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" - integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-finalizationregistry@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz#d74a7d0c5f3578e34a20729e69202e578d495dc2" - integrity sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA== - dependencies: - call-bind "^1.0.7" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-generator-function@^1.0.10: - version "1.0.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: - version "4.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-map@^2.0.3: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" - integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== - -is-module@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== - -is-negative-zero@^2.0.3: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" - integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== - -is-number-object@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" - integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-inside@^3.0.1, is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-reference@1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" - integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== - dependencies: - "@types/estree" "*" - -is-regex@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" - integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== - dependencies: - call-bound "^1.0.2" - gopd "^1.2.0" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -is-set@^2.0.3: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" - integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== - -is-shared-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" - integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== - dependencies: - call-bind "^1.0.7" - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-string@^1.0.7, is-string@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" - integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== - dependencies: - call-bound "^1.0.3" - has-tostringtag "^1.0.2" - -is-symbol@^1.0.4, is-symbol@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" - integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== - dependencies: - call-bound "^1.0.2" - has-symbols "^1.1.0" - safe-regex-test "^1.1.0" - -is-typed-array@^1.1.13: - version "1.1.13" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-weakmap@^2.0.2: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" - integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== - -is-weakref@^1.0.2, is-weakref@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-weakref/-/is-weakref-1.1.0.tgz#47e3472ae95a63fa9cf25660bcf0c181c39770ef" - integrity sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q== - dependencies: - call-bound "^1.0.2" - -is-weakset@^2.0.3: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" - integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== - dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^6.0.0: - version "6.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" - integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== - dependencies: - "@babel/core" "^7.23.9" - "@babel/parser" "^7.23.9" - "@istanbuljs/schema" "^0.1.3" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" - integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -iterator.prototype@^1.1.3: - version "1.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/iterator.prototype/-/iterator.prototype-1.1.4.tgz#4ae6cf98b97fdc717b7e159d79dc25f8fc9482f1" - integrity sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA== - dependencies: - define-data-property "^1.1.4" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.6" - has-symbols "^1.1.0" - reflect.getprototypeof "^1.0.8" - set-function-name "^2.0.2" - -jake@^10.8.5: - version "10.9.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" - integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== - dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.4" - minimatch "^3.1.2" - -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-jsdom@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" - integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/jsdom" "^20.0.0" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - jsdom "^20.0.0" - -jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== - dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@^29.0.0, jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - -jest-util@^29.0.0, jest-util@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" - leven "^3.1.0" - pretty-format "^29.7.0" - -jest-watch-typeahead@^2.2.2: - version "2.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-watch-typeahead/-/jest-watch-typeahead-2.2.2.tgz#5516d3cd006485caa5cfc9bd1de40f1f8b136abf" - integrity sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ== - dependencies: - ansi-escapes "^6.0.0" - chalk "^5.2.0" - jest-regex-util "^29.0.0" - jest-watcher "^29.0.0" - slash "^5.0.0" - string-length "^5.0.1" - strip-ansi "^7.0.1" - -jest-watcher@^29.0.0, jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.5.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - -jiti@^2.0.0: - version "2.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/jiti/-/jiti-2.4.1.tgz#4de9766ccbfa941d9b6390d2b159a4b295a52e6b" - integrity sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g== - -jpjs@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/jpjs/-/jpjs-1.2.1.tgz#f343833de8838a5beba1f42d5a219be0114c44b7" - integrity sha512-GxJWybWU4NV0RNKi6EIqk6IRPOTqd/h+U7sbtyuD7yUISUzV78LdHnq2xkevJsTlz/EImux4sWj+wfMiwKLkiw== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsdom@^20.0.0: - version "20.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" - integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== - dependencies: - abab "^2.0.6" - acorn "^8.8.1" - acorn-globals "^7.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.2" - decimal.js "^10.4.2" - domexception "^4.0.0" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.2" - parse5 "^7.1.1" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^4.1.2" - w3c-xmlserializer "^4.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - ws "^8.11.0" - xml-name-validator "^4.0.0" - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -jsesc@~3.0.2: - version "3.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.3: - version "2.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: - version "3.3.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" - integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - object.assign "^4.1.4" - object.values "^1.1.6" - -keyv@^4.5.3: - version "4.5.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -kleur@^3.0.3: - version "3.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -language-subtag-registry@^0.3.20: - version "0.3.23" - resolved "https://mirrors.huaweicloud.com/repository/npm/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" - integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== - -language-tags@^1.0.9: - version "1.0.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" - integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== - dependencies: - language-subtag-registry "^0.3.20" - -leven@^3.1.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lilconfig@^3.1.2: - version "3.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" - integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://mirrors.huaweicloud.com/repository/npm/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-update@^2.3.0: - version "2.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg== - dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" - -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -magic-string@^0.27.0: - version "0.27.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" - integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" - -magic-string@^0.30.2, magic-string@^0.30.3: - version "0.30.17" - resolved "https://mirrors.huaweicloud.com/repository/npm/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" - integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - -make-dir@^3.0.2: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-dir@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - -make-error@^1.1.1, make-error@^1.3.6: - version "1.3.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://mirrors.huaweicloud.com/repository/npm/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - -math-intrinsics@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/math-intrinsics/-/math-intrinsics-1.0.0.tgz#4e04bf87c85aa51e90d078dac2252b4eb5260817" - integrity sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -micromatch@^4.0.4: - version "4.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://mirrors.huaweicloud.com/repository/npm/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mri@^1.1.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" - integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== - -ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.7: - version "3.3.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" - integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== - -nanoid@^5.0.7: - version "5.0.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/nanoid/-/nanoid-5.0.9.tgz#977dcbaac055430ce7b1e19cf0130cea91a20e50" - integrity sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q== - -nanospinner@^1.1.0: - version "1.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/nanospinner/-/nanospinner-1.2.2.tgz#5a38f4410b5bf7a41585964bee74d32eab3e040b" - integrity sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA== - dependencies: - picocolors "^1.1.1" - -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -no-case@^3.0.4: - version "3.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - -node-releases@^2.0.19: - version "2.0.19" - resolved "https://mirrors.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^4.0.0, npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -nwsapi@^2.2.2: - version "2.2.16" - resolved "https://mirrors.huaweicloud.com/repository/npm/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" - integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.13.3: - version "1.13.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" - integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4, object.assign@^4.1.5: - version "4.1.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.entries@^1.1.8: - version "1.1.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" - integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -object.fromentries@^2.0.8: - version "2.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" - integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.groupby@^1.0.3: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" - integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - -object.values@^1.1.6, object.values@^1.2.0: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" - integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== - dependencies: - mimic-fn "^1.0.0" - -onetime@^5.1.0, onetime@^5.1.2: - version "5.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.9.3: - version "0.9.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - -ora@^5.4.1: - version "5.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2, p-limit@^3.1.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-map@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.0.0, parse-json@^5.2.0: - version "5.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse5@^7.0.0, parse5@^7.1.1: - version "7.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" - integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== - dependencies: - entities "^4.5.0" - -pascal-case@^3.1.2: - version "3.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" - integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -picomatch@^4.0.2: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== - -pirates@^4.0.4: - version "4.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -pkg-dir@^4.1.0, pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - -postcss@^8.4.38: - version "8.4.49" - resolved "https://mirrors.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" - integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== - dependencies: - nanoid "^3.3.7" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.8.1: - version "2.8.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -progress-estimator@^0.3.1: - version "0.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/progress-estimator/-/progress-estimator-0.3.1.tgz#5a4c5bffa2cca09ac233c43bb6a644cbe6706920" - integrity sha512-I5bwE35adOrA2rfZ9iHHPESUp5C6cXrZd0J8LdFD9J+66ijSugBwZHooPCROf94Ox8YZaUyvTLViqSiRvBhSoA== - dependencies: - chalk "^2.4.1" - cli-spinners "^1.3.1" - humanize-duration "^3.15.3" - log-update "^2.3.0" - -prompts@^2.0.1: - version "2.4.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prop-types@^15.8.1: - version "15.8.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -psl@^1.1.33: - version "1.15.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" - integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== - dependencies: - punycode "^2.3.1" - -pump@^3.0.0: - version "3.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" - integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: - version "2.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -pure-rand@^6.0.0: - version "6.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" - integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -react-is@^16.13.1: - version "16.13.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^18.0.0: - version "18.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - -readable-stream@^3.4.0: - version "3.6.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^4.0.1: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" - integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== - dependencies: - resolve "^1.1.6" - -reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz#c58afb17a4007b4d1118c07b92c23fca422c5d82" - integrity sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ== - dependencies: - call-bind "^1.0.8" - define-properties "^1.2.1" - dunder-proto "^1.0.0" - es-abstract "^1.23.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - gopd "^1.2.0" - which-builtin-type "^1.2.0" - -regenerate-unicode-properties@^10.2.0: - version "10.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" - integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.14.0, regenerator-runtime@^0.14.1: - version "0.14.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - -regenerator-transform@^0.15.2: - version "0.15.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" - integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== - dependencies: - "@babel/runtime" "^7.8.4" - -regexp.prototype.flags@^1.5.2, regexp.prototype.flags@^1.5.3: - version "1.5.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" - integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.2" - -regexpu-core@^6.2.0: - version "6.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" - integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.2.0" - regjsgen "^0.8.0" - regjsparser "^0.12.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regjsgen@^0.8.0: - version "0.8.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" - integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== - -regjsparser@^0.12.0: - version "0.12.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" - integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== - dependencies: - jsesc "~3.0.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve.exports@^2.0.0: - version "2.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" - integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== - -resolve@^1.1.6, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4: - version "1.22.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.9.tgz#6da76e4cdc57181fa4471231400e8851d0a924f3" - integrity sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A== - dependencies: - is-core-module "^2.16.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.5: - version "2.0.0-next.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" - integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rollup-plugin-delete@^2.0.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/rollup-plugin-delete/-/rollup-plugin-delete-2.1.0.tgz#41d26b5c9dc16b7e5545030692fba9ac89f57b44" - integrity sha512-TEbqJd7giLvzQDTu4jSPufwhTJs/iYVN2LfR/YIYkqjC/oZ0/h9Q0AeljifIhzBzJYZtHQTWKEbMms5fbh54pw== - dependencies: - del "^5.1.0" - -rollup-plugin-dts@^5.3.1: - version "5.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/rollup-plugin-dts/-/rollup-plugin-dts-5.3.1.tgz#c2841269a3a5cb986b7791b0328e6a178eba108f" - integrity sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg== - dependencies: - magic-string "^0.30.2" - optionalDependencies: - "@babel/code-frame" "^7.22.5" - -rollup-plugin-typescript2@^0.36.0: - version "0.36.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz#309564eb70d710412f5901344ca92045e180ed53" - integrity sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw== - dependencies: - "@rollup/pluginutils" "^4.1.2" - find-cache-dir "^3.3.2" - fs-extra "^10.0.0" - semver "^7.5.4" - tslib "^2.6.2" - -rollup@^3.20.0: - version "3.29.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" - integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== - optionalDependencies: - fsevents "~2.3.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -sade@^1.8.1: - version "1.8.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" - integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== - dependencies: - mri "^1.1.0" - -safe-array-concat@^1.1.2, safe-array-concat@^1.1.3: - version "1.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" - integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - get-intrinsic "^1.2.6" - has-symbols "^1.1.0" - isarray "^2.0.5" - -safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" - integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-regex "^1.2.1" - -"safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -saxes@^6.0.0: - version "6.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - -semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: - version "7.6.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -serialize-javascript@^6.0.1: - version "6.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -set-function-length@^1.2.2: - version "1.2.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -set-function-name@^2.0.2: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shelljs@^0.8.5: - version "0.8.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.0.6, side-channel@^1.1.0: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -size-limit@11.1.6, size-limit@^11.1.6: - version "11.1.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/size-limit/-/size-limit-11.1.6.tgz#75cd54f9326d1b065ebcb6ca9ec27294e7ccdfb1" - integrity sha512-S5ux2IB8rU26xwVgMskmknGMFkieaIAqDLuwgKiypk6oa4lFsie8yFPrzRFV+yrLDY2GddjXuCaVk5PveVOHiQ== - dependencies: - bytes-iec "^3.1.1" - chokidar "^4.0.1" - jiti "^2.0.0" - lilconfig "^3.1.2" - nanospinner "^1.1.0" - picocolors "^1.1.0" - tinyglobby "^0.2.7" - -slash@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slash@^5.0.0: - version "5.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" - integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== - -smob@^1.0.0: - version "1.5.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/smob/-/smob-1.5.0.tgz#85d79a1403abf128d24d3ebc1cdc5e1a9548d3ab" - integrity sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig== - -sort-object-keys@^1.1.3: - version "1.1.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" - integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== - -sort-package-json@^1.57.0: - version "1.57.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/sort-package-json/-/sort-package-json-1.57.0.tgz#e95fb44af8ede0bb6147e3f39258102d4bb23fc4" - integrity sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q== - dependencies: - detect-indent "^6.0.0" - detect-newline "3.1.0" - git-hooks-list "1.0.3" - globby "10.0.0" - is-plain-obj "2.1.0" - sort-object-keys "^1.1.3" - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -source-map-support@0.5.13: - version "0.5.13" - resolved "https://mirrors.huaweicloud.com/repository/npm/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://mirrors.huaweicloud.com/repository/npm/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://mirrors.huaweicloud.com/repository/npm/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -string-length@^4.0.1: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -string-length@^5.0.1: - version "5.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/string-length/-/string-length-5.0.1.tgz#3d647f497b6e8e8d41e422f7e0b23bc536c8381e" - integrity sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow== - dependencies: - char-regex "^2.0.0" - strip-ansi "^7.0.1" - -string-natural-compare@^3.0.1: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" - integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== - -string-width@^2.1.1: - version "2.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string.prototype.includes@^2.0.1: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" - integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - -string.prototype.matchall@^4.0.11: - version "4.0.11" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" - integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - regexp.prototype.flags "^1.5.2" - set-function-name "^2.0.2" - side-channel "^1.0.6" - -string.prototype.repeat@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" - integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - -string.prototype.trim@^1.2.10: - version "1.2.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" - integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - define-data-property "^1.1.4" - define-properties "^1.2.1" - es-abstract "^1.23.5" - es-object-atoms "^1.0.0" - has-property-descriptors "^1.0.2" - -string.prototype.trimend@^1.0.8, string.prototype.trimend@^1.0.9: - version "1.0.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" - integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string.prototype.trimstart@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" - integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -terser@^5.17.4: - version "5.37.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/terser/-/terser-5.37.0.tgz#38aa66d1cfc43d0638fab54e43ff8a4f72a21ba3" - integrity sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" - commander "^2.20.0" - source-map-support "~0.5.20" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -tiny-glob@^0.2.9: - version "0.2.9" - resolved "https://mirrors.huaweicloud.com/repository/npm/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - -tinyglobby@^0.2.7: - version "0.2.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" - integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== - dependencies: - fdir "^6.4.2" - picomatch "^4.0.2" - -tmpl@1.0.5: - version "1.0.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -tough-cookie@^4.1.2: - version "4.1.4" - resolved "https://mirrors.huaweicloud.com/repository/npm/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" - integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tr46@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== - dependencies: - punycode "^2.1.1" - -ts-jest@^29.1.2: - version "29.2.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" - integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== - dependencies: - bs-logger "^0.2.6" - ejs "^3.1.10" - fast-json-stable-stringify "^2.1.0" - jest-util "^29.0.0" - json5 "^2.2.3" - lodash.memoize "^4.1.2" - make-error "^1.3.6" - semver "^7.6.3" - yargs-parser "^21.1.1" - -ts-node@^10.9.2: - version "10.9.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^1.8.1: - version "1.14.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.3, tslib@^2.6.2, tslib@^2.8.1: - version "2.8.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-detect@4.0.8: - version "4.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^2.19.0: - version "2.19.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" - integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== - -typed-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" - integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-typed-array "^1.1.13" - -typed-array-byte-length@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" - integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - -typed-array-byte-offset@^1.0.3: - version "1.0.3" - resolved "https://mirrors.huaweicloud.com/repository/npm/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz#3fa9f22567700cc86aaf86a1e7176f74b59600f2" - integrity sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - reflect.getprototypeof "^1.0.6" - -typed-array-length@^1.0.7: - version "1.0.7" - resolved "https://mirrors.huaweicloud.com/repository/npm/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" - integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - is-typed-array "^1.1.13" - possible-typed-array-names "^1.0.0" - reflect.getprototypeof "^1.0.6" - -typescript@^5.0.2, typescript@^5.7.2: - version "5.7.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" - integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== - -unbox-primitive@^1.0.2: - version "1.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" - integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== - dependencies: - call-bound "^1.0.3" - has-bigints "^1.0.2" - has-symbols "^1.1.0" - which-boxed-primitive "^1.1.1" - -undici-types@~6.20.0: - version "6.20.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" - integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" - integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - -universalify@^0.2.0: - version "0.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -update-browserslist-db@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" - integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -url-parse@^1.5.3: - version "1.5.10" - resolved "https://mirrors.huaweicloud.com/repository/npm/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -v8-to-istanbul@^9.0.1: - version "9.3.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" - integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - -w3c-xmlserializer@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" - integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== - dependencies: - xml-name-validator "^4.0.0" - -walker@^1.0.8: - version "1.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - -whatwg-encoding@^2.0.0: - version "2.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" - integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== - dependencies: - iconv-lite "0.6.3" - -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== - dependencies: - tr46 "^3.0.0" - webidl-conversions "^7.0.0" - -which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: - version "1.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" - integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== - dependencies: - is-bigint "^1.1.0" - is-boolean-object "^1.2.1" - is-number-object "^1.1.1" - is-string "^1.1.1" - is-symbol "^1.1.1" - -which-builtin-type@^1.2.0: - version "1.2.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" - integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== - dependencies: - call-bound "^1.0.2" - function.prototype.name "^1.1.6" - has-tostringtag "^1.0.2" - is-async-function "^2.0.0" - is-date-object "^1.1.0" - is-finalizationregistry "^1.1.0" - is-generator-function "^1.0.10" - is-regex "^1.2.1" - is-weakref "^1.0.2" - isarray "^2.0.5" - which-boxed-primitive "^1.1.0" - which-collection "^1.0.2" - which-typed-array "^1.1.16" - -which-collection@^1.0.2: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" - integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== - dependencies: - is-map "^2.0.3" - is-set "^2.0.3" - is-weakmap "^2.0.2" - is-weakset "^2.0.3" - -which-typed-array@^1.1.14, which-typed-array@^1.1.16: - version "1.1.16" - resolved "https://mirrors.huaweicloud.com/repository/npm/which-typed-array/-/which-typed-array-1.1.16.tgz#db4db429c4706feca2f01677a144278e4a8c216b" - integrity sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - -which@^2.0.1: - version "2.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.5: - version "1.2.5" - resolved "https://mirrors.huaweicloud.com/repository/npm/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -wrap-ansi@^3.0.1: - version "3.0.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" - integrity sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -ws@^8.11.0: - version "8.18.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - -xml-name-validator@^4.0.0: - version "4.0.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" - integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://mirrors.huaweicloud.com/repository/npm/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yaml@^1.10.0: - version "1.10.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.3.1: - version "17.7.2" - resolved "https://mirrors.huaweicloud.com/repository/npm/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yn@3.1.1: - version "3.1.1" - resolved "https://mirrors.huaweicloud.com/repository/npm/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://mirrors.huaweicloud.com/repository/npm/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/data/fix/github-issue-196.fix b/data/fix/github-issue-196.fix deleted file mode 100644 index 249ed77..0000000 --- a/data/fix/github-issue-196.fix +++ /dev/null @@ -1,47 +0,0 @@ -39.144.0.0|39.144.0.255|中国|山东省|菏泽市|移动 -39.144.1.0|39.144.9.255|中国|0|0|移动 -39.144.10.0|39.144.12.255|中国|北京|北京|移动 -39.144.13.0|39.144.16.255|中国|广东|汕头|移动 -39.144.17.0|39.144.18.255|中国|重庆|重庆|移动 -39.144.19.0|39.144.19.255|中国|北京|北京|移动 -39.144.20.0|39.144.20.255|中国|重庆|重庆|移动 -39.144.21.0|39.144.29.255|中国|河南省|0|移动 -39.144.22.0|39.144.22.255|中国|河南|郑州|移动 -39.144.23.0|39.144.24.255|中国|河南|南阳|移动 -39.144.25.0|39.144.29.255|中国|河南|0|移动 -39.144.34.0|39.144.34.255|中国|安徽省|0|移动 -39.144.37.0|39.144.37.255|中国|安徽|蚌埠|移动 -39.144.38.0|39.144.38.255|中国|安徽省|合肥市|移动 -39.144.39.0|39.144.40.255|中国|上海|上海|移动 -39.144.41.0|39.144.42.255|中国|贵州|毕节|移动 -39.144.43.0|39.144.47.255|中国|上海|上海|移动 -39.144.48.0|39.144.48.255|中国|北京|北京|移动 -39.144.49.0|39.144.49.255|中国|河北|廊坊|移动 -39.144.50.0|39.144.50.255|中国|河北|石家庄|移动 -39.144.51.0|39.144.52.255|中国|北京|北京|移动 -39.144.53.0|39.144.53.255|中国|辽宁|朝阳|移动 -39.144.54.0|39.144.54.255|中国|辽宁|盘锦|移动 -39.144.55.0|39.144.55.255|中国|辽宁|大连|移动 -39.144.56.0|39.144.56.255|中国|辽宁|本溪|移动 -39.144.57.0|39.144.58.255|中国|辽宁|沈阳|移动 -39.144.64.0|39.144.64.255|中国|广西|0|移动 -39.144.66.0|39.144.66.255|中国|广西|桂林|移动 -39.144.67.0|39.144.68.255|中国|北京|北京|移动 -39.144.99.0|39.144.99.255|中国|山西|临汾|移动 -39.144.100.0|39.144.100.255|中国|吉林|长春|移动 -39.144.137.0|39.144.137.255|中国|四川省|成都市|移动 -39.144.138.0|39.144.138.255|中国|四川省|阿坝藏族羌族自治州|移动 -39.144.145.0|39.144.145.255|中国|云南省|昆明市|移动 -39.144.147.0|39.144.147.255|中国|云南省|0|移动 -39.144.151.0|39.144.151.255|中国|江苏|淮安|移动 -39.144.153.0|39.144.153.255|中国|江苏|苏州|移动 -39.144.154.0|39.144.154.127|中国|江苏省|无锡市|移动 -39.144.154.128|39.144.154.255|中国|江苏省|常州市|移动 -39.144.169.0|39.144.169.63|中国|江西省|宜春市|移动 -39.144.169.64|39.144.169.127|中国|江西省|吉安市|移动 -39.144.169.128|39.144.169.191|中国|江西省|赣州市|移动 -39.144.169.192|39.144.169.255|中国|江西省|南昌市|移动 -39.144.177.0|39.144.177.255|中国|河南省|郑州市|移动 -39.144.179.0|39.144.182.255|中国|河南|0|移动 -39.144.218.0|39.144.222.255|中国|重庆|重庆市|移动 -39.144.219.0|39.144.219.255|中国|重庆|重庆|移动 diff --git a/data/fix/github-issue-200.fix b/data/fix/github-issue-200.fix deleted file mode 100644 index 0405792..0000000 --- a/data/fix/github-issue-200.fix +++ /dev/null @@ -1,28 +0,0 @@ -112.224.0.0|112.224.63.255|中国|山东省|济南市|联通 -112.224.64.0|112.224.64.255|中国|山东省|青岛市|联通 -112.224.65.0|112.224.65.255|中国|山东省|0|联通 -112.224.66.0|112.224.66.255|中国|山东省|青岛市|联通 -112.224.67.0|112.224.67.255|中国|山东省|0|联通 -112.224.68.0|112.224.71.255|中国|山东省|青岛市|联通 -112.224.72.0|112.224.73.255|中国|山东省|0|联通 -112.224.74.0|112.224.127.255|中国|山东省|青岛市|联通 -112.224.128.0|112.224.128.255|中国|山东省|0|联通 -112.224.129.0|112.224.130.255|中国|山东省|泰安市|联通 -112.224.131.0|112.224.132.255|中国|山东省|0|联通 -112.224.133.0|112.224.136.255|中国|山东省|烟台市|联通 -112.224.137.0|112.224.138.255|中国|山东省|潍坊市|联通 -112.224.139.0|112.224.140.255|中国|山东省|烟台市|联通 -112.224.141.0|112.224.144.255|中国|山东省|0|联通 -112.224.145.0|112.224.146.255|中国|山东省|临沂市|联通 -112.224.147.0|112.224.148.255|中国|山东省|0|联通 -112.224.149.0|112.224.152.255|中国|河北省|0|联通 -112.224.153.0|112.224.153.255|中国|山东省|青岛市|联通 -112.224.154.0|112.224.154.255|中国|山东省|0|联通 -112.224.155.0|112.224.156.255|中国|山东省|青岛市|联通 -112.224.157.0|112.224.157.255|中国|山东省|济南市|联通 -112.224.158.0|112.224.159.255|中国|山东省|临沂市|联通 -112.224.160.0|112.224.163.255|中国|山东省|0|联通 -112.224.164.0|112.224.164.255|中国|山东省|青岛市|联通 -112.224.165.0|112.224.167.255|中国|山东省|0|联通 -112.224.168.0|112.224.255.255|中国|山东省|济南市|联通 -112.225.0.0|112.226.255.255|中国|山东省|青岛市|联通 diff --git a/data/fix/github-issue-243.fix b/data/fix/github-issue-243.fix deleted file mode 100644 index 67f98bf..0000000 --- a/data/fix/github-issue-243.fix +++ /dev/null @@ -1,162 +0,0 @@ -36.132.128.0|36.132.147.255|中国|黑龙江省|哈尔滨市|移动 -36.132.148.0|36.132.150.255|中国|黑龙江省|齐齐哈尔市|移动 -36.132.151.0|36.132.255.255|中国|黑龙江省|哈尔滨市|移动 -36.133.0.0|36.133.11.255|中国|广东省|广州市|移动 -36.133.12.0|36.133.23.255|中国|湖南省|长沙市|移动 -36.133.24.0|36.133.35.255|中国|江苏省|南京市|移动 -36.133.36.0|36.133.47.255|中国|河南省|郑州市|移动 -36.133.48.0|36.133.59.255|中国|北京|北京市|移动 -36.133.60.0|36.133.71.255|中国|四川省|成都市|移动 -36.133.72.0|36.133.83.255|中国|上海|上海市|移动 -36.133.84.0|36.133.95.255|中国|浙江省|0|移动 -36.133.96.0|36.133.107.255|中国|陕西省|西安市|移动 -36.133.108.0|36.133.119.255|中国|重庆|重庆市|移动 -36.133.120.0|36.133.131.255|中国|山东省|济南市|移动 -36.133.132.0|36.133.143.255|中国|江苏省|苏州市|移动 -36.133.144.0|36.133.151.255|中国|广东省|广州市|移动 -36.133.152.0|36.133.159.255|中国|湖南省|长沙市|移动 -36.133.160.0|36.133.175.255|中国|江苏省|南京市|移动 -36.133.176.0|36.133.191.255|中国|广东省|广州市|移动 -36.133.192.0|36.133.199.255|中国|北京|北京市|移动 -36.133.200.0|36.133.207.255|中国|四川省|成都市|移动 -36.133.208.0|36.133.223.255|中国|河南省|0|移动 -36.133.224.0|36.133.231.255|中国|广东省|广州市|移动 -36.133.232.0|36.133.239.255|中国|重庆|重庆市|移动 -36.133.240.0|36.133.247.255|中国|北京|北京市|移动 -36.133.248.0|36.133.255.255|中国|陕西省|西安市|移动 -36.134.0.0|36.134.15.255|中国|浙江省|杭州市|移动 -36.134.16.0|36.134.23.255|中国|江苏省|南京市|移动 -36.134.24.0|36.134.31.255|中国|重庆|重庆市|移动 -36.134.32.0|36.134.39.255|中国|上海|上海市|移动 -36.134.40.0|36.134.47.255|中国|山东省|0|移动 -36.134.48.0|36.134.63.255|中国|浙江省|杭州市|移动 -36.134.64.0|36.134.65.255|中国|天津|天津市|移动 -36.134.66.0|36.134.67.255|中国|吉林省|长春市|移动 -36.134.68.0|36.134.69.255|中国|辽宁省|0|移动 -36.134.70.0|36.134.71.255|中国|福建省|福州市|移动 -36.134.72.0|36.134.73.255|中国|甘肃省|兰州市|移动 -36.134.74.0|36.134.75.255|中国|云南省|昆明市|移动 -36.134.76.0|36.134.77.255|中国|山西省|太原市|移动 -36.134.78.0|36.134.79.255|中国|湖北省|武汉市|移动 -36.134.80.0|36.134.81.255|中国|江西省|南昌市|移动 -36.134.82.0|36.134.83.255|中国|0|0|移动 -36.134.84.0|36.134.85.255|中国|安徽省|合肥市|移动 -36.134.86.0|36.134.87.255|中国|广西|南宁市|移动 -36.134.88.0|36.134.89.255|中国|内蒙古|呼和浩特市|移动 -36.142.0.0|36.142.1.255|中国|四川省|成都市|移动 -36.142.2.0|36.142.31.255|中国|甘肃省|兰州市|移动 -36.142.32.0|36.142.127.255|中国|甘肃省|0|移动 -36.142.128.0|36.142.131.255|中国|甘肃省|白银市|移动 -36.142.132.0|36.142.135.255|中国|甘肃省|张掖市|移动 -36.142.136.0|36.142.139.255|中国|甘肃省|武威市|移动 -36.142.140.0|36.142.145.255|中国|甘肃省|定西市|移动 -36.142.146.0|36.142.153.255|中国|甘肃省|天水市|移动 -36.142.154.0|36.142.157.255|中国|甘肃省|平凉市|移动 -36.142.158.0|36.142.163.255|中国|甘肃省|庆阳市|移动 -36.142.164.0|36.142.165.255|中国|甘肃省|甘南市|移动 -36.142.166.0|36.142.171.255|中国|甘肃省|陇南市|移动 -36.142.172.0|36.142.175.255|中国|甘肃省|临夏市|移动 -36.142.176.0|36.142.187.255|中国|甘肃省|兰州市|移动 -36.142.188.0|36.142.189.255|中国|甘肃省|金昌市|移动 -36.142.190.0|36.142.191.255|中国|甘肃省|嘉峪关市|移动 -36.142.192.0|36.142.195.255|中国|甘肃省|酒泉市|移动 -36.142.196.0|36.142.197.255|中国|甘肃省|白银市|移动 -36.142.198.0|36.142.199.255|中国|甘肃省|张掖市|移动 -36.142.200.0|36.142.201.255|中国|甘肃省|武威市|移动 -36.142.202.0|36.142.203.255|中国|甘肃省|定西市|移动 -36.142.204.0|36.142.205.255|中国|甘肃省|天水市|移动 -36.142.206.0|36.142.207.255|中国|甘肃省|平凉市|移动 -36.142.208.0|36.142.209.255|中国|甘肃省|庆阳市|移动 -36.142.210.0|36.142.211.255|中国|甘肃省|甘南市|移动 -36.142.212.0|36.142.213.255|中国|甘肃省|陇南市|移动 -36.142.214.0|36.142.215.255|中国|甘肃省|临夏市|移动 -36.142.216.0|36.142.217.255|中国|甘肃省|兰州市|移动 -36.142.218.0|36.142.219.255|中国|甘肃省|金昌市|移动 -36.142.220.0|36.142.221.255|中国|甘肃省|嘉峪关市|移动 -36.142.222.0|36.142.223.255|中国|甘肃省|酒泉市|移动 -36.142.224.0|36.142.255.255|中国|甘肃省|0|移动 -36.143.0.0|36.143.0.255|中国|河北省|张家口市|移动 -36.143.1.0|36.143.2.255|中国|河北省|廊坊市|移动 -36.143.3.0|36.143.4.255|中国|河北省|邯郸市|移动 -36.143.5.0|36.143.12.255|中国|河北省|张家口市|移动 -36.143.13.0|36.143.14.255|中国|河北省|保定市|移动 -36.143.15.0|36.143.17.255|中国|河北省|石家庄市|移动 -36.143.18.0|36.143.22.255|中国|河北省|唐山市|移动 -36.143.23.0|36.143.29.255|中国|河北省|保定市|移动 -36.143.30.0|36.143.31.255|中国|河北省|石家庄市|移动 -36.143.32.0|36.143.35.255|中国|河北省|保定市|移动 -36.143.36.0|36.143.39.255|中国|河北省|唐山市|移动 -36.143.40.0|36.143.43.255|中国|河北省|邯郸市|移动 -36.143.44.0|36.143.47.255|中国|河北省|保定市|移动 -36.143.48.0|36.143.51.255|中国|河北省|张家口市|移动 -36.143.52.0|36.143.55.255|中国|河北省|0|移动 -36.143.56.0|36.143.59.255|中国|河北省|邯郸市|移动 -36.143.60.0|36.143.63.255|中国|河北省|廊坊市|移动 -36.143.64.0|36.143.67.255|中国|河北省|石家庄市|移动 -36.143.68.0|36.143.71.255|中国|河北省|保定市|移动 -36.143.72.0|36.143.75.255|中国|河北省|张家口市|移动 -36.143.76.0|36.143.79.255|中国|河北省|保定市|移动 -36.143.80.0|36.143.95.255|中国|河北省|0|移动 -36.143.96.0|36.143.99.255|中国|河北省|唐山市|移动 -36.143.100.0|36.143.107.255|中国|河北省|石家庄市|移动 -36.143.108.0|36.143.115.255|中国|河北省|廊坊市|移动 -36.143.116.0|36.143.119.255|中国|河北省|唐山市|移动 -36.143.120.0|36.143.123.255|中国|河北省|张家口市|移动 -36.143.124.0|36.143.127.255|中国|河北省|保定市|移动 -36.143.128.0|36.143.255.255|中国|北京|北京市|移动 -36.148.0.0|36.148.31.255|中国|湖南省|长沙市|移动 -36.148.32.0|36.148.49.255|中国|湖南省|常德市|移动 -36.148.50.0|36.148.63.255|中国|湖南省|娄底市|移动 -36.148.64.0|36.148.79.255|中国|湖南省|株洲市|移动 -36.148.80.0|36.148.87.255|中国|湖南省|郴州市|移动 -36.148.88.0|36.148.95.255|中国|湖南省|衡阳市|移动 -36.148.96.0|36.148.103.255|中国|湖南省|怀化市|移动 -36.148.104.0|36.148.107.255|中国|湖南省|永州市|移动 -36.148.108.0|36.148.111.255|中国|湖南省|益阳市|移动 -36.148.112.0|36.148.123.255|中国|湖南省|张家界市|移动 -36.148.124.0|36.148.124.255|中国|湖南省|长沙市|移动 -36.148.125.0|36.148.127.255|中国|湖南省|湘潭市|移动 -36.148.128.0|36.148.143.255|中国|湖南省|常德市|移动 -36.148.144.0|36.148.155.255|中国|湖南省|郴州市|移动 -36.148.156.0|36.148.159.255|中国|湖南省|衡阳市|移动 -36.148.160.0|36.148.163.255|中国|湖南省|娄底市|移动 -36.148.164.0|36.148.168.255|中国|湖南省|永州市|移动 -36.148.169.0|36.148.175.255|中国|湖南省|益阳市|移动 -36.148.176.0|36.148.177.255|中国|湖南省|岳阳市|移动 -36.148.178.0|36.148.189.255|中国|湖南省|长沙市|移动 -36.148.190.0|36.148.255.255|中国|湖南省|0|移动 -36.161.0.0|36.161.15.255|中国|安徽省|六安市|移动 -36.161.16.0|36.161.31.255|中国|安徽省|蚌埠市|移动 -36.161.32.0|36.161.47.255|中国|安徽省|淮南市|移动 -36.161.48.0|36.161.63.255|中国|安徽省|铜陵市|移动 -36.161.64.0|36.161.79.255|中国|安徽省|池州市|移动 -36.161.80.0|36.161.95.255|中国|安徽省|黄山市|移动 -36.161.96.0|36.161.127.255|中国|安徽省|合肥市|移动 -36.161.128.0|36.161.131.255|中国|安徽省|淮北市|移动 -36.161.132.0|36.161.135.255|中国|安徽省|合肥市|移动 -36.161.136.0|36.161.143.255|中国|安徽省|宣城市|移动 -36.161.144.0|36.161.151.255|中国|安徽省|马鞍山市|移动 -36.161.152.0|36.161.157.255|中国|安徽省|铜陵市|移动 -36.161.158.0|36.161.159.255|中国|安徽省|合肥市|移动 -36.161.160.0|36.161.167.255|中国|安徽省|池州市|移动 -36.161.168.0|36.161.183.255|中国|安徽省|黄山市|移动 -36.161.184.0|36.161.191.255|中国|安徽省|宿州市|移动 -36.161.192.0|36.161.207.255|中国|安徽省|亳州市|移动 -36.161.208.0|36.161.223.255|中国|安徽省|六安市|移动 -36.161.224.0|36.161.231.255|中国|安徽省|宣城市|移动 -36.161.232.0|36.161.239.255|中国|安徽省|合肥市|移动 -36.161.240.0|36.161.240.255|中国|安徽省|芜湖市|移动 -36.161.241.0|36.161.247.255|中国|安徽省|安庆市|移动 -36.161.248.0|36.161.255.255|中国|安徽省|黄山市|移动 -120.231.0.0|120.231.4.255|中国|广东省|0|移动 -120.231.5.0|120.231.38.255|中国|广东省|湛江市|移动 -120.231.39.0|120.231.68.255|中国|广东省|茂名市|移动 -120.231.69.0|120.231.89.255|中国|广东省|肇庆市|移动 -120.231.90.0|120.231.94.255|中国|广东省|0|移动 -120.231.95.0|120.231.134.255|中国|广东省|中山市|移动 -120.231.135.0|120.231.141.255|中国|广东省|广州市|移动 -120.231.142.0|120.231.161.255|中国|广东省|清远市|移动 -120.231.162.0|120.231.186.255|中国|广东省|珠海市|移动 -120.231.187.0|120.231.201.255|中国|广东省|江门市|移动 -120.231.202.0|120.231.249.255|中国|广东省|深圳市|移动 -120.231.250.0|120.231.255.255|中国|广东省|清远市|移动 diff --git a/data/github-issue-287.bug b/data/github-issue-287.bug deleted file mode 100644 index f3a8dc0..0000000 --- a/data/github-issue-287.bug +++ /dev/null @@ -1,3 +0,0 @@ -# report by https://github.com/lionsoul2014/ip2region/issues/287 -# triggered the bug of getInt2 -193.150.116.0|193.150.116.255|俄罗斯|克拉斯诺亚尔斯克边疆区|克拉斯诺亚尔斯克|Federal State Budgetary Educational Institution of Higher Education Krasnoyarsk State Medical University named after Professor V.F. Voino-Yasenetsky of the Ministry of Health of the Russian Federation diff --git a/data/global_region.csv b/data/global_region.csv deleted file mode 100644 index 3d6681f..0000000 --- a/data/global_region.csv +++ /dev/null @@ -1,3461 +0,0 @@ -1,0,中国,1,0 -2,0,蒙古,1,0 -3,0,朝鲜,1,0 -4,0,韩国,1,0 -5,0,日本,1,0 -6,0,菲律宾,1,0 -7,0,越南,1,0 -8,0,老挝,1,0 -9,0,柬埔寨,1,0 -10,0,缅甸,1,0 -11,0,泰国,1,0 -12,0,马来西亚,1,0 -13,0,文莱,1,0 -14,0,新加坡,1,0 -15,0,印度尼西亚,1,0 -16,0,东帝汶,1,0 -17,0,尼泊尔,1,0 -18,0,不丹,1,0 -19,0,孟加拉国,1,0 -20,0,印度,1,0 -21,0,巴基斯坦,1,0 -22,0,斯里兰卡,1,0 -23,0,马尔代夫,1,0 -24,0,哈萨克斯坦,1,0 -25,0,吉尔吉斯斯坦,1,0 -26,0,塔吉克斯坦,1,0 -27,0,乌兹别克斯坦,1,0 -28,0,土库曼斯坦,1,0 -29,0,阿富汗,1,0 -30,0,伊拉克,1,0 -31,0,伊朗,1,0 -32,0,叙利亚,1,0 -33,0,约旦,1,0 -34,0,黎巴嫩,1,0 -35,0,以色列,1,0 -36,0,巴勒斯坦,1,0 -37,0,沙特阿拉伯,1,0 -38,0,巴林,1,0 -39,0,卡塔尔,1,0 -40,0,科威特,1,0 -41,0,阿拉伯联合酋长国,1,0 -42,0,阿曼,1,0 -43,0,也门,1,0 -44,0,格鲁吉亚,1,0 -45,0,亚美尼亚,1,0 -46,0,阿塞拜疆,1,0 -47,0,土耳其,1,0 -48,0,塞浦路斯,1,0 -49,0,芬兰,1,0 -50,0,瑞典,1,0 -51,0,挪威,1,0 -52,0,冰岛,1,0 -53,0,丹麦,1,0 -54,0,爱沙尼亚,1,0 -55,0,拉脱维亚,1,0 -56,0,立陶宛,1,0 -57,0,白俄罗斯,1,0 -58,0,俄罗斯,1,0 -59,0,乌克兰,1,0 -60,0,摩尔多瓦,1,0 -61,0,波兰,1,0 -62,0,捷克,1,0 -63,0,斯洛伐克,1,0 -64,0,匈牙利,1,0 -65,0,德国,1,0 -66,0,奥地利,1,0 -67,0,瑞士,1,0 -68,0,列支敦士登,1,0 -69,0,英国,1,0 -70,0,爱尔兰,1,0 -71,0,荷兰,1,0 -72,0,比利时,1,0 -73,0,卢森堡,1,0 -74,0,法国,1,0 -75,0,摩纳哥,1,0 -76,0,罗马尼亚,1,0 -77,0,保加利亚,1,0 -78,0,塞尔维亚,1,0 -79,0,马其顿,1,0 -80,0,阿尔巴尼亚,1,0 -81,0,希腊,1,0 -82,0,斯洛文尼亚,1,0 -83,0,克罗地亚,1,0 -84,0,波斯尼亚和墨塞哥维那,1,0 -85,0,意大利,1,0 -86,0,梵蒂冈,1,0 -87,0,圣马力诺,1,0 -88,0,马耳他,1,0 -89,0,西班牙,1,0 -90,0,葡萄牙,1,0 -91,0,安道尔共和国,1,0 -92,0,埃及,1,0 -93,0,利比亚,1,0 -94,0,苏丹,1,0 -95,0,突尼斯,1,0 -96,0,阿尔及利亚,1,0 -97,0,摩洛哥,1,0 -98,0,亚速尔群岛,1,0 -99,0,马德拉群岛,1,0 -100,0,埃塞俄比亚,1,0 -101,0,厄立特里亚,1,0 -102,0,索马里,1,0 -103,0,吉布提,1,0 -104,0,肯尼亚,1,0 -105,0,坦桑尼亚,1,0 -106,0,乌干达,1,0 -107,0,卢旺达,1,0 -108,0,布隆迪,1,0 -109,0,塞舌尔,1,0 -110,0,圣多美及普林西比,1,0 -111,0,塞内加尔,1,0 -112,0,冈比亚,1,0 -113,0,马里,1,0 -114,0,布基纳法索,1,0 -115,0,几内亚,1,0 -116,0,几内亚比绍,1,0 -117,0,佛得角,1,0 -118,0,塞拉利昂,1,0 -119,0,利比里亚,1,0 -120,0,科特迪瓦,1,0 -121,0,加纳,1,0 -122,0,多哥,1,0 -123,0,贝宁,1,0 -124,0,尼日尔,1,0 -125,0,加那利群岛,1,0 -126,0,赞比亚,1,0 -127,0,安哥拉,1,0 -128,0,津巴布韦,1,0 -129,0,马拉维,1,0 -130,0,莫桑比克,1,0 -131,0,博茨瓦纳,1,0 -132,0,纳米比亚,1,0 -133,0,南非,1,0 -134,0,斯威士兰,1,0 -135,0,莱索托,1,0 -136,0,马达加斯加,1,0 -137,0,科摩罗,1,0 -138,0,毛里求斯,1,0 -139,0,留尼旺,1,0 -140,0,圣赫勒拿,1,0 -141,0,澳大利亚,1,0 -142,0,新西兰,1,0 -143,0,巴布亚新几内亚,1,0 -144,0,所罗门群岛,1,0 -145,0,瓦努阿图共和国,1,0 -146,0,密克罗尼西亚,1,0 -147,0,马绍尔群岛,1,0 -148,0,帕劳,1,0 -149,0,瑙鲁,1,0 -150,0,基里巴斯,1,0 -151,0,图瓦卢,1,0 -152,0,萨摩亚,1,0 -153,0,斐济,1,0 -154,0,汤加,1,0 -155,0,库克群岛,1,0 -156,0,关岛,1,0 -157,0,新喀里多尼亚,1,0 -158,0,法属波利尼西亚,1,0 -159,0,皮特凯恩岛,1,0 -160,0,瓦利斯与富图纳,1,0 -161,0,纽埃,1,0 -162,0,托克劳,1,0 -163,0,美属萨摩亚,1,0 -164,0,北马里亚纳,1,0 -165,0,加拿大,1,0 -166,0,美国,1,0 -167,0,墨西哥,1,0 -168,0,格陵兰,1,0 -169,0,危地马拉,1,0 -170,0,伯利兹,1,0 -171,0,萨尔瓦多,1,0 -172,0,洪都拉斯,1,0 -173,0,尼加拉瓜,1,0 -174,0,哥斯达黎加,1,0 -175,0,巴拿马,1,0 -176,0,巴哈马,1,0 -177,0,古巴,1,0 -178,0,牙买加,1,0 -179,0,海地,1,0 -180,0,多米尼加共和国,1,0 -181,0,安提瓜和巴布达,1,0 -182,0,圣基茨和尼维斯,1,0 -183,0,多米尼克,1,0 -184,0,圣卢西亚,1,0 -185,0,圣文森特和格林纳丁斯,1,0 -186,0,格林纳达,1,0 -187,0,巴巴多斯,1,0 -188,0,特立尼达和多巴哥,1,0 -189,0,波多黎各,1,0 -190,0,英属维尔京群岛,1,0 -191,0,美属维尔京群岛,1,0 -192,0,安圭拉,1,0 -193,0,蒙特塞拉特岛,1,0 -194,0,瓜德罗普,1,0 -195,0,马提尼克,1,0 -196,0,荷属安的列斯,1,0 -197,0,阿鲁巴,1,0 -198,0,特克斯和凯科斯群岛,1,0 -199,0,开曼群岛,1,0 -200,0,百慕大,1,0 -201,0,哥伦比亚,1,0 -202,0,委内瑞拉,1,0 -203,0,圭亚那,1,0 -204,0,法属圭亚那,1,0 -205,0,苏里南,1,0 -206,0,厄瓜多尔,1,0 -207,0,秘鲁,1,0 -208,0,玻利维亚,1,0 -209,0,巴西,1,0 -210,0,智利,1,0 -211,0,阿根廷,1,0 -212,0,乌拉圭,1,0 -213,0,巴拉圭,1,0 -214,1,北京市,2,0 -215,214,北京市,3,100000 -216,215,东城区,4,0 -217,215,西城区,4,0 -218,215,崇文区,4,0 -219,215,宣武区,4,0 -220,215,朝阳区,4,0 -221,215,丰台区,4,0 -222,215,石景山区,4,0 -223,215,海淀区,4,0 -224,215,门头沟区,4,0 -225,215,房山区,4,0 -226,215,通州区,4,0 -227,215,顺义区,4,0 -228,215,昌平区,4,0 -229,215,大兴区,4,0 -230,215,怀柔区,4,0 -231,215,平谷区,4,0 -232,215,密云县,4,0 -233,215,延庆县,4,0 -234,1,天津市,2,0 -235,234,天津市,3,100000 -236,235,和平区,4,0 -237,235,河东区,4,0 -238,235,河西区,4,0 -239,235,南开区,4,0 -240,235,河北区,4,0 -241,235,红桥区,4,0 -242,235,塘沽区,4,0 -243,235,汉沽区,4,0 -244,235,大港区,4,0 -245,235,东丽区,4,0 -246,235,西青区,4,0 -247,235,津南区,4,0 -248,235,北辰区,4,0 -249,235,武清区,4,0 -250,235,宝坻区,4,0 -251,235,宁河县,4,0 -252,235,静海县,4,0 -253,235,蓟县,4,0 -254,1,河北省,2,0 -255,254,石家庄市,3,50000 -256,255,长安区,4,0 -257,255,桥东区,4,0 -258,255,桥西区,4,0 -259,255,新华区,4,0 -260,255,井陉矿区,4,0 -261,255,裕华区,4,0 -262,255,井陉县,4,0 -263,255,正定县,4,0 -264,255,栾城县,4,0 -265,255,行唐县,4,0 -266,255,灵寿县,4,0 -267,255,高邑县,4,0 -268,255,深泽县,4,0 -269,255,赞皇县,4,0 -270,255,无极县,4,0 -271,255,平山县,4,0 -272,255,元氏县,4,0 -273,255,赵县,4,0 -274,255,辛集市,4,0 -275,255,藁城市,4,0 -276,255,晋州市,4,0 -277,255,新乐市,4,0 -278,255,鹿泉市,4,0 -279,254,唐山市,3,63000 -280,279,路南区,4,0 -281,279,路北区,4,0 -282,279,古冶区,4,0 -283,279,开平区,4,0 -284,279,丰南区,4,0 -285,279,丰润区,4,0 -286,279,滦县,4,0 -287,279,滦南县,4,0 -288,279,乐亭县,4,0 -289,279,迁西县,4,0 -290,279,玉田县,4,0 -291,279,唐海县,4,0 -292,279,遵化市,4,0 -293,279,迁安市,4,0 -294,254,秦皇岛市,3,66000 -295,294,海港区,4,0 -296,294,山海关区,4,0 -297,294,北戴河区,4,0 -298,294,青龙满族自治县,4,0 -299,294,昌黎县,4,0 -300,294,抚宁县,4,0 -301,294,卢龙县,4,0 -302,254,邯郸市,3,56000 -303,302,邯山区,4,0 -304,302,丛台区,4,0 -305,302,复兴区,4,0 -306,302,峰峰矿区,4,0 -307,302,邯郸县,4,0 -308,302,临漳县,4,0 -309,302,成安县,4,0 -310,302,大名县,4,0 -311,302,涉县,4,0 -312,302,磁县,4,0 -313,302,肥乡县,4,0 -314,302,永年县,4,0 -315,302,邱县,4,0 -316,302,鸡泽县,4,0 -317,302,广平县,4,0 -318,302,馆陶县,4,0 -319,302,魏县,4,0 -320,302,曲周县,4,0 -321,302,武安市,4,0 -322,254,邢台市,3,54000 -323,322,邢台县,4,0 -324,322,临城县,4,0 -325,322,内丘县,4,0 -326,322,柏乡县,4,0 -327,322,隆尧县,4,0 -328,322,任县,4,0 -329,322,南和县,4,0 -330,322,宁晋县,4,0 -331,322,巨鹿县,4,0 -332,322,新河县,4,0 -333,322,广宗县,4,0 -334,322,平乡县,4,0 -335,322,威县,4,0 -336,322,清河县,4,0 -337,322,临西县,4,0 -338,322,南宫市,4,0 -339,322,沙河市,4,0 -340,254,保定市,3,71000 -341,340,新市区,4,0 -342,340,北市区,4,0 -343,340,南市区,4,0 -344,340,满城县,4,0 -345,340,清苑县,4,0 -346,340,涞水县,4,0 -347,340,阜平县,4,0 -348,340,徐水县,4,0 -349,340,定兴县,4,0 -350,340,唐县,4,0 -351,340,高阳县,4,0 -352,340,容城县,4,0 -353,340,涞源县,4,0 -354,340,望都县,4,0 -355,340,安新县,4,0 -356,340,易县,4,0 -357,340,曲阳县,4,0 -358,340,蠡县,4,0 -359,340,顺平县,4,0 -360,340,博野县,4,0 -361,340,雄县,4,0 -362,340,涿州市,4,0 -363,340,定州市,4,0 -364,340,安国市,4,0 -365,340,高碑店市,4,0 -366,254,张家口市,3,75000 -367,366,宣化区,4,0 -368,366,下花园区,4,0 -369,366,宣化县,4,0 -370,366,张北县,4,0 -371,366,康保县,4,0 -372,366,沽源县,4,0 -373,366,尚义县,4,0 -374,366,蔚县,4,0 -375,366,阳原县,4,0 -376,366,怀安县,4,0 -377,366,万全县,4,0 -378,366,怀来县,4,0 -379,366,涿鹿县,4,0 -380,366,赤城县,4,0 -381,366,崇礼县,4,0 -382,254,承德市,3,67000 -383,382,双桥区,4,0 -384,382,双滦区,4,0 -385,382,鹰手营子矿区,4,0 -386,382,承德县,4,0 -387,382,兴隆县,4,0 -388,382,平泉县,4,0 -389,382,滦平县,4,0 -390,382,隆化县,4,0 -391,382,丰宁满族自治县,4,0 -392,382,宽城满族自治县,4,0 -393,382,围场满族蒙古族自治县,4,0 -394,254,沧州市,3,61000 -395,394,运河区,4,0 -396,394,沧县,4,0 -397,394,青县,4,0 -398,394,东光县,4,0 -399,394,海兴县,4,0 -400,394,盐山县,4,0 -401,394,肃宁县,4,0 -402,394,南皮县,4,0 -403,394,吴桥县,4,0 -404,394,献县,4,0 -405,394,孟村回族自治县,4,0 -406,394,泊头市,4,0 -407,394,任丘市,4,0 -408,394,黄骅市,4,0 -409,394,河间市,4,0 -410,254,廊坊市,3,65000 -411,410,安次区,4,0 -412,410,广阳区,4,0 -413,410,固安县,4,0 -414,410,永清县,4,0 -415,410,香河县,4,0 -416,410,大城县,4,0 -417,410,文安县,4,0 -418,410,大厂回族自治县,4,0 -419,410,霸州市,4,0 -420,410,三河市,4,0 -421,254,衡水市,3,53000 -422,421,桃城区,4,0 -423,421,枣强县,4,0 -424,421,武邑县,4,0 -425,421,武强县,4,0 -426,421,饶阳县,4,0 -427,421,安平县,4,0 -428,421,故城县,4,0 -429,421,景县,4,0 -430,421,阜城县,4,0 -431,421,冀州市,4,0 -432,421,深州市,4,0 -433,1,山西省,2,0 -434,433,太原市,3,30000 -435,434,小店区,4,0 -436,434,迎泽区,4,0 -437,434,杏花岭区,4,0 -438,434,尖草坪区,4,0 -439,434,万柏林区,4,0 -440,434,晋源区,4,0 -441,434,清徐县,4,0 -442,434,阳曲县,4,0 -443,434,娄烦县,4,0 -444,434,古交市,4,0 -445,433,大同市,3,37000 -446,445,城区,4,0 -447,445,矿区,4,0 -448,445,南郊区,4,0 -449,445,新荣区,4,0 -450,445,阳高县,4,0 -451,445,天镇县,4,0 -452,445,广灵县,4,0 -453,445,灵丘县,4,0 -454,445,浑源县,4,0 -455,445,左云县,4,0 -456,445,大同县,4,0 -457,433,阳泉市,3,45000 -458,457,郊区,4,0 -459,457,平定县,4,0 -460,457,盂县,4,0 -461,433,长治市,3,46000 -462,461,长治县,4,0 -463,461,襄垣县,4,0 -464,461,屯留县,4,0 -465,461,平顺县,4,0 -466,461,黎城县,4,0 -467,461,壶关县,4,0 -468,461,长子县,4,0 -469,461,武乡县,4,0 -470,461,沁县,4,0 -471,461,沁源县,4,0 -472,461,潞城市,4,0 -473,433,晋城市,3,48000 -474,473,沁水县,4,0 -475,473,阳城县,4,0 -476,473,陵川县,4,0 -477,473,泽州县,4,0 -478,473,高平市,4,0 -479,433,朔州市,3,36000 -480,479,朔城区,4,0 -481,479,平鲁区,4,0 -482,479,山阴县,4,0 -483,479,应县,4,0 -484,479,右玉县,4,0 -485,479,怀仁县,4,0 -486,433,晋中市,3,30600 -487,486,榆次区,4,0 -488,486,榆社县,4,0 -489,486,左权县,4,0 -490,486,和顺县,4,0 -491,486,昔阳县,4,0 -492,486,寿阳县,4,0 -493,486,太谷县,4,0 -494,486,祁县,4,0 -495,486,平遥县,4,0 -496,486,灵石县,4,0 -497,486,介休市,4,0 -498,433,运城市,3,44000 -499,498,盐湖区,4,0 -500,498,临猗县,4,0 -501,498,万荣县,4,0 -502,498,闻喜县,4,0 -503,498,稷山县,4,0 -504,498,新绛县,4,0 -505,498,绛县,4,0 -506,498,垣曲县,4,0 -507,498,夏县,4,0 -508,498,平陆县,4,0 -509,498,芮城县,4,0 -510,498,永济市,4,0 -511,498,河津市,4,0 -512,433,忻州市,3,34000 -513,512,忻府区,4,0 -514,512,定襄县,4,0 -515,512,五台县,4,0 -516,512,代县,4,0 -517,512,繁峙县,4,0 -518,512,宁武县,4,0 -519,512,静乐县,4,0 -520,512,神池县,4,0 -521,512,五寨县,4,0 -522,512,岢岚县,4,0 -523,512,河曲县,4,0 -524,512,保德县,4,0 -525,512,偏关县,4,0 -526,512,原平市,4,0 -527,433,临汾市,3,41000 -528,527,尧都区,4,0 -529,527,曲沃县,4,0 -530,527,翼城县,4,0 -531,527,襄汾县,4,0 -532,527,洪洞县,4,0 -533,527,古县,4,0 -534,527,安泽县,4,0 -535,527,浮山县,4,0 -536,527,吉县,4,0 -537,527,乡宁县,4,0 -538,527,大宁县,4,0 -539,527,隰县,4,0 -540,527,永和县,4,0 -541,527,蒲县,4,0 -542,527,汾西县,4,0 -543,527,侯马市,4,0 -544,527,霍州市,4,0 -545,433,吕梁市,3,30500 -546,545,离石区,4,0 -547,545,文水县,4,0 -548,545,交城县,4,0 -549,545,兴县,4,0 -550,545,临县,4,0 -551,545,柳林县,4,0 -552,545,石楼县,4,0 -553,545,岚县,4,0 -554,545,方山县,4,0 -555,545,中阳县,4,0 -556,545,交口县,4,0 -557,545,孝义市,4,0 -558,545,汾阳市,4,0 -559,1,内蒙古自治区,2,0 -560,559,呼和浩特市,3,10000 -561,560,新城区,4,0 -562,560,回民区,4,0 -563,560,玉泉区,4,0 -564,560,赛罕区,4,0 -565,560,土默特左旗,4,0 -566,560,托克托县,4,0 -567,560,和林格尔县,4,0 -568,560,清水河县,4,0 -569,560,武川县,4,0 -570,559,包头市,3,14000 -571,570,东河区,4,0 -572,570,昆都仑区,4,0 -573,570,青山区,4,0 -574,570,石拐区,4,0 -575,570,白云矿区,4,0 -576,570,九原区,4,0 -577,570,土默特右旗,4,0 -578,570,固阳县,4,0 -579,570,达尔罕茂明安联合旗,4,0 -580,559,乌海市,3,16000 -581,580,海勃湾区,4,0 -582,580,海南区,4,0 -583,580,乌达区,4,0 -584,559,赤峰市,3,24000 -585,584,红山区,4,0 -586,584,元宝山区,4,0 -587,584,松山区,4,0 -588,584,阿鲁科尔沁旗,4,0 -589,584,巴林左旗,4,0 -590,584,巴林右旗,4,0 -591,584,林西县,4,0 -592,584,克什克腾旗,4,0 -593,584,翁牛特旗,4,0 -594,584,喀喇沁旗,4,0 -595,584,宁城县,4,0 -596,584,敖汉旗,4,0 -597,559,通辽市,3,28000 -598,597,科尔沁区,4,0 -599,597,科尔沁左翼中旗,4,0 -600,597,科尔沁左翼后旗,4,0 -601,597,开鲁县,4,0 -602,597,库伦旗,4,0 -603,597,奈曼旗,4,0 -604,597,扎鲁特旗,4,0 -605,597,霍林郭勒市,4,0 -606,559,鄂尔多斯市,3,10300 -607,606,东胜区,4,0 -608,606,达拉特旗,4,0 -609,606,准格尔旗,4,0 -610,606,鄂托克前旗,4,0 -611,606,鄂托克旗,4,0 -612,606,杭锦旗,4,0 -613,606,乌审旗,4,0 -614,606,伊金霍洛旗,4,0 -615,559,呼伦贝尔市,3,21000 -616,615,海拉尔区,4,0 -617,615,阿荣旗,4,0 -618,615,莫力达瓦达斡尔族自治旗,4,0 -619,615,鄂伦春自治旗,4,0 -620,615,鄂温克族自治旗,4,0 -621,615,陈巴尔虎旗,4,0 -622,615,新巴尔虎左旗,4,0 -623,615,新巴尔虎右旗,4,0 -624,615,满洲里市,4,0 -625,615,牙克石市,4,0 -626,615,扎兰屯市,4,0 -627,615,额尔古纳市,4,0 -628,615,根河市,4,0 -629,559,巴彦淖尔市,3,14400 -630,629,临河区,4,0 -631,629,五原县,4,0 -632,629,磴口县,4,0 -633,629,乌拉特前旗,4,0 -634,629,乌拉特中旗,4,0 -635,629,乌拉特后旗,4,0 -636,629,杭锦后旗,4,0 -637,559,乌兰察布市,3,11800 -638,637,集宁区,4,0 -639,637,卓资县,4,0 -640,637,化德县,4,0 -641,637,商都县,4,0 -642,637,兴和县,4,0 -643,637,凉城县,4,0 -644,637,察哈尔右翼前旗,4,0 -645,637,察哈尔右翼中旗,4,0 -646,637,察哈尔右翼后旗,4,0 -647,637,四子王旗,4,0 -648,637,丰镇市,4,0 -649,559,兴安盟,3,137500 -650,649,乌兰浩特市,4,0 -651,649,阿尔山市,4,0 -652,649,科尔沁右翼前旗,4,0 -653,649,科尔沁右翼中旗,4,0 -654,649,扎赉特旗,4,0 -655,649,突泉县,4,0 -656,559,锡林郭勒盟,3,11100 -657,656,二连浩特市,4,0 -658,656,锡林浩特市,4,0 -659,656,阿巴嘎旗,4,0 -660,656,苏尼特左旗,4,0 -661,656,苏尼特右旗,4,0 -662,656,东乌珠穆沁旗,4,0 -663,656,西乌珠穆沁旗,4,0 -664,656,太仆寺旗,4,0 -665,656,镶黄旗,4,0 -666,656,正镶白旗,4,0 -667,656,正蓝旗,4,0 -668,656,多伦县,4,0 -669,559,阿拉善盟,3,16000 -670,669,阿拉善左旗,4,0 -671,669,阿拉善右旗,4,0 -672,669,额济纳旗,4,0 -673,1,辽宁省,2,0 -674,673,沈阳市,3,110000 -675,674,沈河区,4,0 -676,674,大东区,4,0 -677,674,皇姑区,4,0 -678,674,铁西区,4,0 -679,674,苏家屯区,4,0 -680,674,东陵区,4,0 -681,674,新城子区,4,0 -682,674,于洪区,4,0 -683,674,辽中县,4,0 -684,674,康平县,4,0 -685,674,法库县,4,0 -686,674,新民市,4,0 -687,673,大连市,3,116000 -688,687,中山区,4,0 -689,687,西岗区,4,0 -690,687,沙河口区,4,0 -691,687,甘井子区,4,0 -692,687,旅顺口区,4,0 -693,687,金州区,4,0 -694,687,长海县,4,0 -695,687,瓦房店市,4,0 -696,687,普兰店市,4,0 -697,687,庄河市,4,0 -698,673,鞍山市,3,114000 -699,698,铁东区,4,0 -700,698,立山区,4,0 -701,698,千山区,4,0 -702,698,台安县,4,0 -703,698,岫岩满族自治县,4,0 -704,698,海城市,4,0 -705,673,抚顺市,3,113000 -706,705,新抚区,4,0 -707,705,东洲区,4,0 -708,705,望花区,4,0 -709,705,顺城区,4,0 -710,705,抚顺县,4,0 -711,705,新宾满族自治县,4,0 -712,705,清原满族自治县,4,0 -713,673,本溪市,3,117000 -714,713,平山区,4,0 -715,713,溪湖区,4,0 -716,713,明山区,4,0 -717,713,南芬区,4,0 -718,713,本溪满族自治县,4,0 -719,713,桓仁满族自治县,4,0 -720,673,丹东市,3,118000 -721,720,元宝区,4,0 -722,720,振兴区,4,0 -723,720,振安区,4,0 -724,720,宽甸满族自治县,4,0 -725,720,东港市,4,0 -726,720,凤城市,4,0 -727,673,锦州市,3,121000 -728,727,古塔区,4,0 -729,727,凌河区,4,0 -730,727,太和区,4,0 -731,727,黑山县,4,0 -732,727,义县,4,0 -733,727,凌海市,4,0 -734,727,北宁市,4,0 -735,673,营口市,3,115000 -736,735,站前区,4,0 -737,735,西市区,4,0 -738,735,鲅鱼圈区,4,0 -739,735,老边区,4,0 -740,735,盖州市,4,0 -741,735,大石桥市,4,0 -742,673,阜新市,3,123000 -743,742,海州区,4,0 -744,742,新邱区,4,0 -745,742,太平区,4,0 -746,742,清河门区,4,0 -747,742,细河区,4,0 -748,742,阜新蒙古族自治县,4,0 -749,742,彰武县,4,0 -750,673,辽阳市,3,111000 -751,750,白塔区,4,0 -752,750,文圣区,4,0 -753,750,宏伟区,4,0 -754,750,弓长岭区,4,0 -755,750,太子河区,4,0 -756,750,辽阳县,4,0 -757,750,灯塔市,4,0 -758,673,盘锦市,3,124000 -759,758,双台子区,4,0 -760,758,兴隆台区,4,0 -761,758,大洼县,4,0 -762,758,盘山县,4,0 -763,673,铁岭市,3,112000 -764,763,银州区,4,0 -765,763,清河区,4,0 -766,763,铁岭县,4,0 -767,763,西丰县,4,0 -768,763,昌图县,4,0 -769,763,调兵山市,4,0 -770,763,开原市,4,0 -771,673,朝阳市,3,122000 -772,771,双塔区,4,0 -773,771,龙城区,4,0 -774,771,朝阳县,4,0 -775,771,建平县,4,0 -776,771,喀喇沁左翼蒙古族自治县,4,0 -777,771,北票市,4,0 -778,771,凌源市,4,0 -779,673,葫芦岛市,3,125000 -780,779,连山区,4,0 -781,779,龙港区,4,0 -782,779,南票区,4,0 -783,779,绥中县,4,0 -784,779,建昌县,4,0 -785,779,兴城市,4,0 -786,1,吉林省,2,0 -787,786,长春市,3,130000 -788,787,南关区,4,0 -789,787,宽城区,4,0 -790,787,二道区,4,0 -791,787,绿园区,4,0 -792,787,双阳区,4,0 -793,787,农安县,4,0 -794,787,九台市,4,0 -795,787,榆树市,4,0 -796,787,德惠市,4,0 -797,786,吉林市,3,132000 -798,797,昌邑区,4,0 -799,797,龙潭区,4,0 -800,797,船营区,4,0 -801,797,丰满区,4,0 -802,797,永吉县,4,0 -803,797,蛟河市,4,0 -804,797,桦甸市,4,0 -805,797,舒兰市,4,0 -806,797,磐石市,4,0 -807,786,四平市,3,136000 -808,807,梨树县,4,0 -809,807,伊通满族自治县,4,0 -810,807,公主岭市,4,0 -811,807,双辽市,4,0 -812,786,辽源市,3,136200 -813,812,龙山区,4,0 -814,812,西安区,4,0 -815,812,东丰县,4,0 -816,812,东辽县,4,0 -817,786,通化市,3,134000 -818,817,东昌区,4,0 -819,817,二道江区,4,0 -820,817,通化县,4,0 -821,817,辉南县,4,0 -822,817,柳河县,4,0 -823,817,梅河口市,4,0 -824,817,集安市,4,0 -825,786,白山市,3,134300 -826,825,八道江区,4,0 -827,825,抚松县,4,0 -828,825,靖宇县,4,0 -829,825,长白朝鲜族自治县,4,0 -830,825,江源县,4,0 -831,825,临江市,4,0 -832,786,松原市,3,131100 -833,832,宁江区,4,0 -834,832,前郭尔罗斯蒙古族自治县,4,0 -835,832,长岭县,4,0 -836,832,乾安县,4,0 -837,832,扶余县,4,0 -838,786,白城市,3,137000 -839,838,洮北区,4,0 -840,838,镇赉县,4,0 -841,838,通榆县,4,0 -842,838,洮南市,4,0 -843,838,大安市,4,0 -844,786,延边朝鲜族自治州,3,133000 -845,844,延吉市,4,0 -846,844,图们市,4,0 -847,844,敦化市,4,0 -848,844,珲春市,4,0 -849,844,龙井市,4,0 -850,844,和龙市,4,0 -851,844,汪清县,4,0 -852,844,安图县,4,0 -853,1,黑龙江省,2,0 -854,853,哈尔滨市,3,150000 -855,854,道里区,4,0 -856,854,南岗区,4,0 -857,854,道外区,4,0 -858,854,香坊区,4,0 -859,854,动力区,4,0 -860,854,平房区,4,0 -861,854,松北区,4,0 -862,854,呼兰区,4,0 -863,854,依兰县,4,0 -864,854,方正县,4,0 -865,854,宾县,4,0 -866,854,巴彦县,4,0 -867,854,木兰县,4,0 -868,854,通河县,4,0 -869,854,延寿县,4,0 -870,854,阿城市,4,0 -871,854,双城市,4,0 -872,854,尚志市,4,0 -873,854,五常市,4,0 -874,853,齐齐哈尔市,3,161000 -875,874,龙沙区,4,0 -876,874,建华区,4,0 -877,874,铁锋区,4,0 -878,874,昂昂溪区,4,0 -879,874,富拉尔基区,4,0 -880,874,碾子山区,4,0 -881,874,梅里斯达斡尔族区,4,0 -882,874,龙江县,4,0 -883,874,依安县,4,0 -884,874,泰来县,4,0 -885,874,甘南县,4,0 -886,874,富裕县,4,0 -887,874,克山县,4,0 -888,874,克东县,4,0 -889,874,拜泉县,4,0 -890,874,讷河市,4,0 -891,853,鸡西市,3,158100 -892,891,鸡冠区,4,0 -893,891,恒山区,4,0 -894,891,滴道区,4,0 -895,891,梨树区,4,0 -896,891,城子河区,4,0 -897,891,麻山区,4,0 -898,891,鸡东县,4,0 -899,891,虎林市,4,0 -900,891,密山市,4,0 -901,853,鹤岗市,3,154100 -902,901,向阳区,4,0 -903,901,工农区,4,0 -904,901,南山区,4,0 -905,901,兴安区,4,0 -906,901,东山区,4,0 -907,901,兴山区,4,0 -908,901,萝北县,4,0 -909,901,绥滨县,4,0 -910,853,双鸭山市,3,155100 -911,910,尖山区,4,0 -912,910,岭东区,4,0 -913,910,四方台区,4,0 -914,910,宝山区,4,0 -915,910,集贤县,4,0 -916,910,友谊县,4,0 -917,910,宝清县,4,0 -918,910,饶河县,4,0 -919,853,大庆市,3,163000 -920,919,萨尔图区,4,0 -921,919,龙凤区,4,0 -922,919,让胡路区,4,0 -923,919,红岗区,4,0 -924,919,大同区,4,0 -925,919,肇州县,4,0 -926,919,肇源县,4,0 -927,919,林甸县,4,0 -928,919,杜尔伯特蒙古族自治县,4,0 -929,853,伊春市,3,152300 -930,929,伊春区,4,0 -931,929,南岔区,4,0 -932,929,友好区,4,0 -933,929,西林区,4,0 -934,929,翠峦区,4,0 -935,929,新青区,4,0 -936,929,美溪区,4,0 -937,929,金山屯区,4,0 -938,929,五营区,4,0 -939,929,乌马河区,4,0 -940,929,汤旺河区,4,0 -941,929,带岭区,4,0 -942,929,乌伊岭区,4,0 -943,929,红星区,4,0 -944,929,上甘岭区,4,0 -945,929,嘉荫县,4,0 -946,929,铁力市,4,0 -947,853,佳木斯市,3,154000 -948,947,永红区,4,0 -949,947,前进区,4,0 -950,947,东风区,4,0 -951,947,桦南县,4,0 -952,947,桦川县,4,0 -953,947,汤原县,4,0 -954,947,抚远县,4,0 -955,947,同江市,4,0 -956,947,富锦市,4,0 -957,853,七台河市,3,154600 -958,957,新兴区,4,0 -959,957,桃山区,4,0 -960,957,茄子河区,4,0 -961,957,勃利县,4,0 -962,853,牡丹江市,3,157000 -963,962,东安区,4,0 -964,962,阳明区,4,0 -965,962,爱民区,4,0 -966,962,东宁县,4,0 -967,962,林口县,4,0 -968,962,绥芬河市,4,0 -969,962,海林市,4,0 -970,962,宁安市,4,0 -971,962,穆棱市,4,0 -972,853,黑河市,3,164300 -973,972,爱辉区,4,0 -974,972,嫩江县,4,0 -975,972,逊克县,4,0 -976,972,孙吴县,4,0 -977,972,北安市,4,0 -978,972,五大连池市,4,0 -979,853,绥化市,3,152000 -980,979,北林区,4,0 -981,979,望奎县,4,0 -982,979,兰西县,4,0 -983,979,青冈县,4,0 -984,979,庆安县,4,0 -985,979,明水县,4,0 -986,979,绥棱县,4,0 -987,979,安达市,4,0 -988,979,肇东市,4,0 -989,979,海伦市,4,0 -990,853,大兴安岭地区,3,165000 -991,990,呼玛县,4,0 -992,990,塔河县,4,0 -993,990,漠河县,4,0 -994,1,上海市,2,0 -995,994,上海市,3,200000 -996,995,黄浦区,4,0 -997,995,卢湾区,4,0 -998,995,徐汇区,4,0 -999,995,长宁区,4,0 -1000,995,静安区,4,0 -1001,995,普陀区,4,0 -1002,995,闸北区,4,0 -1003,995,虹口区,4,0 -1004,995,杨浦区,4,0 -1005,995,闵行区,4,0 -1006,995,嘉定区,4,0 -1007,995,浦东新区,4,0 -1008,995,金山区,4,0 -1009,995,松江区,4,0 -1010,995,青浦区,4,0 -1011,995,南汇区,4,0 -1012,995,奉贤区,4,0 -1013,995,崇明县,4,0 -1014,1,江苏省,2,0 -1015,1014,南京市,3,210000 -1016,1015,玄武区,4,0 -1017,1015,白下区,4,0 -1018,1015,秦淮区,4,0 -1019,1015,建邺区,4,0 -1020,1015,鼓楼区,4,0 -1021,1015,下关区,4,0 -1022,1015,浦口区,4,0 -1023,1015,栖霞区,4,0 -1024,1015,雨花台区,4,0 -1025,1015,江宁区,4,0 -1026,1015,六合区,4,0 -1027,1015,溧水县,4,0 -1028,1015,高淳县,4,0 -1029,1014,无锡市,3,214000 -1030,1029,崇安区,4,0 -1031,1029,南长区,4,0 -1032,1029,北塘区,4,0 -1033,1029,锡山区,4,0 -1034,1029,惠山区,4,0 -1035,1029,滨湖区,4,0 -1036,1029,江阴市,4,0 -1037,1029,宜兴市,4,0 -1038,1014,徐州市,3,221000 -1039,1038,云龙区,4,0 -1040,1038,九里区,4,0 -1041,1038,贾汪区,4,0 -1042,1038,泉山区,4,0 -1043,1038,丰县,4,0 -1044,1038,沛县,4,0 -1045,1038,铜山县,4,0 -1046,1038,睢宁县,4,0 -1047,1038,新沂市,4,0 -1048,1038,邳州市,4,0 -1049,1014,常州市,3,213000 -1050,1049,天宁区,4,0 -1051,1049,钟楼区,4,0 -1052,1049,戚墅堰区,4,0 -1053,1049,新北区,4,0 -1054,1049,武进区,4,0 -1055,1049,溧阳市,4,0 -1056,1049,金坛市,4,0 -1057,1014,苏州市,3,215000 -1058,1057,沧浪区,4,0 -1059,1057,平江区,4,0 -1060,1057,金阊区,4,0 -1061,1057,虎丘区,4,0 -1062,1057,吴中区,4,0 -1063,1057,相城区,4,0 -1064,1057,常熟市,4,0 -1065,1057,张家港市,4,0 -1066,1057,昆山市,4,0 -1067,1057,吴江市,4,0 -1068,1057,太仓市,4,0 -1069,1014,南通市,3,226000 -1070,1069,崇川区,4,0 -1071,1069,港闸区,4,0 -1072,1069,海安县,4,0 -1073,1069,如东县,4,0 -1074,1069,启东市,4,0 -1075,1069,如皋市,4,0 -1076,1069,通州市,4,0 -1077,1069,海门市,4,0 -1078,1014,连云港市,3,222000 -1079,1078,连云区,4,0 -1080,1078,新浦区,4,0 -1081,1078,赣榆县,4,0 -1082,1078,东海县,4,0 -1083,1078,灌云县,4,0 -1084,1078,灌南县,4,0 -1085,1014,淮安市,3,223200 -1086,1085,楚州区,4,0 -1087,1085,淮阴区,4,0 -1088,1085,清浦区,4,0 -1089,1085,涟水县,4,0 -1090,1085,洪泽县,4,0 -1091,1085,盱眙县,4,0 -1092,1085,金湖县,4,0 -1093,1014,盐城市,3,224000 -1094,1093,亭湖区,4,0 -1095,1093,盐都区,4,0 -1096,1093,响水县,4,0 -1097,1093,滨海县,4,0 -1098,1093,阜宁县,4,0 -1099,1093,射阳县,4,0 -1100,1093,建湖县,4,0 -1101,1093,东台市,4,0 -1102,1093,大丰市,4,0 -1103,1014,扬州市,3,225000 -1104,1103,广陵区,4,0 -1105,1103,邗江区,4,0 -1106,1103,维扬区,4,0 -1107,1103,宝应县,4,0 -1108,1103,仪征市,4,0 -1109,1103,高邮市,4,0 -1110,1103,江都市,4,0 -1111,1014,镇江市,3,212000 -1112,1111,京口区,4,0 -1113,1111,润州区,4,0 -1114,1111,丹徒区,4,0 -1115,1111,丹阳市,4,0 -1116,1111,扬中市,4,0 -1117,1111,句容市,4,0 -1118,1014,泰州市,3,225300 -1119,1118,海陵区,4,0 -1120,1118,高港区,4,0 -1121,1118,兴化市,4,0 -1122,1118,靖江市,4,0 -1123,1118,泰兴市,4,0 -1124,1118,姜堰市,4,0 -1125,1014,宿迁市,3,223800 -1126,1125,宿城区,4,0 -1127,1125,宿豫区,4,0 -1128,1125,沭阳县,4,0 -1129,1125,泗阳县,4,0 -1130,1125,泗洪县,4,0 -1131,1,浙江省,2,0 -1132,1131,杭州市,3,310000 -1133,1132,上城区,4,0 -1134,1132,下城区,4,0 -1135,1132,江干区,4,0 -1136,1132,拱墅区,4,0 -1137,1132,西湖区,4,0 -1138,1132,滨江区,4,0 -1139,1132,萧山区,4,0 -1140,1132,余杭区,4,0 -1141,1132,桐庐县,4,0 -1142,1132,淳安县,4,0 -1143,1132,建德市,4,0 -1144,1132,富阳市,4,0 -1145,1132,临安市,4,0 -1146,1131,宁波市,3,315000 -1147,1146,海曙区,4,0 -1148,1146,江东区,4,0 -1149,1146,江北区,4,0 -1150,1146,北仑区,4,0 -1151,1146,镇海区,4,0 -1152,1146,鄞州区,4,0 -1153,1146,象山县,4,0 -1154,1146,宁海县,4,0 -1155,1146,余姚市,4,0 -1156,1146,慈溪市,4,0 -1157,1146,奉化市,4,0 -1158,1131,温州市,3,325000 -1159,1158,鹿城区,4,0 -1160,1158,龙湾区,4,0 -1161,1158,瓯海区,4,0 -1162,1158,洞头县,4,0 -1163,1158,永嘉县,4,0 -1164,1158,平阳县,4,0 -1165,1158,苍南县,4,0 -1166,1158,文成县,4,0 -1167,1158,泰顺县,4,0 -1168,1158,瑞安市,4,0 -1169,1158,乐清市,4,0 -1170,1131,嘉兴市,3,314000 -1171,1170,秀城区,4,0 -1172,1170,秀洲区,4,0 -1173,1170,嘉善县,4,0 -1174,1170,海盐县,4,0 -1175,1170,海宁市,4,0 -1176,1170,平湖市,4,0 -1177,1170,桐乡市,4,0 -1178,1131,湖州市,3,313000 -1179,1178,吴兴区,4,0 -1180,1178,南浔区,4,0 -1181,1178,德清县,4,0 -1182,1178,长兴县,4,0 -1183,1178,安吉县,4,0 -1184,1131,绍兴市,3,312000 -1185,1184,越城区,4,0 -1186,1184,绍兴县,4,0 -1187,1184,新昌县,4,0 -1188,1184,诸暨市,4,0 -1189,1184,上虞市,4,0 -1190,1184,嵊州市,4,0 -1191,1131,金华市,3,321000 -1192,1191,婺城区,4,0 -1193,1191,金东区,4,0 -1194,1191,武义县,4,0 -1195,1191,浦江县,4,0 -1196,1191,磐安县,4,0 -1197,1191,兰溪市,4,0 -1198,1191,义乌市,4,0 -1199,1191,东阳市,4,0 -1200,1191,永康市,4,0 -1201,1131,衢州市,3,324000 -1202,1201,柯城区,4,0 -1203,1201,衢江区,4,0 -1204,1201,常山县,4,0 -1205,1201,开化县,4,0 -1206,1201,龙游县,4,0 -1207,1201,江山市,4,0 -1208,1131,舟山市,3,316000 -1209,1208,定海区,4,0 -1210,1208,岱山县,4,0 -1211,1208,嵊泗县,4,0 -1212,1131,台州市,3,318000 -1213,1212,椒江区,4,0 -1214,1212,黄岩区,4,0 -1215,1212,路桥区,4,0 -1216,1212,玉环县,4,0 -1217,1212,三门县,4,0 -1218,1212,天台县,4,0 -1219,1212,仙居县,4,0 -1220,1212,温岭市,4,0 -1221,1212,临海市,4,0 -1222,1131,丽水市,3,323000 -1223,1222,莲都区,4,0 -1224,1222,青田县,4,0 -1225,1222,缙云县,4,0 -1226,1222,遂昌县,4,0 -1227,1222,松阳县,4,0 -1228,1222,云和县,4,0 -1229,1222,庆元县,4,0 -1230,1222,景宁畲族自治县,4,0 -1231,1222,龙泉市,4,0 -1232,1,安徽省,2,0 -1233,1232,合肥市,3,230000 -1234,1233,瑶海区,4,0 -1235,1233,庐阳区,4,0 -1236,1233,蜀山区,4,0 -1237,1233,包河区,4,0 -1238,1233,长丰县,4,0 -1239,1233,肥东县,4,0 -1240,1233,肥西县,4,0 -1241,1232,芜湖市,3,241000 -1242,1241,镜湖区,4,0 -1243,1241,马塘区,4,0 -1244,1241,新芜区,4,0 -1245,1241,鸠江区,4,0 -1246,1241,芜湖县,4,0 -1247,1241,繁昌县,4,0 -1248,1241,南陵县,4,0 -1249,1232,蚌埠市,3,233000 -1250,1249,龙子湖区,4,0 -1251,1249,蚌山区,4,0 -1252,1249,禹会区,4,0 -1253,1249,淮上区,4,0 -1254,1249,怀远县,4,0 -1255,1249,五河县,4,0 -1256,1249,固镇县,4,0 -1257,1232,淮南市,3,232000 -1258,1257,大通区,4,0 -1259,1257,田家庵区,4,0 -1260,1257,谢家集区,4,0 -1261,1257,八公山区,4,0 -1262,1257,潘集区,4,0 -1263,1257,凤台县,4,0 -1264,1232,马鞍山市,3,243000 -1265,1264,金家庄区,4,0 -1266,1264,花山区,4,0 -1267,1264,雨山区,4,0 -1268,1264,当涂县,4,0 -1269,1232,淮北市,3,235000 -1270,1269,杜集区,4,0 -1271,1269,相山区,4,0 -1272,1269,烈山区,4,0 -1273,1269,濉溪县,4,0 -1274,1232,铜陵市,3,244000 -1275,1274,铜官山区,4,0 -1276,1274,狮子山区,4,0 -1277,1274,铜陵县,4,0 -1278,1232,安庆市,3,246000 -1279,1278,迎江区,4,0 -1280,1278,大观区,4,0 -1281,1278,怀宁县,4,0 -1282,1278,枞阳县,4,0 -1283,1278,潜山县,4,0 -1284,1278,太湖县,4,0 -1285,1278,宿松县,4,0 -1286,1278,望江县,4,0 -1287,1278,岳西县,4,0 -1288,1278,桐城市,4,0 -1289,1232,黄山市,3,242700 -1290,1289,屯溪区,4,0 -1291,1289,黄山区,4,0 -1292,1289,徽州区,4,0 -1293,1289,歙县,4,0 -1294,1289,休宁县,4,0 -1295,1289,黟县,4,0 -1296,1289,祁门县,4,0 -1297,1232,滁州市,3,239000 -1298,1297,琅琊区,4,0 -1299,1297,南谯区,4,0 -1300,1297,来安县,4,0 -1301,1297,全椒县,4,0 -1302,1297,定远县,4,0 -1303,1297,凤阳县,4,0 -1304,1297,天长市,4,0 -1305,1297,明光市,4,0 -1306,1232,阜阳市,3,236100 -1307,1306,颍州区,4,0 -1308,1306,颍东区,4,0 -1309,1306,颍泉区,4,0 -1310,1306,临泉县,4,0 -1311,1306,太和县,4,0 -1312,1306,阜南县,4,0 -1313,1306,颍上县,4,0 -1314,1306,界首市,4,0 -1315,1232,宿州市,3,234100 -1316,1315,埇桥区,4,0 -1317,1315,砀山县,4,0 -1318,1315,萧县,4,0 -1319,1315,灵璧县,4,0 -1320,1315,泗县,4,0 -1321,1232,巢湖市,3,238000 -1322,1321,居巢区,4,0 -1323,1321,庐江县,4,0 -1324,1321,无为县,4,0 -1325,1321,含山县,4,0 -1326,1321,和县,4,0 -1327,1232,六安市,3,237000 -1328,1327,金安区,4,0 -1329,1327,裕安区,4,0 -1330,1327,寿县,4,0 -1331,1327,霍邱县,4,0 -1332,1327,舒城县,4,0 -1333,1327,金寨县,4,0 -1334,1327,霍山县,4,0 -1335,1232,亳州市,3,236800 -1336,1335,谯城区,4,0 -1337,1335,涡阳县,4,0 -1338,1335,蒙城县,4,0 -1339,1335,利辛县,4,0 -1340,1232,池州市,3,247100 -1341,1340,贵池区,4,0 -1342,1340,东至县,4,0 -1343,1340,石台县,4,0 -1344,1340,青阳县,4,0 -1345,1232,宣城市,3,366000 -1346,1345,宣州区,4,0 -1347,1345,郎溪县,4,0 -1348,1345,广德县,4,0 -1349,1345,泾县,4,0 -1350,1345,绩溪县,4,0 -1351,1345,旌德县,4,0 -1352,1345,宁国市,4,0 -1353,1,福建省,2,0 -1354,1353,福州市,3,350000 -1355,1354,台江区,4,0 -1356,1354,仓山区,4,0 -1357,1354,马尾区,4,0 -1358,1354,晋安区,4,0 -1359,1354,闽侯县,4,0 -1360,1354,连江县,4,0 -1361,1354,罗源县,4,0 -1362,1354,闽清县,4,0 -1363,1354,永泰县,4,0 -1364,1354,平潭县,4,0 -1365,1354,福清市,4,0 -1366,1354,长乐市,4,0 -1367,1353,厦门市,3,361000 -1368,1367,思明区,4,0 -1369,1367,海沧区,4,0 -1370,1367,湖里区,4,0 -1371,1367,集美区,4,0 -1372,1367,同安区,4,0 -1373,1367,翔安区,4,0 -1374,1353,莆田市,3,351100 -1375,1374,城厢区,4,0 -1376,1374,涵江区,4,0 -1377,1374,荔城区,4,0 -1378,1374,秀屿区,4,0 -1379,1374,仙游县,4,0 -1380,1353,三明市,3,365000 -1381,1380,梅列区,4,0 -1382,1380,三元区,4,0 -1383,1380,明溪县,4,0 -1384,1380,清流县,4,0 -1385,1380,宁化县,4,0 -1386,1380,大田县,4,0 -1387,1380,尤溪县,4,0 -1388,1380,沙县,4,0 -1389,1380,将乐县,4,0 -1390,1380,泰宁县,4,0 -1391,1380,建宁县,4,0 -1392,1380,永安市,4,0 -1393,1353,泉州市,3,362000 -1394,1393,鲤城区,4,0 -1395,1393,丰泽区,4,0 -1396,1393,洛江区,4,0 -1397,1393,泉港区,4,0 -1398,1393,惠安县,4,0 -1399,1393,安溪县,4,0 -1400,1393,永春县,4,0 -1401,1393,德化县,4,0 -1402,1393,金门县,4,0 -1403,1393,石狮市,4,0 -1404,1393,晋江市,4,0 -1405,1393,南安市,4,0 -1406,1353,漳州市,3,363000 -1407,1406,芗城区,4,0 -1408,1406,龙文区,4,0 -1409,1406,云霄县,4,0 -1410,1406,漳浦县,4,0 -1411,1406,诏安县,4,0 -1412,1406,长泰县,4,0 -1413,1406,东山县,4,0 -1414,1406,南靖县,4,0 -1415,1406,平和县,4,0 -1416,1406,华安县,4,0 -1417,1406,龙海市,4,0 -1418,1353,南平市,3,353000 -1419,1418,延平区,4,0 -1420,1418,顺昌县,4,0 -1421,1418,浦城县,4,0 -1422,1418,光泽县,4,0 -1423,1418,松溪县,4,0 -1424,1418,政和县,4,0 -1425,1418,邵武市,4,0 -1426,1418,武夷山市,4,0 -1427,1418,建瓯市,4,0 -1428,1418,建阳市,4,0 -1429,1353,龙岩市,3,364000 -1430,1429,新罗区,4,0 -1431,1429,长汀县,4,0 -1432,1429,永定县,4,0 -1433,1429,上杭县,4,0 -1434,1429,武平县,4,0 -1435,1429,连城县,4,0 -1436,1429,漳平市,4,0 -1437,1353,宁德市,3,352100 -1438,1437,蕉城区,4,0 -1439,1437,霞浦县,4,0 -1440,1437,古田县,4,0 -1441,1437,屏南县,4,0 -1442,1437,寿宁县,4,0 -1443,1437,周宁县,4,0 -1444,1437,柘荣县,4,0 -1445,1437,福安市,4,0 -1446,1437,福鼎市,4,0 -1447,1,江西省,2,0 -1448,1447,南昌市,3,330000 -1449,1448,东湖区,4,0 -1450,1448,青云谱区,4,0 -1451,1448,湾里区,4,0 -1452,1448,青山湖区,4,0 -1453,1448,南昌县,4,0 -1454,1448,新建县,4,0 -1455,1448,安义县,4,0 -1456,1448,进贤县,4,0 -1457,1447,景德镇市,3,333000 -1458,1457,昌江区,4,0 -1459,1457,珠山区,4,0 -1460,1457,浮梁县,4,0 -1461,1457,乐平市,4,0 -1462,1447,萍乡市,3,337000 -1463,1462,安源区,4,0 -1464,1462,湘东区,4,0 -1465,1462,莲花县,4,0 -1466,1462,上栗县,4,0 -1467,1462,芦溪县,4,0 -1468,1447,九江市,3,332000 -1469,1468,庐山区,4,0 -1470,1468,浔阳区,4,0 -1471,1468,九江县,4,0 -1472,1468,武宁县,4,0 -1473,1468,修水县,4,0 -1474,1468,永修县,4,0 -1475,1468,德安县,4,0 -1476,1468,星子县,4,0 -1477,1468,都昌县,4,0 -1478,1468,湖口县,4,0 -1479,1468,彭泽县,4,0 -1480,1468,瑞昌市,4,0 -1481,1447,新余市,3,338000 -1482,1481,渝水区,4,0 -1483,1481,分宜县,4,0 -1484,1447,鹰潭市,3,335000 -1485,1484,月湖区,4,0 -1486,1484,余江县,4,0 -1487,1484,贵溪市,4,0 -1488,1447,赣州市,3,341000 -1489,1488,章贡区,4,0 -1490,1488,赣县,4,0 -1491,1488,信丰县,4,0 -1492,1488,大余县,4,0 -1493,1488,上犹县,4,0 -1494,1488,崇义县,4,0 -1495,1488,安远县,4,0 -1496,1488,龙南县,4,0 -1497,1488,定南县,4,0 -1498,1488,全南县,4,0 -1499,1488,宁都县,4,0 -1500,1488,于都县,4,0 -1501,1488,兴国县,4,0 -1502,1488,会昌县,4,0 -1503,1488,寻乌县,4,0 -1504,1488,石城县,4,0 -1505,1488,瑞金市,4,0 -1506,1488,南康市,4,0 -1507,1447,吉安市,3,343000 -1508,1507,吉州区,4,0 -1509,1507,青原区,4,0 -1510,1507,吉安县,4,0 -1511,1507,吉水县,4,0 -1512,1507,峡江县,4,0 -1513,1507,新干县,4,0 -1514,1507,永丰县,4,0 -1515,1507,泰和县,4,0 -1516,1507,遂川县,4,0 -1517,1507,万安县,4,0 -1518,1507,安福县,4,0 -1519,1507,永新县,4,0 -1520,1507,井冈山市,4,0 -1521,1447,宜春市,3,336000 -1522,1521,袁州区,4,0 -1523,1521,奉新县,4,0 -1524,1521,万载县,4,0 -1525,1521,上高县,4,0 -1526,1521,宜丰县,4,0 -1527,1521,靖安县,4,0 -1528,1521,铜鼓县,4,0 -1529,1521,丰城市,4,0 -1530,1521,樟树市,4,0 -1531,1521,高安市,4,0 -1532,1447,抚州市,3,332900 -1533,1532,临川区,4,0 -1534,1532,南城县,4,0 -1535,1532,黎川县,4,0 -1536,1532,南丰县,4,0 -1537,1532,崇仁县,4,0 -1538,1532,乐安县,4,0 -1539,1532,宜黄县,4,0 -1540,1532,金溪县,4,0 -1541,1532,资溪县,4,0 -1542,1532,东乡县,4,0 -1543,1532,广昌县,4,0 -1544,1447,上饶市,3,334000 -1545,1544,信州区,4,0 -1546,1544,上饶县,4,0 -1547,1544,广丰县,4,0 -1548,1544,玉山县,4,0 -1549,1544,铅山县,4,0 -1550,1544,横峰县,4,0 -1551,1544,弋阳县,4,0 -1552,1544,余干县,4,0 -1553,1544,鄱阳县,4,0 -1554,1544,万年县,4,0 -1555,1544,婺源县,4,0 -1556,1544,德兴市,4,0 -1557,1,山东省,2,0 -1558,1557,济南市,3,250000 -1559,1558,历下区,4,0 -1560,1558,市中区,4,0 -1561,1558,槐荫区,4,0 -1562,1558,天桥区,4,0 -1563,1558,历城区,4,0 -1564,1558,长清区,4,0 -1565,1558,平阴县,4,0 -1566,1558,济阳县,4,0 -1567,1558,商河县,4,0 -1568,1558,章丘市,4,0 -1569,1557,青岛市,3,266000 -1570,1569,市南区,4,0 -1571,1569,市北区,4,0 -1572,1569,四方区,4,0 -1573,1569,黄岛区,4,0 -1574,1569,崂山区,4,0 -1575,1569,李沧区,4,0 -1576,1569,城阳区,4,0 -1577,1569,胶州市,4,0 -1578,1569,即墨市,4,0 -1579,1569,平度市,4,0 -1580,1569,胶南市,4,0 -1581,1569,莱西市,4,0 -1582,1557,淄博市,3,255000 -1583,1582,淄川区,4,0 -1584,1582,张店区,4,0 -1585,1582,博山区,4,0 -1586,1582,临淄区,4,0 -1587,1582,周村区,4,0 -1588,1582,桓台县,4,0 -1589,1582,高青县,4,0 -1590,1582,沂源县,4,0 -1591,1557,枣庄市,3,277100 -1592,1591,薛城区,4,0 -1593,1591,峄城区,4,0 -1594,1591,台儿庄区,4,0 -1595,1591,山亭区,4,0 -1596,1591,滕州市,4,0 -1597,1557,东营市,3,257000 -1598,1597,东营区,4,0 -1599,1597,河口区,4,0 -1600,1597,垦利县,4,0 -1601,1597,利津县,4,0 -1602,1597,广饶县,4,0 -1603,1557,烟台市,3,264000 -1604,1603,芝罘区,4,0 -1605,1603,福山区,4,0 -1606,1603,牟平区,4,0 -1607,1603,莱山区,4,0 -1608,1603,长岛县,4,0 -1609,1603,龙口市,4,0 -1610,1603,莱阳市,4,0 -1611,1603,莱州市,4,0 -1612,1603,蓬莱市,4,0 -1613,1603,招远市,4,0 -1614,1603,栖霞市,4,0 -1615,1603,海阳市,4,0 -1616,1557,潍坊市,3,261000 -1617,1616,潍城区,4,0 -1618,1616,寒亭区,4,0 -1619,1616,坊子区,4,0 -1620,1616,奎文区,4,0 -1621,1616,临朐县,4,0 -1622,1616,昌乐县,4,0 -1623,1616,青州市,4,0 -1624,1616,诸城市,4,0 -1625,1616,寿光市,4,0 -1626,1616,安丘市,4,0 -1627,1616,高密市,4,0 -1628,1616,昌邑市,4,0 -1629,1557,济宁市,3,272100 -1630,1629,任城区,4,0 -1631,1629,微山县,4,0 -1632,1629,鱼台县,4,0 -1633,1629,金乡县,4,0 -1634,1629,嘉祥县,4,0 -1635,1629,汶上县,4,0 -1636,1629,泗水县,4,0 -1637,1629,梁山县,4,0 -1638,1629,曲阜市,4,0 -1639,1629,兖州市,4,0 -1640,1629,邹城市,4,0 -1641,1557,泰安市,3,271000 -1642,1641,泰山区,4,0 -1643,1641,岱岳区,4,0 -1644,1641,宁阳县,4,0 -1645,1641,东平县,4,0 -1646,1641,新泰市,4,0 -1647,1641,肥城市,4,0 -1648,1557,威海市,3,265700 -1649,1648,环翠区,4,0 -1650,1648,文登市,4,0 -1651,1648,荣成市,4,0 -1652,1648,乳山市,4,0 -1653,1557,日照市,3,276800 -1654,1653,东港区,4,0 -1655,1653,岚山区,4,0 -1656,1653,五莲县,4,0 -1657,1653,莒县,4,0 -1658,1557,莱芜市,3,271100 -1659,1658,莱城区,4,0 -1660,1658,钢城区,4,0 -1661,1557,临沂市,3,276000 -1662,1661,兰山区,4,0 -1663,1661,罗庄区,4,0 -1664,1661,沂南县,4,0 -1665,1661,郯城县,4,0 -1666,1661,沂水县,4,0 -1667,1661,苍山县,4,0 -1668,1661,费县,4,0 -1669,1661,平邑县,4,0 -1670,1661,莒南县,4,0 -1671,1661,蒙阴县,4,0 -1672,1661,临沭县,4,0 -1673,1557,德州市,3,253000 -1674,1673,德城区,4,0 -1675,1673,陵县,4,0 -1676,1673,宁津县,4,0 -1677,1673,庆云县,4,0 -1678,1673,临邑县,4,0 -1679,1673,齐河县,4,0 -1680,1673,平原县,4,0 -1681,1673,夏津县,4,0 -1682,1673,武城县,4,0 -1683,1673,乐陵市,4,0 -1684,1673,禹城市,4,0 -1685,1557,聊城市,3,252000 -1686,1685,东昌府区,4,0 -1687,1685,阳谷县,4,0 -1688,1685,莘县,4,0 -1689,1685,茌平县,4,0 -1690,1685,东阿县,4,0 -1691,1685,冠县,4,0 -1692,1685,高唐县,4,0 -1693,1685,临清市,4,0 -1694,1557,滨州市,3,256600 -1695,1694,滨城区,4,0 -1696,1694,惠民县,4,0 -1697,1694,阳信县,4,0 -1698,1694,无棣县,4,0 -1699,1694,沾化县,4,0 -1700,1694,博兴县,4,0 -1701,1694,邹平县,4,0 -1702,1557,菏泽市,3,255000 -1703,1702,牡丹区,4,0 -1704,1702,曹县,4,0 -1705,1702,单县,4,0 -1706,1702,成武县,4,0 -1707,1702,巨野县,4,0 -1708,1702,郓城县,4,0 -1709,1702,鄄城县,4,0 -1710,1702,定陶县,4,0 -1711,1702,东明县,4,0 -1712,1,河南省,2,0 -1713,1712,郑州市,3,450000 -1714,1713,中原区,4,0 -1715,1713,二七区,4,0 -1716,1713,管城回族区,4,0 -1717,1713,金水区,4,0 -1718,1713,上街区,4,0 -1719,1713,惠济区,4,0 -1720,1713,中牟县,4,0 -1721,1713,巩义市,4,0 -1722,1713,荥阳市,4,0 -1723,1713,新密市,4,0 -1724,1713,新郑市,4,0 -1725,1713,登封市,4,0 -1726,1712,开封市,3,475000 -1727,1726,龙亭区,4,0 -1728,1726,顺河回族区,4,0 -1729,1726,杞县,4,0 -1730,1726,通许县,4,0 -1731,1726,尉氏县,4,0 -1732,1726,开封县,4,0 -1733,1726,兰考县,4,0 -1734,1712,洛阳市,3,471000 -1735,1734,老城区,4,0 -1736,1734,西工区,4,0 -1737,1734,廛河回族区,4,0 -1738,1734,涧西区,4,0 -1739,1734,吉利区,4,0 -1740,1734,洛龙区,4,0 -1741,1734,孟津县,4,0 -1742,1734,新安县,4,0 -1743,1734,栾川县,4,0 -1744,1734,嵩县,4,0 -1745,1734,汝阳县,4,0 -1746,1734,宜阳县,4,0 -1747,1734,洛宁县,4,0 -1748,1734,伊川县,4,0 -1749,1734,偃师市,4,0 -1750,1712,平顶山市,3,467000 -1751,1750,卫东区,4,0 -1752,1750,石龙区,4,0 -1753,1750,湛河区,4,0 -1754,1750,宝丰县,4,0 -1755,1750,叶县,4,0 -1756,1750,鲁山县,4,0 -1757,1750,郏县,4,0 -1758,1750,舞钢市,4,0 -1759,1750,汝州市,4,0 -1760,1712,安阳市,3,454900 -1761,1760,文峰区,4,0 -1762,1760,北关区,4,0 -1763,1760,殷都区,4,0 -1764,1760,龙安区,4,0 -1765,1760,安阳县,4,0 -1766,1760,汤阴县,4,0 -1767,1760,滑县,4,0 -1768,1760,内黄县,4,0 -1769,1760,林州市,4,0 -1770,1712,鹤壁市,3,456600 -1771,1770,鹤山区,4,0 -1772,1770,山城区,4,0 -1773,1770,淇滨区,4,0 -1774,1770,浚县,4,0 -1775,1770,淇县,4,0 -1776,1712,新乡市,3,453000 -1777,1776,红旗区,4,0 -1778,1776,卫滨区,4,0 -1779,1776,凤泉区,4,0 -1780,1776,牧野区,4,0 -1781,1776,新乡县,4,0 -1782,1776,获嘉县,4,0 -1783,1776,原阳县,4,0 -1784,1776,延津县,4,0 -1785,1776,封丘县,4,0 -1786,1776,长垣县,4,0 -1787,1776,卫辉市,4,0 -1788,1776,辉县市,4,0 -1789,1712,焦作市,3,454100 -1790,1789,解放区,4,0 -1791,1789,中站区,4,0 -1792,1789,马村区,4,0 -1793,1789,山阳区,4,0 -1794,1789,修武县,4,0 -1795,1789,博爱县,4,0 -1796,1789,武陟县,4,0 -1797,1789,温县,4,0 -1798,1789,济源市,4,0 -1799,1789,沁阳市,4,0 -1800,1789,孟州市,4,0 -1801,1712,濮阳市,3,457000 -1802,1801,华龙区,4,0 -1803,1801,清丰县,4,0 -1804,1801,南乐县,4,0 -1805,1801,范县,4,0 -1806,1801,台前县,4,0 -1807,1801,濮阳县,4,0 -1808,1712,许昌市,3,461000 -1809,1808,魏都区,4,0 -1810,1808,许昌县,4,0 -1811,1808,鄢陵县,4,0 -1812,1808,襄城县,4,0 -1813,1808,禹州市,4,0 -1814,1808,长葛市,4,0 -1815,1712,漯河市,3,462000 -1816,1815,源汇区,4,0 -1817,1815,郾城区,4,0 -1818,1815,召陵区,4,0 -1819,1815,舞阳县,4,0 -1820,1815,临颍县,4,0 -1821,1712,三门峡市,3,472000 -1822,1821,市辖区,4,0 -1823,1821,湖滨区,4,0 -1824,1821,渑池县,4,0 -1825,1821,陕县,4,0 -1826,1821,卢氏县,4,0 -1827,1821,义马市,4,0 -1828,1821,灵宝市,4,0 -1829,1712,南阳市,3,473000 -1830,1829,宛城区,4,0 -1831,1829,卧龙区,4,0 -1832,1829,南召县,4,0 -1833,1829,方城县,4,0 -1834,1829,西峡县,4,0 -1835,1829,镇平县,4,0 -1836,1829,内乡县,4,0 -1837,1829,淅川县,4,0 -1838,1829,社旗县,4,0 -1839,1829,唐河县,4,0 -1840,1829,新野县,4,0 -1841,1829,桐柏县,4,0 -1842,1829,邓州市,4,0 -1843,1712,商丘市,3,476000 -1844,1843,梁园区,4,0 -1845,1843,睢阳区,4,0 -1846,1843,民权县,4,0 -1847,1843,睢县,4,0 -1848,1843,宁陵县,4,0 -1849,1843,柘城县,4,0 -1850,1843,虞城县,4,0 -1851,1843,夏邑县,4,0 -1852,1843,永城市,4,0 -1853,1712,信阳市,3,464000 -1854,1853,浉河区,4,0 -1855,1853,平桥区,4,0 -1856,1853,罗山县,4,0 -1857,1853,光山县,4,0 -1858,1853,新县,4,0 -1859,1853,商城县,4,0 -1860,1853,固始县,4,0 -1861,1853,潢川县,4,0 -1862,1853,淮滨县,4,0 -1863,1853,息县,4,0 -1864,1712,周口市,3,466000 -1865,1864,川汇区,4,0 -1866,1864,扶沟县,4,0 -1867,1864,西华县,4,0 -1868,1864,商水县,4,0 -1869,1864,沈丘县,4,0 -1870,1864,郸城县,4,0 -1871,1864,淮阳县,4,0 -1872,1864,太康县,4,0 -1873,1864,鹿邑县,4,0 -1874,1864,项城市,4,0 -1875,1712,驻马店市,3,463000 -1876,1875,驿城区,4,0 -1877,1875,西平县,4,0 -1878,1875,上蔡县,4,0 -1879,1875,平舆县,4,0 -1880,1875,正阳县,4,0 -1881,1875,确山县,4,0 -1882,1875,泌阳县,4,0 -1883,1875,汝南县,4,0 -1884,1875,遂平县,4,0 -1885,1875,新蔡县,4,0 -1886,1,湖北省,2,0 -1887,1886,武汉市,3,430000 -1888,1887,江岸区,4,0 -1889,1887,江汉区,4,0 -1890,1887,硚口区,4,0 -1891,1887,汉阳区,4,0 -1892,1887,武昌区,4,0 -1893,1887,洪山区,4,0 -1894,1887,东西湖区,4,0 -1895,1887,汉南区,4,0 -1896,1887,蔡甸区,4,0 -1897,1887,江夏区,4,0 -1898,1887,黄陂区,4,0 -1899,1887,新洲区,4,0 -1900,1886,黄石市,3,435000 -1901,1900,黄石港区,4,0 -1902,1900,西塞山区,4,0 -1903,1900,下陆区,4,0 -1904,1900,铁山区,4,0 -1905,1900,阳新县,4,0 -1906,1900,大冶市,4,0 -1907,1886,十堰市,3,442000 -1908,1907,茅箭区,4,0 -1909,1907,张湾区,4,0 -1910,1907,郧县,4,0 -1911,1907,郧西县,4,0 -1912,1907,竹山县,4,0 -1913,1907,竹溪县,4,0 -1914,1907,房县,4,0 -1915,1907,丹江口市,4,0 -1916,1886,宜昌市,3,443000 -1917,1916,西陵区,4,0 -1918,1916,伍家岗区,4,0 -1919,1916,点军区,4,0 -1920,1916,猇亭区,4,0 -1921,1916,夷陵区,4,0 -1922,1916,远安县,4,0 -1923,1916,兴山县,4,0 -1924,1916,秭归县,4,0 -1925,1916,长阳土家族自治县,4,0 -1926,1916,五峰土家族自治县,4,0 -1927,1916,宜都市,4,0 -1928,1916,当阳市,4,0 -1929,1916,枝江市,4,0 -1930,1886,襄樊市,3,441000 -1931,1930,襄城区,4,0 -1932,1930,樊城区,4,0 -1933,1930,襄阳区,4,0 -1934,1930,南漳县,4,0 -1935,1930,谷城县,4,0 -1936,1930,保康县,4,0 -1937,1930,老河口市,4,0 -1938,1930,枣阳市,4,0 -1939,1930,宜城市,4,0 -1940,1886,鄂州市,3,436000 -1941,1940,梁子湖区,4,0 -1942,1940,华容区,4,0 -1943,1940,鄂城区,4,0 -1944,1886,荆门市,3,448000 -1945,1944,东宝区,4,0 -1946,1944,掇刀区,4,0 -1947,1944,京山县,4,0 -1948,1944,沙洋县,4,0 -1949,1944,钟祥市,4,0 -1950,1886,孝感市,3,432100 -1951,1950,孝南区,4,0 -1952,1950,孝昌县,4,0 -1953,1950,大悟县,4,0 -1954,1950,云梦县,4,0 -1955,1950,应城市,4,0 -1956,1950,安陆市,4,0 -1957,1950,汉川市,4,0 -1958,1886,荆州市,3,434000 -1959,1958,沙市区,4,0 -1960,1958,荆州区,4,0 -1961,1958,公安县,4,0 -1962,1958,监利县,4,0 -1963,1958,江陵县,4,0 -1964,1958,石首市,4,0 -1965,1958,洪湖市,4,0 -1966,1958,松滋市,4,0 -1967,1886,黄冈市,3,438000 -1968,1967,黄州区,4,0 -1969,1967,团风县,4,0 -1970,1967,红安县,4,0 -1971,1967,罗田县,4,0 -1972,1967,英山县,4,0 -1973,1967,浠水县,4,0 -1974,1967,蕲春县,4,0 -1975,1967,黄梅县,4,0 -1976,1967,麻城市,4,0 -1977,1967,武穴市,4,0 -1978,1886,咸宁市,3,437000 -1979,1978,咸安区,4,0 -1980,1978,嘉鱼县,4,0 -1981,1978,通城县,4,0 -1982,1978,崇阳县,4,0 -1983,1978,通山县,4,0 -1984,1978,赤壁市,4,0 -1985,1886,随州市,3,441300 -1986,1985,曾都区,4,0 -1987,1985,广水市,4,0 -1988,1886,恩施土家族苗族自治州,3,445000 -1989,1988,恩施市,4,0 -1990,1988,利川市,4,0 -1991,1988,建始县,4,0 -1992,1988,巴东县,4,0 -1993,1988,宣恩县,4,0 -1994,1988,咸丰县,4,0 -1995,1988,来凤县,4,0 -1996,1988,鹤峰县,4,0 -1997,1886,神农架,3,442400 -1998,1997,仙桃市,4,0 -1999,1997,潜江市,4,0 -2000,1997,天门市,4,0 -2001,1997,神农架林区,4,0 -2002,1,湖南省,2,0 -2003,2002,长沙市,3,410000 -2004,2003,芙蓉区,4,0 -2005,2003,天心区,4,0 -2006,2003,岳麓区,4,0 -2007,2003,开福区,4,0 -2008,2003,雨花区,4,0 -2009,2003,长沙县,4,0 -2010,2003,望城县,4,0 -2011,2003,宁乡县,4,0 -2012,2003,浏阳市,4,0 -2013,2002,株洲市,3,412000 -2014,2013,荷塘区,4,0 -2015,2013,芦淞区,4,0 -2016,2013,石峰区,4,0 -2017,2013,天元区,4,0 -2018,2013,株洲县,4,0 -2019,2013,攸县,4,0 -2020,2013,茶陵县,4,0 -2021,2013,炎陵县,4,0 -2022,2013,醴陵市,4,0 -2023,2002,湘潭市,3,411100 -2024,2023,雨湖区,4,0 -2025,2023,岳塘区,4,0 -2026,2023,湘潭县,4,0 -2027,2023,湘乡市,4,0 -2028,2023,韶山市,4,0 -2029,2002,衡阳市,3,421000 -2030,2029,珠晖区,4,0 -2031,2029,雁峰区,4,0 -2032,2029,石鼓区,4,0 -2033,2029,蒸湘区,4,0 -2034,2029,南岳区,4,0 -2035,2029,衡阳县,4,0 -2036,2029,衡南县,4,0 -2037,2029,衡山县,4,0 -2038,2029,衡东县,4,0 -2039,2029,祁东县,4,0 -2040,2029,耒阳市,4,0 -2041,2029,常宁市,4,0 -2042,2002,邵阳市,3,422000 -2043,2042,双清区,4,0 -2044,2042,大祥区,4,0 -2045,2042,北塔区,4,0 -2046,2042,邵东县,4,0 -2047,2042,新邵县,4,0 -2048,2042,邵阳县,4,0 -2049,2042,隆回县,4,0 -2050,2042,洞口县,4,0 -2051,2042,绥宁县,4,0 -2052,2042,新宁县,4,0 -2053,2042,城步苗族自治县,4,0 -2054,2042,武冈市,4,0 -2055,2002,岳阳市,3,414000 -2056,2055,岳阳楼区,4,0 -2057,2055,云溪区,4,0 -2058,2055,君山区,4,0 -2059,2055,岳阳县,4,0 -2060,2055,华容县,4,0 -2061,2055,湘阴县,4,0 -2062,2055,平江县,4,0 -2063,2055,汨罗市,4,0 -2064,2055,临湘市,4,0 -2065,2002,常德市,3,415000 -2066,2065,武陵区,4,0 -2067,2065,鼎城区,4,0 -2068,2065,安乡县,4,0 -2069,2065,汉寿县,4,0 -2070,2065,澧县,4,0 -2071,2065,临澧县,4,0 -2072,2065,桃源县,4,0 -2073,2065,石门县,4,0 -2074,2065,津市市,4,0 -2075,2002,张家界市,3,427000 -2076,2075,永定区,4,0 -2077,2075,武陵源区,4,0 -2078,2075,慈利县,4,0 -2079,2075,桑植县,4,0 -2080,2002,益阳市,3,413000 -2081,2080,资阳区,4,0 -2082,2080,赫山区,4,0 -2083,2080,南县,4,0 -2084,2080,桃江县,4,0 -2085,2080,安化县,4,0 -2086,2080,沅江市,4,0 -2087,2002,郴州市,3,423000 -2088,2087,北湖区,4,0 -2089,2087,苏仙区,4,0 -2090,2087,桂阳县,4,0 -2091,2087,宜章县,4,0 -2092,2087,永兴县,4,0 -2093,2087,嘉禾县,4,0 -2094,2087,临武县,4,0 -2095,2087,汝城县,4,0 -2096,2087,桂东县,4,0 -2097,2087,安仁县,4,0 -2098,2087,资兴市,4,0 -2099,2002,永州市,3,425000 -2100,2099,芝山区,4,0 -2101,2099,冷水滩区,4,0 -2102,2099,祁阳县,4,0 -2103,2099,东安县,4,0 -2104,2099,双牌县,4,0 -2105,2099,道县,4,0 -2106,2099,江永县,4,0 -2107,2099,宁远县,4,0 -2108,2099,蓝山县,4,0 -2109,2099,新田县,4,0 -2110,2099,江华瑶族自治县,4,0 -2111,2002,怀化市,3,418000 -2112,2111,鹤城区,4,0 -2113,2111,中方县,4,0 -2114,2111,沅陵县,4,0 -2115,2111,辰溪县,4,0 -2116,2111,溆浦县,4,0 -2117,2111,会同县,4,0 -2118,2111,麻阳苗族自治县,4,0 -2119,2111,新晃侗族自治县,4,0 -2120,2111,芷江侗族自治县,4,0 -2121,2111,靖州苗族侗族自治县,4,0 -2122,2111,通道侗族自治县,4,0 -2123,2111,洪江市,4,0 -2124,2002,娄底市,3,417000 -2125,2124,娄星区,4,0 -2126,2124,双峰县,4,0 -2127,2124,新化县,4,0 -2128,2124,冷水江市,4,0 -2129,2124,涟源市,4,0 -2130,2002,湘西土家族苗族自治州,3,416000 -2131,2130,吉首市,4,0 -2132,2130,泸溪县,4,0 -2133,2130,凤凰县,4,0 -2134,2130,花垣县,4,0 -2135,2130,保靖县,4,0 -2136,2130,古丈县,4,0 -2137,2130,永顺县,4,0 -2138,2130,龙山县,4,0 -2139,1,广东省,2,0 -2140,2139,广州市,3,510000 -2141,2140,荔湾区,4,0 -2142,2140,越秀区,4,0 -2143,2140,海珠区,4,0 -2144,2140,天河区,4,0 -2145,2140,芳村区,4,0 -2146,2140,白云区,4,0 -2147,2140,黄埔区,4,0 -2148,2140,番禺区,4,0 -2149,2140,花都区,4,0 -2150,2140,增城市,4,0 -2151,2140,从化市,4,0 -2152,2139,韶关市,3,521000 -2153,2152,武江区,4,0 -2154,2152,浈江区,4,0 -2155,2152,曲江区,4,0 -2156,2152,始兴县,4,0 -2157,2152,仁化县,4,0 -2158,2152,翁源县,4,0 -2159,2152,乳源瑶族自治县,4,0 -2160,2152,新丰县,4,0 -2161,2152,乐昌市,4,0 -2162,2152,南雄市,4,0 -2163,2139,深圳市,3,518000 -2164,2163,罗湖区,4,0 -2165,2163,福田区,4,0 -2166,2163,宝安区,4,0 -2167,2163,龙岗区,4,0 -2168,2163,盐田区,4,0 -2169,2139,珠海市,3,519000 -2170,2169,香洲区,4,0 -2171,2169,斗门区,4,0 -2172,2169,金湾区,4,0 -2173,2139,汕头市,3,515000 -2174,2173,龙湖区,4,0 -2175,2173,金平区,4,0 -2176,2173,濠江区,4,0 -2177,2173,潮阳区,4,0 -2178,2173,潮南区,4,0 -2179,2173,澄海区,4,0 -2180,2173,南澳县,4,0 -2181,2139,佛山市,3,528000 -2182,2181,禅城区,4,0 -2183,2181,南海区,4,0 -2184,2181,顺德区,4,0 -2185,2181,三水区,4,0 -2186,2181,高明区,4,0 -2187,2139,江门市,3,529000 -2188,2187,蓬江区,4,0 -2189,2187,江海区,4,0 -2190,2187,新会区,4,0 -2191,2187,台山市,4,0 -2192,2187,开平市,4,0 -2193,2187,鹤山市,4,0 -2194,2187,恩平市,4,0 -2195,2139,湛江市,3,524000 -2196,2195,赤坎区,4,0 -2197,2195,霞山区,4,0 -2198,2195,坡头区,4,0 -2199,2195,麻章区,4,0 -2200,2195,遂溪县,4,0 -2201,2195,徐闻县,4,0 -2202,2195,廉江市,4,0 -2203,2195,雷州市,4,0 -2204,2195,吴川市,4,0 -2205,2139,茂名市,3,525000 -2206,2205,茂南区,4,0 -2207,2205,茂港区,4,0 -2208,2205,电白县,4,0 -2209,2205,高州市,4,0 -2210,2205,化州市,4,0 -2211,2205,信宜市,4,0 -2212,2139,肇庆市,3,526000 -2213,2212,端州区,4,0 -2214,2212,鼎湖区,4,0 -2215,2212,广宁县,4,0 -2216,2212,怀集县,4,0 -2217,2212,封开县,4,0 -2218,2212,德庆县,4,0 -2219,2212,高要市,4,0 -2220,2212,四会市,4,0 -2221,2139,惠州市,3,516000 -2222,2221,惠城区,4,0 -2223,2221,惠阳区,4,0 -2224,2221,博罗县,4,0 -2225,2221,惠东县,4,0 -2226,2221,龙门县,4,0 -2227,2139,梅州市,3,514000 -2228,2227,梅江区,4,0 -2229,2227,梅县,4,0 -2230,2227,大埔县,4,0 -2231,2227,丰顺县,4,0 -2232,2227,五华县,4,0 -2233,2227,平远县,4,0 -2234,2227,蕉岭县,4,0 -2235,2227,兴宁市,4,0 -2236,2139,汕尾市,3,516600 -2237,2236,海丰县,4,0 -2238,2236,陆河县,4,0 -2239,2236,陆丰市,4,0 -2240,2139,河源市,3,517000 -2241,2240,源城区,4,0 -2242,2240,紫金县,4,0 -2243,2240,龙川县,4,0 -2244,2240,连平县,4,0 -2245,2240,和平县,4,0 -2246,2240,东源县,4,0 -2247,2139,阳江市,3,529500 -2248,2247,江城区,4,0 -2249,2247,阳西县,4,0 -2250,2247,阳东县,4,0 -2251,2247,阳春市,4,0 -2252,2139,清远市,3,511500 -2253,2252,清城区,4,0 -2254,2252,佛冈县,4,0 -2255,2252,阳山县,4,0 -2256,2252,连山壮族瑶族自治县,4,0 -2257,2252,连南瑶族自治县,4,0 -2258,2252,清新县,4,0 -2259,2252,英德市,4,0 -2260,2252,连州市,4,0 -2261,2139,东莞市,3,511700 -2262,2139,中山市,3,528400 -2263,2139,潮州市,3,515600 -2264,2263,湘桥区,4,0 -2265,2263,潮安县,4,0 -2266,2263,饶平县,4,0 -2267,2139,揭阳市,3,522000 -2268,2267,榕城区,4,0 -2269,2267,揭东县,4,0 -2270,2267,揭西县,4,0 -2271,2267,惠来县,4,0 -2272,2267,普宁市,4,0 -2273,2139,云浮市,3,527300 -2274,2273,云城区,4,0 -2275,2273,新兴县,4,0 -2276,2273,郁南县,4,0 -2277,2273,云安县,4,0 -2278,2273,罗定市,4,0 -2279,1,广西壮族自治区,2,0 -2280,2279,南宁市,3,530000 -2281,2280,兴宁区,4,0 -2282,2280,青秀区,4,0 -2283,2280,江南区,4,0 -2284,2280,西乡塘区,4,0 -2285,2280,良庆区,4,0 -2286,2280,邕宁区,4,0 -2287,2280,武鸣县,4,0 -2288,2280,隆安县,4,0 -2289,2280,马山县,4,0 -2290,2280,上林县,4,0 -2291,2280,宾阳县,4,0 -2292,2280,横县,4,0 -2293,2279,柳州市,3,545000 -2294,2293,城中区,4,0 -2295,2293,鱼峰区,4,0 -2296,2293,柳南区,4,0 -2297,2293,柳北区,4,0 -2298,2293,柳江县,4,0 -2299,2293,柳城县,4,0 -2300,2293,鹿寨县,4,0 -2301,2293,融安县,4,0 -2302,2293,融水苗族自治县,4,0 -2303,2293,三江侗族自治县,4,0 -2304,2279,桂林市,3,541000 -2305,2304,秀峰区,4,0 -2306,2304,叠彩区,4,0 -2307,2304,象山区,4,0 -2308,2304,七星区,4,0 -2309,2304,雁山区,4,0 -2310,2304,阳朔县,4,0 -2311,2304,临桂县,4,0 -2312,2304,灵川县,4,0 -2313,2304,全州县,4,0 -2314,2304,兴安县,4,0 -2315,2304,永福县,4,0 -2316,2304,灌阳县,4,0 -2317,2304,龙胜各族自治县,4,0 -2318,2304,资源县,4,0 -2319,2304,平乐县,4,0 -2320,2304,荔蒲县,4,0 -2321,2304,恭城瑶族自治县,4,0 -2322,2279,梧州市,3,543000 -2323,2322,万秀区,4,0 -2324,2322,蝶山区,4,0 -2325,2322,长洲区,4,0 -2326,2322,苍梧县,4,0 -2327,2322,藤县,4,0 -2328,2322,蒙山县,4,0 -2329,2322,岑溪市,4,0 -2330,2279,北海市,3,536000 -2331,2330,海城区,4,0 -2332,2330,银海区,4,0 -2333,2330,铁山港区,4,0 -2334,2330,合浦县,4,0 -2335,2279,防城港市,3,538000 -2336,2335,港口区,4,0 -2337,2335,防城区,4,0 -2338,2335,上思县,4,0 -2339,2335,东兴市,4,0 -2340,2279,钦州市,3,535000 -2341,2340,钦南区,4,0 -2342,2340,钦北区,4,0 -2343,2340,灵山县,4,0 -2344,2340,浦北县,4,0 -2345,2279,贵港市,3,537100 -2346,2345,港北区,4,0 -2347,2345,港南区,4,0 -2348,2345,覃塘区,4,0 -2349,2345,平南县,4,0 -2350,2345,桂平市,4,0 -2351,2279,玉林市,3,537000 -2352,2351,玉州区,4,0 -2353,2351,容县,4,0 -2354,2351,陆川县,4,0 -2355,2351,博白县,4,0 -2356,2351,兴业县,4,0 -2357,2351,北流市,4,0 -2358,2279,百色市,3,533000 -2359,2358,右江区,4,0 -2360,2358,田阳县,4,0 -2361,2358,田东县,4,0 -2362,2358,平果县,4,0 -2363,2358,德保县,4,0 -2364,2358,靖西县,4,0 -2365,2358,那坡县,4,0 -2366,2358,凌云县,4,0 -2367,2358,乐业县,4,0 -2368,2358,田林县,4,0 -2369,2358,西林县,4,0 -2370,2358,隆林各族自治县,4,0 -2371,2279,贺州市,3,542800 -2372,2371,八步区,4,0 -2373,2371,昭平县,4,0 -2374,2371,钟山县,4,0 -2375,2371,富川瑶族自治县,4,0 -2376,2279,河池市,3,547000 -2377,2376,金城江区,4,0 -2378,2376,南丹县,4,0 -2379,2376,天峨县,4,0 -2380,2376,凤山县,4,0 -2381,2376,东兰县,4,0 -2382,2376,罗城仫佬族自治县,4,0 -2383,2376,环江毛南族自治县,4,0 -2384,2376,巴马瑶族自治县,4,0 -2385,2376,都安瑶族自治县,4,0 -2386,2376,大化瑶族自治县,4,0 -2387,2376,宜州市,4,0 -2388,2279,来宾市,3,546100 -2389,2388,兴宾区,4,0 -2390,2388,忻城县,4,0 -2391,2388,象州县,4,0 -2392,2388,武宣县,4,0 -2393,2388,金秀瑶族自治县,4,0 -2394,2388,合山市,4,0 -2395,2279,崇左市,3,532200 -2396,2395,江洲区,4,0 -2397,2395,扶绥县,4,0 -2398,2395,宁明县,4,0 -2399,2395,龙州县,4,0 -2400,2395,大新县,4,0 -2401,2395,天等县,4,0 -2402,2395,凭祥市,4,0 -2403,1,海南省,2,0 -2404,2403,海口市,3,570000 -2405,2404,秀英区,4,0 -2406,2404,龙华区,4,0 -2407,2404,琼山区,4,0 -2408,2404,美兰区,4,0 -2409,2403,三亚市,3,572000 -2410,2409,五指山市,4,0 -2411,2409,琼海市,4,0 -2412,2409,儋州市,4,0 -2413,2409,文昌市,4,0 -2414,2409,万宁市,4,0 -2415,2409,东方市,4,0 -2416,2409,定安县,4,0 -2417,2409,屯昌县,4,0 -2418,2409,澄迈县,4,0 -2419,2409,临高县,4,0 -2420,2409,白沙黎族自治县,4,0 -2421,2409,昌江黎族自治县,4,0 -2422,2409,乐东黎族自治县,4,0 -2423,2409,陵水黎族自治县,4,0 -2424,2409,保亭黎族苗族自治县,4,0 -2425,2409,琼中黎族苗族自治县,4,0 -2426,2409,西沙群岛,4,0 -2427,2409,南沙群岛,4,0 -2428,2409,中沙群岛的岛礁及其海域,4,0 -2429,1,重庆市,2,0 -2430,2429,重庆市,3,400000 -2431,2430,万州区,4,0 -2432,2430,涪陵区,4,0 -2433,2430,渝中区,4,0 -2434,2430,大渡口区,4,0 -2435,2430,沙坪坝区,4,0 -2436,2430,九龙坡区,4,0 -2437,2430,南岸区,4,0 -2438,2430,北碚区,4,0 -2439,2430,万盛区,4,0 -2440,2430,渝北区,4,0 -2441,2430,巴南区,4,0 -2442,2430,黔江区,4,0 -2443,2430,长寿区,4,0 -2444,2430,綦江县,4,0 -2445,2430,潼南县,4,0 -2446,2430,铜梁县,4,0 -2447,2430,大足县,4,0 -2448,2430,荣昌县,4,0 -2449,2430,璧山县,4,0 -2450,2430,梁平县,4,0 -2451,2430,城口县,4,0 -2452,2430,丰都县,4,0 -2453,2430,垫江县,4,0 -2454,2430,武隆县,4,0 -2455,2430,忠县,4,0 -2456,2430,开县,4,0 -2457,2430,云阳县,4,0 -2458,2430,奉节县,4,0 -2459,2430,巫山县,4,0 -2460,2430,巫溪县,4,0 -2461,2430,石柱土家族自治县,4,0 -2462,2430,秀山土家族苗族自治县,4,0 -2463,2430,酉阳土家族苗族自治县,4,0 -2464,2430,彭水苗族土家族自治县,4,0 -2465,2430,江津市,4,0 -2466,2430,合川市,4,0 -2467,2430,永川市,4,0 -2468,2430,南川市,4,0 -2469,1,四川省,2,0 -2470,2469,成都市,3,610000 -2471,2470,锦江区,4,0 -2472,2470,青羊区,4,0 -2473,2470,金牛区,4,0 -2474,2470,武侯区,4,0 -2475,2470,成华区,4,0 -2476,2470,龙泉驿区,4,0 -2477,2470,青白江区,4,0 -2478,2470,新都区,4,0 -2479,2470,温江区,4,0 -2480,2470,金堂县,4,0 -2481,2470,双流县,4,0 -2482,2470,郫县,4,0 -2483,2470,大邑县,4,0 -2484,2470,蒲江县,4,0 -2485,2470,新津县,4,0 -2486,2470,都江堰市,4,0 -2487,2470,彭州市,4,0 -2488,2470,邛崃市,4,0 -2489,2470,崇州市,4,0 -2490,2469,自贡市,3,643000 -2491,2490,自流井区,4,0 -2492,2490,贡井区,4,0 -2493,2490,大安区,4,0 -2494,2490,沿滩区,4,0 -2495,2490,荣县,4,0 -2496,2490,富顺县,4,0 -2497,2469,攀枝花市,3,617000 -2498,2497,东区,4,0 -2499,2497,西区,4,0 -2500,2497,仁和区,4,0 -2501,2497,米易县,4,0 -2502,2497,盐边县,4,0 -2503,2469,泸州市,3,646100 -2504,2503,江阳区,4,0 -2505,2503,纳溪区,4,0 -2506,2503,龙马潭区,4,0 -2507,2503,泸县,4,0 -2508,2503,合江县,4,0 -2509,2503,叙永县,4,0 -2510,2503,古蔺县,4,0 -2511,2469,德阳市,3,618000 -2512,2511,旌阳区,4,0 -2513,2511,中江县,4,0 -2514,2511,罗江县,4,0 -2515,2511,广汉市,4,0 -2516,2511,什邡市,4,0 -2517,2511,绵竹市,4,0 -2518,2469,绵阳市,3,621000 -2519,2518,涪城区,4,0 -2520,2518,游仙区,4,0 -2521,2518,三台县,4,0 -2522,2518,盐亭县,4,0 -2523,2518,安县,4,0 -2524,2518,梓潼县,4,0 -2525,2518,北川羌族自治县,4,0 -2526,2518,平武县,4,0 -2527,2518,江油市,4,0 -2528,2469,广元市,3,628000 -2529,2528,元坝区,4,0 -2530,2528,朝天区,4,0 -2531,2528,旺苍县,4,0 -2532,2528,青川县,4,0 -2533,2528,剑阁县,4,0 -2534,2528,苍溪县,4,0 -2535,2469,遂宁市,3,629000 -2536,2535,船山区,4,0 -2537,2535,安居区,4,0 -2538,2535,蓬溪县,4,0 -2539,2535,射洪县,4,0 -2540,2535,大英县,4,0 -2541,2469,内江市,3,641000 -2542,2541,东兴区,4,0 -2543,2541,威远县,4,0 -2544,2541,资中县,4,0 -2545,2541,隆昌县,4,0 -2546,2469,乐山市,3,614000 -2547,2546,沙湾区,4,0 -2548,2546,五通桥区,4,0 -2549,2546,金口河区,4,0 -2550,2546,犍为县,4,0 -2551,2546,井研县,4,0 -2552,2546,夹江县,4,0 -2553,2546,沐川县,4,0 -2554,2546,峨边彝族自治县,4,0 -2555,2546,马边彝族自治县,4,0 -2556,2546,峨眉山市,4,0 -2557,2469,南充市,3,637000 -2558,2557,顺庆区,4,0 -2559,2557,高坪区,4,0 -2560,2557,嘉陵区,4,0 -2561,2557,南部县,4,0 -2562,2557,营山县,4,0 -2563,2557,蓬安县,4,0 -2564,2557,仪陇县,4,0 -2565,2557,西充县,4,0 -2566,2557,阆中市,4,0 -2567,2469,眉山市,3,612100 -2568,2567,东坡区,4,0 -2569,2567,仁寿县,4,0 -2570,2567,彭山县,4,0 -2571,2567,洪雅县,4,0 -2572,2567,丹棱县,4,0 -2573,2567,青神县,4,0 -2574,2469,宜宾市,3,644000 -2575,2574,翠屏区,4,0 -2576,2574,宜宾县,4,0 -2577,2574,南溪县,4,0 -2578,2574,江安县,4,0 -2579,2574,长宁县,4,0 -2580,2574,高县,4,0 -2581,2574,珙县,4,0 -2582,2574,筠连县,4,0 -2583,2574,兴文县,4,0 -2584,2574,屏山县,4,0 -2585,2469,广安市,3,638000 -2586,2585,广安区,4,0 -2587,2585,岳池县,4,0 -2588,2585,武胜县,4,0 -2589,2585,邻水县,4,0 -2590,2585,华蓥市,4,0 -2591,2469,达州市,3,635000 -2592,2591,通川区,4,0 -2593,2591,达县,4,0 -2594,2591,宣汉县,4,0 -2595,2591,开江县,4,0 -2596,2591,大竹县,4,0 -2597,2591,渠县,4,0 -2598,2591,万源市,4,0 -2599,2469,雅安市,3,625000 -2600,2599,雨城区,4,0 -2601,2599,名山县,4,0 -2602,2599,荥经县,4,0 -2603,2599,汉源县,4,0 -2604,2599,石棉县,4,0 -2605,2599,天全县,4,0 -2606,2599,芦山县,4,0 -2607,2599,宝兴县,4,0 -2608,2469,巴中市,3,635500 -2609,2608,巴州区,4,0 -2610,2608,通江县,4,0 -2611,2608,南江县,4,0 -2612,2608,平昌县,4,0 -2613,2469,资阳市,3,641300 -2614,2613,雁江区,4,0 -2615,2613,安岳县,4,0 -2616,2613,乐至县,4,0 -2617,2613,简阳市,4,0 -2618,2469,阿坝藏族羌族自治州,3,624600 -2619,2618,汶川县,4,0 -2620,2618,理县,4,0 -2621,2618,茂县,4,0 -2622,2618,松潘县,4,0 -2623,2618,九寨沟县,4,0 -2624,2618,金川县,4,0 -2625,2618,小金县,4,0 -2626,2618,黑水县,4,0 -2627,2618,马尔康县,4,0 -2628,2618,壤塘县,4,0 -2629,2618,阿坝县,4,0 -2630,2618,若尔盖县,4,0 -2631,2618,红原县,4,0 -2632,2469,甘孜藏族自治州,3,626000 -2633,2632,康定县,4,0 -2634,2632,泸定县,4,0 -2635,2632,丹巴县,4,0 -2636,2632,九龙县,4,0 -2637,2632,雅江县,4,0 -2638,2632,道孚县,4,0 -2639,2632,炉霍县,4,0 -2640,2632,甘孜县,4,0 -2641,2632,新龙县,4,0 -2642,2632,德格县,4,0 -2643,2632,白玉县,4,0 -2644,2632,石渠县,4,0 -2645,2632,色达县,4,0 -2646,2632,理塘县,4,0 -2647,2632,巴塘县,4,0 -2648,2632,乡城县,4,0 -2649,2632,稻城县,4,0 -2650,2632,得荣县,4,0 -2651,2469,凉山彝族自治州,3,615000 -2652,2651,西昌市,4,0 -2653,2651,木里藏族自治县,4,0 -2654,2651,盐源县,4,0 -2655,2651,德昌县,4,0 -2656,2651,会理县,4,0 -2657,2651,会东县,4,0 -2658,2651,宁南县,4,0 -2659,2651,普格县,4,0 -2660,2651,布拖县,4,0 -2661,2651,金阳县,4,0 -2662,2651,昭觉县,4,0 -2663,2651,喜德县,4,0 -2664,2651,冕宁县,4,0 -2665,2651,越西县,4,0 -2666,2651,甘洛县,4,0 -2667,2651,美姑县,4,0 -2668,2651,雷波县,4,0 -2669,1,贵州省,2,0 -2670,2669,贵阳市,3,55000 -2671,2670,南明区,4,0 -2672,2670,云岩区,4,0 -2673,2670,花溪区,4,0 -2674,2670,乌当区,4,0 -2675,2670,小河区,4,0 -2676,2670,开阳县,4,0 -2677,2670,息烽县,4,0 -2678,2670,修文县,4,0 -2679,2670,清镇市,4,0 -2680,2669,六盘水市,3,553000 -2681,2680,钟山区,4,0 -2682,2680,六枝特区,4,0 -2683,2680,水城县,4,0 -2684,2680,盘县,4,0 -2685,2669,遵义市,3,563000 -2686,2685,红花岗区,4,0 -2687,2685,汇川区,4,0 -2688,2685,遵义县,4,0 -2689,2685,桐梓县,4,0 -2690,2685,绥阳县,4,0 -2691,2685,正安县,4,0 -2692,2685,道真仡佬族苗族自治县,4,0 -2693,2685,务川仡佬族苗族自治县,4,0 -2694,2685,凤冈县,4,0 -2695,2685,湄潭县,4,0 -2696,2685,余庆县,4,0 -2697,2685,习水县,4,0 -2698,2685,赤水市,4,0 -2699,2685,仁怀市,4,0 -2700,2669,安顺市,3,561000 -2701,2700,西秀区,4,0 -2702,2700,平坝县,4,0 -2703,2700,普定县,4,0 -2704,2700,镇宁布依族苗族自治县,4,0 -2705,2700,关岭布依族苗族自治县,4,0 -2706,2700,紫云苗族布依族自治县,4,0 -2707,2669,铜仁地区,3,554300 -2708,2707,铜仁市,4,0 -2709,2707,江口县,4,0 -2710,2707,玉屏侗族自治县,4,0 -2711,2707,石阡县,4,0 -2712,2707,思南县,4,0 -2713,2707,印江土家族苗族自治县,4,0 -2714,2707,德江县,4,0 -2715,2707,沿河土家族自治县,4,0 -2716,2707,松桃苗族自治县,4,0 -2717,2707,万山特区,4,0 -2718,2669,黔西南布依族苗族自治州,3,551500 -2719,2718,兴义市,4,0 -2720,2718,兴仁县,4,0 -2721,2718,普安县,4,0 -2722,2718,晴隆县,4,0 -2723,2718,贞丰县,4,0 -2724,2718,望谟县,4,0 -2725,2718,册亨县,4,0 -2726,2718,安龙县,4,0 -2727,2669,毕节地区,3,551700 -2728,2727,毕节市,4,0 -2729,2727,大方县,4,0 -2730,2727,黔西县,4,0 -2731,2727,金沙县,4,0 -2732,2727,织金县,4,0 -2733,2727,纳雍县,4,0 -2734,2727,威宁彝族回族苗族自治县,4,0 -2735,2727,赫章县,4,0 -2736,2669,黔东南苗族侗族自治州,3,551500 -2737,2736,凯里市,4,0 -2738,2736,黄平县,4,0 -2739,2736,施秉县,4,0 -2740,2736,三穗县,4,0 -2741,2736,镇远县,4,0 -2742,2736,岑巩县,4,0 -2743,2736,天柱县,4,0 -2744,2736,锦屏县,4,0 -2745,2736,剑河县,4,0 -2746,2736,台江县,4,0 -2747,2736,黎平县,4,0 -2748,2736,榕江县,4,0 -2749,2736,从江县,4,0 -2750,2736,雷山县,4,0 -2751,2736,麻江县,4,0 -2752,2736,丹寨县,4,0 -2753,2669,黔南布依族苗族自治州,3,550100 -2754,2753,都匀市,4,0 -2755,2753,福泉市,4,0 -2756,2753,荔波县,4,0 -2757,2753,贵定县,4,0 -2758,2753,瓮安县,4,0 -2759,2753,独山县,4,0 -2760,2753,平塘县,4,0 -2761,2753,罗甸县,4,0 -2762,2753,长顺县,4,0 -2763,2753,龙里县,4,0 -2764,2753,惠水县,4,0 -2765,2753,三都水族自治县,4,0 -2766,1,云南省,2,0 -2767,2766,昆明市,3,650000 -2768,2767,五华区,4,0 -2769,2767,盘龙区,4,0 -2770,2767,官渡区,4,0 -2771,2767,西山区,4,0 -2772,2767,东川区,4,0 -2773,2767,呈贡县,4,0 -2774,2767,晋宁县,4,0 -2775,2767,富民县,4,0 -2776,2767,宜良县,4,0 -2777,2767,石林彝族自治县,4,0 -2778,2767,嵩明县,4,0 -2779,2767,禄劝彝族苗族自治县,4,0 -2780,2767,寻甸回族彝族自治县,4,0 -2781,2767,安宁市,4,0 -2782,2766,曲靖市,3,655000 -2783,2782,麒麟区,4,0 -2784,2782,马龙县,4,0 -2785,2782,陆良县,4,0 -2786,2782,师宗县,4,0 -2787,2782,罗平县,4,0 -2788,2782,富源县,4,0 -2789,2782,会泽县,4,0 -2790,2782,沾益县,4,0 -2791,2782,宣威市,4,0 -2792,2766,玉溪市,3,653100 -2793,2792,红塔区,4,0 -2794,2792,江川县,4,0 -2795,2792,澄江县,4,0 -2796,2792,通海县,4,0 -2797,2792,华宁县,4,0 -2798,2792,易门县,4,0 -2799,2792,峨山彝族自治县,4,0 -2800,2792,新平彝族傣族自治县,4,0 -2801,2792,元江哈尼族彝族傣族自治县,4,0 -2802,2766,保山市,3,678000 -2803,2802,隆阳区,4,0 -2804,2802,施甸县,4,0 -2805,2802,腾冲县,4,0 -2806,2802,龙陵县,4,0 -2807,2802,昌宁县,4,0 -2808,2766,昭通市,3,657000 -2809,2808,昭阳区,4,0 -2810,2808,鲁甸县,4,0 -2811,2808,巧家县,4,0 -2812,2808,盐津县,4,0 -2813,2808,大关县,4,0 -2814,2808,永善县,4,0 -2815,2808,绥江县,4,0 -2816,2808,镇雄县,4,0 -2817,2808,彝良县,4,0 -2818,2808,威信县,4,0 -2819,2808,水富县,4,0 -2820,2766,丽江市,3,674100 -2821,2820,古城区,4,0 -2822,2820,玉龙纳西族自治县,4,0 -2823,2820,永胜县,4,0 -2824,2820,华坪县,4,0 -2825,2820,宁蒗彝族自治县,4,0 -2826,2766,思茅市,3,665000 -2827,2826,翠云区,4,0 -2828,2826,普洱哈尼族彝族自治县,4,0 -2829,2826,墨江哈尼族自治县,4,0 -2830,2826,景东彝族自治县,4,0 -2831,2826,景谷傣族彝族自治县,4,0 -2832,2826,镇沅彝族哈尼族拉祜族自治县,4,0 -2833,2826,江城哈尼族彝族自治县,4,0 -2834,2826,孟连傣族拉祜族佤族自治县,4,0 -2835,2826,澜沧拉祜族自治县,4,0 -2836,2826,西盟佤族自治县,4,0 -2837,2766,临沧市,3,677000 -2838,2837,临翔区,4,0 -2839,2837,凤庆县,4,0 -2840,2837,云县,4,0 -2841,2837,永德县,4,0 -2842,2837,镇康县,4,0 -2843,2837,双江拉祜族佤族布朗族傣族自治县,4,0 -2844,2837,耿马傣族佤族自治县,4,0 -2845,2837,沧源佤族自治县,4,0 -2846,2766,楚雄彝族自治州,3,675000 -2847,2846,楚雄市,4,0 -2848,2846,双柏县,4,0 -2849,2846,牟定县,4,0 -2850,2846,南华县,4,0 -2851,2846,姚安县,4,0 -2852,2846,大姚县,4,0 -2853,2846,永仁县,4,0 -2854,2846,元谋县,4,0 -2855,2846,武定县,4,0 -2856,2846,禄丰县,4,0 -2857,2766,红河哈尼族彝族自治州,3,654400 -2858,2857,个旧市,4,0 -2859,2857,开远市,4,0 -2860,2857,蒙自县,4,0 -2861,2857,屏边苗族自治县,4,0 -2862,2857,建水县,4,0 -2863,2857,石屏县,4,0 -2864,2857,弥勒县,4,0 -2865,2857,泸西县,4,0 -2866,2857,元阳县,4,0 -2867,2857,红河县,4,0 -2868,2857,金平苗族瑶族傣族自治县,4,0 -2869,2857,绿春县,4,0 -2870,2857,河口瑶族自治县,4,0 -2871,2766,文山壮族苗族自治州,3,663000 -2872,2871,文山县,4,0 -2873,2871,砚山县,4,0 -2874,2871,西畴县,4,0 -2875,2871,麻栗坡县,4,0 -2876,2871,马关县,4,0 -2877,2871,丘北县,4,0 -2878,2871,广南县,4,0 -2879,2871,富宁县,4,0 -2880,2766,西双版纳傣族自治州,3,666200 -2881,2880,景洪市,4,0 -2882,2880,勐海县,4,0 -2883,2880,勐腊县,4,0 -2884,2766,大理白族自治州,3,671000 -2885,2884,大理市,4,0 -2886,2884,漾濞彝族自治县,4,0 -2887,2884,祥云县,4,0 -2888,2884,宾川县,4,0 -2889,2884,弥渡县,4,0 -2890,2884,南涧彝族自治县,4,0 -2891,2884,巍山彝族回族自治县,4,0 -2892,2884,永平县,4,0 -2893,2884,云龙县,4,0 -2894,2884,洱源县,4,0 -2895,2884,剑川县,4,0 -2896,2884,鹤庆县,4,0 -2897,2766,德宏傣族景颇族自治州,3,678400 -2898,2897,瑞丽市,4,0 -2899,2897,潞西市,4,0 -2900,2897,梁河县,4,0 -2901,2897,盈江县,4,0 -2902,2897,陇川县,4,0 -2903,2766,怒江傈僳族自治州,3,671400 -2904,2903,泸水县,4,0 -2905,2903,福贡县,4,0 -2906,2903,贡山独龙族怒族自治县,4,0 -2907,2903,兰坪白族普米族自治县,4,0 -2908,2766,迪庆藏族自治州,3,674400 -2909,2908,香格里拉县,4,0 -2910,2908,德钦县,4,0 -2911,2908,维西傈僳族自治县,4,0 -2912,1,西藏自治区,2,0 -2913,2912,拉萨市,3,850000 -2914,2913,城关区,4,0 -2915,2913,林周县,4,0 -2916,2913,当雄县,4,0 -2917,2913,尼木县,4,0 -2918,2913,曲水县,4,0 -2919,2913,堆龙德庆县,4,0 -2920,2913,达孜县,4,0 -2921,2913,墨竹工卡县,4,0 -2922,2912,昌都地区,3,854000 -2923,2922,昌都县,4,0 -2924,2922,江达县,4,0 -2925,2922,贡觉县,4,0 -2926,2922,类乌齐县,4,0 -2927,2922,丁青县,4,0 -2928,2922,察雅县,4,0 -2929,2922,八宿县,4,0 -2930,2922,左贡县,4,0 -2931,2922,芒康县,4,0 -2932,2922,洛隆县,4,0 -2933,2922,边坝县,4,0 -2934,2912,山南地区,3,856000 -2935,2934,乃东县,4,0 -2936,2934,扎囊县,4,0 -2937,2934,贡嘎县,4,0 -2938,2934,桑日县,4,0 -2939,2934,琼结县,4,0 -2940,2934,曲松县,4,0 -2941,2934,措美县,4,0 -2942,2934,洛扎县,4,0 -2943,2934,加查县,4,0 -2944,2934,隆子县,4,0 -2945,2934,错那县,4,0 -2946,2934,浪卡子县,4,0 -2947,2912,日喀则地区,3,857000 -2948,2947,日喀则市,4,0 -2949,2947,南木林县,4,0 -2950,2947,江孜县,4,0 -2951,2947,定日县,4,0 -2952,2947,萨迦县,4,0 -2953,2947,拉孜县,4,0 -2954,2947,昂仁县,4,0 -2955,2947,谢通门县,4,0 -2956,2947,白朗县,4,0 -2957,2947,仁布县,4,0 -2958,2947,康马县,4,0 -2959,2947,定结县,4,0 -2960,2947,仲巴县,4,0 -2961,2947,亚东县,4,0 -2962,2947,吉隆县,4,0 -2963,2947,聂拉木县,4,0 -2964,2947,萨嘎县,4,0 -2965,2947,岗巴县,4,0 -2966,2912,那曲地区,3,852000 -2967,2966,那曲县,4,0 -2968,2966,嘉黎县,4,0 -2969,2966,比如县,4,0 -2970,2966,聂荣县,4,0 -2971,2966,安多县,4,0 -2972,2966,申扎县,4,0 -2973,2966,索县,4,0 -2974,2966,班戈县,4,0 -2975,2966,巴青县,4,0 -2976,2966,尼玛县,4,0 -2977,2912,阿里地区,3,859100 -2978,2977,普兰县,4,0 -2979,2977,札达县,4,0 -2980,2977,噶尔县,4,0 -2981,2977,日土县,4,0 -2982,2977,革吉县,4,0 -2983,2977,改则县,4,0 -2984,2977,措勤县,4,0 -2985,2912,林芝地区,3,860100 -2986,2985,林芝县,4,0 -2987,2985,工布江达县,4,0 -2988,2985,米林县,4,0 -2989,2985,墨脱县,4,0 -2990,2985,波密县,4,0 -2991,2985,察隅县,4,0 -2992,2985,朗县,4,0 -2993,1,陕西省,2,0 -2994,2993,西安市,3,710000 -2995,2994,碑林区,4,0 -2996,2994,莲湖区,4,0 -2997,2994,灞桥区,4,0 -2998,2994,未央区,4,0 -2999,2994,雁塔区,4,0 -3000,2994,阎良区,4,0 -3001,2994,临潼区,4,0 -3002,2994,蓝田县,4,0 -3003,2994,周至县,4,0 -3004,2994,户县,4,0 -3005,2994,高陵县,4,0 -3006,2993,铜川市,3,727000 -3007,3006,王益区,4,0 -3008,3006,印台区,4,0 -3009,3006,耀州区,4,0 -3010,3006,宜君县,4,0 -3011,2993,宝鸡市,3,721000 -3012,3011,渭滨区,4,0 -3013,3011,金台区,4,0 -3014,3011,陈仓区,4,0 -3015,3011,凤翔县,4,0 -3016,3011,岐山县,4,0 -3017,3011,扶风县,4,0 -3018,3011,眉县,4,0 -3019,3011,陇县,4,0 -3020,3011,千阳县,4,0 -3021,3011,麟游县,4,0 -3022,3011,凤县,4,0 -3023,3011,太白县,4,0 -3024,2993,咸阳市,3,712000 -3025,3024,秦都区,4,0 -3026,3024,杨凌区,4,0 -3027,3024,渭城区,4,0 -3028,3024,三原县,4,0 -3029,3024,泾阳县,4,0 -3030,3024,乾县,4,0 -3031,3024,礼泉县,4,0 -3032,3024,永寿县,4,0 -3033,3024,彬县,4,0 -3034,3024,长武县,4,0 -3035,3024,旬邑县,4,0 -3036,3024,淳化县,4,0 -3037,3024,武功县,4,0 -3038,3024,兴平市,4,0 -3039,2993,渭南市,3,714000 -3040,3039,临渭区,4,0 -3041,3039,华县,4,0 -3042,3039,潼关县,4,0 -3043,3039,大荔县,4,0 -3044,3039,合阳县,4,0 -3045,3039,澄城县,4,0 -3046,3039,蒲城县,4,0 -3047,3039,白水县,4,0 -3048,3039,富平县,4,0 -3049,3039,韩城市,4,0 -3050,3039,华阴市,4,0 -3051,2993,延安市,3,716000 -3052,3051,宝塔区,4,0 -3053,3051,延长县,4,0 -3054,3051,延川县,4,0 -3055,3051,子长县,4,0 -3056,3051,安塞县,4,0 -3057,3051,志丹县,4,0 -3058,3051,吴旗县,4,0 -3059,3051,甘泉县,4,0 -3060,3051,富县,4,0 -3061,3051,洛川县,4,0 -3062,3051,宜川县,4,0 -3063,3051,黄龙县,4,0 -3064,3051,黄陵县,4,0 -3065,2993,汉中市,3,723000 -3066,3065,汉台区,4,0 -3067,3065,南郑县,4,0 -3068,3065,城固县,4,0 -3069,3065,洋县,4,0 -3070,3065,西乡县,4,0 -3071,3065,勉县,4,0 -3072,3065,宁强县,4,0 -3073,3065,略阳县,4,0 -3074,3065,镇巴县,4,0 -3075,3065,留坝县,4,0 -3076,3065,佛坪县,4,0 -3077,2993,榆林市,3,719000 -3078,3077,榆阳区,4,0 -3079,3077,神木县,4,0 -3080,3077,府谷县,4,0 -3081,3077,横山县,4,0 -3082,3077,靖边县,4,0 -3083,3077,定边县,4,0 -3084,3077,绥德县,4,0 -3085,3077,米脂县,4,0 -3086,3077,佳县,4,0 -3087,3077,吴堡县,4,0 -3088,3077,清涧县,4,0 -3089,3077,子洲县,4,0 -3090,2993,安康市,3,725000 -3091,3090,汉滨区,4,0 -3092,3090,汉阴县,4,0 -3093,3090,石泉县,4,0 -3094,3090,宁陕县,4,0 -3095,3090,紫阳县,4,0 -3096,3090,岚皋县,4,0 -3097,3090,平利县,4,0 -3098,3090,镇坪县,4,0 -3099,3090,旬阳县,4,0 -3100,3090,白河县,4,0 -3101,2993,商洛市,3,711500 -3102,3101,商州区,4,0 -3103,3101,洛南县,4,0 -3104,3101,丹凤县,4,0 -3105,3101,商南县,4,0 -3106,3101,山阳县,4,0 -3107,3101,镇安县,4,0 -3108,3101,柞水县,4,0 -3109,1,甘肃省,2,0 -3110,3109,兰州市,3,730000 -3111,3110,七里河区,4,0 -3112,3110,西固区,4,0 -3113,3110,安宁区,4,0 -3114,3110,红古区,4,0 -3115,3110,永登县,4,0 -3116,3110,皋兰县,4,0 -3117,3110,榆中县,4,0 -3118,3109,嘉峪关市,3,735100 -3119,3109,金昌市,3,737100 -3120,3119,金川区,4,0 -3121,3119,永昌县,4,0 -3122,3109,白银市,3,730900 -3123,3122,白银区,4,0 -3124,3122,平川区,4,0 -3125,3122,靖远县,4,0 -3126,3122,会宁县,4,0 -3127,3122,景泰县,4,0 -3128,3109,天水市,3,741000 -3129,3128,秦城区,4,0 -3130,3128,北道区,4,0 -3131,3128,清水县,4,0 -3132,3128,秦安县,4,0 -3133,3128,甘谷县,4,0 -3134,3128,武山县,4,0 -3135,3128,张家川回族自治县,4,0 -3136,3109,武威市,3,733000 -3137,3136,凉州区,4,0 -3138,3136,民勤县,4,0 -3139,3136,古浪县,4,0 -3140,3136,天祝藏族自治县,4,0 -3141,3109,张掖市,3,734000 -3142,3141,甘州区,4,0 -3143,3141,肃南裕固族自治县,4,0 -3144,3141,民乐县,4,0 -3145,3141,临泽县,4,0 -3146,3141,高台县,4,0 -3147,3141,山丹县,4,0 -3148,3109,平凉市,3,744000 -3149,3148,崆峒区,4,0 -3150,3148,泾川县,4,0 -3151,3148,灵台县,4,0 -3152,3148,崇信县,4,0 -3153,3148,华亭县,4,0 -3154,3148,庄浪县,4,0 -3155,3148,静宁县,4,0 -3156,3109,酒泉市,3,735000 -3157,3156,肃州区,4,0 -3158,3156,金塔县,4,0 -3159,3156,安西县,4,0 -3160,3156,肃北蒙古族自治县,4,0 -3161,3156,阿克塞哈萨克族自治县,4,0 -3162,3156,玉门市,4,0 -3163,3156,敦煌市,4,0 -3164,3109,庆阳市,3,744500 -3165,3164,西峰区,4,0 -3166,3164,庆城县,4,0 -3167,3164,环县,4,0 -3168,3164,华池县,4,0 -3169,3164,合水县,4,0 -3170,3164,正宁县,4,0 -3171,3164,宁县,4,0 -3172,3164,镇原县,4,0 -3173,3109,定西市,3,743000 -3174,3173,安定区,4,0 -3175,3173,通渭县,4,0 -3176,3173,陇西县,4,0 -3177,3173,渭源县,4,0 -3178,3173,临洮县,4,0 -3179,3173,漳县,4,0 -3180,3173,岷县,4,0 -3181,3109,陇南市,3,742100 -3182,3181,武都区,4,0 -3183,3181,成县,4,0 -3184,3181,文县,4,0 -3185,3181,宕昌县,4,0 -3186,3181,康县,4,0 -3187,3181,西和县,4,0 -3188,3181,礼县,4,0 -3189,3181,徽县,4,0 -3190,3181,两当县,4,0 -3191,3109,临夏回族自治州,3,731100 -3192,3191,临夏市,4,0 -3193,3191,临夏县,4,0 -3194,3191,康乐县,4,0 -3195,3191,永靖县,4,0 -3196,3191,广河县,4,0 -3197,3191,和政县,4,0 -3198,3191,东乡族自治县,4,0 -3199,3191,积石山保安族东乡族撒拉族自治县,4,0 -3200,3109,甘南藏族自治州,3,747000 -3201,3200,合作市,4,0 -3202,3200,临潭县,4,0 -3203,3200,卓尼县,4,0 -3204,3200,舟曲县,4,0 -3205,3200,迭部县,4,0 -3206,3200,玛曲县,4,0 -3207,3200,碌曲县,4,0 -3208,3200,夏河县,4,0 -3209,1,青海省,2,0 -3210,3209,西宁市,3,810000 -3211,3210,城东区,4,0 -3212,3210,城西区,4,0 -3213,3210,城北区,4,0 -3214,3210,大通回族土族自治县,4,0 -3215,3210,湟中县,4,0 -3216,3210,湟源县,4,0 -3217,3209,海东地区,3,810600 -3218,3217,平安县,4,0 -3219,3217,民和回族土族自治县,4,0 -3220,3217,乐都县,4,0 -3221,3217,互助土族自治县,4,0 -3222,3217,化隆回族自治县,4,0 -3223,3217,循化撒拉族自治县,4,0 -3224,3209,海北藏族自治州,3,810300 -3225,3224,门源回族自治县,4,0 -3226,3224,祁连县,4,0 -3227,3224,海晏县,4,0 -3228,3224,刚察县,4,0 -3229,3209,黄南藏族自治州,3,811300 -3230,3229,同仁县,4,0 -3231,3229,尖扎县,4,0 -3232,3229,泽库县,4,0 -3233,3229,河南蒙古族自治县,4,0 -3234,3209,海南藏族自治州,3,813000 -3235,3234,共和县,4,0 -3236,3234,同德县,4,0 -3237,3234,贵德县,4,0 -3238,3234,兴海县,4,0 -3239,3234,贵南县,4,0 -3240,3209,果洛藏族自治州,3,814000 -3241,3240,玛沁县,4,0 -3242,3240,班玛县,4,0 -3243,3240,甘德县,4,0 -3244,3240,达日县,4,0 -3245,3240,久治县,4,0 -3246,3240,玛多县,4,0 -3247,3209,玉树藏族自治州,3,815000 -3248,3247,玉树县,4,0 -3249,3247,杂多县,4,0 -3250,3247,称多县,4,0 -3251,3247,治多县,4,0 -3252,3247,囊谦县,4,0 -3253,3247,曲麻莱县,4,0 -3254,3209,海西蒙古族藏族自治州,3,817000 -3255,3254,格尔木市,4,0 -3256,3254,德令哈市,4,0 -3257,3254,乌兰县,4,0 -3258,3254,都兰县,4,0 -3259,3254,天峻县,4,0 -3260,1,宁夏回族自治区,2,0 -3261,3260,银川市,3,750000 -3262,3261,兴庆区,4,0 -3263,3261,西夏区,4,0 -3264,3261,金凤区,4,0 -3265,3261,永宁县,4,0 -3266,3261,贺兰县,4,0 -3267,3261,灵武市,4,0 -3268,3260,石嘴山市,3,753000 -3269,3268,大武口区,4,0 -3270,3268,惠农区,4,0 -3271,3268,平罗县,4,0 -3272,3260,吴忠市,3,751100 -3273,3272,利通区,4,0 -3274,3272,盐池县,4,0 -3275,3272,同心县,4,0 -3276,3272,青铜峡市,4,0 -3277,3260,固原市,3,756000 -3278,3277,原州区,4,0 -3279,3277,西吉县,4,0 -3280,3277,隆德县,4,0 -3281,3277,泾源县,4,0 -3282,3277,彭阳县,4,0 -3283,3260,中卫市,3,751700 -3284,3283,沙坡头区,4,0 -3285,3283,中宁县,4,0 -3286,3283,海原县,4,0 -3287,1,新疆维吾尔自治区,2,0 -3288,3287,乌鲁木齐市,3,830000 -3289,3288,天山区,4,0 -3290,3288,沙依巴克区,4,0 -3291,3288,水磨沟区,4,0 -3292,3288,头屯河区,4,0 -3293,3288,达坂城区,4,0 -3294,3288,乌鲁木齐县,4,0 -3295,3287,克拉玛依市,3,834000 -3296,3295,独山子区,4,0 -3297,3295,克拉玛依区,4,0 -3298,3295,白碱滩区,4,0 -3299,3295,乌尔禾区,4,0 -3300,3287,吐鲁番地区,3,838000 -3301,3300,吐鲁番市,4,0 -3302,3300,鄯善县,4,0 -3303,3300,托克逊县,4,0 -3304,3287,哈密地区,3,839000 -3305,3304,哈密市,4,0 -3306,3304,巴里坤哈萨克自治县,4,0 -3307,3304,伊吾县,4,0 -3308,3287,昌吉回族自治州,3,831100 -3309,3308,昌吉市,4,0 -3310,3308,阜康市,4,0 -3311,3308,米泉市,4,0 -3312,3308,呼图壁县,4,0 -3313,3308,玛纳斯县,4,0 -3314,3308,奇台县,4,0 -3315,3308,吉木萨尔县,4,0 -3316,3308,木垒哈萨克自治县,4,0 -3317,3287,博尔塔拉蒙古自治州,3,833400 -3318,3317,博乐市,4,0 -3319,3317,精河县,4,0 -3320,3317,温泉县,4,0 -3321,3287,巴音郭楞蒙古自治州,3,841000 -3322,3321,库尔勒市,4,0 -3323,3321,轮台县,4,0 -3324,3321,尉犁县,4,0 -3325,3321,若羌县,4,0 -3326,3321,且末县,4,0 -3327,3321,焉耆回族自治县,4,0 -3328,3321,和静县,4,0 -3329,3321,和硕县,4,0 -3330,3321,博湖县,4,0 -3331,3287,阿克苏地区,3,843000 -3332,3331,阿克苏市,4,0 -3333,3331,温宿县,4,0 -3334,3331,库车县,4,0 -3335,3331,沙雅县,4,0 -3336,3331,新和县,4,0 -3337,3331,拜城县,4,0 -3338,3331,乌什县,4,0 -3339,3331,阿瓦提县,4,0 -3340,3331,柯坪县,4,0 -3341,3287,克孜勒苏柯尔克孜自治州,3,835600 -3342,3341,阿图什市,4,0 -3343,3341,阿克陶县,4,0 -3344,3341,阿合奇县,4,0 -3345,3341,乌恰县,4,0 -3346,3287,喀什地区,3,844000 -3347,3346,喀什市,4,0 -3348,3346,疏附县,4,0 -3349,3346,疏勒县,4,0 -3350,3346,英吉沙县,4,0 -3351,3346,泽普县,4,0 -3352,3346,莎车县,4,0 -3353,3346,叶城县,4,0 -3354,3346,麦盖提县,4,0 -3355,3346,岳普湖县,4,0 -3356,3346,伽师县,4,0 -3357,3346,巴楚县,4,0 -3358,3346,塔什库尔干塔吉克自治县,4,0 -3359,3287,和田地区,3,848000 -3360,3359,和田市,4,0 -3361,3359,和田县,4,0 -3362,3359,墨玉县,4,0 -3363,3359,皮山县,4,0 -3364,3359,洛浦县,4,0 -3365,3359,策勒县,4,0 -3366,3359,于田县,4,0 -3367,3359,民丰县,4,0 -3368,3287,伊犁哈萨克自治州,3,833200 -3369,3368,伊宁市,4,0 -3370,3368,奎屯市,4,0 -3371,3368,伊宁县,4,0 -3372,3368,察布查尔锡伯自治县,4,0 -3373,3368,霍城县,4,0 -3374,3368,巩留县,4,0 -3375,3368,新源县,4,0 -3376,3368,昭苏县,4,0 -3377,3368,特克斯县,4,0 -3378,3368,尼勒克县,4,0 -3379,3287,塔城地区,3,834700 -3380,3379,塔城市,4,0 -3381,3379,乌苏市,4,0 -3382,3379,额敏县,4,0 -3383,3379,沙湾县,4,0 -3384,3379,托里县,4,0 -3385,3379,裕民县,4,0 -3386,3379,和布克赛尔蒙古自治县,4,0 -3387,3287,阿勒泰地区,3,836500 -3388,3387,阿勒泰市,4,0 -3389,3387,布尔津县,4,0 -3390,3387,富蕴县,4,0 -3391,3387,福海县,4,0 -3392,3387,哈巴河县,4,0 -3393,3387,青河县,4,0 -3394,3387,吉木乃县,4,0 -3395,3287,石河子市,3,832000 -3396,3287,阿拉尔市,3,843300 -3397,3287,图木舒克市,3,843900 -3398,3287,五家渠市,3,831300 -3399,1,香港特别行政区,2,0 -3400,3399,香港特别行政区,3,0 -3401,1,澳门特别行政区,2,0 -3402,3401,澳门特别行政区,3,0 -3403,1,台湾省,2,0 -3404,3403,台湾省,3,0 -3405,0,波黑,1,0 -3406,0,直布罗陀,1,0 -3407,0,新喀里多尼亚群岛,1,0 -3408,0,瓦利斯和富图纳群岛,1,0 -3409,0,泽西岛,1,0 -3410,0,黑山,1,0 -3411,0,英属马恩岛,1,0 -3412,0,尼日利亚,1,0 -3413,0,喀麦隆,1,0 -3414,0,加蓬,1,0 -3415,0,乍得,1,0 -3416,0,刚果共和国,1,0 -3417,0,中非共和国,1,0 -3418,0,南苏丹,1,0 -3419,0,赤道几内亚,1,0 -3420,0,毛里塔尼亚,1,0 -3421,0,刚果民主共和国,1,0 -3422,0,留尼汪岛,1,0 -3423,0,格陵兰岛,1,0 -3424,0,法罗群岛,1,0 -3425,0,根西岛,1,0 -3426,3287,自治区直辖县级行政区划,3,830000 -3427,1712,省直辖县级行政区划,3,450000 -3428,1886,省直辖县级行政区划,3,430000 -3429,2766,普洱市,3,665000 -3430,2403,省直辖县级行政区划,3,570000 -3432,0,百慕大群岛,1,0 -3433,0,圣皮埃尔和密克隆群岛,1,0 -3434,0,法属圣马丁,1,0 -3435,0,奥兰群岛,1,0 -3436,0,北马里亚纳群岛,1,0 -3437,0,库拉索,1,0 -3438,0,博内尔岛,1,0 -3439,0,圣马丁岛,1,0 -3440,0,圣巴泰勒米岛,1,0 -3441,0,福克兰群岛,1,0 -3442,0,圣多美和普林西比,1,0 -3443,0,英属印度洋领地,1,0 -3444,0,东萨摩亚,1,0 -3445,0,诺福克岛,1,0 -3446,1886,湖北省省直辖县级行政区划,3,430000 -3447,1712,河南省省直辖县级行政区划,3,450000 -3448,2403,海南省省直辖县级行政区划,3,570000 -3449,3287,新疆维吾尔自治区省直辖县级行政区划,3,830000 -3450,3260,宁夏回族自治区省直辖县级行政区划,3,750000 -3451,673,辽宁省省直辖县级行政区划,3,110000 -3452,2139,广东省省直辖县级行政区划,3,518000 -3453,1131,浙江省省直辖县级行政区划,3,310000 -3454,786,吉林省省直辖县级行政区划,3,130000 -3455,1232,安徽省省直辖县级行政区划,3,230000 -3456,2993,陕西省省直辖县级行政区划,3,710000 -3457,254,河北省省直辖县级行政区划,3,50000 -3458,1014,江苏省省直辖县级行政区划,3,210000 -3459,853,黑龙江省省直辖县级行政区划,3,150000 -3460,1447,江西省省直辖县级行政区划,3,330000 -3461,2669,贵州省省直辖县级行政区划,3,55000 -3462,2002,湖南省省直辖县级行政区划,3,423000 diff --git a/data/ip.test.txt b/data/ip.test.txt deleted file mode 100644 index 09e0266..0000000 --- a/data/ip.test.txt +++ /dev/null @@ -1,25 +0,0 @@ -1.0.0.0|1.0.0.255|澳大利亚|0|0|0|0 -1.0.1.0|1.0.3.255|中国|0|福建省|福州市|电信 -1.0.4.0|1.0.7.255|澳大利亚|0|维多利亚|墨尔本|0 -1.0.8.0|1.0.15.255|中国|0|广东省|广州市|电信 -1.0.16.0|1.0.31.255|日本|0|0|0|0 -1.0.32.0|1.0.63.255|中国|0|广东省|广州市|电信 -1.0.64.0|1.0.79.255|日本|0|广岛县|0|0 -1.0.80.0|1.0.127.255|日本|0|冈山县|0|0 -1.0.128.0|1.0.128.255|泰国|0|清莱府|0|TOT -1.0.129.0|1.0.132.191|泰国|0|曼谷|曼谷|TOT -1.0.132.192|1.0.132.255|泰国|0|Nakhon-Ratchasima|0|TOT -1.0.133.0|1.0.133.255|泰国|0|素攀武里府|0|TOT -1.0.134.0|1.0.134.255|泰国|0|曼谷|曼谷|TOT -1.0.135.0|1.0.135.127|泰国|0|华富里府|0|TOT -1.0.135.128|1.0.135.255|泰国|0|素攀武里府|0|TOT -1.0.136.0|1.0.136.255|泰国|0|龙仔厝府|0|TOT -1.0.137.0|1.0.137.255|泰国|0|大城府|0|TOT -1.0.138.0|1.0.143.255|泰国|0|曼谷|曼谷|TOT -1.0.144.0|1.0.159.255|泰国|0|春蓬府|0|TOT -1.0.160.0|1.0.162.255|泰国|0|洛坤府|0|TOT -1.0.163.0|1.0.163.255|泰国|0|春蓬府|0|TOT -1.0.164.0|1.0.164.63|泰国|0|0|0|TOT -1.0.164.64|1.0.164.127|泰国|0|普吉府|0|TOT -1.0.164.128|1.0.170.255|泰国|0|0|0|TOT -1.0.171.0|1.0.175.255|泰国|0|攀牙府|0|TOT diff --git a/data/ip2region_v4.xdb b/data/ip2region_v4.xdb deleted file mode 100644 index 6f86c7d9b5ea27ecbe808e2be198bac1aaee0b92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11042429 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%0AxdRZi&BepqSP<9P@0l? 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