Full Code of lionsoul2014/ip2region for AI

master 1209b72452ad cached
272 files
149.0 MB
281.0k tokens
980 symbols
1 requests
Download .txt
Showing preview only (1,045K chars total). Download the full file or copy to clipboard to get everything.
Repository: lionsoul2014/ip2region
Branch: master
Commit: 1209b72452ad
Files: 272
Total size: 149.0 MB

Directory structure:
gitextract_eppf0yhb/

├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── README_zh.md
├── binding/
│   ├── c/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── main.c
│   │   ├── test_util.c
│   │   ├── xdb_api.h
│   │   ├── xdb_searcher.c
│   │   └── xdb_util.c
│   ├── cpp/
│   │   ├── .gitignore
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── src/
│   │   │   ├── base.cc
│   │   │   ├── base.h
│   │   │   ├── bench.cc
│   │   │   ├── bench.h
│   │   │   ├── edit.cc
│   │   │   ├── edit.h
│   │   │   ├── header.cc
│   │   │   ├── header.h
│   │   │   ├── ip.cc
│   │   │   ├── ip.h
│   │   │   ├── make.cc
│   │   │   ├── make.h
│   │   │   ├── search.cc
│   │   │   └── search.h
│   │   └── test/
│   │       ├── bench.cc
│   │       ├── edit_v4.cc
│   │       ├── edit_v6.cc
│   │       ├── header.cc
│   │       ├── make.cc
│   │       └── search.cc
│   ├── csharp/
│   │   ├── .editorconfig
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── IP2Region.Net/
│   │   │   ├── Abstractions/
│   │   │   │   └── ISearcher.cs
│   │   │   ├── Extensions/
│   │   │   │   └── ServiceCollectionExtensions.cs
│   │   │   ├── IP2Region.Net.csproj
│   │   │   ├── Internal/
│   │   │   │   ├── CacheStrategyFactory.cs
│   │   │   │   ├── ContentCacheStrategy.cs
│   │   │   │   ├── FileCacheStrategy.cs
│   │   │   │   ├── ICacheStrategy.cs
│   │   │   │   └── VectorIndexCacheStrategy.cs
│   │   │   └── XDB/
│   │   │       ├── CachePolicy.cs
│   │   │       ├── Searcher.cs
│   │   │       ├── Util.cs
│   │   │       └── XdbVersion.cs
│   │   ├── IP2Region.Net.BenchMark/
│   │   │   ├── Benmarks.cs
│   │   │   ├── IP2Region.Net.BenchMark.csproj
│   │   │   └── Program.cs
│   │   ├── IP2Region.Net.Test/
│   │   │   ├── IP2Region.Net.Test.csproj
│   │   │   ├── SearcherTest.cs
│   │   │   ├── UtilTest.cs
│   │   │   └── XdbTest.cs
│   │   ├── IP2Region.Net.slnx
│   │   └── README.md
│   ├── erlang/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── benchmarks/
│   │   │   └── xdb-benchmark.sh
│   │   ├── include/
│   │   │   └── ip2region.hrl
│   │   ├── priv/
│   │   │   └── dummy
│   │   ├── rebar.config
│   │   ├── src/
│   │   │   ├── ip2region.app.src
│   │   │   ├── ip2region_app.erl
│   │   │   ├── ip2region_sup.erl
│   │   │   ├── ip2region_util.erl
│   │   │   ├── ip2region_worker.erl
│   │   │   ├── xdb.erl
│   │   │   └── xdb_benchmark.erl
│   │   └── test/
│   │       └── xdb_test.erl
│   ├── golang/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   ├── service/
│   │   │   ├── config.go
│   │   │   ├── config_test.go
│   │   │   ├── ip2region.go
│   │   │   ├── ip2region_test.go
│   │   │   ├── searcher_pool.go
│   │   │   └── searcher_pool_test.go
│   │   └── xdb/
│   │       ├── header.go
│   │       ├── searcher.go
│   │       ├── util.go
│   │       ├── util_test.go
│   │       └── version.go
│   ├── java/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── org/
│   │       │           └── lionsoul/
│   │       │               └── ip2region/
│   │       │                   ├── SearcherTest.java
│   │       │                   ├── service/
│   │       │                   │   ├── Config.java
│   │       │                   │   ├── ConfigBuilder.java
│   │       │                   │   ├── InvalidConfigException.java
│   │       │                   │   ├── Ip2Region.java
│   │       │                   │   └── SearcherPool.java
│   │       │                   └── xdb/
│   │       │                       ├── Header.java
│   │       │                       ├── IPv4.java
│   │       │                       ├── IPv6.java
│   │       │                       ├── InetAddressException.java
│   │       │                       ├── LittleEndian.java
│   │       │                       ├── Log.java
│   │       │                       ├── LongByteArray.java
│   │       │                       ├── Searcher.java
│   │       │                       ├── Util.java
│   │       │                       ├── Version.java
│   │       │                       └── XdbException.java
│   │       └── test/
│   │           └── java/
│   │               └── org/
│   │                   └── lionsoul/
│   │                       └── ip2region/
│   │                           ├── service/
│   │                           │   ├── ConfigTest.java
│   │                           │   ├── Ip2RegionTest.java
│   │                           │   └── SearcherPoolTest.java
│   │                           └── xdb/
│   │                               ├── BufferTest.java
│   │                               ├── IPv4Test.java
│   │                               ├── LittleEndianTest.java
│   │                               ├── UtilTest.java
│   │                               └── VersionTest.java
│   ├── javascript/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── index.d.ts
│   │   ├── index.js
│   │   ├── package.json
│   │   ├── searcher.js
│   │   ├── tests/
│   │   │   ├── bench.app.js
│   │   │   ├── search.app.js
│   │   │   ├── searcher.test.js
│   │   │   └── util.test.js
│   │   ├── tsconfig.json
│   │   └── util.js
│   ├── lua/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.lua
│   │   ├── search_test.lua
│   │   ├── util_test.lua
│   │   └── xdb_searcher.lua
│   ├── lua_c/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.lua
│   │   ├── search_test.lua
│   │   ├── util_test.lua
│   │   └── xdb_searcher.c
│   ├── nginx/
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── config
│   │   ├── src/
│   │   │   ├── ngx_http_ip2region_module.c
│   │   │   └── ngx_http_ip2region_module.h
│   │   └── t/
│   │       └── http_ip2region.t
│   ├── nodejs/
│   │   └── README.md
│   ├── php/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── batch_test.php
│   │   ├── bench_test.php
│   │   ├── search_test.php
│   │   └── xdb/
│   │       ├── Searcher.class.php
│   │       └── util_test.php
│   ├── python/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── MANIFEST.in
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.py
│   │   ├── ip2region/
│   │   │   ├── __init__.py
│   │   │   ├── searcher.py
│   │   │   └── util.py
│   │   ├── search_test.py
│   │   ├── setup.py
│   │   └── util_test.py
│   ├── rust/
│   │   ├── Cargo.toml
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── example/
│   │   │   ├── Cargo.toml
│   │   │   └── src/
│   │   │       ├── cmd.rs
│   │   │       └── main.rs
│   │   └── ip2region/
│   │       ├── Cargo.toml
│   │       ├── benches/
│   │       │   └── search.rs
│   │       └── src/
│   │           ├── error.rs
│   │           ├── ip_value.rs
│   │           ├── lib.rs
│   │           └── searcher.rs
│   └── typescript/
│       └── README.md
├── data/
│   ├── ip2region_v4.xdb
│   ├── ip2region_v6.xdb
│   ├── ipv4_source.txt
│   ├── ipv6_source.txt
│   └── sample/
│       ├── github-issue-196.fix
│       ├── github-issue-200.fix
│       ├── github-issue-243.fix
│       ├── github-issue-287.bug
│       ├── ip.test.txt
│       ├── segments.tests
│       └── segments.tests.mixed
└── maker/
    ├── c/
    │   └── ReadMe.md
    ├── cpp/
    │   └── README.md
    ├── csharp/
    │   ├── .gitignore
    │   ├── IP2RegionMaker/
    │   │   ├── IP2RegionMaker.csproj
    │   │   ├── Program.cs
    │   │   ├── Properties/
    │   │   │   └── PublishProfiles/
    │   │   │       └── FolderProfile.pubxml
    │   │   └── XDB/
    │   │       ├── IndexPolicy.cs
    │   │       ├── Maker.cs
    │   │       ├── Segment.cs
    │   │       └── Util.cs
    │   ├── IP2RegionMaker.Test/
    │   │   ├── IP2RegionMaker.Test.csproj
    │   │   ├── Usings.cs
    │   │   └── UtilTest.cs
    │   ├── README.md
    │   └── README_zh.md
    ├── golang/
    │   ├── Dockerfile
    │   ├── Makefile
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── cmd/
    │   │   ├── bench.go
    │   │   ├── edit.go
    │   │   ├── generate.go
    │   │   ├── process.go
    │   │   ├── search.go
    │   │   └── util.go
    │   ├── go.mod
    │   ├── go.sum
    │   ├── main.go
    │   ├── make.bat
    │   └── xdb/
    │       ├── editor.go
    │       ├── index.go
    │       ├── maker.go
    │       ├── processor.go
    │       ├── searcher.go
    │       ├── segment.go
    │       ├── util.go
    │       ├── util_test.go
    │       └── version.go
    ├── java/
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── pom.xml
    │   └── src/
    │       ├── main/
    │       │   └── java/
    │       │       └── org/
    │       │           └── lionsoul/
    │       │               └── ip2region/
    │       │                   ├── MakerApp.java
    │       │                   └── xdb/
    │       │                       ├── IPv4.java
    │       │                       ├── IPv6.java
    │       │                       ├── IndexPolicy.java
    │       │                       ├── InvalidInetAddressException.java
    │       │                       ├── LittleEndian.java
    │       │                       ├── Log.java
    │       │                       ├── Maker.java
    │       │                       ├── Segment.java
    │       │                       ├── Util.java
    │       │                       └── Version.java
    │       └── test/
    │           └── java/
    │               └── org/
    │                   └── lionsoul/
    │                       └── ip2region/
    │                           └── xdb/
    │                               ├── IndexPolicyTest.java
    │                               ├── LittleEndianTest.java
    │                               ├── MakerTest.java
    │                               ├── SegmentTest.java
    │                               ├── UtilTest.java
    │                               └── VersionTest.java
    ├── python/
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── main.py
    │   └── xdb/
    │       ├── __init__.py
    │       ├── index.py
    │       ├── maker.py
    │       ├── segment.py
    │       └── util.py
    └── rust/
        ├── README.md
        ├── README_zh.md
        └── maker/
            ├── Cargo.toml
            └── src/
                ├── command.rs
                ├── error.rs
                ├── header.rs
                ├── lib.rs
                ├── main.rs
                ├── maker.rs
                └── segment.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================


================================================
FILE: .gitignore
================================================
*.class
*.out
*.o
*.pyc
*~
*.log
*.la
*.so
*.iml
META-INF/
.DS_Store

# Binary Files #
*.jar
!dbMaker-*.jar

# ignore all xdb except the one in ./data/
*.xdb
!/data/*.xdb

# Package Files #
.settings/
.classpath
.project

# vim swp file #
*.swp
.idea
.vscode

# binding
/binding/java/classes/
/binding/java/doc/
/binding/java/target/
/binding/java/*.jar

# python
/**/__pycache__

# clang
/binding/c/xdb_searcher
/binding/c/test_util
/binding/c/cmake-build-debug

# lua/luc_c
/binding/lua_c/cmake-build-debug

# golang
/binding/golang/searcher
/binding/golang/xdb_searcher
/binding/golang/golang

# rust
Cargo.lock
target


# VS ignore cases
/**/*.sln
/binding/c#/**/.vs/
/binding/c#/**/packages
/binding/c#/**/bin
/binding/c#/**/obj

# Nodejs
/binding/nodejs/tests/unitTests/__snapshots__
/binding/nodejs/coverage
/binding/nodejs/node_modules
/binding/nodejs/.nyc_output
/binging/nodejs/package-lock.json

# Javascript
/binding/javascript/tests/unitTests/__snapshots__
/binding/javascript/coverage
/binding/javascript/node_modules
/binding/javascript/.nyc_output
/binding/javascript/package-lock.json

# maker
## golang
/maker/golang/dbmaker
/maker/golang/xdb_maker
/maker/golang/golang

#erlang
/binding/erlang/_build
/binding/erlang/doc

#vscode
.vscode
build


================================================
FILE: LICENSE.md
================================================
                                 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<chenxin619315@gmail.com>

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.


================================================
FILE: README.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region

[ip2region](https://ip2region.net) - is an offline IP address localization library and IP localization data management framework. It supports both `IPv4` and `IPv6` with query efficiency at the 10-microsecond level. It provides `xdb` data generation and query client implementations for many mainstream programming languages.


# Features

### 1. Offline Localization Library

The project itself provides raw data for both IPv4 (`data/ipv4_source.txt`) and IPv6 (`data/ipv6_source.txt`), along with corresponding xdb files (`data/ip2region_v4.xdb` and `data/ip2region_v6.xdb`) to achieve city-level query localization. The field format is: `Country|Province|City|ISP|iso-alpha2-code`. Localization information for China is entirely in Chinese, while regional information for non-China areas is entirely in English.

### 2. Data Management Framework

`xdb` supports hundreds of millions of IP data segment rows. Region information supports full customization. The region information of the built-in data is fixed in the format: `Country|Province|City|ISP|iso-alpha2-Code`. You can append data for specific business needs to the region, such as: GPS information/International Standard Regional Codes/Zip codes, etc. In other words, you can fully use ip2region to manage your own IP localization data.

### 3. Data Deduplication and Compression

The `xdb` format generation program automatically processes the input raw data, checks and completes the merging of adjacent IP segments, and performs deduplication and compression of identical regional information.

### 4. High-Speed Query Response

Even for queries based entirely on the `xdb` file, the single query response time is at the 10-microsecond level. Memory-accelerated queries can be enabled through the following two methods:

1. `vIndex` Index Caching: Uses a fixed `512KiB` of memory to cache vector index data, reducing one disk IO operation and maintaining average query efficiency within 100 microseconds.
2. Entire `xdb` File Caching: Loads the entire `xdb` file into memory. Memory usage is equal to the `xdb` file size. There is no disk IO operation, maintaining 10-microsecond level query efficiency.

### 5. Unified Query Interface

`xdb` provides version-compatible query implementations. A unified API can simultaneously provide queries for both IPv4 and IPv6 data and return unified data.


# `xdb` Query

For API introductions, usage documentation, and test programs, please refer to the README introduction under the corresponding `searcher` query client. All query binding implementations are as follows:

| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](binding/golang/README.md)         | golang query client          | :white_check_mark: | :white_check_mark: |
| [PHP](binding/php/README.md)               | php query client             | :white_check_mark: | :white_check_mark: |
| [Java](binding/java/README.md)             | java query client            | :white_check_mark: | :white_check_mark: |
| [C](binding/c/README.md)                   | C[std=c99] query client      | :white_check_mark: | :white_check_mark: |
| [Lua_c](binding/lua_c/README.md)           | lua c extension query client | :white_check_mark: | :white_check_mark: |
| [Lua](binding/lua/README.md)               | lua query client             | :white_check_mark: | :white_check_mark: |
| [Rust](binding/rust/README.md)             | rust query client            | :white_check_mark: | :white_check_mark: |
| [Python](binding/python/README.md)         | python query client          | :white_check_mark: | :white_check_mark: |
| [Javascript](binding/javascript/README.md) | javascript query client      | :white_check_mark: | :white_check_mark: |
| [Csharp](binding/csharp)                   | csharp query client          | :white_check_mark: | :white_check_mark: |
| [Erlang](binding/erlang/README.md)         | erlang query client          | :white_check_mark: | :x:                |
| [Nginx](binding/nginx)                     | nginx extension query client | :white_check_mark: | :white_check_mark: |
| [C++](binding/cpp/README.md)               | C++ query client             | :white_check_mark: | :white_check_mark: |

The following toolchain implementations are contributed by community developers via third-party repositories:

| Language | Description |
| --- | --- |
| [ip2region-composer](https://github.com/zoujingli/ip2region)    | php composer management client       |
| [ip2region-ts](https://github.com/Steven-Qiang/ts-ip2region2)   | node.js addon management client      |
| [ruby-ip2region](https://github.com/jicheng1014/ruby-ip2region) | ruby xdb query client implementation |
| [Ip2regionTool](https://github.com/orestonce/Ip2regionTool)     | ip2region data conversion tool       |


# `xdb` Generation

For API introductions, usage documentation, and test programs, please refer to the README documents under the following `maker` generation programs:

| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](maker/golang/README.md) | golang xdb generation program | :white_check_mark: | :white_check_mark: |
| [Java](maker/java/README.md)     | java xdb generation program   | :white_check_mark: | :white_check_mark: |
| [Python](maker/python/README.md) | python xdb generation program | :white_check_mark: | :x:                |
| [Csharp](maker/csharp/README.md) | csharp xdb generation program | :white_check_mark: | :x:                |
| [Rust](maker/rust/README.md)     | rust xdb generation program   | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp)                 | C++ xdb generation program    | :white_check_mark: | :white_check_mark: |


# `xdb` Update

The core of the ip2region project lies in **researching the design and implementation of IP data storage and fast querying**. The raw data `./data/ipv4_source.txt` and `./data/ipv6_source.txt` included in the project are updated irregularly. For scenarios with high requirements for data accuracy and update frequency, it is recommended to purchase commercial offline data from the [Ip2Region Community](https://ip2region.net/products/offline) or third-party vendors. You can try to update the data yourself using the following methods:

### Manual Editing and Updating

You can modify the data yourself based on the raw IP data provided by ip2region in `./data/ipv4_source.txt` and `./data/ipv6_source.txt` using the editing tools provided by ip2region. Currently, the data sources include:

1. Data provided by the ip2region community (please refer to the official account at the bottom for community notifications)
2. Project Issues tagged with `[Data_Updates]`
3. Other custom data: e.g., data provided by customers, data obtained through GPS and WIFI positioning, or legal and compliant data from other platforms.

For instructions on using the raw IP data editing tools, please refer to the README documents under the following `maker` generation programs:

| Language | Description | IPv4 Support | IPv6 Support |
| --- | --- | --- | --- |
| [Golang](maker/golang/README.md#xdb-data-editing) | golang IP raw data editor | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp/README.md)                        | C++ IP raw data editor    | :white_check_mark: | :white_check_mark: |

### Detection Automatic Update

If you want to update data via your own API or data source, you can refer to the update algorithm based on the "Detection Algorithm" shared in the following videos to write your own update program:

1. [Data Update Implementation Video Sharing - part1](https://www.bilibili.com/video/BV1934y1E7Q5/)
2. [Data Update Implementation Video Sharing - part2](https://www.bilibili.com/video/BV1pF411j7Aw/)


# Official Community

The Ip2Region official community was officially launched on `2025/06/12`. On one hand, it provides stable [commercial offline data](https://ip2region.net/products/offline) services. On the other hand, it facilitates the strengthening of the IP toolchain and data services outside the core code, such as [usage documentation](https://ip2region.net/doc/), [query testing](https://ip2region.net/search/demo), and data correction. For more information and services regarding the community, please visit the [Ip2Region Official Community](https://ip2region.net/).


# Related Remarks

### 1. xdb Technical Documents:

1. xdb Data Structure Analysis: ["ip2region xdb - Data Structure Description"](https://ip2region.net/doc/xdb/structure)
2. xdb Query Process Analysis: ["ip2region xdb - Query Process Description"](https://ip2region.net/doc/xdb/search)
3. xdb Generation Process Analysis: ["ip2region xdb - Generation Process Description"](https://ip2region.net/doc/xdb/generate)
4. xdb File Generation Tutorial: ["ip2region xdb - File Generation Tutorial"](https://ip2region.net/doc/data/xdb_make)
5. xdb Concurrent Safety Query: ["ip2region xdb - Concurrent Safety Query"](https://ip2region.net/doc/xdb/concurrent)
6. xdb Data Update Method: ["ip2region Data Update and Use of xdb Data Editor"](https://mp.weixin.qq.com/s/cZH5qIn4E5rQFy6N32RCzA)

### 3. Technical Information Blogs

1. WeChat Official Account - lionsoul-org, the author's active technical sharing channel
2. [Ip2Region Official Community](https://ip2region.net)


================================================
FILE: README_zh.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region

[ip2region](https://ip2region.net) - 是一个离线IP地址定位库和IP定位数据管理框架,同时支持 `IPv4` 和 `IPv6` ,10微秒级别的查询效率,提供了众多主流编程语言的 `xdb` 数据生成和查询客户端实现。


# 项目特性

### 1、离线定位库

项目本身同时了提供了一份 IPv4 (`data/ipv4_source.txt`) 和 IPv6 (`data/ipv6_source.txt`) 的原始数据和对应的 xdb 文件(`data/ip2region_v4.xdb` 和 `data/ip2region_v6.xdb`) 用于实现精确到城市的查询定位功能,字段格式为:`国家|省份|城市|ISP|iso-alpha2-code(国家两字母简称)`,中国的定位信息全部为中文,非中国地区的地域信息全部为英文。

### 2、数据管理框架

`xdb` 支持亿级别的 IP 数据段行数,region 信息支持完全自定义,自带数据的 region 信息固定了格式为:`国家|省份|城市|ISP|iso-alpha2-Code`,你可以在 region 中追加特定业务需求的数据,例如:GPS信息/国际统一地域信息编码/邮编等。也就是你完全可以使用 ip2region 来管理你自己的 IP 定位数据。

### 3、数据去重和压缩

`xdb` 格式生成程序会自动处理输入的原始数据,检查并且完成相连 IP 段的的合并以及相同地域信息的去重和压缩。

### 4、极速查询响应

即使是完全基于 `xdb` 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:

1. `vIndex` 索引缓存 :使用固定的 `512KiB` 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作,保持平均查询效率稳定在100微秒之内。
2. `xdb` 整个文件缓存:将整个 `xdb` 文件全部加载到内存,内存占用等同于 `xdb` 文件大小,无磁盘 IO 操作,保持10微秒级别的查询效率。

### 5、统一的查询接口

`xdb` 提供了版本兼容的查询实现,一个统一的 API 可以同时提供对 IPv4 和 IPv6 数据的查询并且返回统一的数据。


# `xdb` 查询

API 介绍,使用文档和测试程序请参考对应 `searcher` 查询客户端下的 README 介绍,全部查询 binding 实现情况如下:
| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](binding/golang/README_zh.md)         | golang 查询客户端       | :white_check_mark: | :white_check_mark: |
| [PHP](binding/php/README_zh.md)               | php 查询客户端          | :white_check_mark: | :white_check_mark: |
| [Java](binding/java/README_zh.md)             | java 查询客户端         | :white_check_mark: | :white_check_mark: |
| [C](binding/c/README_zh.md)                   | C[std=c99] 查询客户端   | :white_check_mark: | :white_check_mark: |
| [Lua_c](binding/lua_c/README_zh.md)           | lua c 扩展查询客户端    | :white_check_mark: | :white_check_mark: |
| [Lua](binding/lua/README_zh.md)               | lua 查询客户端          | :white_check_mark: | :white_check_mark: |
| [Rust](binding/rust/README_zh.md)             | rust 查询客户端         | :white_check_mark: | :white_check_mark: |
| [Python](binding/python/README_zh.md)         | python 查询客户端       | :white_check_mark: | :white_check_mark: |
| [Javascript](binding/javascript/README_zh.md) | javascript 查询客户端   | :white_check_mark: | :white_check_mark: |
| [Csharp](binding/csharp)                      | csharp 查询客户端       | :white_check_mark: | :white_check_mark: |
| [Erlang](binding/erlang/README_zh.md)         | erlang 查询客户端       | :white_check_mark: | :x:                |
| [Nginx](binding/nginx)                        | nginx 扩展查询客户端    | :white_check_mark: | :white_check_mark: |
| [C++](binding/cpp/README_zh.md)               | C++ 查询客户端          | :white_check_mark: | :white_check_mark: |


以下工具链实现由社区开发者通过第三方仓库贡献:
| 编程语言 | 描述 |
| --- | --- |
| [ip2region-composer](https://github.com/zoujingli/ip2region)    | php composer 管理客户端 |
| [ip2region-ts](https://github.com/Steven-Qiang/ts-ip2region2)   | node.js addon 管理客户端|
| [ruby-ip2region](https://github.com/jicheng1014/ruby-ip2region) | ruby xdb 查询客户端实现 |
| [Ip2regionTool](https://github.com/orestonce/Ip2regionTool)     | ip2region 数据转换工具  |


# `xdb` 生成

API 介绍,使用文档和测试程序请参考如下 `maker` 生成程序下的 README 文档:

| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](maker/golang/README_zh.md) | golang xdb 生成程序  | :white_check_mark: | :white_check_mark: |
| [Java](maker/java/README_zh.md)     | java xdb 生成程序    | :white_check_mark: | :white_check_mark: |
| [Python](maker/python/README_zh.md) | python xdb 生成程序  | :white_check_mark: | :x:                |
| [Csharp](maker/csharp/README_zh.md) | csharp xdb 生成程序  | :white_check_mark: | :x:                |
| [Rust](maker/rust/README_zh.md)     | rust xdb 生成程序    | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp)                    | C++ xdb 生成程序     | :white_check_mark: | :white_check_mark: |


# `xdb` 更新

ip2region 项目的核心在于 <b>研究 IP 数据的存储和快速查询的设计和实现</b>, 项目自带的 `./data/ipv4_source.txt` 和 `./data/ipv6_source.txt` 原始数据不定期更新,对于数据精度和更新频率要求很高的使用场景建议到 [Ip2Region社区](https://ip2region.net/products/offline) 或者第三方购买商用离线数据,你可以使用如下几种方式来尝试自己更新数据:

### 手动编辑更新
你可以基于 ip2region 自带的 `./data/ipv4_source.txt` 和 `./data/ipv6_source.txt` 原始 IP 数据用 ip2region 提供的编辑工具来自己修改,目前数据源有如下几种方式:
1. ip2region 社区提供的数据(请参考地底部的公众号关注社区通知)
2. ip2region Github/Gitee 中带有 `[数据源补充]` 标签的 Issue
3. 其他自定义数据:例如客户提供的数据,或者通过 GPS 和 WIFI 定位得到的数据,或者来自其他平台的合法合规的数据

原始 IP 数据编辑工具使用方法请参考如下的 `maker` 生成程序下的 README 文档:
| 编程语言 | 描述 | IPv4 支持 | IPv6 支持 |
| --- | --- | --- | --- |
| [Golang](maker/golang/README_zh.md#xdb-数据编辑) | golang IP 原始数据编辑器 | :white_check_mark: | :white_check_mark: |
| [C++](maker/cpp)                                 | C++ IP 原始数据编辑器    | :white_check_mark: | :white_check_mark: |


### 检测自动更新
如果你想通过你自己的 API 或数据源来更新数据,你可以参考以下视频分享的 `基于检测算法` 的更新算法来自己编写一个更新程序:
1. [数据更新实现视频分享 - part1](https://www.bilibili.com/video/BV1934y1E7Q5/)
2. [数据更新实现视频分享 - part2](https://www.bilibili.com/video/BV1pF411j7Aw/)


# 官方社区
Ip2Region 官方社区正式上线于 `2025/06/12` 日,一方面提供了稳定的 [商用离线数据](https://ip2region.net/products/offline) 服务,另一方面便于在核心代码外强化 IP 工具链和数据服务,例如 [使用文档](https://ip2region.net/doc/),[查询测试](https://ip2region.net/search/demo),数据纠错等,更多关于社区的信息和服务请访问 [Ip2Region 官方社区](https://ip2region.net/)。


# 相关备注

### 1、xdb 技术文档:
1. xdb 数据结构分析:[“ip2region xdb-数据结构描述“](https://ip2region.net/doc/xdb/structure)
2. xdb 查询过程分析:[“ip2region xdb-查询过程描述”](https://ip2region.net/doc/xdb/search)
3. xdb 生成过程分析:[“ip2region xdb-生成过程描述”](https://ip2region.net/doc/xdb/generate)
4. xdb 文件生成教程:[“ip2region xdb-文件生成教程”](https://ip2region.net/doc/data/xdb_make)
5. xdb 并发安全查询:[“ip2region xdb-并发安全查询”](https://ip2region.net/doc/xdb/concurrent)
6. xdb 数据更新方法:[“ip2region 数据更新和 xdb 数据编辑器的使用”](https://mp.weixin.qq.com/s/cZH5qIn4E5rQFy6N32RCzA)

### 3、技术信息博客
1. 微信公众号 - lionsoul-org,作者活跃的技术分享渠道
2. [Ip2Region 官方社区](https://ip2region.net)


================================================
FILE: binding/c/Makefile
================================================
all: xdb_searcher test_util

xdb_searcher: xdb_api.h xdb_util.c xdb_searcher.c main.c
	gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher

test_util: xdb_api.h xdb_util.c test_util.c
	gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util

xdb_searcher.o: xdb_searcher.c
	gcc -std=c99 -Wall -c xdb_searcher.c

xdb_util.o: xdb_util.c
	gcc -std=c99 -Wall -c xdb_util.c

xdb_searcher_lib: xdb_util.o xdb_searcher.o
	mkdir -p build/lib
	mkdir -p build/include
	ar -rc build/lib/libxdb.a `find . -name "*.o"`
	cp xdb_api.h build/include

clean:
	find ./ -name \*.o  | xargs rm -f
	find ./ -name test_util | xargs rm -f
	find ./ -name xdb_searcher | xargs rm -f
	rm -rf build

.PHONY: all clean xdb_searcher test_util


================================================
FILE: binding/c/README.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region c Query Client

# Usage

### About Query API

The prototype of the Query API is as follows:

```c
// Query via string IP
int xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);
// Query via binary IP returned by xdb_parse_ip
int xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);
```

If the query fails, a non-`0` error code will be returned. If the query is successful, the `region` information string can be obtained from `xdb_region_buffer_t`. If the input IP cannot be found, `xdb_region_buffer_t` will receive an empty string `""`.

### About IPv4 and IPv6

This xdb query client implementation supports both IPv4 and IPv6 queries. The usage is as follows:

```c
#include "xdb_api.h";

// For IPv4: Set xdb path to the v4 xdb file, specify IP version as IPv4
const char *db_path  = "../../data/ip2region_v4.xdb";  // or your ipv4 xdb path
xdb_version_t *version = XDB_IPv4;

// For IPv6: Set xdb path to the v6 xdb file, specify IP version as IPv6
const char *db_path  = "../../data/ip2region_v6.xdb";  // or your ipv6 xdb path
xdb_version_t *version = XDB_IPv6;

// The IP version of the xdb specified by db_path must be consistent with the version, otherwise an error will occur during query execution
// Note: The following demonstration directly uses db_path and version variables
```

### XDB File Verification

It is recommended that you proactively verify the applicability of the xdb file, as some future new features may cause the current Searcher version to be incompatible with the xdb file you are using. Verification can avoid unpredictable errors during runtime. You do not need to verify every time; for example, verify when the service starts or manually call a command to confirm version matching. Do not run verification every time a Searcher is created, as this will affect query response speed, especially in high-concurrency scenarios.

```c
#include "xdb_api.h";

int errcode = xdb_verify(db_path);
if ($err != 0) {
    // Applicability verification failed!!!
    // The current query client implementation is not suitable for querying the xdb file specified by db_path.
    // You should stop the service and use a suitable xdb file or upgrade to a Searcher implementation compatible with db_path.
    printf("failed to verify xdb file `%s`, errcode: %d\n", db_path, errcode);
    return;
}

// Verification passed, the current Searcher can be safely used for query operations on the xdb pointed to by dbPath
```

### File-Based Query

```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_searcher_t searcher;
    char region_buffer[512] = {'\0'};
    xdb_region_buffer_t region;

    // Initialize region_buffer_t using region_buffer from stack space
    int err = xdb_region_buffer_init(&region, region_buffer, sizeof(region_buffer));
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 1;
    }

    // Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1. Initialize xdb query object from db_path.
    // @Note: Use the db_path and version described above to create the searcher
    err = xdb_new_with_file_only(version, &searcher, db_path);
    if (err != 0) {
        printf("failed to create xdb searcher from `%s` with errno=%d\n", db_path, err);
        return 1;
    }

    // 2. Call search API to query, both IPv4 and IPv6 are supported.
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // Clean up memory resources for region info; must be called after every search
    xdb_region_buffer_free(&region);

    // Note: For concurrent use, each thread needs to define and initialize its own searcher query object independently.

    // 3. Close xdb searcher
    xdb_close(&searcher);
    xdb_clean_winsock();    // Call on Windows
    return 0;
}
```

### Caching `VectorIndex`

We can pre-load VectorIndex data from the xdb file and cache it globally. Using the global VectorIndex cache every time a Searcher object is created can reduce a fixed IO operation, thereby accelerating queries and reducing IO pressure.

```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_vector_index_t *v_index;
    xdb_searcher_t searcher;
    xdb_region_buffer_t region;

    // Initialize region_buffer with NULL to let it manage memory allocation automatically
    int err = xdb_region_buffer_init(&region, NULL, 0);
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 0;
    }

    // Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1. Load VectorIndex from the db_path described above.
    // Obtain v_index to create a global cache for subsequent repeated use.
    // Note: v_index does not need to be loaded every time; it is recommended to load it once at service startup as a global resource.
    v_index = xdb_load_vector_index_from_file(db_path);
    if (v_index == NULL) {
        printf("failed to load vector index from `%s`\n", db_path);
        return 1;
    }

    // 2. Use the global VectorIndex variable to create an xdb searcher with VectorIndex cache.
    // @Note: Use the db_path and version described above to create the searcher
    err = xdb_new_with_vector_index(version, &searcher, db_path, v_index);
    if (err != 0) {
        printf("failed to create vector index cached searcher with errcode=%d\n", err);
        return 2;
    }


    // 3. Call search API to query, both IPv4 and IPv6 are supported
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // Clean up memory resources for region info; must be called after every search
    xdb_region_buffer_free(&region);


    // Note: For concurrent use, each thread needs to define and initialize its own searcher query object independently.

    // 4. Close xdb searcher; if the service is being shut down, the memory for v_index also needs to be freed.
    xdb_close(&searcher);
    xdb_close_vector_index(v_index);
    xdb_clean_winsock();
    return 0;
}
```

### Caching the Entire `xdb` File

We can also pre-load the entire xdb file into memory and then create a query object based on this data to achieve fully memory-based queries, similar to the previous memory search.

```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_content_t *c_buffer;
    xdb_searcher_t searcher;
    xdb_region_buffer_t region;

    // Initialize region_buffer with NULL to let it manage memory allocation automatically
    int err = xdb_region_buffer_init(&region, NULL, 0);
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 0;
    }


    // Initialize winsock when the service starts; no need to call repeatedly, only needed on Windows systems
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1. Load the entire xdb data from the db_path described above.
    c_buffer = xdb_load_content_from_file(db_path);
    if (v_index == NULL) {
        printf("failed to load xdb content from `%s`\n", db_path);
        return 1;
    }

    // 2. Use the global c_buffer variable to create a fully memory-based xdb query object.
    // @Note: Use the version described above to create the searcher.
    err = xdb_new_with_buffer(version, &searcher, c_buffer);
    if (err != 0) {
        printf("failed to create content cached searcher with errcode=%d\n", err);
        return 2;
    }

    // 3. Call search API to query, both IPv4 and IPv6 are supported
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // Clean up memory resources for region info; must be called after every search
    xdb_region_buffer_free(&region);


    // Note: For concurrent use, xdb query objects created this way can be safely used for concurrency.
    // It is recommended to create them when the service starts and then use them safely in parallel until the service shuts down.

    // 4. Close xdb searcher; memory for c_buffer needs to be freed when shutting down the service.
    xdb_close(&searcher);
    xdb_close_content(c_buffer);
    xdb_clean_winsock();
    return 0;
}
```

### About Storage of Location Information

In older implementations, search-related functions relied on a specified `region_buffer` memory to store location information, which had significant limitations.
The new implementation provides an `xdb_region_buffer_t` object to manage these memory allocations. You can still specify a fixed `region_buffer` to create memory management for the region; this is suitable when the maximum length of your location information is known, which helps reduce memory fragmentation during runtime. If the length of the location information is uncertain or if your program is not suited for pre-allocating a block of memory, you can initialize `xdb_region_buffer_t` by specifying `NULL`. In this case, the object will automatically manage memory allocation, making it suitable for storing location information of any length, though this approach will certainly increase memory fragmentation over long-term operation.

```c
// 1. Create region_buffer by specifying a memory block
char buffer[512];
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, buffer, sizeof(buffer));
if (err != 0) {
    // Initialization failed
    printf("failed to init region buffer width errcode=%d", err);
    return;
}

// 2. Create region_buffer by specifying NULL to let it allocate memory as needed automatically
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
    // Initialization failed
    printf("failed to init region buffer width errcode=%d", err);
    return;
}


// Note: After each query call, you must manually call the function to free memory.
// The search function will report an error if used with uncleaned region info.
xdb_region_buffer_free(&region);
```

# Compiling the Test Program

Compile and obtain the `xdb_searcher` executable as follows:

```bash
# cd to the c binding root directory
➜  c git:(master) ✗ make
gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
```

# Query Testing

Test queries against xdb via the `xdb_searcher search` command:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search
./xdb_searcher search [command options]
options:
 --db string              ip2region binary xdb file path
 --cache-policy string    cache policy: file/vectorIndex/content
```

Example: performing IPv4 query testing using the default data/ip2region_v4.xdb:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v4.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
type 'quit' to exit
ip2region>> 1.2.3.4
{region: Australia|Queensland|Brisbane|0|AU, io_count: 5, took: 39 μs}
ip2region>> 120.229.45.2
{region: 中国|广东省|深圳市|移动|CN, io_count: 3, took: 13 μs}
```

Example: performing IPv6 query testing using the default data/ip2region_v6.xdb:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v6.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex)
type 'quit' to exit
ip2region>> ::
{region: , io_count: 1, took: 38 μs}
ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff
{region: United States|Florida|Miami|velia.net Internetdienste GmbH|US, io_count: 14, took: 76 μs}
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|电信|CN, io_count: 8, took: 42 μs}
```

Enter an IP to perform a query; enter `quit` to exit the test program. You can also set `cache-policy` to file/vectorIndex/content respectively to test the efficiency of the three different cache implementations.

# bench Testing

Perform bench testing via the `xdb_searcher bench` command. This ensures there are no errors in the query program and the `xdb` file, while also providing average query performance through a large number of queries:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench                                  
./xdb_searcher bench [command options]
options:
 --db string              ip2region binary xdb file path
 --src string             source ip text file path
 --cache-policy string    cache policy: file/vectorIndex/content
```

Example: performing IPv4 bench testing via the default data/ip2region_v4.xdb and data/ipv4_source.txt:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt
Bench finished, {cache_policy: vectorIndex, total: 1367686, took: 7.640s, cost: 5 μs/op}
```

Example: performing IPv6 bench testing via the default data/ip2region_v6.xdb and data/ipv6_source.txt:

```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt
Bench finished, {cache_policy: vectorIndex, total: 34159862, took: 857.750s, cost: 24 μs/op}
```

You can set the `cache-policy` parameter to test the efficiency of different cache mechanisms (file/vectorIndex/content). @Note: Please ensure that the `src` file used for benching is the same source file used to generate the corresponding `xdb` file.


================================================
FILE: binding/c/README_zh.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region c 查询客户端


# 使用方式

### 关于查询 API
查询 API 的原型如下:
```c
// 通过字符串 IP 进行查询
int xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);
// 通过 xdb_parse_ip 返回的二进制 IP 进行查询
int xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);
```
如果查询失败将会返回非 `0` 的错误代码,如果查询成功 xdb_region_buffer_t 可以获取到字符串的 `region` 信息,如果输入的 IP 找不到相关的信息,xdb_region_buffer_t 将会得到一个空的字符串 `""`。

### 关于 IPv4 和 IPv6
该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下:
```c
#include "xdb_api.h";

// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 IPv4
const char *db_path  = "../../data/ip2region_v4.xdb";  // 或者你的 ipv4 xdb 的路径
xdb_version_t *version = XDB_IPv4;

// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 IPv6
const char *db_path  = "../../data/ip2region_v6.xdb";  // 或者你的 ipv6 xdb 路径
xdb_version_t *version = XDB_IPv6;

// db_path 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错
// 备注:以下演示直接使用 db_path 和 version 变量
```

### XDB 文件验证
建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。
```c
#include "xdb_api.h";

int errcode = xdb_verify(db_path);
if ($err != 0) {
    // 适用性验证失败!!!
    // 当前查询客户端实现不适用于 db_path 指定的 xdb 文件的查询.
    // 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 db_path 的 Searcher 实现。
    printf("failed to verify xdb file `%s`, errcode: %d\n", db_path, errcode);
    return;
}

// 验证通过,当前使用的 Searcher 可以安全的用于对 dbPath 指向的 xdb 的查询操作
```

### 完全基于文件的查询

```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_searcher_t searcher;
    char region_buffer[512] = {'\0'};
    xdb_region_buffer_t region;

    // 使用栈空间的 region_buffer 初始化 region_buffer_t
    int err = xdb_region_buffer_init(&region, region_buffer, sizeof(region_buffer));
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 1;
    }

    // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1、从 db_path 初始化 xdb 查询对象.
    // @Note: 使用顶部描述的 db_path 和 version 来创建 searcher
    err = xdb_new_with_file_only(version, &searcher, db_path);
    if (err != 0) {
        printf("failed to create xdb searcher from `%s` with errno=%d\n", db_path, err);
        return 1;
    }

    // 2、调用 search API 查询,IPv4 和 IPv6 都支持.
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // 清理 region 信息的内存资源,每次 search 之后都得调用
    xdb_region_buffer_free(&region);

    // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。

    // 3、关闭 xdb 查询器
    xdb_close(&searcher);
    xdb_clean_winsock();    // windows 下调用
    return 0;
}
```

### 缓存 `VectorIndex` 索引

我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。
```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_vector_index_t *v_index;
    xdb_searcher_t searcher;
    xdb_region_buffer_t region;

    // 使用 NULL 初始化 region_buffer,让其自动管理内存的分配
    int err = xdb_region_buffer_init(&region, NULL, 0);
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 0;
    }

    // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1、从顶部描述的 db_path 加载 VectorIndex 索引。
    // 得到 v_index 做成全局缓存,便于后续反复使用。
    // 注意:v_index 不需要每次都加载,建议在服务启动的时候加载一次,然后做成全局资源。
    v_index = xdb_load_vector_index_from_file(db_path);
    if (v_index == NULL) {
        printf("failed to load vector index from `%s`\n", db_path);
        return 1;
    }

    // 2、使用全局的 VectorIndex 变量创建带 VectorIndex 缓存的 xdb 查询对象.
    // @Note: 使用顶部描述的 db_path 和 version 来创建 searcher
    err = xdb_new_with_vector_index(version, &searcher, db_path, v_index);
    if (err != 0) {
        printf("failed to create vector index cached searcher with errcode=%d\n", err);
        return 2;
    }


    // 3、调用 search API 查询,IPv4 和 IPv6 都支持
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // 清理 region 信息的内存资源,每次 search 之后都得调用
    xdb_region_buffer_free(&region);


    // 备注:并发使用,每一个线程需要单独定义并且初始化一个 searcher 查询对象。

    // 4、关闭 xdb 查询器,如果是要关闭服务,也需要释放 v_index 的内存。
    xdb_close(&searcher);
    xdb_close_vector_index(v_index);
    xdb_clean_winsock();
    return 0;
}
```

### 缓存整个 `xdb` 文件

我们也可以预先加载整个 xdb 文件到内存,然后基于这个数据创建查询对象来实现完全基于内存的查询,类似之前的 memory search。
```c
#include <stdio.h>
#include "xdb_api.h"

int main(int argc, char *argv[]) {
    xdb_content_t *c_buffer;
    xdb_searcher_t searcher;
    xdb_region_buffer_t region;

    // 使用 NULL 初始化 region_buffer,让其自动管理内存的分配
    int err = xdb_region_buffer_init(&region, NULL, 0);
    if (err != 0) {
        printf("failed to init the region buffer with errcode=%d\n", err);
        return 0;
    }


    // 在服务启动的时候初始化 winsock,不需要重复调用,只需要在 windows 系统下调用
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return 1;
    }

    // 1、从 顶部描述的 db_path 加载整个 xdb 的数据。
    c_buffer = xdb_load_content_from_file(db_path);
    if (v_index == NULL) {
        printf("failed to load xdb content from `%s`\n", db_path);
        return 1;
    }

    // 2、使用全局的 c_buffer 变量创建一个完全基于内存的 xdb 查询对象.
    // @Note: 使用顶部描述的 version 来创建 searcher.
    err = xdb_new_with_buffer(version, &searcher, c_buffer);
    if (err != 0) {
        printf("failed to create content cached searcher with errcode=%d\n", err);
        return 2;
    }

    // 3、调用 search API 查询,IPv4 和 IPv6 都支持
    const char *ip_string = "1.2.3.4";
    // ip_string = "240e:3b7:3272:d8d0:db09:c067:8d59:539e"; // IPv6

    long cost_time = 0, s_time = xdb_now();
    err = xdb_search_by_string(&searcher, ip_string, &region);
    cost_time = (int) (xdb_now() - s_time);
    if (err != 0) {
        printf("failed search(%s) with errno=%d\n", ip_string, err);
    } else {
        printf("{region: %s, took: %d μs}", region.value, cost_time);
    }

    // 清理 region 信息的内存资源,每次 search 之后都得调用
    xdb_region_buffer_free(&region);


    // 备注:并发使用,使用这种方式创建的 xdb 查询对象可以安全用于并发。
    // 建议在服务启动的时候创建好,然后一直安全并发使用,直到服务关闭。

    // 4、关闭 xdb 查询器,关闭服务的时候需要释放 c_buffer 的内存。
    xdb_close(&searcher);
    xdb_close_content(c_buffer);
    xdb_clean_winsock();
    return 0;
}
```

### 关于定位信息的存储
在旧版本的实现中,search相关的函数都是依靠指定一个 `region_buffer` 内存来用于存储地域信息,这种方式还是有很大的局限性。
新的实现提供了一个 `xdb_region_buffer_t` 对象来管理这些内存的分配,你依然可以指定一个固定的 `region_buffer` 来创建 region 的内存管理,这个情况适合当你的地域信息的最大长度是可知的,这种方式可以减少运行过程中内存的碎片堆积。如果地域信息的长度不确定或者你的程序不适合提前分配一块内存来管理,你可以通过指定 NULL 的方式来初始化 `xdb_region_buffer_t`,这样对象会自动管理内存的分配,也适合任意长度的地域信息的存储,不过这种方式在长期的运行过程中肯定会增加内存碎片的堆积。
```c
// 1, 通过指定一块内存来创建 region_buffer
char buffer[512];
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, buffer, sizeof(buffer));
if (err != 0) {
    // 初始化失败
    printf("failed to init region buffer width errcode=%d", err);
    return;
}

// 2,通过指定 NULL 来创建 region_buffer,让其自动按需分配内存
xdb_region_buffer_t region;
int err = xdb_region_buffer_init(&region, NULL, 0);
if (err != 0) {
    // 初始化失败
    printf("failed to init region buffer width errcode=%d", err);
    return;
}


// 备注:在每次调用 search 完成 IP 定位信息的查询后,你需要手动调用函数来释放内存 .
// search 函数使用未经清理的 region 信息会报错。
xdb_region_buffer_free(&region);
```


# 测试程序编译

通过如下方式编译得到 xdb_searcher 可执行程序:
```bash
# cd 到 c binding 根目录
➜  c git:(master) ✗ make
gcc -std=c99 -Wall -O2 -I./ xdb_util.c xdb_searcher.c main.c -o xdb_searcher
gcc -std=c99 -Wall -O2 -I./ xdb_util.c test_util.c -o test_util
```


# 查询测试

通过 `xdb_searcher search` 命令来测试对 xdb 的查询:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search
./xdb_searcher search [command options]
options:
 --db string              ip2region binary xdb file path
 --cache-policy string    cache policy: file/vectorIndex/content
```

例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 查询测试:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v4.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
type 'quit' to exit
ip2region>> 1.2.3.4
{region: Australia|Queensland|Brisbane|0|AU, io_count: 5, took: 39 μs}
ip2region>> 120.229.45.2
{region: 中国|广东省|深圳市|移动|CN, io_count: 3, took: 13 μs}
```

例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 查询测试:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher search --db=../../data/ip2region_v6.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex)
type 'quit' to exit
ip2region>> ::
{region: , io_count: 1, took: 38 μs}
ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff
{region: United States|Florida|Miami|velia.net Internetdienste GmbH|US, io_count: 14, took: 76 μs}
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|电信|CN, io_count: 8, took: 42 μs}
```

输入 ip 即可进行查询,输入 quit 即可退出测试程序。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同的缓存实现的效率。



# bench 测试

通过 `xdb_searcher bench` 命令来进行 bench 测试,一方面确保查询程序和 `xdb` 文件没有错误,另一方面可以通过大量的查询得到平均的查询性能:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench                                  
./xdb_searcher bench [command options]
options:
 --db string              ip2region binary xdb file path
 --src string             source ip text file path
 --cache-policy string    cache policy: file/vectorIndex/content
```

例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 来进行 IPv4 的 bench 测试:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt
Bench finished, {cache_policy: vectorIndex, total: 1367686, took: 7.640s, cost: 5 μs/op}
```

例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 来进行 IPv6 的 bench 测试:
```bash
➜  c git:(fr_c_ipv6) ✗ ./xdb_searcher bench --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt
Bench finished, {cache_policy: vectorIndex, total: 34159862, took: 857.750s, cost: 24 μs/op}
```

可以设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 不同缓存实现机制的效率。 @Note:请注意 bench 使用的 src 文件需要是生成对应的 xdb 文件相同的源文件。


================================================
FILE: binding/c/main.c
================================================
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.

// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date   2022/06/28

#include "stdio.h"
#include "xdb_api.h"

struct searcher_test_entry {
    xdb_searcher_t searcher;
    xdb_vector_index_t *v_index;
    xdb_content_t *c_buffer;

    // xdb region buffer
    // char region_buffer[256];
    xdb_region_buffer_t region;
};
typedef struct searcher_test_entry searcher_test_t;

int init_searcher_test(searcher_test_t *test, char *db_path, char *cache_policy) {
    int err, errcode = 0;
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return -1;
    }

    // auto detect the version from the xdb header
    xdb_header_t *header = xdb_load_header(handle);
    if (header == NULL) {
        printf("failed to load header from `%s`\n", db_path);
        errcode = 1;
        goto defer;
    }

    // verify the current xdb
    err = xdb_verify_from_header(handle, header);
    if (err != 0) {
        printf("failed to verify xdb file `%s` with errno=%d\n", db_path, err);
        errcode = 2;
        goto defer;
    }

    xdb_version_t *version = xdb_version_from_header(header);
    if (version == NULL) {
        printf("failed to load version from header\n");
        errcode = 3;
        goto defer;
    }

    test->v_index = NULL;
    test->c_buffer = NULL;

    if (strcmp(cache_policy, "file") == 0) {
        err = xdb_new_with_file_only(version, &test->searcher, db_path);
        if (err != 0) {
            printf("failed to create searcher with errcode=%d\n", err);
            errcode = 4;
            goto defer;
        }
    } else if (strcmp(cache_policy, "vectorIndex") == 0) {
        test->v_index = xdb_load_vector_index_from_file(db_path);
        if (test->v_index == NULL) {
            printf("failed to load vector index from `%s`\n", db_path);
            errcode = 4;
            goto defer;
        }

        err = xdb_new_with_vector_index(version, &test->searcher, db_path, test->v_index);
        if (err != 0) {
            printf("failed to create vector index cached searcher with errcode=%d\n", err);
            errcode = 5;
            goto defer;
        }
    } else if (strcmp(cache_policy, "content") == 0) {
        test->c_buffer = xdb_load_content_from_file(db_path);
        if (test->c_buffer == NULL) {
            printf("failed to load xdb content from `%s`\n", db_path);
            errcode = 4;
            goto defer;
        }

        err = xdb_new_with_buffer(version, &test->searcher, test->c_buffer);
        if (err != 0) {
            printf("failed to create content cached searcher with errcode=%d\n", err);
            errcode = 5;
            goto defer;
        }
    } else {
        printf("invalid cache policy `%s`, options: file/vectorIndex/content\n", cache_policy);
        errcode = 6;
        goto defer;
    }

    // init the region buffer
    // err = xdb_region_buffer_init(&test->region, test->region_buffer, sizeof(test->region_buffer));
    err = xdb_region_buffer_init(&test->region, NULL, 0);
    if (err != 0) {
        printf("failed to init the region buffer with err=%d\n", err);
        errcode = 7;
        goto defer;
    }

defer:
    if (header != NULL) {
        xdb_free_header(header);
    }

    if (handle != NULL) {
        fclose(handle);
    }

    return errcode;
}

void destroy_searcher_test(searcher_test_t *test) {
    xdb_close(&test->searcher);

    // check and free the vector index
    if (test->v_index != NULL) {
        xdb_free_vector_index(test->v_index);
        test->v_index = NULL;
    }

    // check and free the content buffer
    if (test->c_buffer != NULL) {
        xdb_free_content(test->c_buffer);
        test->c_buffer = NULL;
    }
}

//read a line from a command line.
static char *get_line(FILE *fp, char *__dst) {
    register int c;
    register char *cs;

    cs = __dst;
    while ( ( c = getc( fp ) ) != EOF ) {
        if ( c == '\n' ) break;
        *cs++ = c;
    }
    *cs = '\0';

    return ( c == EOF && cs == __dst ) ? NULL : __dst;
}

void print_help(char *argv[]) {
    printf("ip2region xdb searcher\n");
    printf("%s [command] [command options]\n", argv[0]);
    printf("Command: \n");
    printf("  search    search input test\n");
    printf("  bench     search bench test\n");
}

void test_search(int argc, char *argv[]) {
    int i, n, err;

    // for args parse
    char *r, key[33] = {'\0'}, val[256] = {'\0'};
    char db_file[256] = {'\0'}, cache_policy[16] = {"vectorIndex"};

    // for search
    long s_time, c_time;
    char line[512] = {'\0'};

    // ip parse
    xdb_version_t *version;
    bytes_ip_t ip_bytes[16] = {'\0'};

    searcher_test_t test;

    for (i = 2; i < argc; i++) {
        r = argv[i];
        if (strlen(r) < 5) {
            continue;
        }

        if (r[0] != '-' || r[1] != '-') {
            continue;
        }

        if (strchr(r, '=') == NULL) {
            printf("missing = for args pair '%s'\n", r);
            return;
        }

        n = sscanf(r+2, "%32[^=]=%255[^\n]", key, val);
        if (n != 2) {
            printf("invalid option flag `%s`\n", r);
            return;
        }

        // printf("key=%s, val=%s\n", key, val);
        if (strcmp(key, "db") == 0) {
            snprintf(db_file, sizeof(db_file), "%s", val);
        } else if (strcmp(key, "cache-policy") == 0) {
            memcpy(cache_policy, val, sizeof(cache_policy) - 1);
            // snprintf(cache_policy, sizeof(cache_policy), "%s", val);
        } else {
            printf("undefined option `%s`\n", r);
            return;
        }
    }

    if (strlen(db_file) < 1) {
        printf("%s search [command options]\n", argv[0]);
        printf("options:\n");
        printf(" --db string              ip2region binary xdb file path\n");
        printf(" --cache-policy string    cache policy: file/vectorIndex/content\n");
        return;
    }

    // init the win sock
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return;
    }

    // printf("db_file=%s, cache_policy=%s\n", db_file, cache_policy);
    err = init_searcher_test(&test, db_file, cache_policy);
    if (err != 0) {
        // init program will print the error reasons;
        return;
    }

    printf("ip2region xdb searcher test program\n"
            "source xdb: %s (%s, %s)\n"
            "type 'quit' to exit\n", db_file, xdb_get_version(&test.searcher)->name, cache_policy);
    while ( 1 ) {
        printf("ip2region>> ");
        get_line(stdin, line);
        if ( strlen(line) < 2 ) {
            continue;
        }

        if (strcmp(line, "quit") == 0 ) {
            break;
        }

        version = xdb_parse_ip(line, ip_bytes, sizeof(ip_bytes));
        if (version == NULL) {
            printf("invalid ip address `%s`\n", line);
            continue;
        }

        s_time = xdb_now();
        err = xdb_search(&test.searcher, ip_bytes, version->bytes, &test.region);
        if (err != 0) {
            printf("{err: %d, io_count: %d}\n", err, xdb_get_io_count(&test.searcher));
        } else {
            c_time = xdb_now() - s_time;
            printf("{region: %s, io_count: %d, took: %ld μs}\n", test.region.value, xdb_get_io_count(&test.searcher), c_time);
        }

        // free the region
        xdb_region_buffer_free(&test.region);
    }

    destroy_searcher_test(&test);
    xdb_clean_winsock();
    printf("searcher test program exited, thanks for trying\n");
}

void test_bench(int argc, char *argv[]) {
    int i, n, err;
    char *r, key[33] = {'\0'}, val[256] = {'\0'};
    char db_file[256] = {'\0'}, src_file[256] = {'\0'}, cache_policy[16] = {"vectorIndex"};

    FILE *handle;
    char line[1024] = {'\0'}, sip_str[INET6_ADDRSTRLEN+1] = {'\0'}, eip_str[INET6_ADDRSTRLEN+1] = {'\0'};
    char src_region[512] = {'\0'};
    int count = 0, took;
    long s_time, t_time, c_time = 0;

    // ip parse
    xdb_version_t *s_version, *e_version;
    bytes_ip_t sip_bytes[16] = {'\0'}, eip_bytes[16] = {'\0'};
    string_ip_t ip_string[INET6_ADDRSTRLEN] = {'\0'};
    bytes_ip_t *ip_list[2];

    searcher_test_t test;
    for (i = 2; i < argc; i++) {
        r = argv[i];
        if (strlen(r) < 5) {
            continue;
        }

        if (r[0] != '-' || r[1] != '-') {
            continue;
        }

        if (strchr(r, '=') == NULL) {
            printf("missing = for args pair '%s'\n", r);
            return;
        }

        n = sscanf(r+2, "%32[^=]=%255[^\n]", key, val);
        if (n != 2) {
            printf("invalid option flag `%s`\n", r);
            return;
        }

        if (strcmp(key, "db") == 0) {
            snprintf(db_file, sizeof(db_file), "%s", val);
        } else if (strcmp(key, "src") == 0) {
            snprintf(src_file, sizeof(src_file), "%s", val);
        } else if (strcmp(key, "cache-policy") == 0) {
            memcpy(cache_policy, val, sizeof(cache_policy) - 1);
        } else {
            printf("undefined option `%s`\n", r);
            return;
        }
    }

    if (strlen(db_file) < 1 || strlen(src_file) < 1) {
        printf("%s bench [command options]\n", argv[0]);
        printf("options:\n");
        printf(" --db string              ip2region binary xdb file path\n");
        printf(" --src string             source ip text file path\n");
        printf(" --cache-policy string    cache policy: file/vectorIndex/content\n");
        return;
    }

    // init the win sock
    err = xdb_init_winsock();
    if (err != 0) {
        printf("failed to init the winsock with errno=%d\n", err);
        return;
    }

    // printf("db_file=%s, src_file=%s, cache_policy=%s\n", db_file, src_file, cache_policy);
    s_time = xdb_now();
    err = init_searcher_test(&test, db_file, cache_policy);
    if (err != 0) {
        // the init function will print the details;
        return;
    }

    // open the source file
    handle = fopen(src_file, "r");
    if (handle == NULL) {
        printf("failed to open source text file `%s`\n", src_file);
        return;
    }

    while(fgets(line, sizeof(line), handle) != NULL) {
        n = sscanf(line, "%46[^|]|%46[^|]|%511[^\n]", sip_str, eip_str, src_region);
        if (n != 3) {
            printf("invalid ip segment line `%s`\n", line);
            return;
        }

        s_version = xdb_parse_ip(sip_str, sip_bytes, sizeof(sip_bytes));
        if (s_version == NULL) {
            printf("invalid start ip `%s`\n", sip_str);
            return;
        }

        e_version = xdb_parse_ip(eip_str, eip_bytes, sizeof(eip_bytes));
        if (e_version == NULL) {
            printf("invalid end ip `%s`\n", sip_str);
            return;
        }

        if (s_version->id != e_version->id) {
            printf("start ip and end ip version not match for line `%s`\n", line);
            return;
        }

        if (xdb_ip_sub_compare(sip_bytes, s_version->bytes, (string_ip_t *) eip_bytes, 0) > 0) {
            printf("start ip(%s) should not be greater than end ip(%s)\n", sip_str, eip_str);
            return;
        }

        ip_list[0] = sip_bytes;
        ip_list[1] = eip_bytes;
        for (i = 0; i < 2; i++) {
            t_time = xdb_now();
            err = xdb_search(&test.searcher, ip_list[i], s_version->bytes, &test.region);
            c_time += xdb_now() - t_time;
            if (err != 0) {
                xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string));
                printf("failed to search ip `%s` with errno=%d\n", ip_string, err);
                return;
            }

            // check the region info
            if (strcmp(test.region.value, src_region) != 0) {
                xdb_ip_to_string(ip_list[i], s_version->bytes, ip_string, sizeof(ip_string));
                printf("failed to search(%s) with (%s != %s)\n", ip_string, test.region.value, src_region);
                return;
            }

            // free the region buffer
            xdb_region_buffer_free(&test.region);

            count++;
        }
    };

    took = xdb_now() - s_time;
    destroy_searcher_test(&test);
    xdb_clean_winsock();
    fclose(handle);
    printf("Bench finished, {cache_policy: %s, total: %d, took: %.3fs, cost: %d μs/op}\n",
           cache_policy, count, took/1e6, count == 0 ? 0 : (int)(c_time/count));
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        print_help(argv);
        return 0;
    }

    char *opt = argv[1];
    if (strcmp(opt, "search") == 0) {
        test_search(argc, argv);
    } else if (strcmp(opt, "bench") == 0) {
        test_bench(argc, argv);
    } else {
        print_help(argv);
    }

    return 0;
}


================================================
FILE: binding/c/test_util.c
================================================
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.

// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date   2022/06/27

#include "stdio.h"
#include "xdb_api.h"

typedef void (* test_func_ptr) ();
struct test_func_entry {
    char *name;
    test_func_ptr func;
};
typedef struct test_func_entry test_func_t;

void test_load_header() {
    xdb_header_t *header = xdb_load_header_from_file("../../data/ip2region_v4.xdb");
    if (header == NULL) {
        printf("failed to load header");
    } else {
        printf("header loaded: {\n"
           "    version: %d, \n"
           "    index_policy: %d, \n"
           "    created_at: %u, \n"
           "    start_index_ptr: %d, \n"
           "    end_index_ptr: %d\n"
           "    ip_version: %d\n"
           "    runtime_ptr_bytes: %d\n"
           "    length: %d\n"
           "}\n",
           header->version, header->index_policy, header->created_at,
           header->start_index_ptr, header->end_index_ptr, 
           header->ip_version, header->runtime_ptr_bytes, header->length
       );
    }

    xdb_free_header(header);
}

void test_load_vector_index() {
    xdb_vector_index_t *v_index = xdb_load_vector_index_from_file("../../data/ip2region_v4.xdb");
    if (v_index == NULL) {
        printf("failed to load vector index from file\n");
    } else {
        printf("vector index loaded from file, length=%d\n", v_index->length);
    }

    xdb_free_vector_index(v_index);
}

void test_load_content() {
    xdb_content_t *content = xdb_load_content_from_file("../../data/ip2region_v4.xdb");
    if (content == NULL) {
        printf("failed to load content from file\n");
    } else {
        printf("content loaded from file, length=%d\n", content->length);
    }

    xdb_free_content(content);
}

void test_parse_ip() {
    const char *ip_list[] = {
        "1.0.0.0", "58.251.30.115", "192.168.1.100",
        "::", "2c0f:fff0::", "2fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "240e:982:e617:ffff:ffff:ffff:ffff:ffff", 
        "219.xx.xx.11", "::xx:ffff",
        NULL
    };

    int errcode;
    xdb_version_t *version;
    bytes_ip_t ip_bytes[16] = {'\0'};
    string_ip_t ip_string[INET6_ADDRSTRLEN] = {'\0'};

    // init the sock env (for windows)
    if ((errcode = xdb_init_winsock()) != 0) {
        printf("failed to init the winsock");
        return;
    }

    for (int i = 0;; i++) {
        if (ip_list[i] == NULL) {
            break;
        }

        version = xdb_parse_ip(ip_list[i], ip_bytes, sizeof(ip_bytes));
        if (version == NULL) {
            printf("failed to parse ip `%s`\n", ip_list[i]);
            continue;
        }

        xdb_ip_to_string(ip_bytes, version->bytes, ip_string, sizeof(ip_string));
        printf("ip: %s (version=v%d), toString: %s\n", ip_list[i], version->id, ip_string);
    }

    // clean up the winsock
    xdb_clean_winsock();
}

struct ip_pair {
    const char *sip;
    const char *eip;
};
void test_ip_compare() {
    struct ip_pair ip_pair_list[] = {
        {"1.0.0.0", "1.0.0.1"},
        {"192.168.1.101", "192.168.1.90"},
        {"219.133.111.87", "114.114.114.114"},
        {"1.0.4.0", "1.0.1.0"},
        {"1.0.4.0", "1.0.3.255"},
        {"2000::", "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"},
        {"2001:4:112::", "2001:4:112:ffff:ffff:ffff:ffff:ffff"},
        {"ffff::", "2001:4:ffff:ffff:ffff:ffff:ffff:ffff"},
        {NULL, NULL}
    };

    struct ip_pair *pair_ptr = NULL;
    bytes_ip_t sip_bytes[16] = {'\0'};
    bytes_ip_t eip_bytes[16] = {'\0'};
    xdb_version_t *s_version, *e_version;
    int errcode;

    // init the sock env (for windows)
    if ((errcode = xdb_init_winsock()) != 0) {
        printf("failed to init the winsock");
        return;
    }

    for (int i = 0; ;i++) {
        pair_ptr = &ip_pair_list[i];
        if (pair_ptr->sip == NULL) {
            break;
        }

        s_version = xdb_parse_ip(pair_ptr->sip, sip_bytes, sizeof(sip_bytes));
        if (s_version == NULL) {
            printf("failed to parse sip `%s`", pair_ptr->sip);
            continue;
        }

        e_version = xdb_parse_ip(pair_ptr->eip, eip_bytes, sizeof(eip_bytes));
        if (e_version == NULL) {
            printf("failed to parse eip `%s`", pair_ptr->eip);
            continue;
        }

        if (s_version->id != e_version->id) {
            printf("sip and eip version not match `%s` != `%s`\n", s_version->name, e_version->name);
            continue;
        }

        printf(
            "ip_sub_compare(%s, %s): %d\n", 
            pair_ptr->sip, pair_ptr->eip, 
            xdb_ip_sub_compare(sip_bytes, s_version->bytes, (string_ip_t *) eip_bytes, 0)
        );
    }

    // clean up the winsock
    xdb_clean_winsock();
}

// please register your function heare
static test_func_t _test_function_list[] = {
    // xdb buffer
    {"test_load_header", test_load_header},
    {"test_load_vector_index", test_load_vector_index},
    {"test_load_content", test_load_content},

    // ip utils
    {"test_parse_ip", test_parse_ip},
    {"test_ip_compare", test_ip_compare},

    {NULL, NULL}
};

// valgrind --tool=memcheck --leak-check=full ./a.out
int main(int argc, char *argv[]) {
    int i;
    char *name;

    // check and call the function
    if (argc < 2) {
        printf("please specified the function name to call\n");
        return 1;
    }

    name = argv[1];
    test_func_ptr func = NULL;
    for (i = 0; ; i++) {
        if (_test_function_list[i].name == NULL) {
            break;
        }

        if (strcmp(name, _test_function_list[i].name) == 0) {
            func = _test_function_list[i].func;
            break;
        }
    }

    if (func == NULL) {
        printf("can't find test function `%s`\n", name);
        return 1;
    }
    
    // call the function
    func();

    return 0;
}


================================================
FILE: binding/c/xdb_api.h
================================================
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.

// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date   2022/06/27

#ifndef C_IP2REGION_XDB_H
#define C_IP2REGION_XDB_H

// @Note: 
// this define must be put before any header include
// force the LFS for ftell
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if ( defined(WIN32) || defined(_WIN32) || defined(__WINDOWS_) || defined(WINNT) )
#   define XDB_PUBLIC(type)    extern __declspec(dllexport) type
#   define XDB_PRIVATE(type)   static type
#   define XDB_WINDOWS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#elif (defined(linux) || defined(_UNIX) || defined(__APPLE__) || defined(unix) || defined(__unix) || defined(__unix__) || defined(__linux__) || defined(linux) || defined(__linux))
#   define XDB_PUBLIC(type)    extern type
#   define XDB_PRIVATE(type)   static inline type
#   define XDB_LINUX
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#   define XDB_PUBLIC(type) type
#   define XDB_PRIVATE(type) static type
#endif

#define xdb_calloc( _blocks, _bytes )  calloc( _blocks, _bytes )
#define xdb_malloc( _bytes )           malloc( _bytes )
#define xdb_free( _ptr )               free( _ptr )

// public constants define
#define xdb_structure_20 2
#define xdb_structure_30 3
#define xdb_header_info_length 256
#define xdb_vector_index_rows  256
#define xdb_vector_index_cols  256
#define xdb_vector_index_size  8
#define xdb_v4_index_size 14    // 4 + 4 + 2 + 4
#define xdb_v6_index_size 38    // 16 + 16 + 2 + 4

// --- ip version info
#define xdb_ipv4_id 4
#define xdb_ipv6_id 6
#define xdb_ipv4_bytes 4
#define xdb_ipv6_bytes 16
// cache of vector_index_row × vector_index_rows × vector_index_size
#define xdb_vector_index_length 524288

// --- xdb buffer functions

// use the following buffer struct to wrap the binary buffer data
// since the buffer data could not be operated with the string API.
struct xdb_header {
    unsigned short version;
    unsigned short index_policy;
    unsigned int created_at;
    unsigned int start_index_ptr;
    unsigned int end_index_ptr;
    
    // since 3.0+ with IPv6 supporting
    unsigned short ip_version;
    unsigned short runtime_ptr_bytes;

    // the original buffer
    unsigned int length;
    char buffer[xdb_header_info_length];
};
typedef struct xdb_header xdb_header_t;

XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *);

XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *);

XDB_PUBLIC(void) xdb_free_header(void *);


// --- vector index buffer
struct xdb_vector_index {
    unsigned int length;
    char buffer[xdb_vector_index_length];
};
typedef struct xdb_vector_index xdb_vector_index_t;

XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *);

XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *);

XDB_PUBLIC(void) xdb_free_vector_index(void *);


// --- content buffer
struct xdb_content {
    unsigned int length;
    char *buffer;
};
typedef struct xdb_content xdb_content_t;

XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *);

XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *);

XDB_PUBLIC(void) xdb_free_content(void *);

// --- xdb verify

// Verify if the current Searcher could be used to search the specified xdb file.
// Why do we need this check ?
// The future features of the xdb impl may cause the current searcher not able to work properly.
//
// @Note: You Just need to check this ONCE when the service starts
// Or use another process (eg, A command) to check once Just to confirm the suitability.
XDB_PUBLIC(int) xdb_verify(FILE *);

XDB_PUBLIC(int) xdb_verify_from_header(FILE *handle, xdb_header_t *);

XDB_PUBLIC(int) xdb_verify_from_file(const char *);

// --- End xdb buffer


// types type define
typedef char string_ip_t;
typedef unsigned char bytes_ip_t;

// --- ip version
#define XDB_IPv4 (xdb_version_v4())
#define XDB_IPv6 (xdb_version_v6())
typedef int (* ip_compare_fn_t) (const bytes_ip_t *, int, const char *, int);
struct xdb_ip_version_entry {
    int id;                 // version id
    char *name;             // version name
    int bytes;              // ip bytes number
    int segment_index_size; // segment index size in bytes

    // function to compare two ips
    ip_compare_fn_t ip_compare;
};
typedef struct xdb_ip_version_entry xdb_version_t;

XDB_PUBLIC(xdb_version_t *) xdb_version_v4();
XDB_PUBLIC(xdb_version_t *) xdb_version_v6();

XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *);
XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *);

XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *);
XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *);

// --- END ip version

// --- xdb util functions

// to compatiable with the windows
// returns: 0 for ok and -1 for failed
XDB_PUBLIC(int) xdb_init_winsock();
XDB_PUBLIC(void) xdb_clean_winsock();

// get the current time in microseconds
XDB_PUBLIC(long) xdb_now();

// get unsigned long (4bytes) from a specified buffer start from the specified offset with little-endian
XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *, int);

// get unsigned short (2bytes) from a specified buffer start from the specified offset with little-endian
XDB_PUBLIC(int) xdb_le_get_uint16(const char *, int);


// parse the specified IP address to byte array.
// returns: xdb_version_t for valid ipv4 / ipv6, or NULL for failed
XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *, bytes_ip_t *, size_t);

// parse the specified IPv4 address to byte array
// returns: xdb_version_t for valid ipv4, or NULL for failed
XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *, bytes_ip_t *, size_t);

// parse the specified IPv6 address to byte array
// returns: xdb_version_t for valid ipv6, or NULL for failed
XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *, bytes_ip_t *, size_t);

// convert a specified ip bytes to humen-readable string.
// returns: 0 for success or -1 for failed.
XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *, int, char *, size_t);

// ipv4 bytes to string
XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *, char *, size_t);

// ipv6 bytes to string
XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *, char *, size_t);

// compare the specified ip bytes with another ip bytes in the specified buff from offset.
// ip args must be the return value from #xdb_parse_ip.
// returns: -1 if ip1 < ip2, 1 if ip1 > ip2 or 0
XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *, int, const char *, int);

// large file seek and tell
XDB_PUBLIC(int) xdb_fseek(FILE *, long long, int);

XDB_PUBLIC(long long) xdb_ftell(FILE *);

// --- END xdb utils


// --- xdb searcher api

// xdb region info structure
#define xdb_region_buffer_wrapper 1
#define xdb_region_buffer_auto    2
struct xdb_region_buffer_entry {
    int type;           // buffer type
    char *value;        // region value
    size_t length;      // buffer length
};
typedef struct xdb_region_buffer_entry xdb_region_buffer_t;

// wrapper the region from a local stack buffer.
// returns: 0 for succeed or failed
XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *, char *, size_t);

// do the buffer alloc.
// returns: 0 for ok or failed
XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *, int);

// empty alloc - empty string
// returns:  0 - always
XDB_PUBLIC(int) xdb_region_buffer_empty(xdb_region_buffer_t *);

XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *);

// xdb searcher structure
struct xdb_searcher_entry {
    // ip version
    xdb_version_t *version;

    // xdb file handle
    FILE *handle;

    // header info
    const char *header;
    int io_count;

    // vector index buffer cache.
    // preload the vector index will reduce the number of IO operations
    // thus speedup the search process.
    const xdb_vector_index_t *v_index;

    // content buffer.
    // cache the whole xdb content.
    const xdb_content_t *content;
};
typedef struct xdb_searcher_entry xdb_searcher_t;

// xdb searcher new api define
XDB_PUBLIC(int) xdb_new_with_file_only(xdb_version_t *, xdb_searcher_t *, const char *);

XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_version_t *, xdb_searcher_t *, const char *, const xdb_vector_index_t *);

XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *, xdb_searcher_t *, const xdb_content_t *);

XDB_PUBLIC(void) xdb_close(void *);

// xdb searcher search api define
XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *, const string_ip_t *, xdb_region_buffer_t *);

XDB_PUBLIC(int) xdb_search(xdb_searcher_t *, const bytes_ip_t *, int, xdb_region_buffer_t *);

XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *);

XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *);

// --- END xdb searcher api

#endif // C_IP2REGION_XDB_H


================================================
FILE: binding/c/xdb_searcher.c
================================================
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.

// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date   2022/06/27

#include "xdb_api.h"

// --- region buffer
XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *region, char *buffer, size_t length) {
    if (buffer == NULL) {
        region->type   = xdb_region_buffer_auto;
        region->length = 0;
    } else if (length <= 0) {
        return 1;
    } else {
        region->type   = xdb_region_buffer_wrapper;
        region->length = length;
        memset(buffer, 0x00, length);   // zero-fill the buffer
    }

    region->value = buffer;
    return 0;
}

XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *region, int length) {
    if (length <= 0) {
        return 1;
    }

    // no allocation supports for the buffer wapper
    if (region->type == xdb_region_buffer_wrapper) {
        if (length >= region->length) {
            return 2;
        }

        region->value[length] = '\0';
        return 0;
    }

    // ensure that the value were freed
    // by calling #xdb_region_buffer_free
    if (region->value != NULL) {
        return 3;
    }

    char *ptr = (char *) xdb_malloc(length + 1);
    if (ptr == NULL) {
        return 4;
    }

    ptr[length]    = '\0';  // NULL-end
    region->value  = ptr;
    region->length = length;
    return 0;
}

// fixed internal empty string ptr
static char * _empty_region_string = "\0";

XDB_PUBLIC(int) xdb_region_buffer_empty(xdb_region_buffer_t *region) {
    // no allocation supports for the buffer wapper
    if (region->type == xdb_region_buffer_wrapper) {
        region->value[0] = '\0';
        return 0;
    }

    // ensure that the value were freed
    // by calling #xdb_region_buffer_free
    if (region->value != NULL) {
        return 3;
    }

    region->value  = _empty_region_string;
    region->length = 0;
    return 0;
}

XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *region) {
    if (region->type == xdb_region_buffer_auto) {
        // empty string interception
        if (region->length == 0 
            || region->value == _empty_region_string) {
            // do nothing for empty string
        } else {
            xdb_free(region->value);
        }

        // reset the value
        region->value = NULL;
    }
}

// --- END region buffer

// internal function prototype define
XDB_PRIVATE(int) read(xdb_searcher_t *, long offset, char *, size_t length);

XDB_PRIVATE(int) xdb_new_base(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index, const xdb_content_t *c_buffer) {
    memset(xdb, 0x00, sizeof(xdb_searcher_t));

    // set the version
    xdb->version = version;

    // check the content buffer first
    if (c_buffer != NULL) {
        xdb->v_index = NULL;
        xdb->content = c_buffer;
        return 0;
    }

    // open the xdb binary file
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return 1;
    }

    xdb->handle = handle;
    xdb->v_index = v_index;

    return 0;
}

// xdb searcher new api define
XDB_PUBLIC(int) xdb_new_with_file_only(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path) {
    return xdb_new_base(version, xdb, db_path, NULL, NULL);
}

XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_version_t *version, xdb_searcher_t *xdb, const char *db_path, const xdb_vector_index_t *v_index) {
    return xdb_new_base(version, xdb, db_path, v_index, NULL);
}

XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *version, xdb_searcher_t *xdb, const xdb_content_t *c_buffer) {
    return xdb_new_base(version, xdb, NULL, NULL, c_buffer);
}

XDB_PUBLIC(void) xdb_close(void *ptr) {
    xdb_searcher_t *xdb = (xdb_searcher_t *) ptr;
    if (xdb->handle != NULL) {
        fclose(xdb->handle);
        xdb->handle = NULL;
    }
}

// --- xdb searcher search api define

XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_ip_t *ip_string, xdb_region_buffer_t *region) {
    bytes_ip_t ip_bytes[16] = {'\0'};
    xdb_version_t *version = xdb_parse_ip(ip_string, ip_bytes, sizeof(ip_bytes));
    if (version == NULL) {
        return 10;
    } else {
        return xdb_search(xdb, ip_bytes, version->bytes, region);
    }
}

XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_bytes, int ip_len, xdb_region_buffer_t *region) {
    int il0, il1, idx, err, bytes, d_bytes;
    register int seg_index_size, l, h, m, p;
    unsigned int s_ptr, e_ptr, data_ptr, data_len;
    char vector_buffer[xdb_vector_index_size];
    char segment_buffer[xdb_v6_index_size];

    // ip version check
    if (ip_len != xdb->version->bytes) {
        return -1;
    }

    // some resets
    err = 0;
    data_len = 0;
    bytes   = xdb->version->bytes;
    d_bytes = xdb->version->bytes << 1;
    xdb->io_count = 0;

    // locate the segment index block based on the vector index
    il0 = (int) (ip_bytes[0]);
    il1 = (int) (ip_bytes[1]);
    idx = il0 * xdb_vector_index_cols * xdb_vector_index_size + il1 * xdb_vector_index_size;
    if (xdb->v_index != NULL) {
        s_ptr = xdb_le_get_uint32(xdb->v_index->buffer, idx);
        e_ptr = xdb_le_get_uint32(xdb->v_index->buffer, idx + 4);
    } else if (xdb->content != NULL) {
        s_ptr = xdb_le_get_uint32(xdb->content->buffer, xdb_header_info_length + idx);
        e_ptr = xdb_le_get_uint32(xdb->content->buffer, xdb_header_info_length + idx + 4);
    } else {
        err = read(xdb, xdb_header_info_length + idx, vector_buffer, sizeof(vector_buffer));
        if (err != 0) {
            return 10 + err;
        }

        s_ptr = xdb_le_get_uint32(vector_buffer, 0);
        e_ptr = xdb_le_get_uint32(vector_buffer, 4);
    }

    // printf("s_ptr=%u, e_ptr=%u\n", s_ptr, e_ptr);
    // @Note: ptr validate, zero ptr means source data missing
    // so we could just stop here and return an empty string.
    if (s_ptr == 0 || e_ptr == 0) {
        xdb_region_buffer_empty(region);
        return err;
    }

    // binary search to get the final region info
    // segment_buffer = xdb_malloc(seg_index_size);
    seg_index_size = xdb->version->segment_index_size;
    data_len = 0, data_ptr = 0;
    l = 0, h = ((int) (e_ptr - s_ptr)) / seg_index_size;
    while (l <= h) {
        m = (l + h) >> 1;
        p = s_ptr + m * seg_index_size;

        // read the segment index item
        err = read(xdb, p, segment_buffer, seg_index_size);
        if (err != 0) {
            return 20 + err;
        }

        // decode the data fields as needed
        if (xdb->version->ip_compare(ip_bytes, bytes, segment_buffer, 0) < 0) {
            h = m - 1;
        } else if (xdb->version->ip_compare(ip_bytes, bytes, segment_buffer, bytes) > 0) {
            l = m + 1;
        } else {
            data_len = xdb_le_get_uint16(segment_buffer, d_bytes);
            data_ptr = xdb_le_get_uint32(segment_buffer, d_bytes + 2);
            break;
        }
    }

    // printf("data_len=%u, data_ptr=%u\n", data_len, data_ptr);
    if (data_len == 0) {
        // return 100;
        xdb_region_buffer_empty(region);
        return err;
    }

    // buffer alloc checking
    err = xdb_region_buffer_alloc(region, data_len);
    if (err != 0) {
        return 100 + err;
    }

    err = read(xdb, data_ptr, region->value, data_len);
    if (err != 0) {
        return 30 + err;
    }

    return err;
}

XDB_PRIVATE(int) read(xdb_searcher_t *xdb, long offset, char *buffer, size_t length) {
    // check the xdb content cache first
    if (xdb->content != NULL) {
        memcpy(buffer, xdb->content->buffer + offset, length);
        return 0;
    }

    // seek to the offset
    if (fseek(xdb->handle, offset, SEEK_SET) == -1) {
        return 1;
    }

    xdb->io_count++;
    if (fread(buffer, 1, length, xdb->handle) != length) {
        return 2;
    }

    return 0;
}

XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *xdb) {
    return xdb->version;
}

XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *xdb) {
    return xdb->io_count;
}


================================================
FILE: binding/c/xdb_util.c
================================================
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.

// ---
// @Author Lion <chenxin619315@gmail.com>
// @Date   2022/06/27

#include "xdb_api.h"
#include <ctype.h>

// for Linux
#ifdef XDB_LINUX
#include "sys/time.h"
#endif

#ifdef XDB_WINDOWS
#include <time.h>
#endif

// @Note: since 2023/10/13 to compatible with the windows system
#ifdef XDB_WINDOWS
static int winsock_initialized = 0;
XDB_PUBLIC(int) xdb_init_winsock() {
    if (winsock_initialized == 1) {
        return 0;
    }

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        return -1; 
    }
    winsock_initialized = 1;
    return 0;
}

XDB_PUBLIC(void) xdb_clean_winsock() {
    if (winsock_initialized == 1) {
        WSACleanup();
        winsock_initialized = 0;
    }
}

XDB_PRIVATE(int) gettimeofday(struct timeval* tp, void* tzp) {
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year = wtm.wYear - 1900;
    tm.tm_mon = wtm.wMonth - 1;
    tm.tm_mday = wtm.wDay;
    tm.tm_hour = wtm.wHour;
    tm.tm_min = wtm.wMinute;
    tm.tm_sec = wtm.wSecond;
    tm.tm_isdst = -1;
    clock = mktime(&tm);
    tp->tv_sec = clock;
    tp->tv_usec = wtm.wMilliseconds * 1000;
    return (0);
}
#else
XDB_PUBLIC(int) xdb_init_winsock() {return 0;}
XDB_PUBLIC(void) xdb_clean_winsock() {}
#endif

// --- xdb buffer function implementations

XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *handle) {
    xdb_header_t *header;
    unsigned int size = xdb_header_info_length;

    // entry alloc
    header = (xdb_header_t *) xdb_malloc(sizeof(xdb_header_t));
    if (header == NULL) {
        return NULL;
    }

    if (fseek(handle, 0, SEEK_SET) == -1) {
        xdb_free(header);
        return NULL;
    }

    if (fread(header->buffer, 1,size, handle) != size) {
        xdb_free(header);
        return NULL;
    }

    // fill the fields
    header->length = size;
    header->version = (unsigned short) xdb_le_get_uint16(header->buffer, 0);
    header->index_policy = (unsigned short) xdb_le_get_uint16(header->buffer, 2);
    header->created_at = xdb_le_get_uint32(header->buffer, 4);
    header->start_index_ptr = xdb_le_get_uint32(header->buffer, 8);
    header->end_index_ptr = xdb_le_get_uint32(header->buffer,12);

    // since IPv6 supporting
    header->ip_version = xdb_le_get_uint16(header->buffer, 16);
    header->runtime_ptr_bytes = xdb_le_get_uint16(header->buffer, 18);

    return header;
}

XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *db_path) {
    xdb_header_t *header;
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return NULL;
    }

    header = xdb_load_header(handle);
    fclose(handle);
    return header;
}

XDB_PUBLIC(void) xdb_free_header(void *ptr) {
    xdb_header_t *header = (xdb_header_t *) ptr;
    if (header->length > 0) {
        header->length = 0;
        xdb_free(header);
    }
}

// --- vector index

XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *handle) {
    xdb_vector_index_t *v_index;
    unsigned int size = xdb_vector_index_length;

    // seek to the vector index offset
    if (fseek(handle, xdb_header_info_length, SEEK_SET) == -1) {
        return NULL;
    }

    // do the buffer read
    v_index = (xdb_vector_index_t *) xdb_malloc(sizeof(xdb_vector_index_t));
    if (v_index == NULL) {
        return NULL;
    }

    v_index->length = size;
    if (fread(v_index->buffer, 1, size, handle) != size) {
        xdb_free(v_index);
        return NULL;
    }

    return v_index;
}

XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const char *db_path) {
    xdb_vector_index_t *v_index;
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return NULL;
    }

    v_index = xdb_load_vector_index(handle);
    fclose(handle);
    return v_index;
}

XDB_PUBLIC(void) xdb_free_vector_index(void *ptr) {
    xdb_vector_index_t *v_index = (xdb_vector_index_t *) ptr;
    if (v_index->length > 0) {
        v_index->length = 0;
        xdb_free(v_index);
    }
}

// --- content buffer

XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *handle) {
    unsigned int size;
    xdb_content_t *content;

    // determine the file size
    if (fseek(handle, 0, SEEK_END) == -1) {
        return NULL;
    }

    size = (unsigned int) ftell(handle);
    if (fseek(handle, 0, SEEK_SET) == -1) {
        return NULL;
    }

    // do the file read
    content = (xdb_content_t *) xdb_malloc(sizeof(xdb_content_t));
    if (content == NULL) {
        return NULL;
    }

    // do the buffer alloc
    content->buffer = (char *) xdb_malloc(size);
    if (content->buffer == NULL) {
        xdb_free(content);
        return NULL;
    }

    // read the content into the buffer
    content->length = size;
    if (fread(content->buffer, 1, size, handle) != size) {
        xdb_free(content);
        return NULL;
    }

    return content;
}

XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *db_path) {
    xdb_content_t *content;
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return NULL;
    }

    content = xdb_load_content(handle);
    fclose(handle);
    return content;
}

XDB_PUBLIC(void) xdb_free_content(void *ptr) {
    xdb_content_t *content = (xdb_content_t *) ptr;
    if (content->length > 0) {
        content->length = 0;
        xdb_free(content->buffer);
        content->buffer = NULL;
        xdb_free(content);
    }
}

XDB_PUBLIC(int) xdb_verify_from_header(FILE *handle, xdb_header_t *header) {
    unsigned int runtime_ptr_bytes = 0;  // runtime ptr bytes
    if (header->version == xdb_structure_20) {
        runtime_ptr_bytes = 4;
    } else if (header->version == xdb_structure_30) {
        runtime_ptr_bytes = header->runtime_ptr_bytes;
    } else {
        return 2;
    }

    // 1, confirm the xdb file size.
    // to ensure that the maximum file pointer does not overflow.
    int err = fseek(handle, 0L, SEEK_END);
    if (err != 0) {
        return 3;
    }

    long long fileBytes = xdb_ftell(handle);
    long long maxFilePtr = (1LL << (runtime_ptr_bytes * 8)) - 1;
    // printf("fileBytes: %lld, maxFilePtr: %lld\n", fileBytes, maxFilePtr);
    if (fileBytes > maxFilePtr) {
        return 4;
    }

    return 0;
}

XDB_PUBLIC(int) xdb_verify(FILE *handle) {
    xdb_header_t *header = xdb_load_header(handle);
    if (header == NULL) {
        return 1;
    }

    int errcode = xdb_verify_from_header(handle, header);
    if (errcode != 0) {
        goto done;
    }

    // what next ?
done:
    xdb_free_header(header);
    return errcode;
}

XDB_PUBLIC(int) xdb_verify_from_file(const char *db_path) {
    FILE *handle = fopen(db_path, "rb");
    if (handle == NULL) {
        return -1;
    }

    int r = xdb_verify(handle);
    fclose(handle);
    return r;
}

// --- End content buffer


// --- ip version

// ip compare for IPv4
// ip1 - with Big endian byte order parsed from an input
// ip2 - with Little endian byte order read from the xdb index.
// to compatiable with the Little Endian encoded IPv4 on xdb 2.0.
XDB_PRIVATE(int) _ipv4_sub_compare(const bytes_ip_t *ip_bytes, int bytes, const char *buffer, int offset) {
    register int i0, i1;
    for (int i = 0, j = offset + bytes - 1; i < bytes; i++, j--) {
        i0 = ip_bytes[i];
        i1 = buffer[j] & 0xFF;
        if (i0 > i1) {
            return 1;
        } else if (i0 < i1) {
            return -1;
        }
    }
    return 0;
}

static xdb_version_t _ip_version_list[] = {
    // 14 = 4 + 4 + 2 + 4
    {xdb_ipv4_id, "IPv4", xdb_ipv4_bytes, xdb_v4_index_size, _ipv4_sub_compare},

    // 38 = 16 + 16 + 2 + 4
    {xdb_ipv6_id, "IPv6", xdb_ipv6_bytes, xdb_v6_index_size, xdb_ip_sub_compare},

    // END
    {0, NULL, 0, 0, NULL}
};

XDB_PUBLIC(xdb_version_t *) xdb_version_v4() {
    return &_ip_version_list[0];
}

XDB_PUBLIC(xdb_version_t *) xdb_version_v6() {
    return &_ip_version_list[1];
}

XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *version) {
    return version->id == xdb_ipv4_id;
}

XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *version) {
    return version->id == xdb_ipv6_id;
}

XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *name) {
    // to upper case the name
    for (int i = 0; name[i] != '\0'; i++) {
        name[i] = toupper((unsigned char) name[i]);
    }

    if (strcmp(name, "V4") == 0 || strcmp(name, "IPV4") == 0) {
        return xdb_version_v4();
    } else if (strcmp(name, "V6") == 0 || strcmp(name, "IPV6") == 0) {
        return xdb_version_v6();
    } else {
        return NULL;
    }
}

XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *header) {
    // Old structure with ONLY IPv4 supports
    if (header->version == xdb_structure_20) {
        return xdb_version_v4();
    }

    // structure 3.0 with IPv6 supporting
    if (header->version != xdb_structure_30) {
        return NULL;
    }

    if (header->ip_version == xdb_ipv4_id) {
        return xdb_version_v4();
    } else if (header->ip_version == xdb_ipv6_id) {
        return xdb_version_v6();
    } else {
        return NULL;
    }
}

// --- END ip version

XDB_PUBLIC(long) xdb_now() {
    struct timeval c_time;
    gettimeofday(&c_time, NULL);
    return c_time.tv_sec * (int)1e6 + c_time.tv_usec;
}

XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *buffer, int offset) {
    return (
        ((buffer[offset  ]) & 0x000000FF) |
        ((buffer[offset+1] <<  8) & 0x0000FF00) |
        ((buffer[offset+2] << 16) & 0x00FF0000) |
        ((buffer[offset+3] << 24) & 0xFF000000)
    );
}

XDB_PUBLIC(int) xdb_le_get_uint16(const char *buffer, int offset) {
    return (
        ((buffer[offset  ]) & 0x000000FF) |
        ((buffer[offset+1] << 8) & 0x0000FF00)
    );
}

XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) {
    char *d_ptr = strchr(ip_string, '.');
    char *c_ptr = strchr(ip_string, ':');
    // version check
    if (d_ptr != NULL && c_ptr == NULL) {
        return xdb_parse_v4_ip(ip_string, buffer, length);
    } else if (c_ptr != NULL) {
        return xdb_parse_v6_ip(ip_string, buffer, length);
    }

    return NULL;
}

XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) {
    struct in_addr addr;

    // buffer length checking
    if (length < xdb_ipv4_bytes) {
        return NULL;
    }

    if (inet_pton(AF_INET, ip_string, &addr) != 1) {
        return NULL;
    }

    // encode the address to buffer with big endian byte bufffer.
    buffer[0] = (addr.s_addr) & 0xFF;
    buffer[1] = (addr.s_addr >> 8) & 0xFF;
    buffer[2] = (addr.s_addr >> 16) & 0xFF;
    buffer[3] = (addr.s_addr >> 24) & 0xFF;
    return XDB_IPv4;
}

XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *ip_string, bytes_ip_t *buffer, size_t length) {
    struct  in6_addr addr;

    // buffer length checking
    if (length < xdb_ipv6_bytes) {
        return NULL;
    }

    if (inet_pton(AF_INET6, ip_string, &addr) != 1) {
        return NULL;
    }

    memcpy(buffer, addr.s6_addr, xdb_ipv6_bytes);
    return XDB_IPv6;
}

XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *ip_bytes, int bytes, char *ip_string, size_t length) {
    if (bytes == xdb_ipv4_bytes) {
        return xdb_v4_ip_to_string(ip_bytes, ip_string, length);
    } else if (bytes == xdb_ipv6_bytes) {
        return xdb_v6_ip_to_string(ip_bytes, ip_string, length);
    }

    return -1;
}

XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) {
    if (!ip_bytes || !ip_string || length == 0) {
        return -1;
    }

    // buffer length checking
    if (length < INET_ADDRSTRLEN) {
        return -1;
    }

    if (inet_ntop(AF_INET, ip_bytes, ip_string, length) == NULL) {
        return -1;
    }

    return 0;
}

XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *ip_bytes, char *ip_string, size_t length) {
    if (!ip_bytes || !ip_string || length == 0) {
        return -1;
    }

    if (length < INET6_ADDRSTRLEN) {
        return -1;
    }

    if (inet_ntop(AF_INET6, ip_bytes, ip_string, length) == NULL) {
        return -1;
    }

    return 0;
}

XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *ip1, int bytes, const char *buffer, int offset) {
    register int i, i1, i2;
    for (i = 0; i < bytes; i++) {
        i1 = ip1[i];
        i2 = buffer[offset + i] & 0xFF;
        if (i1 > i2) {
            return 1;
        } else if (i1 < i2) {
            return -1;
        }
    }
    return 0;
}

XDB_PUBLIC(int) xdb_fseek(FILE *handle, long long offset, int whence) {
    // we may have to use the large file solution later
    // #if defined(XDB_LINUX)
    //     return fseeko(handle, (off_t) offset, whence);
    // #elif defined(XDB_WINDOWS)
    //     return _fseeki64(handle, (__int64) offset, whence)
    // #else
    //     return fseek(handle, (long) offset, whence);
    // #endif

    return fseek(handle, (long) offset, whence);
}

XDB_PUBLIC(long long) xdb_ftell(FILE *handle) {
    // we may have to use the large file solution later
    // #if defined(XDB_LINUX)
    //     return (long long) ftello(handle);
    // #elif defined(XDB_WINDOWS)
    //     return (long long) _ftelli64(handle);
    // #else
    //     // report error ?
    //     return (long long) ftell(handle);
    // #endif

    return (long long) ftell(handle);
}

================================================
FILE: binding/cpp/.gitignore
================================================
bin/


================================================
FILE: binding/cpp/Makefile
================================================

all: bin header search bench make edit

FILES=$(wildcard src/*.cc)

bin:
	mkdir -p bin

header: $(FILES) test/header.cc
	g++ -std=c++11 -O2 $^ -o bin/$@

search: $(FILES) test/search.cc
	g++ -std=c++11 -O2 $^ -o bin/$@

bench: $(FILES) test/bench.cc
	g++ -std=c++11 -O2 $^ -o bin/$@

make: $(FILES) test/make.cc
	g++ -std=c++11 -O2 $^ -o bin/$@

edit: $(FILES)
	g++ -std=c++11 -O2 $^ test/edit_v4.cc -o bin/edit_v4
	g++ -std=c++11 -O2 $^ test/edit_v6.cc -o bin/edit_v6

clean:
	rm -rf bin



================================================
FILE: binding/cpp/README.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region C++ query client

## 0. File Description

```
Makefile --------- Build

src ------------------ Source directory
src/base.* ----------- Constants and utility functions
src/ip.* ------------- IP processing implementation
src/header.* --------- xdb header parsing implementation
src/search.* --------- xdb search implementation
src/bench.* ---------- Search benchmarking implementation
src/make.* ----------- xdb file generation implementation
src/edit.* ----------- Raw data editing implementation

test ---------------- Test directory
test/header.cc ------ Test header
test/search.cc ------ Test search
test/bench.cc ------- Benchmarking
test/make.cc -------- Generate xdb file
test/edit_v4.cc ----- Test raw data editing (ipv4)
test/edit_v6.cc ----- Test raw data editing (ipv6)


bin --------------- Executable directory (generated via make)
bin/header -------- Test header
bin/search -------- Test search
bin/bench --------- Benchmarking
bin/make ---------- Generate xdb file
bin/edit_v4 ------- Test raw data editing (ipv4)
bin/edit_v6 ------- Test raw data editing (ipv6)

readme.md --------- readme

```

## 1. Compilation

```
$ make
```

## 2. Search

### 2.1 Example

```cpp
#include "src/search.h"

// IP Version: xdb::ipv4 xdb::ipv6
//    Policy: xdb::policy_file xdb::policy_vector xdb::policy_content
//               No cache           Partial cache        Full cache
int main() {
    std::string xdb_name = "../../data/ip2region_v6.xdb";
    int         version  = xdb::ipv6;
    int         policy   = xdb::policy_content;
    std::string ip       = "2001:200:124::";

    xdb::search_t s(xdb_name, version, policy);
    std::cout << s.search(ip) << std::endl;
    return 0;
}

// $ g++ src/*.cc 1.cc --- Compile
// $ ./a.out ------------- Test
// Japan|Tokyo|Asagaya-minami|WIDE Project|JP
```

### 2.2 Test xdb Header

```
$ ./bin/header
Test IPv4
Version: 3
Cache Policy: 1
File Generation Time: 2025-09-06 02:24:16
Index Start Address: 955933
Index End Address: 11042415
IP Version: 4
Pointer Bytes: 4

Test IPv6
Version: 3
Cache Policy: 1
File Generation Time: 2025-10-17 04:41:04
Index Start Address: 3094259
Index End Address: 36258303
IP Version: 6
Pointer Bytes: 4
```

### 2.3 Test Search

```
$ ./bin/search
Test IPv4   No cache: Success
Test IPv4 Partial cache: Success
Test IPv4 Full cache: Success
Test IPv6   No cache: Success
Test IPv6 Partial cache: Success
Test IPv6 Full cache: Success
```

## 3. Benchmarking and Correctness Verification

```
./bin/bench
Test IPv4,   No cache, total: 3910284, took:    27.60s, cost:   6.59μs/op, io count: 28227147
Test IPv4, Partial cache, total: 3910284, took:    21.85s, cost:   5.15μs/op, io count: 24316863
Test IPv4, Full cache, total: 3910284, took:     2.26s, cost:   0.25μs/op, io count: 0
Test IPv6,   No cache, total: 4792520, took:   100.40s, cost:  20.22μs/op, io count: 80758866
Test IPv6, Partial cache, total: 4792520, took:    93.06s, cost:  18.71μs/op, io count: 75966346
Test IPv6, Full cache, total: 4792520, took:     6.24s, cost:   0.81μs/op, io count: 0
```

## 4. Generate xdb File

### 4.1 Generate xdb File

```
$ ./bin/make
Generate ipv4 xdb file, took: 0.57s
Generate ipv6 xdb file, took: 1.24s
```

## 5. Raw Data Editing

### 5.1. Instructions for Use

* New IP attribution files can contain empty lines
* New IP attribution files can be out of order; the program will automatically sort them
* New IP attribution files can overlap; as long as there is no ambiguity, the program will automatically merge them
* The final result will automatically merge adjacent lines with the same attribution
* For the following tests, the original file uses the data file provided in the repository, and the new file uses 1.txt in the current directory


================================================
FILE: binding/cpp/README_zh.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region C++ 查询客户端

## 0. 文件说明
```
Makefile --------- 构建

src ------------------ 源文件目录
src/base.* ----------- 常量及工具函数
src/ip.* ------------- 实现 IP 处理
src/header.* --------- 实现 xdb 头部解析
src/search.* --------- 实现 xdb 查找
src/bench.* ---------- 实现 查找 测速
src/make.* ----------- 实现 生成 xdb 文件
src/edit.* ----------- 实现 原始数据编辑

test ---------------- 测试目录
test/header.cc ------ 测试 头部
test/search.cc ------ 测试 查找
test/bench.cc ------- 测速
test/make.cc -------- 生成 xdb 文件
test/edit_v4.cc ----- 测试 原始数据编辑(ipv4)
test/edit_v6.cc ----- 测试 原始数据编辑(ipv6)


bin --------------- 可执行文件目录(通过 make 生成)
bin/header -------- 测试 头部
bin/search -------- 测试 查找
bin/bench --------- 测速
bin/make ---------- 生成 xdb 文件
bin/edit_v4 ------- 测试 原始数据编辑(ipv4)
bin/edit_v6 ------- 测试 原始数据编辑(ipv6)

readme.md --------- readme
```

## 1. 编译
```
$ make
```

## 2. 查找
### 2.1 示例
```cpp
#include "src/search.h"

// IP 版本: xdb::ipv4 xdb::ipv6
//    策略: xdb::policy_file xdb::policy_vector xdb::policy_content
//               不缓存           部分缓存           全部缓存
int main() {
    std::string xdb_name = "../../data/ip2region_v6.xdb";
    int         version  = xdb::ipv6;
    int         policy   = xdb::policy_content;
    std::string ip       = "2001:200:124::";

    xdb::search_t s(xdb_name, version, policy);
    std::cout << s.search(ip) << std::endl;
    return 0;
}

// $ g++ src/*.cc 1.cc --- 编译
// $ ./a.out ------------- 测试
// Japan|Tokyo|Asagaya-minami|WIDE Project|JP
```

### 2.2 测试 xdb 头部
```
$ ./bin/header
测试 IPv4
版本号: 3
缓存策略: 1
文件生成时间: 2025-09-06 02:24:16
索引起始地址: 955933
索引结束地址: 11042415
IP版本: 4
指针字节数: 4

测试 IPv6
版本号: 3
缓存策略: 1
文件生成时间: 2025-10-17 04:41:04
索引起始地址: 3094259
索引结束地址: 36258303
IP版本: 6
指针字节数: 4
```

### 2.3 测试查找
```
$ ./bin/search
测试 IPv4   不缓存: 成功
测试 IPv4 部分缓存: 成功
测试 IPv4 全部缓存: 成功
测试 IPv6   不缓存: 成功
测试 IPv6 部分缓存: 成功
测试 IPv6 全部缓存: 成功
```

## 3. 测速以及检验正确性
```
./bin/bench
测试 IPv4,   不缓存, total: 3910284, took:    27.60s, cost:   6.59μs/op, io count: 28227147
测试 IPv4, 部分缓存, total: 3910284, took:    21.85s, cost:   5.15μs/op, io count: 24316863
测试 IPv4, 全部缓存, total: 3910284, took:     2.26s, cost:   0.25μs/op, io count: 0
测试 IPv6,   不缓存, total: 4792520, took:   100.40s, cost:  20.22μs/op, io count: 80758866
测试 IPv6, 部分缓存, total: 4792520, took:    93.06s, cost:  18.71μs/op, io count: 75966346
测试 IPv6, 全部缓存, total: 4792520, took:     6.24s, cost:   0.81μs/op, io count: 0
```

## 4. 生成 xdb 文件
### 4.1 生成 xdb 文件
```
$ ./bin/make
生成 ipv4 的 xdb 文件, took: 0.57s
生成 ipv6 的 xdb 文件, took: 1.24s
```

## 5. 原始数据编辑
### 5.1. 使用说明
* 新的IP归属地文件可以包含空行
* 新的IP归属地文件顺序可以乱序, 程序会自动排序
* 新的IP归属地文件顺序可以重叠, 只要无二义性, 程序会自动合并
* 最终的结果会将相邻的且归属地相同的行自动合并
* 以下测试, 原文件使用仓库自带的数据文件, 新文件使用当前目录下的 1.txt


================================================
FILE: binding/cpp/src/base.cc
================================================

#include "base.h"

namespace xdb {

int ip_version;  // ip 版本
int ip_size;     // ip 占的字节数
int content_size;

void init_xdb(int version) {
    ip_version   = version;
    ip_size      = version == ipv4 ? 4 : 16;
    content_size = ip_size * 2 + 2 + 4;
}

void log_exit(const string &msg) {
    std::cout << msg << std::endl;
    exit(-1);
}

void read_bin(int index, char *buf, size_t len, FILE *db) {
    fseek(db, index, SEEK_SET);
    if (fread(buf, 1, len, db) != len)
        log_exit(__func__);
}

unsigned to_uint(const char *buf) {
    return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00) |
           ((buf[2] << 16) & 0x00FF0000) | ((buf[3] << 24) & 0xFF000000);
}

unsigned to_ushort(const char *buf) {
    return ((buf[0]) & 0x000000FF) | ((buf[1] << 8) & 0x0000FF00);
}

unsigned to_int(const char *buf, int n) {
    return n == 2 ? to_ushort(buf) : to_uint(buf);
}

void write_uint(unsigned data, char buf[]) {
    buf[0] = (data >> 0) & 0xFF;
    buf[1] = (data >> 8) & 0xFF;
    buf[2] = (data >> 16) & 0xFF;
    buf[3] = (data >> 24) & 0xFF;
}

void write_uint(unsigned data, FILE *dst) {
    char buf[4];
    write_uint(data, buf);
    fwrite(buf, 1, sizeof(buf), dst);
}

void write_ushort(unsigned data, char buf[]) {
    buf[0] = (data >> 0) & 0xFF;
    buf[1] = (data >> 8) & 0xFF;
}

void write_ushort(unsigned data, FILE *dst) {
    char buf[2];
    write_ushort(data, buf);
    fwrite(buf, 1, sizeof(buf), dst);
}

void write_string(const char *buf, unsigned len, FILE *dst) {
    fwrite(buf, 1, len, dst);
}

unsigned long long get_time() {
    struct timeval tv1;
    gettimeofday(&tv1, NULL);
    return (unsigned long long)tv1.tv_sec * 1000 * 1000 + tv1.tv_usec;
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/base.h
================================================
#ifndef BASE_H
#define BASE_H

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#include <algorithm>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>

namespace xdb {

using std::string;

constexpr int ipv4 = 4;
constexpr int ipv6 = 6;

constexpr int policy_file    = 0;
constexpr int policy_vector  = 1;
constexpr int policy_content = 2;

constexpr int length_header = 256;
constexpr int length_vector = 256 * 256 * 8;

extern int ip_version;  // ip 版本
extern int ip_size;     // ip 占的字节数
extern int content_size;

void init_xdb(int version);

void log_exit(const string &msg);

void read_bin(int index, char *buf, size_t len, FILE *db);

unsigned to_uint(const char *buf);
unsigned to_ushort(const char *buf);
unsigned to_int(const char *buf, int n);

void write_uint(unsigned data, char buf[]);
void write_uint(unsigned data, FILE *dst);

void write_ushort(unsigned data, char buf[]);
void write_ushort(unsigned data, FILE *dst);

void write_string(const char *buf, unsigned len, FILE *dst);

unsigned long long get_time();

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/src/bench.cc
================================================

#include "bench.h"

namespace xdb {

bench_t::bench_t(const std::string &file_name, int version, int policy)
    : search(file_name, version, policy) {
}

void bench_t::test_one(const ip_t &ip, const string region) {
    if (search.search(ip.to_string()) != region)
        xdb::log_exit("failed: " + ip.to_string() + " " + region);
    sum_io_count += search.get_io_count();
    sum_cost_time += search.get_cost_time();
    sum_count++;
}

void bench_t::test_line(char *buf) {
    size_t buf_len = strlen(buf);
    if (buf_len == 0)
        return;
    buf[buf_len - 1] = '\0';  // 去掉换行符

    node_t node(buf);

    // 只测五个
    for (int i = 0; i < 5 && node.ip1 < node.ip2; ++i) {
        test_one(node.ip1, node.region);
        node.ip1 = node.ip1 + 1;
    }
    test_one(node.ip2, node.region);
}

void bench_t::test_file(const std::string &file_name) {
    FILE *f = fopen(file_name.data(), "r");
    if (f == NULL)
        xdb::log_exit("can't open " + file_name);
    char buf[1024];
    while (fgets(buf, sizeof(buf), f) != NULL)
        test_line(buf);
}

void bench_t::test(const string &file_name) {
    sum_io_count  = 0;
    sum_cost_time = 0;
    sum_count     = 0;

    unsigned long long tv1 = xdb::get_time();
    test_file(file_name);
    unsigned long long tv2 = xdb::get_time();

    double took = (tv2 - tv1) * 1.0 / 1000 / 1000;
    double cost = sum_cost_time * 1.0 / sum_count;

    printf(
        "total: %llu, took: %8.2fs, cost: %6.2fμs/op, io "
        "count: "
        "%llu\n",
        sum_count,
        took,
        cost,
        sum_io_count);
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/bench.h
================================================
#ifndef BENCH_H
#define BENCH_H

#include "search.h"

namespace xdb {

class bench_t {
public:
    bench_t(const string &file_name, int version, int policy);

    void test(const string &file_name);

private:
    void test_one(const ip_t &ip, const string region);
    void test_line(char *buf);
    void test_file(const std::string &file_name);

    search_t search;

    unsigned long long sum_io_count;
    unsigned long long sum_cost_time;
    unsigned long long sum_count;
};

}  // namespace xdb
#endif


================================================
FILE: binding/cpp/src/edit.cc
================================================

#include "edit.h"

namespace xdb {

void handle_ip_txt(const string& name, std::list<node_t>& regions) {
    FILE* f = fopen(name.data(), "r");
    if (f == NULL)
        log_exit("can't open " + name);

    char buf[1024];
    while (fgets(buf, sizeof(buf), f) != NULL) {
        unsigned int buf_len = strlen(buf);
        // 去掉多余的空
        while (buf_len > 0 && isspace(buf[buf_len - 1]))
            --buf_len;
        if (buf_len == 0)
            continue;
        buf[buf_len] = '\0';
        regions.push_back(node_t(buf));
    }

    fclose(f);
}

void edit_t::handle_new_file(const std::string& file_name) {
    handle_ip_txt(file_name, new_regions);  // 输入
    new_regions.sort();                     // 排序

    // 检验及其去重
    auto it = new_regions.begin();

    for (;;) {
        if (it == new_regions.end())
            break;
        auto next = it;
        ++next;
        if (next == new_regions.end())
            break;
        if (it->ip1 > it->ip2)
            it = new_regions.erase(it);  // 非法, 直接跳过
        else if (it->ip1 == next->ip1 || next->ip1 <= it->ip2) {
            // 数据重叠
            if (it->region != next->region)
                log_exit("数据有二义性: " + it->to_string() + ", " +
                         next->to_string());
            it->ip2 = std::max(it->ip2, next->ip2);
            new_regions.erase(next);
        } else if (it->ip2 + 1 == next->ip1 && it->region == next->region) {
            // 数据连接
            it->ip2 = next->ip2;
            new_regions.erase(next);
        } else {
            ++it;
        }
    }
}

void edit_t::handle_old_file(const std::string& file_name) {
    handle_ip_txt(file_name, old_regions);
}

void edit_t::merge() {
    auto it1 = old_regions.begin();
    auto it2 = new_regions.begin();

    for (;;) {
        if (it2 == new_regions.end())
            break;
        if (it2->ip1 > it2->ip2) {
            ++it2;
            continue;
        }
        // it1->ip1 it1->ip2 it2->ip1 it2->ip2
        while (it1->ip2 < it2->ip1)
            ++it1;
        if (it1->ip2 <= it2->ip2) {
            // it1->ip1 it2->ip1 it1->ip2 it2->ip2
            node_t node;
            node.ip1    = it2->ip1;
            node.ip2    = it1->ip2;
            node.region = it2->region;

            it1->ip2 = node.ip1 - 1;
            it2->ip1 = node.ip2 + 1;

            ++it1;
            it1 = old_regions.insert(it1, node);
            ++it1;
        } else {
            // it1->ip1 it2->ip1 it2->ip2 it1->ip2
            node_t node;
            node.ip1    = it2->ip2 + 1;
            node.ip2    = it1->ip2;
            node.region = it1->region;

            it1->ip2 = it2->ip1 - 1;

            ++it1;
            it1 = old_regions.insert(it1, *it2);

            ++it1;
            it1 = old_regions.insert(it1, node);

            ++it2;
        }
    }
}

void edit_t::write_old_file(const std::string& file_name) {
    FILE* f = fopen(file_name.data(), "w");
    if (f == NULL)
        log_exit("can't open " + file_name);

    auto it = old_regions.begin();

    // 删除非法的数据
    for (;;) {
        if (it == old_regions.end())
            break;
        if (it->ip1 > it->ip2)
            it = old_regions.erase(it);
        else
            ++it;
    }

    // 合并数据域相同的相邻数据
    it = old_regions.begin();
    for (;;) {
        if (it == old_regions.end())
            break;
        auto next = it;
        ++next;
        if (next == old_regions.end())
            break;
        if (it->region == next->region) {
            it->ip2 = next->ip2;
            old_regions.erase(next);
        } else {
            ++it;
        }
    }

    for (auto& d : old_regions) {
        string res =
            d.ip1.to_string() + "|" + d.ip2.to_string() + "|" + d.region + "\n";
        fputs(res.data(), f);
    }

    fclose(f);
}

edit_t::edit_t(const string& name_old, const string& name_new, int version) {
    unsigned long long tv1 = get_time();

    init_xdb(version);

    handle_new_file(name_new);
    handle_old_file(name_old);
    merge();
    write_old_file(name_old);

    unsigned long long tv2 = get_time();

    double took = (tv2 - tv1) * 1.0 / 1000 / 1000;

    printf("took: %.2fs\n", took);
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/edit.h
================================================
#ifndef EDIT_H
#define EDIT_H

#include "ip.h"

namespace xdb {

class edit_t {
public:
    edit_t(const string& old_name, const string& new_name, int version);

private:
    void handle_new_file(const string& file_name);
    void handle_old_file(const string& file_name);
    void merge();
    void write_old_file(const string& file_name);

    std::list<node_t> old_regions;
    std::list<node_t> new_regions;
};

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/src/header.cc
================================================

#include "header.h"

namespace xdb {

header_t::header_t(FILE* db) {
    read_bin(0, header, sizeof(header), db);
}

header_t::~header_t() {
}

int header_t::version() {
    return to_int(header, 2);  // 版本号(2)
}

int header_t::index_policy() {
    return to_int(header + 2, 2);  // 缓存策略(2)
}

int header_t::create_at() {
    return to_int(header + 4, 4);  // 文件生成时间(4)
}

int header_t::index_start() {
    return to_int(header + 8, 4);  // 索引起始地址(4)
}

int header_t::index_end() {
    return to_int(header + 12, 4);  // 索引结束地址(4)
}

int header_t::ip_version() {
    return to_int(header + 16, 2);  // IP 版本(2)
}

int header_t::ptr() {
    return to_int(header + 18, 2);  // 指针字节数(2)
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/header.h
================================================
#ifndef HEADER_H
#define HEADER_H

#include "base.h"

namespace xdb {

class header_t {
public:
    header_t(FILE* db);
    virtual ~header_t();

    int version();       // 版本号
    int index_policy();  // 缓存策略
    int create_at();     // 文件生成时间
    int index_start();   // 索引起始地址
    int index_end();     // 索引结束地址
    int ip_version();    // IP 版本
    int ptr();           // 指针字节数

protected:
    char header[length_header];
};

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/src/ip.cc
================================================

#include "ip.h"

namespace xdb {

ip_t::ip_t() {
    memset(p, '\0', sizeof(p));
}

ip_t::ip_t(const ip_t& rhs, int val) {
    memcpy(p, rhs.p, ip_size);

    if (val == 0 || val == 255)
        for (int i = 2; i < ip_size; ++i)
            p[i] = val;
}

ip_t::ip_t(const char* p) {
    from_xdb(p);
}

bool ip_t::from_str(const string& str) {
    int af_inet = ip_version == ipv4 ? AF_INET : AF_INET6;
    return inet_pton(af_inet, str.data(), p) == 1;
}

void ip_t::from_xdb(const char str[16]) {
    for (int i = 0; i < ip_size; ++i)
        if (ip_version == ipv6)
            p[i] = str[i];
        else
            p[i] = str[ip_size - 1 - i];
}

ip_t& ip_t::operator=(const ip_t& rhs) {
    memcpy(p, rhs.p, ip_size);
    return *this;
}

int ip_t::compare(const ip_t& rhs) const {
    for (int i = 0; i < ip_size; ++i) {
        if ((unsigned char)p[i] > (unsigned char)rhs.p[i])
            return 1;
        if ((unsigned char)p[i] < (unsigned char)rhs.p[i])
            return -1;
    }
    return 0;
}

bool ip_t::operator<(const ip_t& rhs) const {
    return compare(rhs) < 0;
}

bool ip_t::operator<=(const ip_t& rhs) const {
    return compare(rhs) <= 0;
}

bool ip_t::operator>(const ip_t& rhs) const {
    return compare(rhs) > 0;
}

bool ip_t::operator>=(const ip_t& rhs) const {
    return compare(rhs) >= 0;
}

bool ip_t::operator==(const ip_t& rhs) const {
    return compare(rhs) == 0;
}

bool ip_t::operator!=(const ip_t& rhs) const {
    return compare(rhs) != 0;
}

string ip_t::to_string() const {
    char buf[INET6_ADDRSTRLEN + 1];
    int  af_inet = ip_version == ipv4 ? AF_INET : AF_INET6;
    inet_ntop(af_inet, p, buf, sizeof(buf));
    return string(buf);
}

string ip_t::to_bit() const {
    string str;
    for (int i = 0; i < ip_size; ++i)
        if (ip_version == ipv6)
            str.push_back(p[i]);
        else
            str.push_back(p[ip_size - 1 - i]);
    return str;
}

ip_t operator+(const ip_t& lhs, int v) {
    ip_t ip;

    int i = ip_size;
    while (--i >= 0) {
        v += lhs.p[i];
        ip.p[i] = v % 256;
        v /= 256;
    }
    return ip;
}

ip_t operator-(const ip_t& lhs, int v) {
    ip_t ip;

    int i = ip_size;
    v     = -v;
    while (--i >= 0) {
        v += lhs.p[i];
        if (v == -1)
            ip.p[i] = 255;
        else {
            ip.p[i] = v;
            v       = 0;
        }
    }
    return ip;
}

// node_t
node_t::node_t() {
}

node_t::node_t(char* buf) {
    char* pos1 = strchr(buf, '|');

    if (pos1 == NULL)
        log_exit("invalid data: " + std::string(buf));
    char* pos2 = strchr(pos1 + 1, '|');
    if (pos2 == NULL)
        log_exit("invalid data: " + std::string(buf));
    *pos1 = '\0';
    *pos2 = '\0';

    region = pos2 + 1;

    if (!ip1.from_str(buf) || !ip2.from_str(pos1 + 1) || ip2 < ip1 ||
        region.empty()) {
        *pos1 = *pos2 = '|';
        log_exit(string("invalid data: ") + buf);
    }
}

bool node_t::operator<(const node_t& rhs) const {
    if (ip1 < rhs.ip1)
        return true;
    return ip2 < rhs.ip2;
}

string node_t::to_string() const {
    return ip1.to_string() + "|" + ip2.to_string() + "|" + region;
}

string node_t::to_bit() const {
    return ip1.to_bit() + ip2.to_bit();
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/ip.h
================================================
#ifndef IP_H
#define IP_H

#include "base.h"

namespace xdb {

struct ip_t {
    unsigned char p[16];

    ip_t();
    ip_t(const char* p);
    // val 为 0 或 255 时, 将 ip 的后几位置为 val
    ip_t(const ip_t& rhs, int val = -1);

    bool from_str(const string& str);
    void from_xdb(const char str[16]);

    ip_t& operator=(const ip_t& rhs);

    int  compare(const ip_t& rhs) const;
    bool operator<(const ip_t& rhs) const;
    bool operator<=(const ip_t& rhs) const;
    bool operator>(const ip_t& rhs) const;
    bool operator>=(const ip_t& rhs) const;
    bool operator==(const ip_t& rhs) const;
    bool operator!=(const ip_t& rhs) const;

    string to_string() const;
    string to_bit() const;
};

ip_t operator+(const ip_t& lhs, int v);
ip_t operator-(const ip_t& lhs, int v);

struct node_t {
    ip_t   ip1;
    ip_t   ip2;
    string region;

    node_t();
    node_t(char* buf);

    bool operator<(const node_t& rhs) const;

    string to_string() const;
    string to_bit() const;
};

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/src/make.cc
================================================

#include "make.h"

namespace xdb {

void make_t::vector_index_push_back(int row, int col, const node_t &node) {
    vector_index[row][col].push_back(
        std::make_pair<string, string>(node.to_bit(), string(node.region)));
}

void make_t::vector_index_push_back(node_t &node) {
    ip_t ip1 = node.ip1;
    ip_t ip2 = node.ip2;

    unsigned ip1_1 = ip1.p[0];
    unsigned ip1_2 = ip1.p[1];
    unsigned ip2_1 = ip2.p[0];
    unsigned ip2_2 = ip2.p[1];

    if (ip1_1 == ip2_1 && ip1_2 == ip2_2) {
        vector_index_push_back(ip1_1, ip1_2, node);
        return;
    }

    node.ip1 = ip1;
    node.ip2 = ip_t(ip1, 255);
    vector_index_push_back(ip1_1, ip1_2, node);

    node.ip1 = ip_t(ip2, 0);
    node.ip2 = ip2;
    vector_index_push_back(ip2_1, ip2_2, node);

    for (;;) {
        ++ip1_2;
        if (ip1_2 == 256) {
            ++ip1_1;
            ip1_2 = 0;
        }
        if (ip1_1 == ip2_1 && ip1_2 == ip2_2)
            break;
        ip1.p[0] = ip1_1;
        ip1.p[1] = ip1_2;
        node.ip1 = ip_t(ip1, 0);
        node.ip2 = ip_t(ip1, 255);
        vector_index_push_back(ip1_1, ip1_2, node);
    }
}

void make_t::handle_input_help(char *buf) {
    // 去掉多余的空
    unsigned int buf_len = strlen(buf);
    while (buf_len > 0 && isspace(buf[buf_len - 1]))
        --buf_len;
    if (buf_len == 0)
        return;
    buf[buf_len] = '\0';

    node_t node(buf);

    if (node.ip1 < next_ip) {
        log_exit("ip 未排序: " + node.ip1.to_string() + ", " +
                 next_ip.to_string());
    }

    next_ip = node.ip2 + 1;

    if (region.find(node.region) == region.end()) {
        region[node.region] = region_index;
        region_index += node.region.size();
    }

    vector_index_push_back(node);
}

void make_t::handle_input(const std::string &file_name) {
    FILE *src = fopen(file_name.data(), "r");
    if (src == NULL)
        log_exit("can't open " + file_name);

    char buf[1024];
    while (fgets(buf, sizeof(buf), src) != NULL)
        handle_input_help(buf);
    fclose(src);
}

void make_t::handle_header() {
    char buf[length_header];
    memset(buf, 0, length_header);
    write_ushort(3, buf);             // 版本号
    write_ushort(1, buf + 2);         // 缓存策略
    write_uint(time(NULL), buf + 4);  // 时间
    // 索引
    unsigned int content_left = length_header + length_vector;
    for (auto &d : region)
        content_left += d.first.size();
    unsigned int content_right = content_left;

    for (int i = 0; i < 256; ++i)
        for (int j = 0; j < 256; ++j)
            content_right += vector_index[i][j].size() * content_size;
    content_right -= content_size;
    write_uint(content_left, buf + 8);
    write_uint(content_right, buf + 12);
    write_ushort(ip_version, buf + 16);  // IP
    write_ushort(4, buf + 18);           // 指针数

    write_string(buf, length_header, db);
}

void make_t::handle_vector_index() {
    unsigned index = length_header + length_vector;
    for (auto &d : region)
        index += d.first.size();
    for (unsigned i = 0; i < 256; ++i)
        for (unsigned j = 0; j < 256; ++j)
            if (vector_index[i][j].size() == 0) {
                write_uint(0, db);
                write_uint(0, db);
            } else {
                write_uint(index, db);
                index += content_size * vector_index[i][j].size();
                write_uint(index, db);
            }
}

void make_t::handle_region() {
    for (auto &d : region) {
        fseek(db, d.second, SEEK_SET);
        write_string(d.first.data(), d.first.size(), db);
    }
}

void make_t::handle_content() {
    fseek(db, 0, SEEK_END);
    for (unsigned i = 0; i < 256; ++i)
        for (unsigned j = 0; j < 256; ++j)
            for (auto d : vector_index[i][j]) {
                write_string(d.first.data(), d.first.size(), db);
                write_ushort(d.second.size(), db);
                write_uint(region[d.second], db);
            }
}

make_t::make_t(const string &src, const string &dst, int version)
    : region_index(length_vector + length_header) {
    unsigned long long tv1 = get_time();

    init_xdb(version);

    handle_input(src);

    db = fopen(dst.data(), "w");
    if (db == NULL)
        log_exit("can't open " + dst);

    handle_header();
    handle_vector_index();
    handle_region();
    handle_content();

    fclose(db);

    unsigned long long tv2 = get_time();
    printf("took: %.2fs\n", (tv2 - tv1) * 1.0 / 1000 / 1000);
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/make.h
================================================
#ifndef MAKE_H
#define MAKE_H

#include "ip.h"

namespace xdb {

class make_t {
public:
    make_t(const string &src, const string &dst, int version);

private:
    void vector_index_push_back(int row, int col, const node_t &node);
    void vector_index_push_back(node_t &node);
    void handle_input_help(char buf[]);
    void handle_input(const std::string &file_name);

    void handle_header();
    void handle_vector_index();
    void handle_region();
    void handle_content();

    FILE *db = NULL;

    std::vector<std::pair<string, string>> vector_index[256][256];

    std::unordered_map<string, unsigned> region;

    unsigned region_index;
    ip_t     next_ip;
};

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/src/search.cc
================================================

#include "search.h"

namespace xdb {

search_t::search_t(const string &file, int version, int p)
    : db(fopen(file.data(), "r")), header(db), policy(p) {
    init_xdb(version);

    if (db == NULL)
        log_exit("can't open " + file);
    if (header.ip_version() != version)
        log_exit("ip 版本不匹配");

    if (policy != policy_file) {
        read_bin(length_header, vector, length_vector, db);
        if (policy == policy_content) {
            fseek(db, 0, SEEK_END);
            int size = ftell(db) - length_vector - length_header;
            content  = (char *)malloc(size);
            read_bin(length_vector + length_header, content, size, db);
        }
    }
}

search_t::~search_t() {
    fclose(db);
    if (policy == policy_content)
        free(content);
}

int search_t::get_io_count() {
    return io_count;
}

int search_t::get_cost_time() {
    return cost_time;
}

char const *search_t::get_content_index_help(int index) {
    if (policy != policy_file)
        return vector + index;

    ++io_count;
    static char v[8];
    read_bin(length_header + index, v, sizeof(v), db);
    return v;
}

void search_t::get_content_index(const ip_t &ip, int &left, int &right) {
    int index = ((unsigned char)ip.p[0] * 256 + (unsigned char)ip.p[1]) * 8;

    const char *p = get_content_index_help(index);
    left          = to_uint(p);
    right         = to_uint(p + 4);
}

char const *search_t::get_content_help(int index) {
    if (policy == policy_content)
        return content + index - length_header - length_vector;
    ++io_count;
    static char v[16 + 16 + 2 + 4];
    read_bin(index, v, content_size, db);
    return v;
}

string search_t::get_region(int index, int len) {
    if (policy == policy_content)
        return string(content + index - length_header - length_vector, len);
    ++io_count;
    char *p = (char *)malloc(sizeof(char) * len);
    read_bin(index, p, len, db);
    string res(p, len);
    free(p);
    return res;
}

void search_t::get_content(int   index,
                           ip_t &ip_left,
                           ip_t &ip_right,
                           int  &region_len,
                           int  &region_index) {
    const char *p = get_content_help(index);

    ip_left.from_xdb(p);
    ip_right.from_xdb(p + ip_size);

    region_len   = to_ushort(p + ip_size * 2);
    region_index = to_uint(p + ip_size * 2 + 2);
}

string search_t::search(const ip_t &ip) {
    io_count = 0;

    int content_left, content_right;
    get_content_index(ip, content_left, content_right);

    if (content_left == 0 || content_right == 0)
        return "";

    ip_t ip_left, ip_right;
    int  region_len;
    int  region_index;

    int left  = 0;
    int right = (content_right - content_left) / content_size;

    for (;;) {
        int mid       = left + (right - left) / 2;
        int mid_index = content_left + mid * content_size;
        get_content(mid_index, ip_left, ip_right, region_len, region_index);

        // ip ip_left ip_right
        if (ip < ip_left)
            right = mid - 1;
        // ip_left ip_right ip
        else if (ip_right < ip)
            left = mid + 1;
        else
            return get_region(region_index, region_len);
    }
}

string search_t::search(const string &str) {
    unsigned long long t1 = get_time();

    ip_t ip;
    if (ip.from_str(str) == false)
        return "invalid ipv" + std::to_string(ip_version) + ": " + str;
    string region = search(ip);

    unsigned long long t2 = get_time();
    cost_time             = t2 - t1;
    return region;
}

}  // namespace xdb


================================================
FILE: binding/cpp/src/search.h
================================================
#ifndef SEARCH_H
#define SEARCH_H

#include "header.h"
#include "ip.h"

namespace xdb {

class search_t {
protected:
    FILE *db;

    header_t header;

    int policy;

    int io_count;
    int cost_time;

    char  vector[length_vector];
    char *content;

public:
    search_t(const string &file_name, int version, int policy);
    virtual ~search_t();

    int get_io_count();
    int get_cost_time();

    string search(const string &str);

protected:
    string search(const ip_t &ip);

    void get_content_index(const ip_t &ip1, int &left, int &right);
    void get_content(int   index,
                     ip_t &left,
                     ip_t &right,
                     int  &region_len,
                     int  &region_index);

    char const *get_content_index_help(int index);
    char const *get_content_help(int index);
    string      get_region(int index, int len);
};

}  // namespace xdb

#endif


================================================
FILE: binding/cpp/test/bench.cc
================================================

#include "../src/bench.h"

std::map<int, std::string> prompt;

void test_ipv4(int policy) {
    std::cout << "测试 IPv4, " << prompt[policy];
    xdb::bench_t("../../data/ip2region_v4.xdb", xdb::ipv4, policy)
        .test("../../data/ipv4_source.txt");
}

void test_ipv6(int policy) {
    std::cout << "测试 IPv6, " << prompt[policy];
    xdb::bench_t("../../data/ip2region_v6.xdb", xdb::ipv6, policy)
        .test("../../data/ipv6_source.txt");
}

int main() {
    prompt[xdb::policy_file]    = "  不缓存, ";
    prompt[xdb::policy_vector]  = "部分缓存, ";
    prompt[xdb::policy_content] = "全部缓存, ";

    test_ipv4(xdb::policy_file);
    test_ipv4(xdb::policy_vector);
    test_ipv4(xdb::policy_content);

    test_ipv6(xdb::policy_file);
    test_ipv6(xdb::policy_vector);
    test_ipv6(xdb::policy_content);
    return 0;
}


================================================
FILE: binding/cpp/test/edit_v4.cc
================================================

#include "../src/edit.h"

int main() {
    std::string file_name_old = "../../data/ipv4_source.txt";
    std::string file_name_new = "./1.txt";
    xdb::edit_t xdb(file_name_old, file_name_new, xdb::ipv4);
    return 0;
}


================================================
FILE: binding/cpp/test/edit_v6.cc
================================================

#include "../src/edit.h"

int main() {
    std::string file_name_old = "../../data/ipv6_source.txt";
    std::string file_name_new = "./1.txt";
    xdb::edit_t xdb(file_name_old, file_name_new, xdb::ipv6);
    return 0;
}


================================================
FILE: binding/cpp/test/header.cc
================================================

#include "../src/header.h"

void test(const std::string& prompt, const std::string& file_name) {
    std::cout << prompt << std::endl;

    xdb::header_t head(fopen(file_name.data(), "r"));

    std::cout << "版本号: " << head.version() << std::endl;
    std::cout << "缓存策略: " << head.index_policy() << std::endl;

    time_t     rawtime = head.create_at();
    struct tm* info    = localtime(&rawtime);
    char       buf[80];
    strftime(buf, 80, "%Y-%m-%d %H:%M:%S", info);

    std::cout << "文件生成时间: " << buf << std::endl;
    std::cout << "索引起始地址: " << head.index_start() << std::endl;
    std::cout << "索引结束地址: " << head.index_end() << std::endl;
    std::cout << "IP版本: " << head.ip_version() << std::endl;
    std::cout << "指针字节数: " << head.ptr() << std::endl;

    std::cout << std::endl;
}

int main() {
    test("测试 IPv4", "../../data/ip2region_v4.xdb");
    test("测试 IPv6", "../../data/ip2region_v6.xdb");
    return 0;
}


================================================
FILE: binding/cpp/test/make.cc
================================================

#include "../src/make.h"

void test(const std::string& prompt,
          const std::string& filename_xdb,
          const std::string& filename_src,
          int                version

) {
    std::cout << prompt;
    xdb::make_t(filename_xdb, filename_src, version);
}

int main() {
    test("生成 ipv4 的 xdb 文件, ",
         "../../data/ipv4_source.txt",
         "./ip2region_v4.xdb",
         xdb::ipv4);

    test("生成 ipv6 的 xdb 文件, ",
         "../../data/ipv6_source.txt",
         "./ip2region_v6.xdb",
         xdb::ipv6);

    return 0;
}


================================================
FILE: binding/cpp/test/search.cc
================================================

#include "../src/search.h"

std::map<int, std::string> prompt;

void test(xdb::search_t& s, const std::string& ip, const std::string& region) {
    if (s.search(ip) != region)
        xdb::log_exit("测试失败, ip " + ip + ", region " + region);
}

void test_ipv4(int policy) {
    std::cout << "测试 IPv4 " << prompt[policy];

    xdb::search_t s("../../data/ip2region_v4.xdb", xdb::ipv4, policy);
    test(s, "0.0.0.0", "Reserved|Reserved|Reserved|0|0");
    test(s, "1.2.3.4", "Australia|Queensland|Brisbane|0|AU");

    std::cout << " 成功" << std::endl;
}

void test_ipv6(int policy) {
    std::cout << "测试 IPv6 " << prompt[policy];

    xdb::search_t s("../../data/ip2region_v6.xdb", xdb::ipv6, policy);
    test(s, "::1", "");
    test(s, "2001:200:124::", "Japan|Tokyo|Asagaya-minami|WIDE Project|JP");
    test(s, "2001:200:124::", "Japan|Tokyo|Asagaya-minami|WIDE Project|JP");
    test(s, "240e:3b7:3273:51d0:cd38:8ae1:e3c0:b708", "中国|广东省|深圳市|电信|CN");

    std::cout << " 成功" << std::endl;
}

int main() {
    prompt[xdb::policy_file]    = "  不缓存:";
    prompt[xdb::policy_vector]  = "部分缓存:";
    prompt[xdb::policy_content] = "全部缓存:";

    test_ipv4(xdb::policy_file);
    test_ipv4(xdb::policy_vector);
    test_ipv4(xdb::policy_content);

    test_ipv6(xdb::policy_file);
    test_ipv6(xdb::policy_vector);
    test_ipv6(xdb::policy_content);
    return 0;
}


================================================
FILE: binding/csharp/.editorconfig
================================================
root = true

# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
[*]
charset = utf-8
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true
spelling_exclusion_path = .\exclusion.dic

[*.csproj]
charset = utf-8
insert_final_newline = true

[*.{xml,config,csproj,nuspec,props,resx,targets,yml,tasks,json}]
indent_size = 2

[*.sh]
end_of_line = lf

[*.cs]
csharp_style_namespace_declarations = file_scoped:silent

[*.cs]
file_header_template = Copyright 2025 The Ip2Region Authors. All rights reserved.\nUse of this source code is governed by a Apache2.0-style\nlicense that can be found in the LICENSE file.\n@Author Alan <lzh.shap@gmail.com>\n@Date   2023/07/25\nUpdated by Argo Zhang <argo@live.ca> at 2025/11/21


================================================
FILE: binding/csharp/.gitignore
================================================
## 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/

================================================
FILE: binding/csharp/CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

## [3.0.0] - 2025-11-22
- 支持 .NET 10.0
- 增加 IPv6 支持
- 修复若干 bug

## [2.0.1] - 2023-07-30

### Added
- Support netstandard2.0

## [2.0.0] - 2023-07-26

### Removed
- Remove nuget include xdb file
- Searcher cache policy default parameters
- Searcher xdb file path default parameters

### Added
- Dependent file query policies CachePolicy.VectorIndex, CachePolicy.File support thread-safe concurrent queries
- Dramatically optimizes overall performance


================================================
FILE: binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using System.Net;

namespace IP2Region.Net.Abstractions;

/// <summary>
/// IP 转化为地理位置搜索器接口
/// </summary>
public interface ISearcher : IDisposable
{
    /// <summary>
    /// 搜索方法
    /// </summary>
    /// <param name="ipStr">IP 地址字符串 如 192.168.0.1</param>
    /// <returns></returns>
    string? Search(string ipStr);

    /// <summary>
    /// 搜索方法
    /// </summary>
    string? Search(IPAddress ipAddress);

    /// <summary>
    /// 搜索方法 仅限 IPv4 使用
    /// </summary>
    /// <param name="ipAddress">IPv4 地址字节数组小端读取 uint 数值</param>
    [Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search(IPAddress) method.")]
    string? Search(uint ipAddress);

    /// <summary>
    /// 获得 内部 IO 访问次数
    /// </summary>
    int IoCount { get; }
}


================================================
FILE: binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// IP2Region 服务扩展类
/// </summary>
public static class IP2RegionExtensions
{
    /// <summary>
    /// 添加 IP2RegionService 服务。
    /// </summary>
    /// <param name="services"><see cref="IServiceCollection"/> 集合</param>
    /// <param name="path">IP2Region 数据库文件的路径。</param>
    /// <param name="cachePolicy">缓存策略,默认为 <see cref="CachePolicy.Content"/>。</param>
    public static IServiceCollection AddIP2RegionService(this IServiceCollection services, string path, CachePolicy cachePolicy = CachePolicy.Content)
    {
        services.TryAddSingleton<ISearcher>(provider =>
        {
            return new Searcher(cachePolicy, path);
        });
#if NET8_0_OR_GREATER
        services.TryAddKeyedSingleton("IP2Region.Net", (provider, _) =>
        {
            return provider.GetRequiredService<ISearcher>();
        });
#endif

        return services;
    }
}


================================================
FILE: binding/csharp/IP2Region.Net/IP2Region.Net.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

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

	<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.*" />
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.*" />
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
	</ItemGroup>

	<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
	</ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.*" />
  </ItemGroup>

  <ItemGroup>
      <None Include="..\CHANGELOG.md">
        <Link>CHANGELOG.md</Link>
      </None>
    </ItemGroup>
</Project>


================================================
FILE: binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using IP2Region.Net.XDB;

namespace IP2Region.Net.Internal;

static class CacheStrategyFactory
{
    public static ICacheStrategy CreateCacheStrategy(CachePolicy cachePolicy, string xdbPath) => cachePolicy switch
    {
        CachePolicy.Content => new ContentCacheStrategy(xdbPath),
        CachePolicy.VectorIndex => new VectorIndexCacheStrategy(xdbPath),
        _ => new FileCacheStrategy(xdbPath),
    };
}


================================================
FILE: binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Wong <vcd.hai@outlook.com> at 2025/12/31

namespace IP2Region.Net.Internal;

class ContentCacheStrategy(string xdbPath) : ICacheStrategy
{
    // TODO: these constants can be moved to the interface as defaults when using .NET 10
    private const int HeaderInfoLength = 256;
    private const int VectorIndexSize = 8;

    private readonly ReadOnlyMemory<byte> _cacheData = File.ReadAllBytes(xdbPath);

    public int IoCount => 0;

    public void ResetIoCount()
    {
        // Do nothing
    }

    public ReadOnlyMemory<byte> GetVectorIndex(int offset)
        => _cacheData.Slice(HeaderInfoLength + offset, VectorIndexSize);

    public ReadOnlyMemory<byte> GetData(long offset, int length) => _cacheData.Slice((int)offset, length);

    public void Dispose()
    {
        // Do nothing
    }
}


================================================
FILE: binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using System.Buffers;

namespace IP2Region.Net.Internal;

class FileCacheStrategy(string xdbPath) : ICacheStrategy
{
    protected const int HeaderInfoLength = 256;
    protected const int VectorIndexSize = 8;

    protected const int BufferSize = 64 * 1024;

    protected FileStream XdbFileStream = new(xdbPath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.RandomAccess);

    public int IoCount { get; set; }

    public void ResetIoCount()
    {
        IoCount = 0;
    }

    public virtual ReadOnlyMemory<byte> GetVectorIndex(int offset) => GetData(HeaderInfoLength + offset, VectorIndexSize);

    public virtual ReadOnlyMemory<byte> GetData(long offset, int length)
    {
        var buffer = ArrayPool<byte>.Shared.Rent(length);
        try
        {
            int totalBytesRead = 0;
            XdbFileStream.Seek(offset, SeekOrigin.Begin);

            int bytesRead;
            while (totalBytesRead < length)
            {
                bytesRead = XdbFileStream.Read(buffer, totalBytesRead, length - totalBytesRead);
                if (bytesRead == 0)
                {
                    break;
                }

                totalBytesRead += bytesRead;
                IoCount++;
            }

            var ret = new byte[totalBytesRead];
            if (totalBytesRead > 0)
            {
                Array.Copy(buffer, 0, ret, 0, totalBytesRead);
            }
            return ret;
        }
        finally
        {
            ArrayPool<byte>.Shared.Return(buffer);
        }
    }

    /// <summary>
    /// 释放文件句柄
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            XdbFileStream.Close();
            XdbFileStream.Dispose();
        }
    }

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}


================================================
FILE: binding/csharp/IP2Region.Net/Internal/ICacheStrategy.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

namespace IP2Region.Net.Internal;

internal interface ICacheStrategy : IDisposable
{
    int IoCount { get; }

    void ResetIoCount();

    ReadOnlyMemory<byte> GetVectorIndex(int offset);

    ReadOnlyMemory<byte> GetData(long offset, int length);
}


================================================
FILE: binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

namespace IP2Region.Net.Internal;

class VectorIndexCacheStrategy : FileCacheStrategy
{
    private const int VectorIndexRows = 256;
    private const int VectorIndexCols = 256;

    private readonly ReadOnlyMemory<byte> _vectorCache;

    public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath)
    {
        _vectorCache = GetData(HeaderInfoLength, VectorIndexRows * VectorIndexCols * VectorIndexSize);
    }

    public override ReadOnlyMemory<byte> GetVectorIndex(int offset) => _vectorCache.Slice(offset, VectorIndexSize);
}


================================================
FILE: binding/csharp/IP2Region.Net/XDB/CachePolicy.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

namespace IP2Region.Net.XDB;

/// <summary>
/// 缓存策略枚举
/// </summary>
public enum CachePolicy
{
    /// <summary>
    /// no cache 
    /// </summary>
    File,

    /// <summary>
    /// cache vector index , reduce the number of IO operations
    /// </summary>
    VectorIndex,

    /// <summary>
    /// default cache policy , cache whole xdb file
    /// </summary>
    Content
}


================================================
FILE: binding/csharp/IP2Region.Net/XDB/Searcher.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using IP2Region.Net.Abstractions;
using IP2Region.Net.Internal;
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;

namespace IP2Region.Net.XDB;

/// <summary>
/// <see cref="ISearcher"/> 实现类
/// </summary>
/// <remarks>
/// <inheritdoc/>
/// </remarks>
public class Searcher(CachePolicy cachePolicy, string xdbPath) : ISearcher
{
    private readonly ICacheStrategy _cacheStrategy = CacheStrategyFactory.CreateCacheStrategy(cachePolicy, xdbPath);

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    public int IoCount => _cacheStrategy.IoCount;

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    public string? Search(string ipStr)
    {
        var ipAddress = IPAddress.Parse(ipStr);
        return SearchCore(ipAddress.GetAddressBytes());
    }

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    public string? Search(IPAddress ipAddress) => SearchCore(ipAddress.GetAddressBytes());

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    [Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search(IPAddress) method.")]
    [ExcludeFromCodeCoverage]
    public string? Search(uint ipAddress)
    {
        var bytes = BitConverter.GetBytes(ipAddress);
        Array.Reverse(bytes);
        return SearchCore(bytes);
    }

    string? SearchCore(byte[] ipBytes)
    {
        // 重置 IO 计数器
        _cacheStrategy.ResetIoCount();

        // 每个 vector 索引项的字节数
        var vectorIndexSize = 8;

        // vector 索引的列数
        var vectorIndexCols = 256;

        // 计算得到 vector 索引项的开始地址。
        var il0 = ipBytes[0];
        var il1 = ipBytes[1];
        var idx = il0 * vectorIndexCols * vectorIndexSize + il1 * vectorIndexSize;

        var vector = _cacheStrategy.GetVectorIndex(idx);
        var sPtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span);
        var ePtr = BinaryPrimitives.ReadUInt32LittleEndian(vector.Span.Slice(4));

        // @Note: ptr validate, zero ptr means source data missing
        // so we could just stop here and return an empty string.
        if (sPtr == 0 || ePtr == 0)
        {
            return "";
        }


        var length = ipBytes.Length;
        var indexSize = length * 2 + 6;
        var l = 0;
        var h = (ePtr - sPtr) / indexSize;
        var dataLen = 0;
        long dataPtr = 0;

        while (l <= h)
        {
            int m = (int)(l + h) >> 1;

            var p = sPtr + m * indexSize;
            var buff = _cacheStrategy.GetData(p, indexSize);

            var s = buff.Span.Slice(0, length);
            var e = buff.Span.Slice(length, length);
            if (ByteCompare(ipBytes, s) < 0)
            {
                h = m - 1;
            }
            else if (ByteCompare(ipBytes, e) > 0)
            {
                l = m + 1;
            }
            else
            {
                dataLen = BinaryPrimitives.ReadUInt16LittleEndian(buff.Span.Slice(length * 2, 2));
                dataPtr = BinaryPrimitives.ReadUInt32LittleEndian(buff.Span.Slice(length * 2 + 2, 4));
                break;
            }
        }

        var regionBuff = _cacheStrategy.GetData(dataPtr, dataLen);
        return Encoding.UTF8.GetString(regionBuff.Span.ToArray());
    }

    static int ByteCompare(byte[] ip1, ReadOnlySpan<byte> ip2) => ip1.Length == 4 ? IPv4Compare(ip1, ip2) : IPv6Compare(ip1, ip2);

    static int IPv4Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
    {
        var ret = 0;
        for (int i = 0; i < ip1.Length; i++)
        {
            var ip2Index = ip1.Length - 1 - i;
            if (ip1[i] < ip2[ip2Index])
            {
                return -1;
            }
            else if (ip1[i] > ip2[ip2Index])
            {
                return 1;
            }
        }
        return ret;
    }

    static int IPv6Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
    {
        var ret = 0;
        for (int i = 0; i < ip1.Length; i++)
        {
            if (ip1[i] < ip2[i])
            {
                return -1;
            }
            else if (ip1[i] > ip2[i])
            {
                return 1;
            }
        }
        return ret;
    }

    /// <summary>
    /// <inheritdoc/>
    /// </summary>
    public void Dispose()
    {
        _cacheStrategy.Dispose();
        GC.SuppressFinalize(this);
    }
}


================================================
FILE: binding/csharp/IP2Region.Net/XDB/Util.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using System.Buffers;
using System.Buffers.Binary;
using System.Net;
using System.Runtime.InteropServices;

namespace IP2Region.Net.XDB;

/// <summary>
/// 工具类
/// </summary>
public static class Util
{
    public static uint IpAddressToUInt32(string ipAddress)
    {
        var address = IPAddress.Parse(ipAddress);
        return IpAddressToUInt32(address);
    }

    public static uint IpAddressToUInt32(IPAddress ipAddress)
    {
        byte[] bytes = ipAddress.GetAddressBytes();
        Array.Reverse(bytes);
        return MemoryMarshal.Read<uint>(bytes);
    }

    public static uint GetMidIp(uint x, uint y)
        => (x & y) + ((x ^ y) >> 1);

    public static async Task<XdbVersion> GetVersionAsync(string dbPath, CancellationToken token = default)
    {
        if (string.IsNullOrEmpty(dbPath))
        {
            throw new ArgumentNullException(nameof(dbPath));
        }

        if (!File.Exists(dbPath))
        {
            throw new FileNotFoundException("xdb file not found.", dbPath);
        }

        using var reader = File.OpenRead(dbPath);
        return await GetVersionAsync(reader, token);
    }

    internal static async Task<XdbVersion> GetVersionAsync(FileStream reader, CancellationToken token = default)
    {
        XdbVersion ret = default;
        var buffer = ArrayPool<byte>.Shared.Rent(256);

        try
        {
            var length = await reader.ReadAsync(buffer, 0, 256, token);
            if (length == 256)
            {
                ret = Parse(buffer);
            }
        }
        finally
        {
            ArrayPool<byte>.Shared.Return(buffer);
        }

        return ret;
    }

    private static XdbVersion Parse(ReadOnlySpan<byte> buffer)
    {
        var ret = new XdbVersion
        {
            Ver = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)),
            CachePolice = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)),
            StartIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(8, 4)),
            EndIndex = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(12, 4)),
            IPVer = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(16, 2)),
            BytesCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(18, 2))
        };

        var createdAt = BinaryPrimitives.ReadUInt32LittleEndian(buffer.Slice(4, 4));
        var dtm = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(0));
        ret.CreatedTime = dtm.AddSeconds(createdAt);

        return ret;
    }
}


================================================
FILE: binding/csharp/IP2Region.Net/XDB/XdbVersion.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

namespace IP2Region.Net.XDB;

/// <summary>
/// XdbVersion 结构体
/// </summary>
public struct XdbVersion
{
    /// <summary>
    /// 获得/设置 版本号
    /// </summary>
    public ushort Ver { get; set; }

    /// <summary>
    /// 获得/设置 缓存策略
    /// </summary>
    public ushort CachePolice { get; set; }

    /// <summary>
    /// 获得/设置 文件生成时间
    /// </summary>
    public DateTimeOffset CreatedTime { get; set; }

    /// <summary>
    /// 获得/设置 索引起始地址
    /// </summary>
    public uint StartIndex { get; set; }

    /// <summary>
    /// 获得/设置 索引结束地址
    /// </summary>
    public uint EndIndex { get; set; }

    /// <summary>
    /// 获得/设置 IP版本
    /// </summary>
    public ushort IPVer { get; set; }

    /// <summary>
    /// 获得/设置 指针字节数
    /// </summary>
    public ushort BytesCount { get; set; }
}


================================================
FILE: binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using BenchmarkDotNet.Attributes;
using IP2Region.Net.XDB;

namespace IP2Region.Net.BenchMark;

[MemoryDiagnoser]
public class Benchmarks
{
    private static readonly string XdbPathV4 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v4.xdb");
    private static readonly string XdbPathV6 = Path.Combine(AppContext.BaseDirectory, "IP2Region", "ip2region_v6.xdb");
    private static readonly Searcher _contentV4Searcher = new(CachePolicy.Content, XdbPathV4);
    private static readonly Searcher _vectorV4Searcher = new(CachePolicy.VectorIndex, XdbPathV4);
    private static readonly Searcher _fileV4Searcher = new(CachePolicy.File, XdbPathV4);
    private static readonly Searcher _contentV6Searcher = new(CachePolicy.Content, XdbPathV6);
    private static readonly Searcher _vectorV6Searcher = new(CachePolicy.VectorIndex, XdbPathV6);
    private static readonly Searcher _fileV6Searcher = new(CachePolicy.File, XdbPathV6);

    private readonly string _testIPv4Address = "114.114.114.114";
    private readonly string _testIPv6Address = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";

    public Benchmarks()
    {
        _contentV4Searcher.Search(_testIPv4Address);
        _vectorV4Searcher.Search(_testIPv4Address);
        _fileV4Searcher.Search(_testIPv4Address);

        _contentV6Searcher.Search(_testIPv6Address);
        _vectorV6Searcher.Search(_testIPv6Address);
        _fileV6Searcher.Search(_testIPv6Address);
    }

    [Benchmark]
    [BenchmarkCategory("IPv4")]
    public void ContentIPv4() => _contentV4Searcher.Search(_testIPv4Address);

    [Benchmark]
    [BenchmarkCategory("IPv4")]
    public void VectorIPv4() => _vectorV4Searcher.Search(_testIPv4Address);

    [Benchmark]
    [BenchmarkCategory("IPv4")]
    public void FileIPv4() => _fileV4Searcher.Search(_testIPv4Address);

    [Benchmark]
    [BenchmarkCategory("IPv6")]
    public void ContentIPv6() => _contentV6Searcher.Search(_testIPv6Address);

    [Benchmark]
    [BenchmarkCategory("IPv6")]
    public void VectorIPv6() => _vectorV6Searcher.Search(_testIPv6Address);

    [Benchmark]
    [BenchmarkCategory("IPv6")]
    public void FileIPv6() => _fileV6Searcher.Search(_testIPv6Address);
}


================================================
FILE: binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="BenchmarkDotNet" Version="0.15.6" />
      <PackageReference Include="BenchmarkDotNet.Annotations" Version="0.15.6" />
    </ItemGroup>

    <ItemGroup>
      <ProjectReference Include="..\IP2Region.Net\IP2Region.Net.csproj" />
    </ItemGroup>

    <ItemGroup>
      <Content Include="..\..\..\data\ip2region_v4.xdb">
        <Link>IP2Region/ip2region_v4.xdb</Link>
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
	  <Content Include="..\..\..\data\ip2region_v6.xdb">
		<Link>IP2Region/ip2region_v6.xdb</Link>
		<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	  </Content>
	</ItemGroup>

</Project>


================================================
FILE: binding/csharp/IP2Region.Net.BenchMark/Program.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using BenchmarkDotNet.Running;
using IP2Region.Net.BenchMark;

BenchmarkRunner.Run<Benchmarks>();


================================================
FILE: binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net10.0</TargetFramework>
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>

		<IsPackable>false</IsPackable>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
		<PackageReference Include="xunit" Version="2.*" />
		<PackageReference Include="xunit.runner.visualstudio" Version="3.*">
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
			<PrivateAssets>all</PrivateAssets>
		</PackageReference>
		<PackageReference Include="coverlet.collector" Version="6.0.4">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
	</ItemGroup>

	<ItemGroup>
		<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
	</ItemGroup>

	<ItemGroup>
		<ProjectReference Include="..\IP2Region.Net\IP2Region.Net.csproj" />
	</ItemGroup>

	<ItemGroup>
		<Content Include="..\..\..\data\ipv4_source.txt">
			<Link>TestData/ipv4_source.txt</Link>
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</Content>
		<Content Include="..\..\..\data\ip2region_v4.xdb">
			<Link>TestData/ip2region_v4.xdb</Link>
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</Content>
		<Content Include="..\..\..\data\ipv6_source.txt">
			<Link>TestData/ipv6_source.txt</Link>
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</Content>
		<Content Include="..\..\..\data\ip2region_v6.xdb">
			<Link>TestData/ip2region_v6.xdb</Link>
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</Content>
	</ItemGroup>

</Project>


================================================
FILE: binding/csharp/IP2Region.Net.Test/SearcherTest.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
using Xunit;

namespace IP2Region.Net.Test;

public class SearcherTest
{
    private readonly string _xdbPathV4 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v4.xdb");
    private readonly string _xdbPathV6 = Path.Combine(AppContext.BaseDirectory, "TestData", "ip2region_v6.xdb");

    [Theory]
    [InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
    [InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
    [InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
    [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
    [InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
    [InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
    [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
    public void TestSearchCacheContent(string ip, string expected, string version)
    {
        var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
        var contentSearcher = new Searcher(CachePolicy.Content, _xdbPath);
        var region = contentSearcher.Search(ip);
        Assert.Equal(expected, region);
    }

    [Theory]
    [InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
    [InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
    [InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
    [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
    [InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
    [InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
    [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
    public void TestSearchCacheVector(string ip, string expected, string version)
    {
        var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
        var vectorSearcher = new Searcher(CachePolicy.VectorIndex, _xdbPath);
        var region = vectorSearcher.Search(ip);
        Assert.Equal(expected, region);
    }

    [Theory]
    [InlineData("58.251.0.0", "中国|广东省|深圳市|联通|CN", "v4")]
    [InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN", "v4")]
    [InlineData("58.251.27.201", "中国|广东省|深圳市|联通|CN", "v4")]
    [InlineData("114.114.114.114", "中国|江苏省|南京市|0|CN", "v4")]
    [InlineData("119.29.29.29", "中国|北京|北京市|腾讯|CN", "v4")]
    [InlineData("223.5.5.5", "中国|浙江省|杭州市|阿里|CN", "v4")]
    [InlineData("180.76.76.76", "中国|北京|北京市|百度|CN", "v4")]
    [InlineData("8.8.8.8", "United States|California|0|Google LLC|US", "v4")]
    [InlineData("240e:3b7:3272:d8d0:db09:c067:8d59:539e", "中国|广东省|深圳市|电信|CN", "v6")]
    [InlineData("240e:044d:2d00:0000:0000:0000:0000:0000", "中国|云南|楚雄|电信|CN", "v6")]
    public void TestSearchCacheFile(string ip, string expected, string version)
    {
        var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
        var fileSearcher = new Searcher(CachePolicy.File, _xdbPath);
        var region = fileSearcher.Search(ip);
        Assert.Equal(expected, region);
    }

    [Fact]
    public void IoCount_File_Ok()
    {
        var searcher = new Searcher(CachePolicy.File, _xdbPathV4);
        searcher.Search("58.251.27.201");
        Assert.Equal(3, searcher.IoCount);

        searcher.Search("58.251.27.201");
        Assert.Equal(3, searcher.IoCount);

        searcher.Dispose();
    }

    [Fact]
    public void IoCount_Vector_Ok()
    {
        var searcher = new Searcher(CachePolicy.VectorIndex, _xdbPathV4);
        searcher.Search("58.251.27.201");
        Assert.Equal(2, searcher.IoCount);

        searcher.Search("58.251.27.201");
        Assert.Equal(2, searcher.IoCount);

        searcher.Dispose();
    }

    [Fact]
    public void IoCount_Content_Ok()
    {
        var searcher = new Searcher(CachePolicy.Content, _xdbPathV4);
        searcher.Search("58.251.27.201");
        Assert.Equal(0, searcher.IoCount);

        searcher.Search("58.251.27.201");
        Assert.Equal(0, searcher.IoCount);

        searcher.Dispose();
    }

    [Theory]
    [InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN")]
    public void Search_Ip_Ok(string ipStr, string expected)
    {
        var fileSearcher = new Searcher(CachePolicy.File, _xdbPathV4);
        var ipAddress = IPAddress.Parse(ipStr);
        var region = fileSearcher.Search(ipAddress);
        Assert.Equal(expected, region);
    }

    [Theory]
    [InlineData("58.251.255.255", "中国|广东省|深圳市|联通|CN")]
    public void AddIP2RegionService_Ok(string ipStr, string expected)
    {
        var services = new ServiceCollection();
        services.AddIP2RegionService(_xdbPathV4, CachePolicy.File);

        var provider = services.BuildServiceProvider();
        var searcher = provider.GetRequiredService<ISearcher>();
        var region = searcher.Search(ipStr);
        Assert.Equal(expected, region);

        searcher = provider.GetRequiredKeyedService<ISearcher>("IP2Region.Net");
        region = searcher.Search(ipStr);
        Assert.Equal(expected, region);
    }

    [Theory]
    [InlineData(CachePolicy.Content, "v4")]
    [InlineData(CachePolicy.VectorIndex, "v4")]
    [InlineData(CachePolicy.File, "v4")]
    [InlineData(CachePolicy.Content, "v6")]
    [InlineData(CachePolicy.VectorIndex, "v6")]
    [InlineData(CachePolicy.File, "v6")]
    public void TestBenchSearch(CachePolicy cachePolicy, string version)
    {
        var _xdbPath = version == "v4" ? _xdbPathV4 : _xdbPathV6;
        var searcher = new Searcher(cachePolicy, _xdbPath);
        var srcPath = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip{version}_source.txt");

        foreach (var line in File.ReadLines(srcPath))
        {
            var ps = line.Trim().Split("|", 3);
            var sip = ps[0];
            var eip = ps[1];

            var s1 = searcher.Search(sip);
            var s2 = searcher.Search(eip);
            Assert.Equal(s1, ps[2]);
            Assert.Equal(s2, ps[2]);
        }
    }
}


================================================
FILE: binding/csharp/IP2Region.Net.Test/UtilTest.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using Xunit;

namespace IP2Region.Net.Test;

public class UtilTest
{
    [Fact]
    public void IpAddressToUInt32_Ok()
    {
        var uintIp = XDB.Util.IpAddressToUInt32("114.114.114.114");
        Assert.Equal((uint)1920103026, uintIp);
    }

    [Fact]
    public void GetMidIp_Ok()
    {
        var uintIp = XDB.Util.GetMidIp(1, 10);
        Assert.Equal((uint)5, uintIp);
    }
}

================================================
FILE: binding/csharp/IP2Region.Net.Test/XdbTest.cs
================================================
// Copyright 2025 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// @Author Alan <lzh.shap@gmail.com>
// @Date   2023/07/25
// Updated by Argo Zhang <argo@live.ca> at 2025/11/21

using Xunit;

namespace IP2Region.Net.Test;

public class XdbTest
{
    [Fact]
    public async Task VersionIPV4_Ok()
    {
        var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v4.xdb");

        var version = await XDB.Util.GetVersionAsync(db);
        Assert.Equal(3, version.Ver);
        Assert.Equal(1, version.CachePolice);
        //Assert.Equal("2025-09-06 02:24:16", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"));
        //Assert.Equal((uint)955933, version.StartIndex);
        //Assert.Equal((uint)11042415, version.EndIndex);
        Assert.Equal(4, version.IPVer);
        Assert.Equal(4, version.BytesCount);
    }

    [Fact]
    public async Task VersionIPV6_Ok()
    {
        var db = Path.Combine(AppContext.BaseDirectory, "TestData", $"ip2region_v6.xdb");
        var version = await XDB.Util.GetVersionAsync(db);
        Assert.Equal(3, version.Ver);
        Assert.Equal(1, version.CachePolice);
        //Assert.Equal("2025-10-17 04:41:04", version.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"));
        //Assert.Equal((uint)3094259, version.StartIndex);
        //Assert.Equal((uint)36258303, version.EndIndex);
        Assert.Equal(6, version.IPVer);
        Assert.Equal(4, version.BytesCount);
    }

    [Fact]
    public async Task GetVersionAsync_Error()
    {
        await Assert.ThrowsAsync<ArgumentNullException>(async () => await XDB.Util.GetVersionAsync(null!));
        await Assert.ThrowsAsync<FileNotFoundException>(async () => await XDB.Util.GetVersionAsync(Path.Combine(AppContext.BaseDirectory, "test.xdb")));
    }
}


================================================
FILE: binding/csharp/IP2Region.Net.slnx
================================================
<Solution>
  <Folder Name="/Solution Items/">
    <File Path=".editorconfig" />
    <File Path="README.md" />
  </Folder>
  <Project Path="IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj" />
  <Project Path="IP2Region.Net.Test/IP2Region.Net.Test.csproj" />
  <Project Path="IP2Region.Net/IP2Region.Net.csproj" />
</Solution>


================================================
FILE: binding/csharp/README.md
================================================
# IP2Region.Net

.NET client library for IP2Region

## Installation

Install the package with [NuGet](https://www.nuget.org/packages/IP2Region.Net)

```bash
Install-Package IP2Region.Net
```

## Usage

```csharp
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;

ISearcher searcher = new Searcher(CachePolicy , "your xdb file path");
```
### Cache Policy Description
| Cache Policy            | Description                                                                                                | Thread Safe |
|-------------------------|------------------------------------------------------------------------------------------------------------|-------------|
| CachePolicy.Content     | Cache the entire `xdb` data.                                                                               | Yes         |
| CachePolicy.VectorIndex | Cache `vecotorIndex` to speed up queries and reduce system io pressure by reducing one fixed IO operation. | Yes         |
| CachePolicy.File        | Completely file-based queries                                                                              | Yes         |

### XDB File Description
Generate using [maker](https://github.com/lionsoul2014/ip2region/tree/master/maker/csharp), or [download](https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb) pre-generated xdb files

## ASP.NET Core Usage

```csharp
services.AddIP2RegionService("your xdb file path", cachePolicy: CachePolicy.Content);
```

NET6/7
```csharp
provider.GetRequiredService<ISearcher>()
```

NET8+ support keyed service
```csharp
provider.GetRequiredKeyedService<ISearcher>("IP2Region.Net");
```

## TargetFrameworks
netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0

## Performance
// * Summary *

BenchmarkDotNet v0.15.6, Windows 11 (10.0.26200.7171)
13th Gen Intel Core i7-13700 2.10GHz, 1 CPU, 24 logical and 16 physical cores
.NET SDK 10.0.100
  [Host]     : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3
  DefaultJob : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3


| Method      | Mean         | Error      | StdDev     | Gen0   | Allocated |
|------------ |-------------:|-----------:|-----------:|-------:|----------:|
| ContentIPv4 |     53.70 ns |   0.296 ns |   0.277 ns | 0.0086 |     136 B |
| VectorIPv4  |  4,446.04 ns |  18.673 ns |  15.593 ns | 0.0076 |     232 B |
| FileIPv4    |  6,712.40 ns |  15.718 ns |  13.934 ns | 0.0153 |     264 B |
| ContentIPv6 |    145.53 ns |   0.331 ns |   0.277 ns | 0.0126 |     200 B |
| VectorIPv6  |  7,058.39 ns | 125.505 ns | 117.398 ns | 0.0381 |     712 B |
| FileIPv6    | 10,657.97 ns |  53.907 ns |  50.425 ns | 0.0458 |     744 B |

// * Hints *
Outliers
  Benchmarks.VectorIPv4: Default  -> 2 outliers were removed (4.55 us, 4.58 us)
  Benchmarks.FileIPv4: Default    -> 1 outlier  was  removed (6.79 us)
  Benchmarks.ContentIPv6: Default -> 2 outliers were removed (148.08 ns, 152.27 ns)

// * Legends *
  Mean      : Arithmetic mean of all measurements
  Error     : Half of 99.9% confidence interval
  StdDev    : Standard deviation of all measurements
  Gen0      : GC Generation 0 collects per 1000 operations
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 ns      : 1 Nanosecond (0.000000001 sec)

// * Diagnostic Output - MemoryDiagnoser *


// ***** BenchmarkRunner: End *****
Run time: 00:02:06 (126.09 sec), executed benchmarks: 6

Global total time: 00:02:13 (133.47 sec), executed benchmarks: 6
// * Artifacts cleanup *
Artifacts cleanup is finished

## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

## License
[Apache License 2.0](https://github.com/lionsoul2014/ip2region/blob/master/LICENSE.md)


================================================
FILE: binding/erlang/README.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region erlang query client

### Introduction

This binding implements the xdb query client in Erlang, based on the Erlang OTP Application. The query logic is implemented by the `ip2region_worker` worker process, supporting multiple worker processes for load balancing.

### Application Configuration

The configurable parameters for this application are in `ip2region.app.src`, as follows:

```erlang
  {env,[
    {poolargs, [
        {size, 1},  %% Default number of worker processes
        {max_overflow, 5}  %% Maximum number of worker processes
    ]}
  ]}
```

### Compile

```
$ rebar3 compile
```

### Run

Place the xdb file in the `priv` directory, then start the Erlang node:

```
$ rebar3 shell
```

Call the `xdb:search/1` interface in the Erlang shell to query IP address information. This interface supports IP addresses represented as list strings, binary strings, tuples, and integers, as follows:

```
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|广东省|广州市|电信
```

### Usage

* Add the dependency in `rebar.config`

```
{deps, [
  ip2region
]}.
```

* Start the ip2region Application

```
......

application:ensure_started(ip2region),

......
```

* Call the `xdb:search/1` interface to query IP information

```
......

ip2region:search("1.0.8.0"),

......
```

### Unit Test

```
$ 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
```

### Benchmark

```
$ 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
```


================================================
FILE: binding/erlang/README_zh.md
================================================
:globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)

# ip2region erlang 查询客户端

### 简介
该bingding以erlang语言实现xdb查询客户端,基于Erlang OTP Application,查询逻辑由ip2region_worker工作进程实现,支持配多个工作进程来进行负载均衡。

### 应用配置
该应用可配置的参数在ip2region.app.src中,如下:
``` erlang
  {env,[
    {poolargs, [
        {size, 1},  %% 工作进程默认数量
        {max_overflow, 5}  %% 工作进程最大数量
    ]}
  ]}
```

### 编译

```
$ 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"),

......
```

#
Download .txt
gitextract_eppf0yhb/

├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── README_zh.md
├── binding/
│   ├── c/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── main.c
│   │   ├── test_util.c
│   │   ├── xdb_api.h
│   │   ├── xdb_searcher.c
│   │   └── xdb_util.c
│   ├── cpp/
│   │   ├── .gitignore
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── src/
│   │   │   ├── base.cc
│   │   │   ├── base.h
│   │   │   ├── bench.cc
│   │   │   ├── bench.h
│   │   │   ├── edit.cc
│   │   │   ├── edit.h
│   │   │   ├── header.cc
│   │   │   ├── header.h
│   │   │   ├── ip.cc
│   │   │   ├── ip.h
│   │   │   ├── make.cc
│   │   │   ├── make.h
│   │   │   ├── search.cc
│   │   │   └── search.h
│   │   └── test/
│   │       ├── bench.cc
│   │       ├── edit_v4.cc
│   │       ├── edit_v6.cc
│   │       ├── header.cc
│   │       ├── make.cc
│   │       └── search.cc
│   ├── csharp/
│   │   ├── .editorconfig
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── IP2Region.Net/
│   │   │   ├── Abstractions/
│   │   │   │   └── ISearcher.cs
│   │   │   ├── Extensions/
│   │   │   │   └── ServiceCollectionExtensions.cs
│   │   │   ├── IP2Region.Net.csproj
│   │   │   ├── Internal/
│   │   │   │   ├── CacheStrategyFactory.cs
│   │   │   │   ├── ContentCacheStrategy.cs
│   │   │   │   ├── FileCacheStrategy.cs
│   │   │   │   ├── ICacheStrategy.cs
│   │   │   │   └── VectorIndexCacheStrategy.cs
│   │   │   └── XDB/
│   │   │       ├── CachePolicy.cs
│   │   │       ├── Searcher.cs
│   │   │       ├── Util.cs
│   │   │       └── XdbVersion.cs
│   │   ├── IP2Region.Net.BenchMark/
│   │   │   ├── Benmarks.cs
│   │   │   ├── IP2Region.Net.BenchMark.csproj
│   │   │   └── Program.cs
│   │   ├── IP2Region.Net.Test/
│   │   │   ├── IP2Region.Net.Test.csproj
│   │   │   ├── SearcherTest.cs
│   │   │   ├── UtilTest.cs
│   │   │   └── XdbTest.cs
│   │   ├── IP2Region.Net.slnx
│   │   └── README.md
│   ├── erlang/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── benchmarks/
│   │   │   └── xdb-benchmark.sh
│   │   ├── include/
│   │   │   └── ip2region.hrl
│   │   ├── priv/
│   │   │   └── dummy
│   │   ├── rebar.config
│   │   ├── src/
│   │   │   ├── ip2region.app.src
│   │   │   ├── ip2region_app.erl
│   │   │   ├── ip2region_sup.erl
│   │   │   ├── ip2region_util.erl
│   │   │   ├── ip2region_worker.erl
│   │   │   ├── xdb.erl
│   │   │   └── xdb_benchmark.erl
│   │   └── test/
│   │       └── xdb_test.erl
│   ├── golang/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   ├── service/
│   │   │   ├── config.go
│   │   │   ├── config_test.go
│   │   │   ├── ip2region.go
│   │   │   ├── ip2region_test.go
│   │   │   ├── searcher_pool.go
│   │   │   └── searcher_pool_test.go
│   │   └── xdb/
│   │       ├── header.go
│   │       ├── searcher.go
│   │       ├── util.go
│   │       ├── util_test.go
│   │       └── version.go
│   ├── java/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── pom.xml
│   │   └── src/
│   │       ├── main/
│   │       │   └── java/
│   │       │       └── org/
│   │       │           └── lionsoul/
│   │       │               └── ip2region/
│   │       │                   ├── SearcherTest.java
│   │       │                   ├── service/
│   │       │                   │   ├── Config.java
│   │       │                   │   ├── ConfigBuilder.java
│   │       │                   │   ├── InvalidConfigException.java
│   │       │                   │   ├── Ip2Region.java
│   │       │                   │   └── SearcherPool.java
│   │       │                   └── xdb/
│   │       │                       ├── Header.java
│   │       │                       ├── IPv4.java
│   │       │                       ├── IPv6.java
│   │       │                       ├── InetAddressException.java
│   │       │                       ├── LittleEndian.java
│   │       │                       ├── Log.java
│   │       │                       ├── LongByteArray.java
│   │       │                       ├── Searcher.java
│   │       │                       ├── Util.java
│   │       │                       ├── Version.java
│   │       │                       └── XdbException.java
│   │       └── test/
│   │           └── java/
│   │               └── org/
│   │                   └── lionsoul/
│   │                       └── ip2region/
│   │                           ├── service/
│   │                           │   ├── ConfigTest.java
│   │                           │   ├── Ip2RegionTest.java
│   │                           │   └── SearcherPoolTest.java
│   │                           └── xdb/
│   │                               ├── BufferTest.java
│   │                               ├── IPv4Test.java
│   │                               ├── LittleEndianTest.java
│   │                               ├── UtilTest.java
│   │                               └── VersionTest.java
│   ├── javascript/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── index.d.ts
│   │   ├── index.js
│   │   ├── package.json
│   │   ├── searcher.js
│   │   ├── tests/
│   │   │   ├── bench.app.js
│   │   │   ├── search.app.js
│   │   │   ├── searcher.test.js
│   │   │   └── util.test.js
│   │   ├── tsconfig.json
│   │   └── util.js
│   ├── lua/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.lua
│   │   ├── search_test.lua
│   │   ├── util_test.lua
│   │   └── xdb_searcher.lua
│   ├── lua_c/
│   │   ├── Makefile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.lua
│   │   ├── search_test.lua
│   │   ├── util_test.lua
│   │   └── xdb_searcher.c
│   ├── nginx/
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── config
│   │   ├── src/
│   │   │   ├── ngx_http_ip2region_module.c
│   │   │   └── ngx_http_ip2region_module.h
│   │   └── t/
│   │       └── http_ip2region.t
│   ├── nodejs/
│   │   └── README.md
│   ├── php/
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── batch_test.php
│   │   ├── bench_test.php
│   │   ├── search_test.php
│   │   └── xdb/
│   │       ├── Searcher.class.php
│   │       └── util_test.php
│   ├── python/
│   │   ├── .gitignore
│   │   ├── LICENSE
│   │   ├── MANIFEST.in
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── bench_test.py
│   │   ├── ip2region/
│   │   │   ├── __init__.py
│   │   │   ├── searcher.py
│   │   │   └── util.py
│   │   ├── search_test.py
│   │   ├── setup.py
│   │   └── util_test.py
│   ├── rust/
│   │   ├── Cargo.toml
│   │   ├── README.md
│   │   ├── README_zh.md
│   │   ├── example/
│   │   │   ├── Cargo.toml
│   │   │   └── src/
│   │   │       ├── cmd.rs
│   │   │       └── main.rs
│   │   └── ip2region/
│   │       ├── Cargo.toml
│   │       ├── benches/
│   │       │   └── search.rs
│   │       └── src/
│   │           ├── error.rs
│   │           ├── ip_value.rs
│   │           ├── lib.rs
│   │           └── searcher.rs
│   └── typescript/
│       └── README.md
├── data/
│   ├── ip2region_v4.xdb
│   ├── ip2region_v6.xdb
│   ├── ipv4_source.txt
│   ├── ipv6_source.txt
│   └── sample/
│       ├── github-issue-196.fix
│       ├── github-issue-200.fix
│       ├── github-issue-243.fix
│       ├── github-issue-287.bug
│       ├── ip.test.txt
│       ├── segments.tests
│       └── segments.tests.mixed
└── maker/
    ├── c/
    │   └── ReadMe.md
    ├── cpp/
    │   └── README.md
    ├── csharp/
    │   ├── .gitignore
    │   ├── IP2RegionMaker/
    │   │   ├── IP2RegionMaker.csproj
    │   │   ├── Program.cs
    │   │   ├── Properties/
    │   │   │   └── PublishProfiles/
    │   │   │       └── FolderProfile.pubxml
    │   │   └── XDB/
    │   │       ├── IndexPolicy.cs
    │   │       ├── Maker.cs
    │   │       ├── Segment.cs
    │   │       └── Util.cs
    │   ├── IP2RegionMaker.Test/
    │   │   ├── IP2RegionMaker.Test.csproj
    │   │   ├── Usings.cs
    │   │   └── UtilTest.cs
    │   ├── README.md
    │   └── README_zh.md
    ├── golang/
    │   ├── Dockerfile
    │   ├── Makefile
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── cmd/
    │   │   ├── bench.go
    │   │   ├── edit.go
    │   │   ├── generate.go
    │   │   ├── process.go
    │   │   ├── search.go
    │   │   └── util.go
    │   ├── go.mod
    │   ├── go.sum
    │   ├── main.go
    │   ├── make.bat
    │   └── xdb/
    │       ├── editor.go
    │       ├── index.go
    │       ├── maker.go
    │       ├── processor.go
    │       ├── searcher.go
    │       ├── segment.go
    │       ├── util.go
    │       ├── util_test.go
    │       └── version.go
    ├── java/
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── pom.xml
    │   └── src/
    │       ├── main/
    │       │   └── java/
    │       │       └── org/
    │       │           └── lionsoul/
    │       │               └── ip2region/
    │       │                   ├── MakerApp.java
    │       │                   └── xdb/
    │       │                       ├── IPv4.java
    │       │                       ├── IPv6.java
    │       │                       ├── IndexPolicy.java
    │       │                       ├── InvalidInetAddressException.java
    │       │                       ├── LittleEndian.java
    │       │                       ├── Log.java
    │       │                       ├── Maker.java
    │       │                       ├── Segment.java
    │       │                       ├── Util.java
    │       │                       └── Version.java
    │       └── test/
    │           └── java/
    │               └── org/
    │                   └── lionsoul/
    │                       └── ip2region/
    │                           └── xdb/
    │                               ├── IndexPolicyTest.java
    │                               ├── LittleEndianTest.java
    │                               ├── MakerTest.java
    │                               ├── SegmentTest.java
    │                               ├── UtilTest.java
    │                               └── VersionTest.java
    ├── python/
    │   ├── README.md
    │   ├── README_zh.md
    │   ├── main.py
    │   └── xdb/
    │       ├── __init__.py
    │       ├── index.py
    │       ├── maker.py
    │       ├── segment.py
    │       └── util.py
    └── rust/
        ├── README.md
        ├── README_zh.md
        └── maker/
            ├── Cargo.toml
            └── src/
                ├── command.rs
                ├── error.rs
                ├── header.rs
                ├── lib.rs
                ├── main.rs
                ├── maker.rs
                └── segment.rs
Download .txt
SYMBOL INDEX (980 symbols across 149 files)

FILE: binding/c/main.c
  type searcher_test_entry (line 12) | struct searcher_test_entry {
  type searcher_test_t (line 21) | typedef struct searcher_test_entry searcher_test_t;
  function init_searcher_test (line 23) | int init_searcher_test(searcher_test_t *test, char *db_path, char *cache...
  function destroy_searcher_test (line 118) | void destroy_searcher_test(searcher_test_t *test) {
  function print_help (line 149) | void print_help(char *argv[]) {
  function test_search (line 157) | void test_search(int argc, char *argv[]) {
  function test_bench (line 267) | void test_bench(int argc, char *argv[]) {
  function main (line 412) | int main(int argc, char *argv[]) {

FILE: binding/c/test_util.c
  type test_func_entry (line 13) | struct test_func_entry {
  type test_func_t (line 17) | typedef struct test_func_entry test_func_t;
  function test_load_header (line 19) | void test_load_header() {
  function test_load_vector_index (line 43) | void test_load_vector_index() {
  function test_load_content (line 54) | void test_load_content() {
  function test_parse_ip (line 65) | void test_parse_ip() {
  type ip_pair (line 103) | struct ip_pair {
  function test_ip_compare (line 107) | void test_ip_compare() {
  function main (line 181) | int main(int argc, char *argv[]) {

FILE: binding/c/xdb_api.h
  type xdb_header (line 66) | struct xdb_header {
  type xdb_header_t (line 81) | typedef struct xdb_header xdb_header_t;
  type xdb_vector_index (line 91) | struct xdb_vector_index {
  type xdb_vector_index_t (line 95) | typedef struct xdb_vector_index xdb_vector_index_t;
  type xdb_content (line 105) | struct xdb_content {
  type xdb_content_t (line 109) | typedef struct xdb_content xdb_content_t;
  type string_ip_t (line 135) | typedef char string_ip_t;
  type bytes_ip_t (line 136) | typedef unsigned char bytes_ip_t;
  type xdb_ip_version_entry (line 142) | struct xdb_ip_version_entry {
  type xdb_version_t (line 151) | typedef struct xdb_ip_version_entry xdb_version_t;
  type xdb_region_buffer_entry (line 221) | struct xdb_region_buffer_entry {
  type xdb_region_buffer_t (line 226) | typedef struct xdb_region_buffer_entry xdb_region_buffer_t;
  type xdb_searcher_entry (line 243) | struct xdb_searcher_entry {
  type xdb_searcher_t (line 263) | typedef struct xdb_searcher_entry xdb_searcher_t;

FILE: binding/c/xdb_searcher.c
  function xdb_region_buffer_init (line 12) | XDB_PUBLIC(int) xdb_region_buffer_init(xdb_region_buffer_t *region, char...
  function xdb_region_buffer_alloc (line 28) | XDB_PUBLIC(int) xdb_region_buffer_alloc(xdb_region_buffer_t *region, int...
  function xdb_region_buffer_empty (line 63) | XDB_PUBLIC(int) xdb_region_buffer_empty(xdb_region_buffer_t *region) {
  function xdb_region_buffer_free (line 81) | XDB_PUBLIC(void) xdb_region_buffer_free(xdb_region_buffer_t *region) {
  function xdb_new_base (line 101) | XDB_PRIVATE(int) xdb_new_base(xdb_version_t *version, xdb_searcher_t *xd...
  function xdb_new_with_file_only (line 127) | XDB_PUBLIC(int) xdb_new_with_file_only(xdb_version_t *version, xdb_searc...
  function xdb_new_with_vector_index (line 131) | XDB_PUBLIC(int) xdb_new_with_vector_index(xdb_version_t *version, xdb_se...
  function xdb_new_with_buffer (line 135) | XDB_PUBLIC(int) xdb_new_with_buffer(xdb_version_t *version, xdb_searcher...
  function xdb_close (line 139) | XDB_PUBLIC(void) xdb_close(void *ptr) {
  function xdb_search_by_string (line 149) | XDB_PUBLIC(int) xdb_search_by_string(xdb_searcher_t *xdb, const string_i...
  function xdb_search (line 159) | XDB_PUBLIC(int) xdb_search(xdb_searcher_t *xdb, const bytes_ip_t *ip_byt...
  function read (line 254) | XDB_PRIVATE(int) read(xdb_searcher_t *xdb, long offset, char *buffer, si...
  function xdb_get_version (line 274) | XDB_PUBLIC(xdb_version_t *) xdb_get_version(xdb_searcher_t *xdb) {
  function xdb_get_io_count (line 278) | XDB_PUBLIC(int) xdb_get_io_count(xdb_searcher_t *xdb) {

FILE: binding/c/xdb_util.c
  function xdb_init_winsock (line 24) | XDB_PUBLIC(int) xdb_init_winsock() {
  function xdb_clean_winsock (line 37) | XDB_PUBLIC(void) xdb_clean_winsock() {
  function gettimeofday (line 44) | XDB_PRIVATE(int) gettimeofday(struct timeval* tp, void* tzp) {
  function xdb_init_winsock (line 62) | XDB_PUBLIC(int) xdb_init_winsock() {return 0;}
  function xdb_clean_winsock (line 63) | XDB_PUBLIC(void) xdb_clean_winsock() {}
  function xdb_load_header (line 68) | XDB_PUBLIC(xdb_header_t *) xdb_load_header(FILE *handle) {
  function xdb_load_header_from_file (line 103) | XDB_PUBLIC(xdb_header_t *) xdb_load_header_from_file(const char *db_path) {
  function xdb_free_header (line 115) | XDB_PUBLIC(void) xdb_free_header(void *ptr) {
  function xdb_load_vector_index (line 125) | XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index(FILE *handle) {
  function xdb_load_vector_index_from_file (line 149) | XDB_PUBLIC(xdb_vector_index_t *) xdb_load_vector_index_from_file(const c...
  function xdb_free_vector_index (line 161) | XDB_PUBLIC(void) xdb_free_vector_index(void *ptr) {
  function xdb_load_content (line 171) | XDB_PUBLIC(xdb_content_t *) xdb_load_content(FILE *handle) {
  function xdb_load_content_from_file (line 208) | XDB_PUBLIC(xdb_content_t *) xdb_load_content_from_file(const char *db_pa...
  function xdb_free_content (line 220) | XDB_PUBLIC(void) xdb_free_content(void *ptr) {
  function xdb_verify_from_header (line 230) | XDB_PUBLIC(int) xdb_verify_from_header(FILE *handle, xdb_header_t *heade...
  function xdb_verify (line 257) | XDB_PUBLIC(int) xdb_verify(FILE *handle) {
  function xdb_verify_from_file (line 274) | XDB_PUBLIC(int) xdb_verify_from_file(const char *db_path) {
  function _ipv4_sub_compare (line 294) | XDB_PRIVATE(int) _ipv4_sub_compare(const bytes_ip_t *ip_bytes, int bytes...
  function xdb_version_v4 (line 319) | XDB_PUBLIC(xdb_version_t *) xdb_version_v4() {
  function xdb_version_v6 (line 323) | XDB_PUBLIC(xdb_version_t *) xdb_version_v6() {
  function xdb_version_is_v4 (line 327) | XDB_PUBLIC(int) xdb_version_is_v4(const xdb_version_t *version) {
  function xdb_version_is_v6 (line 331) | XDB_PUBLIC(int) xdb_version_is_v6(const xdb_version_t *version) {
  function xdb_version_from_name (line 335) | XDB_PUBLIC(xdb_version_t *) xdb_version_from_name(char *name) {
  function xdb_version_from_header (line 350) | XDB_PUBLIC(xdb_version_t *) xdb_version_from_header(xdb_header_t *header) {
  function xdb_now (line 372) | XDB_PUBLIC(long) xdb_now() {
  function xdb_le_get_uint32 (line 378) | XDB_PUBLIC(unsigned int) xdb_le_get_uint32(const char *buffer, int offse...
  function xdb_le_get_uint16 (line 387) | XDB_PUBLIC(int) xdb_le_get_uint16(const char *buffer, int offset) {
  function xdb_parse_ip (line 394) | XDB_PUBLIC(xdb_version_t *) xdb_parse_ip(const string_ip_t *ip_string, b...
  function xdb_parse_v4_ip (line 407) | XDB_PUBLIC(xdb_version_t *) xdb_parse_v4_ip(const string_ip_t *ip_string...
  function xdb_parse_v6_ip (line 427) | XDB_PUBLIC(xdb_version_t *) xdb_parse_v6_ip(const string_ip_t *ip_string...
  function xdb_ip_to_string (line 443) | XDB_PUBLIC(int) xdb_ip_to_string(const bytes_ip_t *ip_bytes, int bytes, ...
  function xdb_v4_ip_to_string (line 453) | XDB_PUBLIC(int) xdb_v4_ip_to_string(const bytes_ip_t *ip_bytes, char *ip...
  function xdb_v6_ip_to_string (line 470) | XDB_PUBLIC(int) xdb_v6_ip_to_string(const bytes_ip_t *ip_bytes, char *ip...
  function xdb_ip_sub_compare (line 486) | XDB_PUBLIC(int) xdb_ip_sub_compare(const bytes_ip_t *ip1, int bytes, con...
  function xdb_fseek (line 500) | XDB_PUBLIC(int) xdb_fseek(FILE *handle, long long offset, int whence) {
  function xdb_ftell (line 513) | XDB_PUBLIC(long long) xdb_ftell(FILE *handle) {

FILE: binding/cpp/src/base.cc
  type xdb (line 4) | namespace xdb {
    function init_xdb (line 10) | void init_xdb(int version) {
    function log_exit (line 16) | void log_exit(const string &msg) {
    function read_bin (line 21) | void read_bin(int index, char *buf, size_t len, FILE *db) {
    function to_uint (line 27) | unsigned to_uint(const char *buf) {
    function to_ushort (line 32) | unsigned to_ushort(const char *buf) {
    function to_int (line 36) | unsigned to_int(const char *buf, int n) {
    function write_uint (line 40) | void write_uint(unsigned data, char buf[]) {
    function write_uint (line 47) | void write_uint(unsigned data, FILE *dst) {
    function write_ushort (line 53) | void write_ushort(unsigned data, char buf[]) {
    function write_ushort (line 58) | void write_ushort(unsigned data, FILE *dst) {
    function write_string (line 64) | void write_string(const char *buf, unsigned len, FILE *dst) {
    function get_time (line 68) | unsigned long long get_time() {

FILE: binding/cpp/src/base.h
  function namespace (line 18) | namespace xdb {

FILE: binding/cpp/src/bench.cc
  type xdb (line 4) | namespace xdb {

FILE: binding/cpp/src/bench.h
  function namespace (line 6) | namespace xdb {

FILE: binding/cpp/src/edit.cc
  type xdb (line 4) | namespace xdb {
    function handle_ip_txt (line 6) | void handle_ip_txt(const string& name, std::list<node_t>& regions) {

FILE: binding/cpp/src/edit.h
  function namespace (line 6) | namespace xdb {

FILE: binding/cpp/src/header.cc
  type xdb (line 4) | namespace xdb {

FILE: binding/cpp/src/header.h
  function namespace (line 6) | namespace xdb {

FILE: binding/cpp/src/ip.cc
  type xdb (line 4) | namespace xdb {
    function ip_t (line 35) | ip_t& ip_t::operator=(const ip_t& rhs) {
    function string (line 74) | string ip_t::to_string() const {
    function string (line 81) | string ip_t::to_bit() const {
    function ip_t (line 91) | ip_t operator+(const ip_t& lhs, int v) {
    function ip_t (line 103) | ip_t operator-(const ip_t& lhs, int v) {
    function string (line 150) | string node_t::to_string() const {
    function string (line 154) | string node_t::to_bit() const {

FILE: binding/cpp/src/ip.h
  function namespace (line 6) | namespace xdb {

FILE: binding/cpp/src/make.cc
  type xdb (line 4) | namespace xdb {

FILE: binding/cpp/src/make.h
  function namespace (line 6) | namespace xdb {

FILE: binding/cpp/src/search.cc
  type xdb (line 4) | namespace xdb {
    function string (line 67) | string search_t::get_region(int index, int len) {
    function string (line 92) | string search_t::search(const ip_t &ip) {
    function string (line 124) | string search_t::search(const string &str) {

FILE: binding/cpp/src/search.h
  function namespace (line 7) | namespace xdb {

FILE: binding/cpp/test/bench.cc
  function test_ipv4 (line 6) | void test_ipv4(int policy) {
  function test_ipv6 (line 12) | void test_ipv6(int policy) {
  function main (line 18) | int main() {

FILE: binding/cpp/test/edit_v4.cc
  function main (line 4) | int main() {

FILE: binding/cpp/test/edit_v6.cc
  function main (line 4) | int main() {

FILE: binding/cpp/test/header.cc
  function test (line 4) | void test(const std::string& prompt, const std::string& file_name) {
  function main (line 26) | int main() {

FILE: binding/cpp/test/make.cc
  function test (line 4) | void test(const std::string& prompt,
  function main (line 14) | int main() {

FILE: binding/cpp/test/search.cc
  function test (line 6) | void test(xdb::search_t& s, const std::string& ip, const std::string& re...
  function test_ipv4 (line 11) | void test_ipv4(int policy) {
  function test_ipv6 (line 21) | void test_ipv6(int policy) {
  function main (line 33) | int main() {

FILE: binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs
  class Benchmarks (line 13) | [MemoryDiagnoser]
    method Benchmarks (line 28) | public Benchmarks()
    method ContentIPv4 (line 39) | [Benchmark]
    method VectorIPv4 (line 43) | [Benchmark]
    method FileIPv4 (line 47) | [Benchmark]
    method ContentIPv6 (line 51) | [Benchmark]
    method VectorIPv6 (line 55) | [Benchmark]
    method FileIPv6 (line 59) | [Benchmark]

FILE: binding/csharp/IP2Region.Net.Test/SearcherTest.cs
  class SearcherTest (line 16) | public class SearcherTest
    method TestSearchCacheContent (line 21) | [Theory]
    method TestSearchCacheVector (line 37) | [Theory]
    method TestSearchCacheFile (line 53) | [Theory]
    method IoCount_File_Ok (line 72) | [Fact]
    method IoCount_Vector_Ok (line 85) | [Fact]
    method IoCount_Content_Ok (line 98) | [Fact]
    method Search_Ip_Ok (line 111) | [Theory]
    method AddIP2RegionService_Ok (line 121) | [Theory]
    method TestBenchSearch (line 138) | [Theory]

FILE: binding/csharp/IP2Region.Net.Test/UtilTest.cs
  class UtilTest (line 12) | public class UtilTest
    method IpAddressToUInt32_Ok (line 14) | [Fact]
    method GetMidIp_Ok (line 21) | [Fact]

FILE: binding/csharp/IP2Region.Net.Test/XdbTest.cs
  class XdbTest (line 12) | public class XdbTest
    method VersionIPV4_Ok (line 14) | [Fact]
    method VersionIPV6_Ok (line 29) | [Fact]
    method GetVersionAsync_Error (line 43) | [Fact]

FILE: binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs
  type ISearcher (line 15) | public interface ISearcher : IDisposable
    method Search (line 22) | string? Search(string ipStr);
    method Search (line 27) | string? Search(IPAddress ipAddress);
    method Search (line 33) | [Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search...

FILE: binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs
  class IP2RegionExtensions (line 17) | public static class IP2RegionExtensions
    method AddIP2RegionService (line 25) | public static IServiceCollection AddIP2RegionService(this IServiceColl...

FILE: binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs
  class CacheStrategyFactory (line 12) | static class CacheStrategyFactory
    method CreateCacheStrategy (line 14) | public static ICacheStrategy CreateCacheStrategy(CachePolicy cachePoli...

FILE: binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs
  class ContentCacheStrategy (line 10) | class ContentCacheStrategy(string xdbPath) : ICacheStrategy
    method ResetIoCount (line 20) | public void ResetIoCount()
    method GetVectorIndex (line 25) | public ReadOnlyMemory<byte> GetVectorIndex(int offset)
    method GetData (line 28) | public ReadOnlyMemory<byte> GetData(long offset, int length) => _cache...
    method Dispose (line 30) | public void Dispose()

FILE: binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs
  class FileCacheStrategy (line 12) | class FileCacheStrategy(string xdbPath) : ICacheStrategy
    method ResetIoCount (line 23) | public void ResetIoCount()
    method GetVectorIndex (line 28) | public virtual ReadOnlyMemory<byte> GetVectorIndex(int offset) => GetD...
    method GetData (line 30) | public virtual ReadOnlyMemory<byte> GetData(long offset, int length)
    method Dispose (line 68) | protected virtual void Dispose(bool disposing)
    method Dispose (line 80) | public void Dispose()

FILE: binding/csharp/IP2Region.Net/Internal/ICacheStrategy.cs
  type ICacheStrategy (line 10) | internal interface ICacheStrategy : IDisposable
    method ResetIoCount (line 14) | void ResetIoCount();
    method GetVectorIndex (line 16) | ReadOnlyMemory<byte> GetVectorIndex(int offset);
    method GetData (line 18) | ReadOnlyMemory<byte> GetData(long offset, int length);

FILE: binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs
  class VectorIndexCacheStrategy (line 10) | class VectorIndexCacheStrategy : FileCacheStrategy
    method VectorIndexCacheStrategy (line 17) | public VectorIndexCacheStrategy(string xdbPath) : base(xdbPath)
    method GetVectorIndex (line 22) | public override ReadOnlyMemory<byte> GetVectorIndex(int offset) => _ve...

FILE: binding/csharp/IP2Region.Net/XDB/CachePolicy.cs
  type CachePolicy (line 13) | public enum CachePolicy

FILE: binding/csharp/IP2Region.Net/XDB/Searcher.cs
  class Searcher (line 23) | public class Searcher(CachePolicy cachePolicy, string xdbPath) : ISearcher
    method Search (line 35) | public string? Search(string ipStr)
    method Search (line 44) | public string? Search(IPAddress ipAddress) => SearchCore(ipAddress.Get...
    method Search (line 49) | [Obsolete("已弃用,请改用其他方法;Deprecated; please use Search(string) or Search...
    method SearchCore (line 58) | string? SearchCore(byte[] ipBytes)
    method ByteCompare (line 122) | static int ByteCompare(byte[] ip1, ReadOnlySpan<byte> ip2) => ip1.Leng...
    method IPv4Compare (line 124) | static int IPv4Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
    method IPv6Compare (line 142) | static int IPv6Compare(byte[] ip1, ReadOnlySpan<byte> ip2)
    method Dispose (line 162) | public void Dispose()

FILE: binding/csharp/IP2Region.Net/XDB/Util.cs
  class Util (line 18) | public static class Util
    method IpAddressToUInt32 (line 20) | public static uint IpAddressToUInt32(string ipAddress)
    method IpAddressToUInt32 (line 26) | public static uint IpAddressToUInt32(IPAddress ipAddress)
    method GetMidIp (line 33) | public static uint GetMidIp(uint x, uint y)
    method GetVersionAsync (line 36) | public static async Task<XdbVersion> GetVersionAsync(string dbPath, Ca...
    method GetVersionAsync (line 52) | internal static async Task<XdbVersion> GetVersionAsync(FileStream read...
    method Parse (line 73) | private static XdbVersion Parse(ReadOnlySpan<byte> buffer)

FILE: binding/csharp/IP2Region.Net/XDB/XdbVersion.cs
  type XdbVersion (line 13) | public struct XdbVersion

FILE: binding/golang/main.go
  function getXdbPath (line 25) | func getXdbPath(fileName string) (string, error) {
  function createService (line 41) | func createService(v4XdbPath string, v4CachePolicy string, v6XdbPath str...
  function createSearcher (line 75) | func createSearcher(dbPath string, cachePolicy string) (*xdb.Searcher, e...
  function printHelp (line 124) | func printHelp() {
  function testSearch (line 132) | func testSearch() {
  function testBench (line 251) | func testBench() {
  function main (line 367) | func main() {

FILE: binding/golang/service/config.go
  constant NoCache (line 22) | NoCache     = 0
  constant VIndexCache (line 23) | VIndexCache = 1
  constant BufferCache (line 24) | BufferCache = 2
  function CachePolicyFromName (line 27) | func CachePolicyFromName(name string) (int, error) {
  type Config (line 40) | type Config struct
    method String (line 128) | func (c *Config) String() string {
    method CachePolicy (line 145) | func (c *Config) CachePolicy() int {
    method IPVersion (line 149) | func (c *Config) IPVersion() *xdb.Version {
    method Header (line 153) | func (c *Config) Header() *xdb.Header {
    method VIndex (line 157) | func (c *Config) VIndex() []byte {
    method CBuffer (line 161) | func (c *Config) CBuffer() []byte {
    method Searchers (line 165) | func (c *Config) Searchers() int {
  function NewV4Config (line 55) | func NewV4Config(cachePolicy int, xdbPath string, searchers int) (*Confi...
  function NewV6Config (line 59) | func NewV6Config(cachePolicy int, xdbPath string, searchers int) (*Confi...
  function newConfig (line 63) | func newConfig(cachePolicy int, ipVersion *xdb.Version, xdbPath string, ...

FILE: binding/golang/service/config_test.go
  function TestV4Config (line 12) | func TestV4Config(t *testing.T) {
  function TestV6Config (line 29) | func TestV6Config(t *testing.T) {

FILE: binding/golang/service/ip2region.go
  type Ip2Region (line 22) | type Ip2Region struct
    method SearchByStr (line 121) | func (ip2r *Ip2Region) SearchByStr(ipStr string) (string, error) {
    method Search (line 130) | func (ip2r *Ip2Region) Search(ipBytes []byte) (string, error) {
    method v4Search (line 140) | func (ip2r *Ip2Region) v4Search(ipBytes []byte) (string, error) {
    method v6Search (line 155) | func (ip2r *Ip2Region) v6Search(ipBytes []byte) (string, error) {
    method Close (line 170) | func (ip2r *Ip2Region) Close() {
    method CloseTimeout (line 174) | func (ip2r *Ip2Region) CloseTimeout(d time.Duration) {
  function NewIp2Region (line 38) | func NewIp2Region(v4Config *Config, v6Config *Config) (*Ip2Region, error) {
  function NewIp2RegionWithPath (line 93) | func NewIp2RegionWithPath(v4XdbPath string, v6XdbPath string) (*Ip2Regio...

FILE: binding/golang/service/ip2region_test.go
  function TestConfigCreate (line 17) | func TestConfigCreate(t *testing.T) {
  function TestPathCreate (line 66) | func TestPathCreate(t *testing.T) {
  function TestInMemSearch (line 105) | func TestInMemSearch(t *testing.T) {
  function TestV4Only (line 154) | func TestV4Only(t *testing.T) {
  function TestConcurrentCall (line 198) | func TestConcurrentCall(t *testing.T) {

FILE: binding/golang/service/searcher_pool.go
  type SearcherPool (line 21) | type SearcherPool struct
    method LoanCount (line 62) | func (sp *SearcherPool) LoanCount() int {
    method BorrowSearcher (line 66) | func (sp *SearcherPool) BorrowSearcher() *xdb.Searcher {
    method ReturnSearcher (line 73) | func (sp *SearcherPool) ReturnSearcher(searcher *xdb.Searcher) {
    method Close (line 88) | func (sp *SearcherPool) Close() {
    method CloseTimeout (line 92) | func (sp *SearcherPool) CloseTimeout(d time.Duration) {
  function NewSearcherPool (line 35) | func NewSearcherPool(config *Config) (*SearcherPool, error) {

FILE: binding/golang/service/searcher_pool_test.go
  function TestV4SearcherPool (line 12) | func TestV4SearcherPool(t *testing.T) {
  function TestV6SearcherPool (line 42) | func TestV6SearcherPool(t *testing.T) {

FILE: binding/golang/xdb/header.go
  constant Structure20 (line 20) | Structure20      = 2
  constant Structure30 (line 21) | Structure30      = 3
  constant HeaderInfoLength (line 22) | HeaderInfoLength = 256
  constant VectorIndexRows (line 23) | VectorIndexRows  = 256
  constant VectorIndexCols (line 24) | VectorIndexCols  = 256
  constant VectorIndexSize (line 25) | VectorIndexSize  = 8
  type IndexPolicy (line 30) | type IndexPolicy
    method String (line 37) | func (i IndexPolicy) String() string {
  constant VectorIndexPolicy (line 33) | VectorIndexPolicy IndexPolicy = 1
  constant BTreeIndexPolicy (line 34) | BTreeIndexPolicy  IndexPolicy = 2
  type Header (line 50) | type Header struct
    method String (line 80) | func (h *Header) String() string {
  function NewHeader (line 63) | func NewHeader(input []byte) (*Header, error) {

FILE: binding/golang/xdb/searcher.go
  type Searcher (line 20) | type Searcher struct
    method Close (line 73) | func (s *Searcher) Close() {
    method IPVersion (line 83) | func (s *Searcher) IPVersion() *Version {
    method GetIOCount (line 88) | func (s *Searcher) GetIOCount() int {
    method SearchByStr (line 93) | func (s *Searcher) SearchByStr(str string) (string, error) {
    method Search (line 103) | func (s *Searcher) Search(ip []byte) (string, error) {
    method read (line 185) | func (s *Searcher) read(offset int64, buff []byte) error {
  function NewWithFileOnly (line 36) | func NewWithFileOnly(version *Version, dbFile string) (*Searcher, error) {
  function NewWithVectorIndex (line 40) | func NewWithVectorIndex(version *Version, dbFile string, vIndex []byte) ...
  function NewWithBuffer (line 44) | func NewWithBuffer(version *Version, cBuff []byte) (*Searcher, error) {
  function NewSearcher (line 48) | func NewSearcher(version *Version, dbFile string, vIndex []byte, cBuff [...

FILE: binding/golang/xdb/util.go
  function ParseIP (line 20) | func ParseIP(ip string) ([]byte, error) {
  function IP2String (line 39) | func IP2String(ip []byte) string {
  function IPCompare (line 45) | func IPCompare(ip1, ip2 []byte) int {
  function IPAddOne (line 58) | func IPAddOne(ip []byte) []byte {
  function IPSubOne (line 71) | func IPSubOne(ip []byte) []byte {
  function Verify (line 91) | func Verify(handle *os.File) error {
  function VerifyFromFile (line 124) | func VerifyFromFile(dbFile string) error {
  function LoadHeader (line 135) | func LoadHeader(handle *os.File) (*Header, error) {
  function LoadHeaderFromFile (line 155) | func LoadHeaderFromFile(dbFile string) (*Header, error) {
  function LoadHeaderFromBuff (line 171) | func LoadHeaderFromBuff(cBuff []byte) (*Header, error) {
  function LoadVectorIndex (line 176) | func LoadVectorIndex(handle *os.File) ([]byte, error) {
  function LoadVectorIndexFromFile (line 197) | func LoadVectorIndexFromFile(dbFile string) ([]byte, error) {
  function LoadContent (line 213) | func LoadContent(handle *os.File) ([]byte, error) {
  function LoadContentFromFile (line 242) | func LoadContentFromFile(dbFile string) ([]byte, error) {
  function LoadContentFromFS (line 258) | func LoadContentFromFS(fs embed.FS, filePath string) ([]byte, error) {

FILE: binding/golang/xdb/util_test.go
  function TestParseIP (line 17) | func TestParseIP(t *testing.T) {
  function TestIPCompare (line 30) | func TestIPCompare(t *testing.T) {
  function TestLoadVectorIndex (line 44) | func TestLoadVectorIndex(t *testing.T) {
  function TestLoadContent (line 54) | func TestLoadContent(t *testing.T) {
  function TestLoadHeader (line 64) | func TestLoadHeader(t *testing.T) {

FILE: binding/golang/xdb/version.go
  type Version (line 13) | type Version struct
    method String (line 23) | func (v *Version) String() string {
  constant IPv4VersionNo (line 31) | IPv4VersionNo = 4
  constant IPv6VersionNo (line 32) | IPv6VersionNo = 6
  function VersionFromIP (line 61) | func VersionFromIP(ip string) (*Version, error) {
  function VersionFromName (line 74) | func VersionFromName(name string) (*Version, error) {
  function VersionFromHeader (line 85) | func VersionFromHeader(header *Header) (*Version, error) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java
  class SearcherTest (line 26) | public class SearcherTest {
    method printHelp (line 28) | public static void printHelp(String[] args) {
    method getXdbPath (line 36) | public static final String getXdbPath(String fileName) throws IOExcept...
    method createService (line 51) | public static final Ip2Region createService(
    method createSearcher (line 66) | public static Searcher createSearcher(String dbPath, String cachePolic...
    method searchTest (line 92) | public static void searchTest(String[] args) throws IOException, XdbEx...
    method benchTest (line 187) | public static void benchTest(String[] args) throws IOException, XdbExc...
    method main (line 293) | public static void main(String[] args) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java
  class Config (line 20) | public class Config {
    method custom (line 43) | public static ConfigBuilder custom() {
    method Config (line 47) | protected Config(int cachePolicy, Version ipVersion, File xdbFile,
    method toString (line 67) | @Override public String toString() {
    method cachePolicyFromName (line 89) | public static final int cachePolicyFromName(String name) throws Invali...

FILE: binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java
  class ConfigBuilder (line 23) | public class ConfigBuilder {
    method ConfigBuilder (line 40) | public ConfigBuilder() {}
    method ConfigBuilder (line 42) | public ConfigBuilder(String xdbPath) {
    method setCachePolicy (line 46) | public ConfigBuilder setCachePolicy(int cachePolicy) {
    method setXdbPath (line 51) | public ConfigBuilder setXdbPath(String xdbPath) {
    method setXdbFile (line 57) | public ConfigBuilder setXdbFile(File xdbFile) {
    method setXdbInputStream (line 63) | public ConfigBuilder setXdbInputStream(InputStream xdbInputStream) {
    method setCacheSliceBytes (line 69) | public ConfigBuilder setCacheSliceBytes(int cacheSliceBytes) {
    method setSearchers (line 74) | public ConfigBuilder setSearchers(int searchers) {
    method build (line 79) | private Config build(Version ipVersion) throws IOException, XdbExcepti...
    method asV4 (line 129) | public Config asV4() throws IOException, XdbException, InvalidConfigEx...
    method asV6 (line 134) | public Config asV6() throws IOException, XdbException, InvalidConfigEx...

FILE: binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java
  class InvalidConfigException (line 3) | public class InvalidConfigException extends Exception {
    method InvalidConfigException (line 4) | public InvalidConfigException(String str) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java
  class Ip2Region (line 23) | public class Ip2Region {
    method create (line 37) | public static final Ip2Region create(final Config v4Config, final Conf...
    method create (line 41) | public static final Ip2Region create(final String v4XdbPath, final Str...
    method create (line 45) | public static final Ip2Region create(final File v4XdbFile, final File ...
    method Ip2Region (line 59) | protected Ip2Region(File v4XdbFile, File v6XdbFile) throws IOException...
    method Ip2Region (line 74) | protected Ip2Region(Config v4Config, Config v6Config) throws IOExcepti...
    method init (line 101) | protected Ip2Region init() throws IOException {
    method search (line 113) | public String search(String ipString) throws InetAddressException, IOE...
    method search (line 117) | public String search(byte[] ipBytes) throws InetAddressException, IOEx...
    method v4Search (line 127) | protected String v4Search(final byte[] ipBytes) throws IOException, In...
    method v6Search (line 145) | protected String v6Search(final byte[] ipBytes) throws IOException, In...
    method close (line 163) | public void close() throws InterruptedException {
    method close (line 167) | public void close(long timeoutMillis) throws InterruptedException {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java
  class SearcherPool (line 22) | public class SearcherPool {
    method create (line 38) | public static final SearcherPool create(final Config config) throws IO...
    method create (line 42) | public static final SearcherPool create(final Config config, boolean f...
    method SearcherPool (line 46) | protected SearcherPool(Config config) throws IOException {
    method SearcherPool (line 50) | protected SearcherPool(Config config, boolean fair) {
    method init (line 60) | protected SearcherPool init() throws IOException {
    method getLoanCount (line 70) | public int getLoanCount() {
    method borrowSearcher (line 77) | public Searcher borrowSearcher() throws InterruptedException {
    method returnSearcher (line 91) | public void returnSearcher(final Searcher searcher) {
    method close (line 109) | public void close() throws InterruptedException {
    method close (line 113) | public void close(long timeoutMillis) throws InterruptedException {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java
  class Header (line 9) | public class Header {
    method Header (line 22) | public Header(byte[] buff) {
    method toString (line 34) | @Override public String toString() {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java
  class IPv4 (line 11) | public class IPv4 extends Version {
    method IPv4 (line 12) | public IPv4() {
    method putBytes (line 17) | @Override
    method ipSubCompare (line 27) | @Override

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java
  class IPv6 (line 11) | public class IPv6 extends Version {
    method IPv6 (line 13) | public IPv6() {
    method putBytes (line 18) | @Override
    method ipSubCompare (line 24) | @Override

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java
  class InetAddressException (line 7) | public class InetAddressException extends Exception {
    method InetAddressException (line 9) | public InetAddressException(String str) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java
  class LittleEndian (line 11) | public class LittleEndian {
    method put (line 16) | public static void put(final byte[] buff, int offset, long value, int ...
    method putUint32 (line 27) | public static void putUint32(final byte[] buff, int offset, long value) {
    method putUint16 (line 35) | public static void putUint16(final byte[] buff, int offset, int value) {
    method getUint32 (line 41) | public static long getUint32(final byte[] buff, int offset) {
    method getUint16 (line 51) | public static int getUint16(final byte[] buff, int offset) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java
  class Log (line 14) | public class Log {
    method Log (line 33) | public Log(Class<?> baseClass) {
    method getLogger (line 37) | public static Log getLogger(Class<?> baseClass) {
    method format (line 41) | public String format(int level, String format, Object... args) {
    method printf (line 53) | public void printf(int level, String format, Object... args) {
    method getDebugf (line 67) | public String getDebugf(String format, Object... args) {
    method debugf (line 71) | public void debugf(String format, Object... args) {
    method getInfof (line 75) | public String getInfof(String format, Object... args) {
    method infof (line 79) | public void infof(String format, Object... args) {
    method getWarnf (line 83) | public String getWarnf(String format, Object... args) {
    method warnf (line 87) | public void warnf(String format, Object... args) {
    method getErrorf (line 91) | public String getErrorf(String format, Object... args) {
    method errorf (line 95) | public void errorf(String format, Object... args) {
    method setLevel (line 99) | public Log setLevel(int level) {
    method setLevel (line 104) | public Log setLevel(String level) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java
  class LongByteArray (line 18) | public class LongByteArray {
    method LongByteArray (line 33) | public LongByteArray() {
    method LongByteArray (line 38) | public LongByteArray(int sliceBytes) {
    method append (line 45) | public void append(final byte[] buffer) throws IOException{
    method length (line 61) | public long length() {
    method size (line 65) | public int size() {
    method determinate (line 70) | private Position determinate(final long offset) {
    method copy (line 98) | public byte[] copy(final long srcPos, final byte[] dest, final int des...
    method slice (line 135) | public byte[] slice(long offset, int length) {
    method getUint32 (line 145) | public long getUint32(long offset) {
    method getInt2 (line 151) | public int getInt2(long offset) {
    class Position (line 158) | public static class Position {
      method Position (line 161) | public Position(int index, int offset) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java
  class Searcher (line 17) | public class Searcher {
    method newWithFileOnly (line 58) | public static Searcher newWithFileOnly(Version version, String xdbPath...
    method newWithFileOnly (line 62) | public static Searcher newWithFileOnly(Version version, File xdbFile) ...
    method newWithVectorIndex (line 66) | public static Searcher newWithVectorIndex(Version version, String xdbP...
    method newWithVectorIndex (line 70) | public static Searcher newWithVectorIndex(Version version, File xdbFil...
    method newWithBuffer (line 74) | public static Searcher newWithBuffer(Version version, LongByteArray cB...
    method Searcher (line 80) | public Searcher(Version version, File xdbFile, byte[] vectorIndex, Lon...
    method close (line 94) | public void close() throws IOException {
    method getIPVersion (line 100) | public Version getIPVersion() {
    method getIOCount (line 104) | public int getIOCount() {
    method search (line 108) | public String search(String ipStr) throws Exception {
    method search (line 112) | public String search(byte[] ip) throws IOException, InetAddressExcepti...
    method read (line 182) | protected void read(long offset, byte[] buffer) throws IOException {
    method toString (line 200) | @Override public String toString() {
    method loadHeader (line 214) | public static Header loadHeader(RandomAccessFile handle) throws IOExce...
    method loadHeaderFromFile (line 221) | public static Header loadHeaderFromFile(File xdbFile) throws IOExcepti...
    method loadHeaderFromFile (line 228) | public static Header loadHeaderFromFile(String xdbPath) throws IOExcep...
    method loadHeaderFromBuffer (line 232) | public static Header loadHeaderFromBuffer(LongByteArray cBuffer) throw...
    method loadVectorIndex (line 238) | public static byte[] loadVectorIndex(RandomAccessFile handle) throws I...
    method loadVectorIndexFromFile (line 250) | public static byte[] loadVectorIndexFromFile(File xdbFile) throws IOEx...
    method loadVectorIndexFromFile (line 257) | public static byte[] loadVectorIndexFromFile(String xdbPath) throws IO...
    method loadVectorIndexFromBuffer (line 261) | public static byte[] loadVectorIndexFromBuffer(LongByteArray cBuffer) ...
    method loadContent (line 270) | public static LongByteArray loadContent(RandomAccessFile handle) throw...
    method loadContent (line 274) | public static LongByteArray loadContent(RandomAccessFile handle, final...
    method loadContentFromFile (line 295) | public static LongByteArray loadContentFromFile(File xdbFile) throws I...
    method loadContentFromFile (line 299) | public static LongByteArray loadContentFromFile(File xdbFile, final in...
    method loadContentFromFile (line 308) | public static LongByteArray loadContentFromFile(String xdbPath) throws...
    method loadContentFromFile (line 312) | public static LongByteArray loadContentFromFile(String xdbPath, final ...
    method loadContentFromInputStream (line 318) | public static LongByteArray loadContentFromInputStream(InputStream is)...
    method loadContentFromInputStream (line 322) | public static LongByteArray loadContentFromInputStream(InputStream is,...
    method verify (line 369) | public static void verify(Header header, long fileBytes) throws IOExce...
    method verify (line 388) | public static void verify(RandomAccessFile handle) throws IOException,...
    method verifyFromFile (line 392) | public static void verifyFromFile(File xdbFile) throws IOException, Xd...
    method verifyFromFile (line 398) | public static void verifyFromFile(String xdbPath) throws IOException, ...

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java
  class Util (line 13) | public class Util
    method parseIP (line 18) | public static byte[] parseIP(String ip) throws InetAddressException {
    method ipToString (line 27) | public static String ipToString(final byte[] ip) {
    method ipJoin (line 40) | public static String ipJoin(byte[] ip) {
    method bytesToString (line 44) | public static String bytesToString(byte[] buff, int offset, int length) {
    method ipCompare (line 59) | public static int ipCompare(byte[] ip1, byte[] ip2) {
    method ipSubCompare (line 65) | public static int ipSubCompare(byte[] ip, byte[] buff, int offset) {
    method ipAddOne (line 82) | public static byte[] ipAddOne(byte[] ip) {
    method ipSubOne (line 98) | public static byte[] ipSubOne(byte[] ip) {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java
  class Version (line 11) | public abstract class Version {
    method Version (line 28) | public Version(int id, String name, int bytes, int segmentIndexSize) {
    method putBytes (line 36) | public abstract int putBytes(byte[] buff, int offset, byte[] ip);
    method ipCompare (line 40) | public int ipCompare(byte[] ip1, byte[] ip2) {
    method ipSubCompare (line 45) | public abstract int ipSubCompare(byte[] ip1, byte[] buff, int offset);
    method fromName (line 48) | public static final Version fromName(String name) throws Exception {
    method fromHeader (line 60) | public static final Version fromHeader(Header header) throws XdbExcept...
    method toString (line 80) | @Override public String toString() {

FILE: binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java
  class XdbException (line 7) | public class XdbException extends Exception {
    method XdbException (line 9) | public XdbException(String str) {

FILE: binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java
  class ConfigTest (line 11) | public class ConfigTest {
    method getDataPath (line 15) | public static final String getDataPath(String xdbFile) {
    method testBuildV4Config (line 25) | @Test
    method testBuildV4ConfigFromFile (line 35) | @Test
    method testBuildV4ConfigFromInputStream (line 45) | @Test
    method testBuildV4SliceBytes (line 55) | @Test
    method testBuildV6Config (line 68) | @Test
    method testBuildV6ConfigFromFile (line 78) | @Test
    method testBuildV6ConfigFromInputStream (line 88) | @Test
    method testBuildV6SliceBytes (line 98) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java
  class Ip2RegionTest (line 15) | public class Ip2RegionTest {
    method TestConfigCreate (line 19) | @Test
    method TestPathCreate (line 48) | @Test
    method TestInMemSearch (line 65) | @Test
    method TestConcurrentCall (line 92) | @Test
    method TestV4Only (line 146) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java
  class SearcherPoolTest (line 7) | public class SearcherPoolTest {
    method testV4SearcherPool (line 11) | @Test
    method testInMemV4SearcherPool (line 35) | @Test
    method testV6SearcherPool (line 59) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/xdb/BufferTest.java
  class BufferTest (line 10) | public class BufferTest {
    method getDataPath (line 14) | public static final String getDataPath(String xdbFile) {
    method testV4InputStreamBuffer (line 26) | @Test
    method testV4FixedBuffer (line 34) | @Test
    method testV4BufferAssert (line 44) | @Test
    method testV4BufferEOF (line 61) | @Test
    method testV6InputStreamBuffer (line 76) | @Test
    method testV6FixedBuffer (line 84) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java
  class IPv4Test (line 5) | public class IPv4Test {
    method testIpSubCompare (line 9) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java
  class LittleEndianTest (line 7) | public class LittleEndianTest {
    method testAll (line 11) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java
  class UtilTest (line 5) | public class UtilTest {
    method testCheckIP (line 9) | @Test
    method testIpCompare (line 27) | @Test
    method testIpSubCompare (line 45) | @Test

FILE: binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java
  class VersionTest (line 7) | public class VersionTest {
    method testFromName (line 11) | @Test
    method testFromHeader (line 23) | @Test

FILE: binding/javascript/index.d.ts
  class Header (line 8) | class Header {
  class Version (line 22) | class Version {
  class Searcher (line 35) | class Searcher {

FILE: binding/javascript/searcher.js
  class Searcher (line 15) | class Searcher {
    method constructor (line 16) | constructor(version, dbPath, vectorIndex, cBuffer) {
    method getIPVersion (line 31) | getIPVersion() {
    method getIOCount (line 35) | getIOCount() {
    method search (line 39) | async search(ip) {
    method read (line 109) | read(offset, buff, stats) {
    method close (line 127) | close() {
    method toString (line 133) | toString() {
  function newWithFileOnly (line 141) | function newWithFileOnly(version, dbPath) {
  function newWithVectorIndex (line 145) | function newWithVectorIndex(version, dbPath, vectorIndex) {
  function newWithBuffer (line 149) | function newWithBuffer(version, cBuffer) {

FILE: binding/javascript/tests/searcher.test.js
  function _get_creater_list (line 94) | function _get_creater_list(version) {

FILE: binding/javascript/util.js
  class Header (line 20) | class Header {
    method constructor (line 21) | constructor(buff) {
    method toString (line 36) | toString() {
  function _parse_ipv4_addr (line 51) | function _parse_ipv4_addr(v4String) {
  function _parse_ipv6_addr (line 76) | function _parse_ipv6_addr(v6String) {
  function parseIP (line 137) | function parseIP(ipString) {
  function _ipv4_to_string (line 152) | function _ipv4_to_string(v4Bytes) {
  function _ipv6_to_string (line 157) | function _ipv6_to_string(v6Bytes, compress) {
  function ipToString (line 218) | function ipToString(ipBytes, compress) {
  function ipBytesString (line 232) | function ipBytesString(ipBytes) {
  function ipSubCompare (line 248) | function ipSubCompare(ip1, buff, offset) {
  function ipCompare (line 252) | function ipCompare(ip1, ip2) {
  class Version (line 258) | class Version {
    method constructor (line 259) | constructor(id, name, bytes, indexSize, ipCompareFunc) {
    method ipCompare (line 267) | ipCompare(ip1, ip2) {
    method ipSubCompare (line 271) | ipSubCompare(ip1, ip2, offset) {
    method toString (line 275) | toString() {
  function versionFromName (line 304) | function versionFromName(name) {
  function versionFromHeader (line 315) | function versionFromHeader(h) {
  function loadHeader (line 338) | function loadHeader(fd) {
  function loadHeaderFromFile (line 347) | function loadHeaderFromFile(dbPath) {
  function loadVectorIndex (line 354) | function loadVectorIndex(fd) {
  function loadVectorIndexFromFile (line 364) | function loadVectorIndexFromFile(dbPath) {
  function loadContent (line 371) | function loadContent(fd) {
  function loadContentFromFile (line 381) | function loadContentFromFile(dbPath) {
  function verify (line 396) | function verify(fd) {
  function verifyFromFile (line 418) | function verifyFromFile(dbPath) {

FILE: binding/lua_c/xdb_searcher.c
  type xdb_buffer_entry (line 25) | struct xdb_buffer_entry {
  type xdb_buffer_t (line 31) | typedef struct xdb_buffer_entry xdb_buffer_t;
  function lua_xdb_buffer_name (line 33) | static int lua_xdb_buffer_name(lua_State *L) {
  function lua_xdb_buffer_type (line 43) | static int lua_xdb_buffer_type(lua_State *L) {
  function lua_xdb_buffer_to_table (line 53) | static int lua_xdb_buffer_to_table(lua_State *L) {
  function lua_xdb_buffer_length (line 90) | static int lua_xdb_buffer_length(lua_State *L) {
  function lua_xdb_buffer_tostring (line 109) | static int lua_xdb_buffer_tostring(lua_State *L) {
  function lua_xdb_buffer_close (line 119) | static int lua_xdb_buffer_close(lua_State *L) {
  type luaL_Reg (line 135) | struct luaL_Reg
  function lua_xdb_load_header_from_file (line 151) | static int lua_xdb_load_header_from_file(lua_State *L) {
  function lua_xdb_load_vector_index_from_file (line 187) | static int lua_xdb_load_vector_index_from_file(lua_State *L) {
  function lua_xdb_load_content_from_file (line 223) | static int lua_xdb_load_content_from_file(lua_State *L) {
  function lua_xdb_verify_from_file (line 259) | static int lua_xdb_verify_from_file(lua_State *L) {
  function lua_xdb_version_from_header (line 268) | static int lua_xdb_version_from_header(lua_State *L) {
  function xdb_version_t (line 291) | static xdb_version_t *_get_version(lua_State *L, int arg) {
  function lua_xdb_version_info (line 302) | static int lua_xdb_version_info(lua_State *L) {
  function lua_xdb_parse_ip (line 327) | static int lua_xdb_parse_ip(lua_State *L) {
  function lua_xdb_ip_to_string (line 351) | static int lua_xdb_ip_to_string(lua_State *L) {
  function _validate_bytes_ip (line 395) | static int _validate_bytes_ip(const string_ip_t *ip_bytes) {
  function lua_xdb_ip_compare (line 412) | static int lua_xdb_ip_compare(lua_State *L) {
  function lua_xdb_now (line 448) | static int lua_xdb_now(lua_State *L) {
  function lua_xdb_new_with_file_only (line 459) | static int lua_xdb_new_with_file_only(lua_State *L) {
  function lua_xdb_new_with_vector_index (line 499) | static int lua_xdb_new_with_vector_index(lua_State *L) {
  function lua_xdb_new_with_buffer (line 546) | static int lua_xdb_new_with_buffer(lua_State *L) {
  function lua_xdb_close (line 589) | static int lua_xdb_close(lua_State *L) {
  function lua_xdb_search (line 602) | static int lua_xdb_search(lua_State *L) {
  function lua_xdb_get_io_count (line 672) | static int lua_xdb_get_io_count(lua_State *L) {
  function lua_xdb_tostring (line 681) | static int lua_xdb_tostring(lua_State *L) {
  function lua_xdb_cleanup (line 691) | static int lua_xdb_cleanup(lua_State *L) {
  type luaL_Reg (line 697) | struct luaL_Reg
  type luaL_Reg (line 707) | struct luaL_Reg
  function luaopen_xdb_searcher (line 726) | int luaopen_xdb_searcher(lua_State *L)

FILE: binding/nginx/src/ngx_http_ip2region_module.c
  function ngx_int_t (line 323) | static ngx_int_t
  function ngx_int_t (line 342) | static ngx_int_t
  function ngx_http_ip2region_cleanup (line 480) | static void
  function ngx_int_t (line 525) | static ngx_int_t

FILE: binding/nginx/src/ngx_http_ip2region_module.h
  type ip2region_searcher_t (line 19) | typedef struct {
  type ngx_http_ip2region_conf_t (line 25) | typedef struct {

FILE: binding/php/batch_test.php
  function printHelp (line 15) | function printHelp($argv) {

FILE: binding/php/bench_test.php
  function printHelp (line 15) | function printHelp($argv) {

FILE: binding/php/search_test.php
  function printHelp (line 15) | function printHelp($argv) {

FILE: binding/php/xdb/Searcher.class.php
  class Util (line 24) | class Util {
    method parseIP (line 27) | public static function parseIP($ipString) {
    method ipToString (line 37) | public static function ipToString($ipBytes) {
    method ipSubCompare (line 44) | public static function ipSubCompare($ip1, $buff, $offset) {
    method ipCompare (line 58) | public static function ipCompare($ip1, $ip2) {
    method versionFromName (line 70) | public static function versionFromName($ver_name) {
    method versionFromHeader (line 82) | public static function versionFromHeader($header) {
    method bytesToString (line 103) | public static function bytesToString($buff, $offset, $length) {
    method le_getUint32 (line 112) | public static function le_getUint32($b, $idx) {
    method le_getUint16 (line 125) | public static function le_getUint16($b, $idx) {
    method verify (line 136) | public static function verify($handle) {
    method verifyFromFile (line 169) | public static function verifyFromFile($dbFile) {
    method loadHeader (line 181) | public static function loadHeader($handle) {
    method loadHeaderFromFile (line 209) | public static function loadHeaderFromFile($dbFile) {
    method loadVectorIndex (line 221) | public static function loadVectorIndex($handle) {
    method loadVectorIndexFromFile (line 240) | public static function loadVectorIndexFromFile($dbFile) {
    method loadContent (line 252) | public static function loadContent($handle) {
    method loadContentFromFile (line 281) | public static function loadContentFromFile($dbFile) {
    method now (line 290) | public static function now() {
  class IPv4 (line 296) | class IPv4 {
    method default (line 303) | public static function default() {
    method __construct (line 311) | public function __construct($id, $name, $bytes, $segmentIndexSize) {
    method ipSubCompare (line 319) | public function ipSubCompare($ip1, $buff, $offset) {
    method __toString (line 338) | public function __toString() {
  class IPv6 (line 346) | class IPv6 {
    method default (line 353) | public static function default() {
    method __construct (line 362) | public function __construct($id, $name, $bytes, $segmentIndexSize) {
    method ipSubCompare (line 369) | public function ipSubCompare($ip, $buff, $offset) {
    method __toString (line 374) | public function __toString() {
  class Searcher (line 383) | class Searcher {
    method newWithFileOnly (line 404) | public static function newWithFileOnly($version, $dbFile) {
    method newWithVectorIndex (line 411) | public static function newWithVectorIndex($version, $dbFile, $vIndex) {
    method newWithBuffer (line 418) | public static function newWithBuffer($version, $cBuff) {
    method __construct (line 428) | function __construct($version, $dbFile, $vectorIndex=null, $cBuff=null) {
    method close (line 445) | public function close() {
    method getIPVersion (line 451) | public function getIPVersion() {
    method getIOCount (line 455) | public function getIOCount() {
    method search (line 466) | public function search($ip) {
    method searchByBytes (line 480) | public function searchByBytes($ipBytes) {
    method read (line 548) | private function read($offset, $len) {

FILE: binding/php/xdb/util_test.php
  function testLoadHeader (line 25) | function testLoadHeader() {
  function testLoadVectorIndex (line 37) | function testLoadVectorIndex() {
  function testLoadContent (line 48) | function testLoadContent() {
  function testParseIP (line 59) | function testParseIP() {
  function testIPCompare (line 89) | function testIPCompare() {
  function testAttributes (line 108) | function testAttributes() {

FILE: binding/python/bench_test.py
  function create_searcher (line 15) | def create_searcher(db_path, cache_policy):
  function run_bench_test (line 48) | def run_bench_test(db_path: str, src_path: str, cache_policy: str):

FILE: binding/python/ip2region/searcher.py
  class Searcher (line 12) | class Searcher(object):
    method __init__ (line 17) | def __init__(self, version: util.Version,
    method get_ip_version (line 31) | def get_ip_version(self):
    method get_io_count (line 34) | def get_io_count(self):
    method search (line 37) | def search(self, ip: Union[bytes, str]):
    method read (line 104) | def read(self, offset: int, length: int):
    method close (line 114) | def close(self):
    method __str__ (line 118) | def __str__(self):
  function new_with_file_only (line 130) | def new_with_file_only(version: util.Version, db_path: str):
  function new_with_vector_index (line 133) | def new_with_vector_index(version: util.Version, db_path: str, vector_in...
  function new_with_buffer (line 136) | def new_with_buffer(version: util.Version, c_buffer: bytes):

FILE: binding/python/ip2region/util.py
  class Header (line 26) | class Header(object):
    method __init__ (line 27) | def __init__(self, buff: bytes):
    method __str__ (line 42) | def __str__(self):
  function parse_ip (line 65) | def parse_ip(ip_string: str):
  function ip_to_string (line 71) | def ip_to_string(ip_bytes: bytes):
  function ip_compare (line 77) | def ip_compare(ip1: bytes, ip2: bytes):
  function ip_sub_compare (line 85) | def ip_sub_compare(ip1: bytes, buff: bytes, offset: int):
  class Version (line 98) | class Version(object):
    method __init__ (line 99) | def __init__(self, id: int, name: str, byte_num: int, index_size: int,...
    method ip_compare (line 106) | def ip_compare(self, ip1: bytes, ip2: bytes):
    method ip_sub_compare (line 109) | def ip_sub_compare(self, ip1: bytes, buff: bytes, offset: int):
    method __str__ (line 112) | def __str__(self):
  function _v4_sub_compare (line 120) | def _v4_sub_compare(ip1: bytes, buff: bytes, offset: int):
  function version_from_name (line 147) | def version_from_name(name: str):
  function version_from_header (line 156) | def version_from_header(header: bytes):
  function le_get_uint32 (line 174) | def le_get_uint32(buff: bytes, offset: int):
  function le_get_uint16 (line 186) | def le_get_uint16(buff: bytes, offset: int):
  function load_header (line 200) | def load_header(handle):
  function load_header_from_file (line 207) | def load_header_from_file(db_file: str):
  function load_vector_index (line 213) | def load_vector_index(handle):
  function load_vector_index_from_file (line 220) | def load_vector_index_from_file(db_file: str):
  function load_content (line 226) | def load_content(handle):
  function load_content_from_file (line 233) | def load_content_from_file(db_file: str):
  function verify (line 246) | def verify(handle):
  function verify_from_file (line 267) | def verify_from_file(db_file: str):

FILE: binding/python/search_test.py
  function create_searcher (line 15) | def create_searcher(db_path, cache_policy):
  function run_search_test (line 48) | def run_search_test(db_path: str, cache_policy: str):

FILE: binding/python/util_test.py
  function test_version (line 20) | def test_version():
  function test_verify (line 37) | def test_verify():
  function test_load_header (line 54) | def test_load_header():
  function test_load_vector_index (line 60) | def test_load_vector_index():
  function test_load_content (line 66) | def test_load_content():
  function test_parse_ip (line 72) | def test_parse_ip():
  function test_ip_compare (line 86) | def test_ip_compare():
  function _get_searcher_list (line 102) | def _get_searcher_list(version: util.Version):
  function test_ip_search (line 110) | def test_ip_search():

FILE: binding/rust/example/src/cmd.rs
  type Command (line 21) | pub struct Command {
  type Action (line 32) | pub enum Action {
  type CmdCachePolicy (line 40) | pub enum CmdCachePolicy {

FILE: binding/rust/example/src/main.rs
  function check (line 45) | fn check(searcher: &Searcher, start_ip: IpAddr, end_ip: IpAddr, check: &...
  function bench (line 61) | fn bench(searcher: &Searcher, check_filepath: &str) {
  function query (line 79) | fn query(searcher: &Searcher) {
  function main (line 97) | fn main() {

FILE: binding/rust/ip2region/benches/search.rs
  function ipv4_range (line 24) | fn ipv4_range() -> Range<u32> {
  function ipv6_range (line 30) | fn ipv6_range() -> Range<u128> {
  constant IPV4_XDB (line 36) | const IPV4_XDB: &str = "../../../data/ip2region_v4.xdb";
  constant IPV6_XDB (line 37) | const IPV6_XDB: &str = "../../../data/ip2region_v6.xdb";

FILE: binding/rust/ip2region/src/error.rs
  type Ip2RegionError (line 2) | pub enum Ip2RegionError {
  type Result (line 28) | pub type Result<T> = std::result::Result<T, Ip2RegionError>;

FILE: binding/rust/ip2region/src/ip_value.rs
  type IpValueExt (line 7) | pub trait IpValueExt {
    method to_ipaddr (line 8) | fn to_ipaddr(self) -> Result<IpAddr>;
    method to_ipaddr (line 12) | fn to_ipaddr(self) -> Result<IpAddr> {
    method to_ipaddr (line 18) | fn to_ipaddr(self) -> Result<IpAddr> {
    method to_ipaddr (line 24) | fn to_ipaddr(self) -> Result<IpAddr> {
    method to_ipaddr (line 30) | fn to_ipaddr(self) -> Result<IpAddr> {
    method to_ipaddr (line 36) | fn to_ipaddr(self) -> Result<IpAddr> {
  type CompareExt (line 41) | pub trait CompareExt {
    method ip_lt (line 42) | fn ip_lt(&self, other: Cow<'_, [u8]>) -> bool;
    method ip_gt (line 43) | fn ip_gt(&self, other: Cow<'_, [u8]>) -> bool;
    method ip_lt (line 47) | fn ip_lt(&self, other: Cow<'_, [u8]>) -> bool {
    method ip_gt (line 54) | fn ip_gt(&self, other: Cow<'_, [u8]>) -> bool {

FILE: binding/rust/ip2region/src/searcher.rs
  type Searcher (line 18) | pub struct Searcher {
    method new (line 34) | pub fn new(filepath: String, cache_policy: CachePolicy) -> Result<Self> {
    method search (line 51) | pub fn search<T>(&self, ip: T) -> Result<String>
    method vector_index (line 118) | pub fn vector_index(&self) -> Result<Cow<'_, [u8]>> {
    method read_buf (line 142) | pub fn read_buf(&self, offset: usize, size: usize) -> Result<Cow<'_, [...
  type CachePolicy (line 27) | pub enum CachePolicy {
  constant IPV4_XDB_PATH (line 186) | const IPV4_XDB_PATH: &str = "../../../data/ip2region_v4.xdb";
  constant IPV4_CHECK_PATH (line 187) | const IPV4_CHECK_PATH: &str = "../../../data/ipv4_source.txt";
  constant IPV6_XDB_PATH (line 188) | const IPV6_XDB_PATH: &str = "../../../data/ip2region_v6.xdb";
  constant IPV6_CHECK_PATH (line 189) | const IPV6_CHECK_PATH: &str = "../../../data/ipv6_source.txt";
  function test_multi_type_ip (line 193) | fn test_multi_type_ip() {
  function match_ip_correct (line 211) | fn match_ip_correct(xdb_filepath: &str, check_path: &str, cache_policy: ...
  function test_match_ip_correct (line 245) | fn test_match_ip_correct() {

FILE: maker/csharp/IP2RegionMaker.Test/UtilTest.cs
  class UtilTest (line 9) | [TestFixture]
    method TestIpAddressToUInt32 (line 12) | [TestCase("114.114.114.114")]
    method TestUInt32ToIpAddress (line 18) | [TestCase(1920103026)]
    method TestSplitSegment (line 24) | [TestCase("28.201.224.0|29.34.191.255|美国|0|0|0|0")]

FILE: maker/csharp/IP2RegionMaker/XDB/IndexPolicy.cs
  type IndexPolicy (line 9) | public enum IndexPolicy

FILE: maker/csharp/IP2RegionMaker/XDB/Maker.cs
  class Maker (line 56) | public class Maker
    method Maker (line 73) | public Maker(IndexPolicy indexPolicy,string srcFile, string dstFile)
    method InitDbHeader (line 90) | private void InitDbHeader()
    method LoadSegments (line 105) | private void LoadSegments()
    method Init (line 132) | public void Init()
    method Build (line 138) | public void Build()
    method SetVectorIndex (line 233) | private void SetVectorIndex(uint ip, uint ptr)

FILE: maker/csharp/IP2RegionMaker/XDB/Segment.cs
  class Segment (line 9) | public class Segment
    method Split (line 17) | public List<Segment> Split()
    method ToString (line 80) | public override string ToString()

FILE: maker/csharp/IP2RegionMaker/XDB/Util.cs
  class Util (line 10) | public static class Util
    method IpAddressToUInt32 (line 12) | public static uint IpAddressToUInt32(string ipAddress)
    method UInt32ToIpAddress (line 20) | public static string UInt32ToIpAddress(uint ipAddress)
    method GetSegment (line 27) | public static Segment GetSegment(string line)
    method CheckSegments (line 57) | public static void CheckSegments(List<Segment> segments)

FILE: maker/golang/cmd/bench.go
  function Bench (line 12) | func Bench() {

FILE: maker/golang/cmd/edit.go
  function Edit (line 21) | func Edit() {

FILE: maker/golang/cmd/generate.go
  function Generate (line 18) | func Generate() {

FILE: maker/golang/cmd/process.go
  function Process (line 19) | func Process() {

FILE: maker/golang/cmd/search.go
  function Search (line 21) | func Search() {

FILE: maker/golang/cmd/util.go
  function PrintHelp (line 13) | func PrintHelp() {
  function iterateFlags (line 25) | func iterateFlags(cb func(key string, val string) error) error {
  function applyLogLevel (line 49) | func applyLogLevel(logLevel string) error {
  function getFilterFields (line 74) | func getFilterFields(fieldList string) ([]int, error) {

FILE: maker/golang/main.go
  function main (line 15) | func main() {

FILE: maker/golang/xdb/editor.go
  type Editor (line 17) | type Editor struct
    method loadSegments (line 58) | func (e *Editor) loadSegments() error {
    method NeedSave (line 150) | func (e *Editor) NeedSave() bool {
    method SegLen (line 154) | func (e *Editor) SegLen() int {
    method Slice (line 158) | func (e *Editor) Slice(offset int, size int) []*Segment {
    method Put (line 184) | func (e *Editor) Put(ip string) (int, int, error) {
    method PutSegment (line 204) | func (e *Editor) PutSegment(seg *Segment) (int, int, error) {
    method PutFile (line 298) | func (e *Editor) PutFile(src string) (int, int, error) {
    method Save (line 324) | func (e *Editor) Save() error {
    method Close (line 366) | func (e *Editor) Close() {
  function NewEditor (line 29) | func NewEditor(version *Version, srcFile string) (*Editor, error) {

FILE: maker/golang/xdb/index.go
  type IndexPolicy (line 12) | type IndexPolicy
  constant VectorIndexPolicy (line 15) | VectorIndexPolicy IndexPolicy = 1
  constant BTreeIndexPolicy (line 16) | BTreeIndexPolicy  IndexPolicy = 2
  function IndexPolicyFromString (line 19) | func IndexPolicyFromString(str string) (IndexPolicy, error) {

FILE: maker/golang/xdb/maker.go
  constant VersionNo (line 66) | VersionNo         = 3
  constant HeaderInfoLength (line 67) | HeaderInfoLength  = 256
  constant VectorIndexRows (line 68) | VectorIndexRows   = 256
  constant VectorIndexCols (line 69) | VectorIndexCols   = 256
  constant VectorIndexSize (line 70) | VectorIndexSize   = 8
  constant RuntimePtrSize (line 71) | RuntimePtrSize    = 4
  constant VectorIndexLength (line 72) | VectorIndexLength = VectorIndexRows * VectorIndexCols * VectorIndexSize
  type Maker (line 75) | type Maker struct
    method initDbHeader (line 125) | func (m *Maker) initDbHeader() error {
    method loadSegments (line 165) | func (m *Maker) loadSegments() error {
    method Init (line 225) | func (m *Maker) Init() error {
    method Append (line 246) | func (m *Maker) Append(seg *Segment) {
    method setVectorIndex (line 251) | func (m *Maker) setVectorIndex(ip []byte, ptr uint32) {
    method Start (line 265) | func (m *Maker) Start() error {
    method End (line 404) | func (m *Maker) End() error {
  function NewMaker (line 90) | func NewMaker(version *Version, policy IndexPolicy, srcFile string, dstF...

FILE: maker/golang/xdb/processor.go
  type Processor (line 18) | type Processor struct
    method loadSegments (line 61) | func (p *Processor) loadSegments() error {
    method Init (line 116) | func (p *Processor) Init() error {
    method Start (line 126) | func (p *Processor) Start() error {
    method End (line 144) | func (p *Processor) End() error {
  function NewProcessor (line 31) | func NewProcessor(srcFile string, dstFile string, fields []int,

FILE: maker/golang/xdb/searcher.go
  type Searcher (line 19) | type Searcher struct
    method Close (line 49) | func (s *Searcher) Close() {
    method LoadVectorIndex (line 60) | func (s *Searcher) LoadVectorIndex() error {
    method ClearVectorIndex (line 87) | func (s *Searcher) ClearVectorIndex() {
    method Search (line 92) | func (s *Searcher) Search(ip []byte) (string, int, error) {
  function NewSearcher (line 33) | func NewSearcher(version *Version, dbFile string) (*Searcher, error) {
  function LoadXdbHeader (line 194) | func LoadXdbHeader(handle *os.File) ([]byte, error) {
  function LoadXdbHeaderFromFile (line 214) | func LoadXdbHeaderFromFile(dbFile string) ([]byte, error) {

FILE: maker/golang/xdb/segment.go
  type Segment (line 12) | type Segment struct
    method RightBehind (line 47) | func (s *Segment) RightBehind(last *Segment) error {
    method After (line 62) | func (s *Segment) After(last *Segment) error {
    method Split (line 76) | func (s *Segment) Split() []*Segment {
    method String (line 170) | func (s *Segment) String() string {
    method Contains (line 175) | func (s *Segment) Contains(ip []byte) bool {
  function SegmentFrom (line 18) | func SegmentFrom(seg string) (*Segment, error) {

FILE: maker/golang/xdb/util.go
  function ParseIP (line 19) | func ParseIP(ip string) ([]byte, error) {
  function IP2String (line 38) | func IP2String(ip []byte) string {
  function IP2Long (line 42) | func IP2Long(ip []byte) *big.Int {
  function IPCompare (line 48) | func IPCompare(ip1, ip2 []byte) int {
  function IPAddOne (line 63) | func IPAddOne(ip []byte) []byte {
  function IPSubOne (line 76) | func IPSubOne(ip []byte) []byte {
  function IPSub (line 91) | func IPSub(sip, eip []byte) ([]byte, error) {
  function IPHalf (line 115) | func IPHalf(ip []byte) []byte {
  function IPMiddle (line 134) | func IPMiddle(sip, eip []byte) ([]byte, error) {
  function IterateSegments (line 143) | func IterateSegments(handle *os.File, autoMerge bool, before func(l stri...
  function CheckSegments (line 236) | func CheckSegments(segList []*Segment) error {
  function RegionFiltering (line 257) | func RegionFiltering(region string, fields []int) (string, error) {

FILE: maker/golang/xdb/util_test.go
  function TestParseIP (line 14) | func TestParseIP(t *testing.T) {
  function TestIPCompare (line 27) | func TestIPCompare(t *testing.T) {
  function TestIPAddOne (line 41) | func TestIPAddOne(t *testing.T) {
  function TestIPAddOne2 (line 66) | func TestIPAddOne2(t *testing.T) {
  function TestIPSubOne (line 72) | func TestIPSubOne(t *testing.T) {
  function TestIPSubOne2 (line 97) | func TestIPSubOne2(t *testing.T) {
  function TestIPSub (line 103) | func TestIPSub(t *testing.T) {
  function TestIPHalf (line 134) | func TestIPHalf(t *testing.T) {
  function TestSubOverflow (line 149) | func TestSubOverflow(t *testing.T) {
  function TestIPMiddle (line 168) | func TestIPMiddle(t *testing.T) {
  function TestSplitSegmentV4 (line 199) | func TestSplitSegmentV4(t *testing.T) {
  function TestRegionFiltering (line 221) | func TestRegionFiltering(t *testing.T) {
  function TestSplitSegmentV6 (line 236) | func TestSplitSegmentV6(t *testing.T) {
  function TestIterateSegments (line 255) | func TestIterateSegments(t *testing.T) {

FILE: maker/golang/xdb/version.go
  constant IPv4VersionNo (line 14) | IPv4VersionNo = 4
  constant IPv6VersionNo (line 15) | IPv6VersionNo = 6
  type Version (line 18) | type Version struct
    method String (line 34) | func (v *Version) String() string {
  function VersionFromIP (line 91) | func VersionFromIP(ip string) (*Version, error) {
  function VersionFromName (line 104) | func VersionFromName(name string) (*Version, error) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/MakerApp.java
  class MakerApp (line 22) | public class MakerApp {
    method printHelp (line 27) | public static void printHelp(String[] args) {
    method getFieldList (line 38) | private static int[] getFieldList(String fieldList) {
    method genDb (line 112) | public static void genDb(String[] args) throws Exception {
    method main (line 175) | public static void main(String[] args) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java
  class IPv4 (line 11) | public class IPv4 extends Version {
    method IPv4 (line 12) | public IPv4() {
    method putBytes (line 17) | @Override

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java
  class IPv6 (line 11) | public class IPv6 extends Version {
    method IPv6 (line 13) | public IPv6() {
    method putBytes (line 18) | @Override

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/IndexPolicy.java
  class IndexPolicy (line 10) | public class IndexPolicy {
    method parse (line 15) | public static int parse(String policy) throws Exception {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/InvalidInetAddressException.java
  class InvalidInetAddressException (line 7) | public class InvalidInetAddressException extends Exception {
    method InvalidInetAddressException (line 9) | public InvalidInetAddressException(String str) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java
  class LittleEndian (line 11) | public class LittleEndian {
    method put (line 16) | public static void put(final byte[] buff, int offset, long value, int ...
    method putUint32 (line 27) | public static void putUint32(final byte[] buff, int offset, long value) {
    method putInt2 (line 35) | public static void putInt2(final byte[] buff, int offset, int value) {
    method getUint32 (line 41) | public static long getUint32(final byte[] buff, int offset) {
    method getInt2 (line 51) | public static int getInt2(final byte[] buff, int offset) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java
  class Log (line 14) | public class Log {
    method Log (line 33) | public Log(Class<?> baseClass) {
    method getLogger (line 37) | public static Log getLogger(Class<?> baseClass) {
    method format (line 41) | public String format(int level, String format, Object... args) {
    method printf (line 53) | public void printf(int level, String format, Object... args) {
    method getDebugf (line 67) | public String getDebugf(String format, Object... args) {
    method debugf (line 71) | public void debugf(String format, Object... args) {
    method getInfof (line 75) | public String getInfof(String format, Object... args) {
    method infof (line 79) | public void infof(String format, Object... args) {
    method getWarnf (line 83) | public String getWarnf(String format, Object... args) {
    method warnf (line 87) | public void warnf(String format, Object... args) {
    method getErrorf (line 91) | public String getErrorf(String format, Object... args) {
    method errorf (line 95) | public void errorf(String format, Object... args) {
    method setLevel (line 99) | public Log setLevel(int level) {
    method setLevel (line 104) | public Log setLevel(String level) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/Maker.java
  class Maker (line 62) | public class Maker {
    method Maker (line 97) | public Maker(Version version, int policy, String srcPath, String dstPa...
    method initHeader (line 124) | private void initHeader() throws IOException {
    method loadSegments (line 158) | private void loadSegments() throws Exception {
    method init (line 232) | public void init() throws Exception {
    method setVectorIndex (line 241) | private void setVectorIndex(final byte[] ip, long ptr) {
    method start (line 255) | public void start() throws Exception {
    method end (line 359) | public void end() throws IOException {
    class DataEntry (line 363) | private static class DataEntry {
      method DataEntry (line 367) | DataEntry(int length, long ptr) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/Segment.java
  class Segment (line 17) | public class Segment {
    method Segment (line 22) | public Segment(final byte[] startIP, final byte[] endIP, String region) {
    method split (line 29) | public List<Segment> split() {
    method toString (line 92) | @Override public String toString() {
    method contains (line 97) | public boolean contains(final byte[] ip) {
    method rightBehind (line 103) | public boolean rightBehind(final Segment last) {
    method after (line 109) | public boolean after(final Segment last) {
    method parse (line 114) | public static Segment parse(String input) throws Exception {
    type IterateAction (line 130) | public static interface IterateAction {
      method sorting (line 132) | default boolean sorting() {
      method before (line 136) | public void before(final String line);
      method filter (line 137) | public String filter(final String region);
      method handle (line 138) | public void handle(final Segment seg) throws Exception;
    method iterate (line 142) | public static void iterate(final String srcFile, IterateAction action)...
    method iterate (line 146) | public static void iterate(final File srcFile, IterateAction action) t...

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java
  class Util (line 13) | public class Util
    method parseIP (line 18) | public static byte[] parseIP(String ip) throws InvalidInetAddressExcep...
    method ipToString (line 27) | public static String ipToString(final byte[] ip) {
    method ipArrayString (line 40) | public static String ipArrayString(byte[] ip) {
    method ipCompare (line 55) | public static int ipCompare(byte[] ip1, byte[] ip2) {
    method ipAddOne (line 72) | public static byte[] ipAddOne(byte[] ip) {
    method ipSubOne (line 88) | public static byte[] ipSubOne(byte[] ip) {
    method regionFiltering (line 105) | public static String regionFiltering(String region, int[] fields) {

FILE: maker/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java
  class Version (line 11) | public abstract class Version {
    method Version (line 28) | public Version(int id, String name, int bytes, int segmentIndexSize) {
    method putBytes (line 36) | public abstract int putBytes(byte[] buff, int offset, byte[] ip);
    method ipCompare (line 40) | public int ipCompare(byte[] ip1, byte[] ip2) {
    method fromName (line 45) | public static final Version fromName(String name) throws Exception {
    method toString (line 56) | @Override public String toString() {

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/IndexPolicyTest.java
  class IndexPolicyTest (line 5) | public class IndexPolicyTest {
    method testParse (line 9) | @Test

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java
  class LittleEndianTest (line 7) | public class LittleEndianTest {
    method testAll (line 11) | @Test

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/MakerTest.java
  class MakerTest (line 5) | public class MakerTest {
    method testInit (line 9) | @Test

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/SegmentTest.java
  class SegmentTest (line 8) | public class SegmentTest {
    method testParse (line 12) | @Test
    method testSplit (line 26) | @Test
    method testIterate (line 43) | @Test

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java
  class UtilTest (line 5) | public class UtilTest {
    method testCheckIP (line 9) | @Test
    method testIpCompare (line 27) | @Test
    method testIpAddOne (line 45) | @Test
    method testIpSubOne (line 62) | @Test
    method testRegionFiltering (line 79) | @Test

FILE: maker/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java
  class VersionTest (line 7) | public class VersionTest {
    method testFromName (line 11) | @Test

FILE: maker/python/main.py
  function print_help (line 23) | def print_help():
  function gen_db (line 30) | def gen_db():
  function main (line 74) | def main():

FILE: maker/python/xdb/index.py
  function index_policy_from_string (line 14) | def index_policy_from_string(s: str) -> int:
  class VectorIndexBlock (line 25) | class VectorIndexBlock:
    method __init__ (line 29) | def __init__(self, fp=0, lp=0):
    method __str__ (line 33) | def __str__(self):
    method encode (line 36) | def encode(self) -> bytes:
  class SegmentIndexBlock (line 43) | class SegmentIndexBlock:
    method __init__ (line 49) | def __init__(self, sip, eip, dl, dp):
    method __str__ (line 55) | def __str__(self):
    method encode (line 60) | def encode(self) -> bytes:

FILE: maker/python/xdb/maker.py
  class Maker (line 70) | class Maker:
    method __init__ (line 78) | def __init__(self, sh, dh, ip, sg, rp, vi):
    method init (line 86) | def init(self):
    method init_db_header (line 95) | def init_db_header(self):
    method load_segments (line 117) | def load_segments(self):
    method set_vector_index (line 170) | def set_vector_index(self, ip, ptr):
    method start (line 183) | def start(self):
    method end (line 273) | def end(self):
  function new_maker (line 285) | def new_maker(policy: int, srcfile: str, dstfile: str) -> Maker:

FILE: maker/python/xdb/segment.py
  class Segment (line 11) | class Segment:
    method __init__ (line 16) | def __init__(self, sip=0, eip=0, reg=""):
    method __str__ (line 20) | def __str__(self):
    method split (line 25) | def split(self) -> list:

FILE: maker/python/xdb/util.py
  function check_ip (line 11) | def check_ip(ip: str) -> int:
  function long2ip (line 26) | def long2ip(num: int) -> str:
  function is_ipv4 (line 38) | def is_ipv4(ip: str) -> bool:

FILE: maker/rust/maker/src/command.rs
  type Command (line 7) | pub struct Command {

FILE: maker/rust/maker/src/error.rs
  type MakerError (line 2) | pub enum MakerError {
  type Result (line 34) | pub type Result<T> = std::result::Result<T, MakerError>;

FILE: maker/rust/maker/src/header.rs
  constant VERSION_NO (line 12) | pub const VERSION_NO: u16 = 3;
  constant HEADER_INFO_LENGTH (line 13) | pub const HEADER_INFO_LENGTH: usize = 256;
  constant VECTOR_INDEX_COLS (line 14) | pub const VECTOR_INDEX_COLS: usize = 256;
  constant VECTOR_INDEX_ROWS (line 15) | pub const VECTOR_INDEX_ROWS: usize = 256;
  constant VECTOR_INDEX_SIZE (line 16) | pub const VECTOR_INDEX_SIZE: usize = 8;
  constant VECTOR_INDEX_LENGTH (line 17) | pub const VECTOR_INDEX_LENGTH: usize = VECTOR_INDEX_COLS * VECTOR_INDEX_...
  constant RUNTIME_PTR_SIZE (line 18) | pub const RUNTIME_PTR_SIZE: u16 = 4;
  constant REGION_START (line 19) | pub const REGION_START: u64 = (HEADER_INFO_LENGTH + VECTOR_INDEX_LENGTH)...
  type Header (line 23) | pub struct Header {
    type Error (line 34) | type Error = MakerError;
    method try_from (line 36) | fn try_from(value: &[u8; 256]) -> Result<Self> {
    method new (line 64) | pub fn new(index_policy: IndexPolicy, ip_version: IpVersion) -> Header {
    method encode_bytes (line 76) | pub fn encode_bytes(&self, start_index_ptr: u32, end_index_ptr: u32) -...
    method ip_bytes_len (line 132) | pub fn ip_bytes_len(&self) -> usize {
    method segment_index_size (line 136) | pub fn segment_index_size(&self) -> usize {
    method ip_version (line 140) | pub fn ip_version(&self) -> &IpVersion {
  type IndexPolicy (line 92) | pub enum IndexPolicy {
  method fmt (line 98) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  type IpVersion (line 108) | pub enum IpVersion {
    method ip_bytes_len (line 116) | pub fn ip_bytes_len(&self) -> usize {
    method segment_index_size (line 123) | pub fn segment_index_size(&self) -> usize {
  type IPAddrExt (line 145) | pub trait IPAddrExt {
    method ipaddr_bytes (line 146) | fn ipaddr_bytes(&self) -> Vec<u8>;
    method encode_ipaddr_bytes (line 148) | fn encode_ipaddr_bytes(&self) -> Vec<u8>;
    method ipaddr_bytes (line 152) | fn ipaddr_bytes(&self) -> Vec<u8> {
    method encode_ipaddr_bytes (line 159) | fn encode_ipaddr_bytes(&self) -> Vec<u8> {

FILE: maker/rust/maker/src/main.rs
  function main (line 9) | fn main() -> Result<()> {

FILE: maker/rust/maker/src/maker.rs
  type Maker (line 15) | pub struct Maker {
    method new (line 25) | pub fn new(
    method set_vector_index (line 63) | fn set_vector_index(&mut self, ip: &[u8], ptr: u32) -> Result<()> {
    method start (line 75) | pub fn start(&mut self) -> Result<()> {

FILE: maker/rust/maker/src/segment.rs
  type IpPlusEq (line 14) | pub trait IpPlusEq {
    method ip_plus_eq (line 15) | fn ip_plus_eq(&self, other: &Self) -> bool;
    method ip_plus_eq (line 19) | fn ip_plus_eq(&self, other: &Self) -> bool {
  type Segment (line 29) | pub struct Segment {
    method from_file (line 55) | pub fn from_file(
    method split (line 125) | pub fn split(self) -> Result<Vec<Segment>> {
  function region_filter (line 35) | fn region_filter(region: &str, filter_fields: &[usize]) -> Result<String> {
Condensed preview — 272 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,055K chars).
[
  {
    "path": ".gitattributes",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".gitignore",
    "chars": 1354,
    "preview": "*.class\r\n*.out\r\n*.o\r\n*.pyc\r\n*~\r\n*.log\r\n*.la\r\n*.so\r\n*.iml\r\nMETA-INF/\r\n.DS_Store\r\n\r\n# Binary Files #\r\n*.jar\r\n!dbMaker-*.ja"
  },
  {
    "path": "LICENSE.md",
    "chars": 12640,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 9402,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region\n\n[ip2region](https://ip2region.net) - is"
  },
  {
    "path": "README_zh.md",
    "chars": 5892,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\r\n\r\n# ip2region\r\n\r\n[ip2region](https://ip2region.net) "
  },
  {
    "path": "binding/c/Makefile",
    "chars": 747,
    "preview": "all: xdb_searcher test_util\n\nxdb_searcher: xdb_api.h xdb_util.c xdb_searcher.c main.c\n\tgcc -std=c99 -Wall -O2 -I./ xdb_u"
  },
  {
    "path": "binding/c/README.md",
    "chars": 14933,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region c Query Client\n\n# Usage\n\n### About Query"
  },
  {
    "path": "binding/c/README_zh.md",
    "chars": 10965,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region c 查询客户端\n\n\n# 使用方式\n\n### 关于查询 API\n查询 API 的原"
  },
  {
    "path": "binding/c/main.c",
    "chars": 12869,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/c/test_util.c",
    "chars": 5949,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/c/xdb_api.h",
    "chars": 9035,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/c/xdb_searcher.c",
    "chars": 8173,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/c/xdb_util.c",
    "chars": 13570,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/cpp/.gitignore",
    "chars": 5,
    "preview": "bin/\n"
  },
  {
    "path": "binding/cpp/Makefile",
    "chars": 491,
    "preview": "\nall: bin header search bench make edit\n\nFILES=$(wildcard src/*.cc)\n\nbin:\n\tmkdir -p bin\n\nheader: $(FILES) test/header.cc"
  },
  {
    "path": "binding/cpp/README.md",
    "chars": 3818,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region C++ query client\n\n## 0. File Description"
  },
  {
    "path": "binding/cpp/README_zh.md",
    "chars": 2722,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region C++ 查询客户端\n\n## 0. 文件说明\n```\nMakefile -----"
  },
  {
    "path": "binding/cpp/src/base.cc",
    "chars": 1725,
    "preview": "\n#include \"base.h\"\n\nnamespace xdb {\n\nint ip_version;  // ip 版本\nint ip_size;     // ip 占的字节数\nint content_size;\n\nvoid init"
  },
  {
    "path": "binding/cpp/src/base.h",
    "chars": 1175,
    "preview": "#ifndef BASE_H\n#define BASE_H\n\n#include <arpa/inet.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#includ"
  },
  {
    "path": "binding/cpp/src/bench.cc",
    "chars": 1604,
    "preview": "\n#include \"bench.h\"\n\nnamespace xdb {\n\nbench_t::bench_t(const std::string &file_name, int version, int policy)\n    : sear"
  },
  {
    "path": "binding/cpp/src/bench.h",
    "chars": 509,
    "preview": "#ifndef BENCH_H\n#define BENCH_H\n\n#include \"search.h\"\n\nnamespace xdb {\n\nclass bench_t {\npublic:\n    bench_t(const string "
  },
  {
    "path": "binding/cpp/src/edit.cc",
    "chars": 4218,
    "preview": "\n#include \"edit.h\"\n\nnamespace xdb {\n\nvoid handle_ip_txt(const string& name, std::list<node_t>& regions) {\n    FILE* f = "
  },
  {
    "path": "binding/cpp/src/edit.h",
    "chars": 444,
    "preview": "#ifndef EDIT_H\n#define EDIT_H\n\n#include \"ip.h\"\n\nnamespace xdb {\n\nclass edit_t {\npublic:\n    edit_t(const string& old_nam"
  },
  {
    "path": "binding/cpp/src/header.cc",
    "chars": 708,
    "preview": "\n#include \"header.h\"\n\nnamespace xdb {\n\nheader_t::header_t(FILE* db) {\n    read_bin(0, header, sizeof(header), db);\n}\n\nhe"
  },
  {
    "path": "binding/cpp/src/header.h",
    "chars": 460,
    "preview": "#ifndef HEADER_H\n#define HEADER_H\n\n#include \"base.h\"\n\nnamespace xdb {\n\nclass header_t {\npublic:\n    header_t(FILE* db);\n"
  },
  {
    "path": "binding/cpp/src/ip.cc",
    "chars": 3256,
    "preview": "\n#include \"ip.h\"\n\nnamespace xdb {\n\nip_t::ip_t() {\n    memset(p, '\\0', sizeof(p));\n}\n\nip_t::ip_t(const ip_t& rhs, int val"
  },
  {
    "path": "binding/cpp/src/ip.h",
    "chars": 1026,
    "preview": "#ifndef IP_H\n#define IP_H\n\n#include \"base.h\"\n\nnamespace xdb {\n\nstruct ip_t {\n    unsigned char p[16];\n\n    ip_t();\n    i"
  },
  {
    "path": "binding/cpp/src/make.cc",
    "chars": 4461,
    "preview": "\n#include \"make.h\"\n\nnamespace xdb {\n\nvoid make_t::vector_index_push_back(int row, int col, const node_t &node) {\n    vec"
  },
  {
    "path": "binding/cpp/src/make.h",
    "chars": 706,
    "preview": "#ifndef MAKE_H\n#define MAKE_H\n\n#include \"ip.h\"\n\nnamespace xdb {\n\nclass make_t {\npublic:\n    make_t(const string &src, co"
  },
  {
    "path": "binding/cpp/src/search.cc",
    "chars": 3600,
    "preview": "\n#include \"search.h\"\n\nnamespace xdb {\n\nsearch_t::search_t(const string &file, int version, int p)\n    : db(fopen(file.da"
  },
  {
    "path": "binding/cpp/src/search.h",
    "chars": 923,
    "preview": "#ifndef SEARCH_H\n#define SEARCH_H\n\n#include \"header.h\"\n#include \"ip.h\"\n\nnamespace xdb {\n\nclass search_t {\nprotected:\n   "
  },
  {
    "path": "binding/cpp/test/bench.cc",
    "chars": 820,
    "preview": "\n#include \"../src/bench.h\"\n\nstd::map<int, std::string> prompt;\n\nvoid test_ipv4(int policy) {\n    std::cout << \"测试 IPv4, "
  },
  {
    "path": "binding/cpp/test/edit_v4.cc",
    "chars": 223,
    "preview": "\n#include \"../src/edit.h\"\n\nint main() {\n    std::string file_name_old = \"../../data/ipv4_source.txt\";\n    std::string fi"
  },
  {
    "path": "binding/cpp/test/edit_v6.cc",
    "chars": 223,
    "preview": "\n#include \"../src/edit.h\"\n\nint main() {\n    std::string file_name_old = \"../../data/ipv6_source.txt\";\n    std::string fi"
  },
  {
    "path": "binding/cpp/test/header.cc",
    "chars": 933,
    "preview": "\n#include \"../src/header.h\"\n\nvoid test(const std::string& prompt, const std::string& file_name) {\n    std::cout << promp"
  },
  {
    "path": "binding/cpp/test/make.cc",
    "chars": 549,
    "preview": "\n#include \"../src/make.h\"\n\nvoid test(const std::string& prompt,\n          const std::string& filename_xdb,\n          con"
  },
  {
    "path": "binding/cpp/test/search.cc",
    "chars": 1364,
    "preview": "\n#include \"../src/search.h\"\n\nstd::map<int, std::string> prompt;\n\nvoid test(xdb::search_t& s, const std::string& ip, cons"
  },
  {
    "path": "binding/csharp/.editorconfig",
    "chars": 759,
    "preview": "root = true\n\n# To learn more about .editorconfig see https://aka.ms/editorconfigdocs\n[*]\ncharset = utf-8\nindent_style = "
  },
  {
    "path": "binding/csharp/.gitignore",
    "chars": 5691,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "binding/csharp/CHANGELOG.md",
    "chars": 533,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [3.0.0] - 2025-11-22\n- 支持 .NET 10."
  },
  {
    "path": "binding/csharp/IP2Region.Net/Abstractions/ISearcher.cs",
    "chars": 1045,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/Extensions/ServiceCollectionExtensions.cs",
    "chars": 1339,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/IP2Region.Net.csproj",
    "chars": 2892,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <id>IP2Region.Net</id>\n        <version>3.0.2</version>\n "
  },
  {
    "path": "binding/csharp/IP2Region.Net/Internal/CacheStrategyFactory.cs",
    "chars": 699,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/Internal/ContentCacheStrategy.cs",
    "chars": 1047,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/Internal/FileCacheStrategy.cs",
    "chars": 2270,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/Internal/ICacheStrategy.cs",
    "chars": 538,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/Internal/VectorIndexCacheStrategy.cs",
    "chars": 824,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/XDB/CachePolicy.cs",
    "chars": 670,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/XDB/Searcher.cs",
    "chars": 4639,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/XDB/Util.cs",
    "chars": 2824,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net/XDB/XdbVersion.cs",
    "chars": 1090,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.BenchMark/Benmarks.cs",
    "chars": 2482,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.BenchMark/IP2Region.Net.BenchMark.csproj",
    "chars": 952,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <OutputType>Exe</OutputType>\n        <TargetFramework>net"
  },
  {
    "path": "binding/csharp/IP2Region.Net.BenchMark/Program.cs",
    "chars": 384,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.Test/IP2Region.Net.Test.csproj",
    "chars": 1777,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0</TargetFramework>\n\t\t<ImplicitUsings>enab"
  },
  {
    "path": "binding/csharp/IP2Region.Net.Test/SearcherTest.cs",
    "chars": 6202,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.Test/UtilTest.cs",
    "chars": 674,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.Test/XdbTest.cs",
    "chars": 1880,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/csharp/IP2Region.Net.slnx",
    "chars": 332,
    "preview": "<Solution>\n  <Folder Name=\"/Solution Items/\">\n    <File Path=\".editorconfig\" />\n    <File Path=\"README.md\" />\n  </Folder"
  },
  {
    "path": "binding/csharp/README.md",
    "chars": 3854,
    "preview": "# IP2Region.Net\n\n.NET client library for IP2Region\n\n## Installation\n\nInstall the package with [NuGet](https://www.nuget."
  },
  {
    "path": "binding/erlang/README.md",
    "chars": 3231,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region erlang query client\n\n### Introduction\n\nT"
  },
  {
    "path": "binding/erlang/README_zh.md",
    "chars": 2715,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region erlang 查询客户端\n\n### 简介\n该bingding以erlang语言实"
  },
  {
    "path": "binding/erlang/benchmarks/xdb-benchmark.sh",
    "chars": 104,
    "preview": "#!/bin/bash\n\ncd ..\n\nrebar3 shell --eval=\"xdb_benchmark:main(\\\"../../data/ip.merge.txt\\\"), init:stop().\"\n"
  },
  {
    "path": "binding/erlang/include/ip2region.hrl",
    "chars": 622,
    "preview": "-ifndef(IP2REGION_HRL).\n-define(IP2REGION_HRL, true).\n\n-define(NONE, none).\n-define(APP_NAME, ip2region).\n\n-define(XDB_V"
  },
  {
    "path": "binding/erlang/priv/dummy",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "binding/erlang/rebar.config",
    "chars": 395,
    "preview": "{erl_opts, [\n    debug_info, \n    export_all, \n    nowarn_export_all\n]}.\n\n{plugins, [rebar3_hex, rebar3_ex_doc]}.\n\n{deps"
  },
  {
    "path": "binding/erlang/src/ip2region.app.src",
    "chars": 423,
    "preview": "{application, ip2region,\n [{description, \"ip2region xdb client application\"},\n  {vsn, \"0.1.0\"},\n  {registered, []},\n  {m"
  },
  {
    "path": "binding/erlang/src/ip2region_app.erl",
    "chars": 529,
    "preview": "%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All righ"
  },
  {
    "path": "binding/erlang/src/ip2region_sup.erl",
    "chars": 2001,
    "preview": "%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All righ"
  },
  {
    "path": "binding/erlang/src/ip2region_util.erl",
    "chars": 758,
    "preview": "%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All righ"
  },
  {
    "path": "binding/erlang/src/ip2region_worker.erl",
    "chars": 6128,
    "preview": "%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All righ"
  },
  {
    "path": "binding/erlang/src/xdb.erl",
    "chars": 1065,
    "preview": "\n%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All rig"
  },
  {
    "path": "binding/erlang/src/xdb_benchmark.erl",
    "chars": 2489,
    "preview": "%%%-------------------------------------------------------------------\n%% Copyright 2022 The Ip2Region Authors. All righ"
  },
  {
    "path": "binding/erlang/test/xdb_test.erl",
    "chars": 469,
    "preview": "-module(xdb_test).\n\n-include_lib(\"eunit/include/eunit.hrl\").\n\nsearch_test_() ->\n    application:ensure_started(ip2region"
  },
  {
    "path": "binding/golang/Makefile",
    "chars": 165,
    "preview": "# ip2region golang binding makefile\nall: build\n.PHONY: all\n\nbuild:\n\tgo build -o xdb_searcher\ntest:\n\tgo test -v ./...\ncle"
  },
  {
    "path": "binding/golang/README.md",
    "chars": 11039,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region golang query client\n\n# Usage\n\n### packag"
  },
  {
    "path": "binding/golang/README_zh.md",
    "chars": 7393,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region golang 查询客户端\n\n# 使用方式\n\n### package 获取\n```"
  },
  {
    "path": "binding/golang/go.mod",
    "chars": 112,
    "preview": "module github.com/lionsoul2014/ip2region/binding/golang\n\ngo 1.17\n\nrequire github.com/mitchellh/go-homedir v1.1.0"
  },
  {
    "path": "binding/golang/go.sum",
    "chars": 181,
    "preview": "github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v"
  },
  {
    "path": "binding/golang/main.go",
    "chars": 9611,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/config.go",
    "chars": 3429,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/config_test.go",
    "chars": 901,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/ip2region.go",
    "chars": 4608,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/ip2region_test.go",
    "chars": 6831,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/searcher_pool.go",
    "chars": 2292,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/service/searcher_pool_test.go",
    "chars": 1818,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/xdb/header.go",
    "chars": 1958,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/xdb/searcher.go",
    "chars": 5460,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/xdb/util.go",
    "chars": 6345,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/xdb/util_test.go",
    "chars": 2265,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/golang/xdb/version.go",
    "chars": 2252,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/README.md",
    "chars": 17162,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region java Query Client\n\n# Usage\n\n### Maven Re"
  },
  {
    "path": "binding/java/README_zh.md",
    "chars": 12100,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region java 查询客户端\n\n# 使用方式\n\n### maven 仓库:\n```xml"
  },
  {
    "path": "binding/java/pom.xml",
    "chars": 7897,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/SearcherTest.java",
    "chars": 12184,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/service/Config.java",
    "chars": 3562,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/service/ConfigBuilder.java",
    "chars": 4586,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/service/InvalidConfigException.java",
    "chars": 173,
    "preview": "package org.lionsoul.ip2region.service;\n\npublic class InvalidConfigException extends Exception {\n    public InvalidConfi"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/service/Ip2Region.java",
    "chars": 5730,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/service/SearcherPool.java",
    "chars": 3640,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java",
    "chars": 1513,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv4.java",
    "chars": 1427,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/IPv6.java",
    "chars": 755,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/InetAddressException.java",
    "chars": 340,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/LittleEndian.java",
    "chars": 2080,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/Log.java",
    "chars": 3189,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/LongByteArray.java",
    "chars": 5466,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java",
    "chars": 14444,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/Util.java",
    "chars": 3471,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/Version.java",
    "chars": 2748,
    "preview": "// Copyright 2025 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/main/java/org/lionsoul/ip2region/xdb/XdbException.java",
    "chars": 324,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/service/ConfigTest.java",
    "chars": 4230,
    "preview": "package org.lionsoul.ip2region.service;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;"
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/service/Ip2RegionTest.java",
    "chars": 7299,
    "preview": "package org.lionsoul.ip2region.service;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io.IOException;\nimpor"
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/service/SearcherPoolTest.java",
    "chars": 2946,
    "preview": "package org.lionsoul.ip2region.service;\n\nimport org.junit.Test;\nimport org.lionsoul.ip2region.xdb.Log;\nimport org.lionso"
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/xdb/BufferTest.java",
    "chars": 3395,
    "preview": "package org.lionsoul.ip2region.xdb;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nim"
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/xdb/IPv4Test.java",
    "chars": 1437,
    "preview": "package org.lionsoul.ip2region.xdb;\n\nimport org.junit.Test;\n\npublic class IPv4Test {\n\n    private static final Log log ="
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/xdb/LittleEndianTest.java",
    "chars": 1126,
    "preview": "package org.lionsoul.ip2region.xdb;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\npublic class "
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/xdb/UtilTest.java",
    "chars": 2033,
    "preview": "package org.lionsoul.ip2region.xdb;\n\nimport org.junit.Test;\n\npublic class UtilTest {\n\n    private static final Log log ="
  },
  {
    "path": "binding/java/src/test/java/org/lionsoul/ip2region/xdb/VersionTest.java",
    "chars": 1326,
    "preview": "package org.lionsoul.ip2region.xdb;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\npublic class "
  },
  {
    "path": "binding/javascript/README.md",
    "chars": 9413,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region JavaScript Query Client\n\n# Usage\n\n### In"
  },
  {
    "path": "binding/javascript/README_zh.md",
    "chars": 6867,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region javascript 查询客户端\n\n# 使用方式\n\n### 安装 `ip2reg"
  },
  {
    "path": "binding/javascript/index.d.ts",
    "chars": 3128,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/index.js",
    "chars": 908,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/package.json",
    "chars": 1117,
    "preview": "{\n  \"name\": \"ip2region.js\",\n  \"version\": \"3.1.8\",\n  \"description\": \"official javascript binding for ip2region with both "
  },
  {
    "path": "binding/javascript/searcher.js",
    "chars": 4914,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/tests/bench.app.js",
    "chars": 3876,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/tests/search.app.js",
    "chars": 3462,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/tests/searcher.test.js",
    "chars": 4528,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/tests/util.test.js",
    "chars": 3839,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/javascript/tsconfig.json",
    "chars": 509,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ES2020\",\n    \"moduleResolution\": \"node\",\n    \"allowJs\": "
  },
  {
    "path": "binding/javascript/util.js",
    "chars": 11234,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua/README.md",
    "chars": 9727,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region lua query client\n\n#### Note: Please prio"
  },
  {
    "path": "binding/lua/README_zh.md",
    "chars": 6818,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region lua 查询客户端\n\n#### 备注:请优先使用 lua_c 扩展 xdb 查询"
  },
  {
    "path": "binding/lua/bench_test.lua",
    "chars": 5598,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua/search_test.lua",
    "chars": 4465,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua/util_test.lua",
    "chars": 5340,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua/xdb_searcher.lua",
    "chars": 19003,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua_c/Makefile",
    "chars": 510,
    "preview": "LuaVersion ?= 5.4\nLIB_DIR = /usr/local/share/lua/$(LuaVersion)\n\nall: ../c/xdb_api.h ../c/xdb_util.c ../c/xdb_searcher.c "
  },
  {
    "path": "binding/lua_c/README.md",
    "chars": 10565,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region lua c extension query client\n\n# Version "
  },
  {
    "path": "binding/lua_c/README_zh.md",
    "chars": 7387,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region lua c 扩展查询客户端\n\n# 版本兼容\n该实现兼容 lua `5.1`,`5"
  },
  {
    "path": "binding/lua_c/bench_test.lua",
    "chars": 5653,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua_c/search_test.lua",
    "chars": 4615,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua_c/util_test.lua",
    "chars": 4807,
    "preview": "-- Copyright 2022 The Ip2Region Authors. All rights reserved.\n-- Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/lua_c/xdb_searcher.c",
    "chars": 22728,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-styl"
  },
  {
    "path": "binding/nginx/Dockerfile",
    "chars": 1287,
    "preview": "ARG NGINX_VERSION=1.29.6\nFROM nginx:${NGINX_VERSION} AS build\nARG NGINX_VERSION\n\n# prepare the build environment\nRUN apt"
  },
  {
    "path": "binding/nginx/README.md",
    "chars": 2654,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# nginx-ip2region\n\n## build\n\n```shell\n$ mkdir -p wor"
  },
  {
    "path": "binding/nginx/README_zh.md",
    "chars": 2504,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# nginx-ip2region\n\n## build\n\n```shell\n$ mkdir -p wor"
  },
  {
    "path": "binding/nginx/config",
    "chars": 756,
    "preview": "ngx_addon_name=ngx_http_ip2region_module\n\nNGX_HTTP_IP2REGION_SRCS=\"                                                     "
  },
  {
    "path": "binding/nginx/src/ngx_http_ip2region_module.c",
    "chars": 19361,
    "preview": "/*\n * Created by Wu Jian Ping on - 2023/03/30.\n */\n\n#include \"ngx_http_ip2region_module.h\"\n\nstatic ngx_int_t ngx_http_ip"
  },
  {
    "path": "binding/nginx/src/ngx_http_ip2region_module.h",
    "chars": 690,
    "preview": "/*\n * Created by Wu Jian Ping on - 2023/03/30.\n */\n\n#ifndef __NGX_HTTP_IP2REGION_MODULE_H_INCLUDED__\n#define __NGX_HTTP_"
  },
  {
    "path": "binding/nginx/t/http_ip2region.t",
    "chars": 375,
    "preview": "use lib 'lib';\nuse Test::Nginx::Socket; # 'no_plan';\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 124;\n\nno_long_strin"
  },
  {
    "path": "binding/nodejs/README.md",
    "chars": 282,
    "preview": "# :cn: [中文简体]\n\n# ip2region nodejs 查询客户端\n\n请使用最新的 IPv6 兼容的 javascript binding:[javascript binding](../javascript/)\n\n---\n\n#"
  },
  {
    "path": "binding/php/README.md",
    "chars": 9313,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region PHP Query Client\n\n# Usage\n\n### About Que"
  },
  {
    "path": "binding/php/README_zh.md",
    "chars": 6821,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region php 查询客户端\n\n# 使用方式\n\n### 关于查询 API\n查询 API 的"
  },
  {
    "path": "binding/php/batch_test.php",
    "chars": 4318,
    "preview": "<?php\n// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2."
  },
  {
    "path": "binding/php/bench_test.php",
    "chars": 5715,
    "preview": "<?php\n// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2."
  },
  {
    "path": "binding/php/search_test.php",
    "chars": 4216,
    "preview": "<?php\n// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2."
  },
  {
    "path": "binding/php/xdb/Searcher.class.php",
    "chars": 17205,
    "preview": "<?php\n// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2."
  },
  {
    "path": "binding/php/xdb/util_test.php",
    "chars": 3409,
    "preview": "<?php\n// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2."
  },
  {
    "path": "binding/python/.gitignore",
    "chars": 332,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# Distribution / packaging\n.Python\nbuild/\ndev"
  },
  {
    "path": "binding/python/LICENSE",
    "chars": 12640,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "binding/python/MANIFEST.in",
    "chars": 104,
    "preview": "include LICENSE\ninclude ReadMe.md\ninclude search_test.py\ninclude bench_test.py\nrecursive-include tests\n"
  },
  {
    "path": "binding/python/README.md",
    "chars": 9113,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region python query client\n\n# Version Compatibi"
  },
  {
    "path": "binding/python/README_zh.md",
    "chars": 6606,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n# ip2region python 查询客户端\n\n# 版本兼容\n该实现兼容 Python `>=` *"
  },
  {
    "path": "binding/python/bench_test.py",
    "chars": 4133,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/python/ip2region/__init__.py",
    "chars": 184,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/python/ip2region/searcher.py",
    "chars": 4981,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/python/ip2region/util.py",
    "chars": 7260,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/python/search_test.py",
    "chars": 3391,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/python/setup.py",
    "chars": 1049,
    "preview": "import setuptools\n\nsetuptools.setup(\n    name=\"py-ip2region\",\n    version=\"3.0.4\",\n    description=\"ip2region official "
  },
  {
    "path": "binding/python/util_test.py",
    "chars": 6057,
    "preview": "# Copyright 2022 The Ip2Region Authors. All rights reserved.\n# Use of this source code is governed by a Apache2.0-style"
  },
  {
    "path": "binding/rust/Cargo.toml",
    "chars": 62,
    "preview": "[workspace]\nresolver = \"2\"\nmembers = [\"example\", \"ip2region\"]\n"
  },
  {
    "path": "binding/rust/README.md",
    "chars": 7987,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n## `ip2region rust` Query Client\n\n## Features\n\n* Sup"
  },
  {
    "path": "binding/rust/README_zh.md",
    "chars": 6890,
    "preview": ":globe_with_meridians: [中文简体](README_zh.md) | [English](README.md)\n\n## `ip2region rust` 查询客户端\n\n## Features\n- 支持`ip`字符串和`"
  },
  {
    "path": "binding/rust/example/Cargo.toml",
    "chars": 438,
    "preview": "[package]\nname = \"searcher\"\ndefault-run = \"searcher\"\nversion = \"0.2.0\"\nedition = \"2024\"\nrust-version = \"1.89.0\"\ndescript"
  },
  {
    "path": "binding/rust/example/src/cmd.rs",
    "chars": 1161,
    "preview": "use clap::{Parser, Subcommand, ValueEnum};\n\n/// Rust binding example for ip2region\n///\n/// e.g\n///\n/// ```\n///\n/// expor"
  },
  {
    "path": "binding/rust/example/src/main.rs",
    "chars": 3614,
    "preview": "use std::fs::File;\nuse std::io::Write;\nuse std::io::{BufRead, BufReader};\nuse std::net::IpAddr;\nuse std::str::FromStr;\nu"
  },
  {
    "path": "binding/rust/ip2region/Cargo.toml",
    "chars": 444,
    "preview": "[package]\nname = \"ip2region\"\nversion = \"0.2.1\"\nedition = \"2024\"\nrust-version = \"1.89.0\"\ndescription = \"The rust binding "
  },
  {
    "path": "binding/rust/ip2region/benches/search.rs",
    "chars": 2053,
    "preview": "use std::net::Ipv6Addr;\nuse std::ops::Range;\nuse std::str::FromStr;\n\nuse criterion::{Criterion, criterion_group, criteri"
  },
  {
    "path": "binding/rust/ip2region/src/error.rs",
    "chars": 768,
    "preview": "#[derive(Debug, thiserror::Error)]\npub enum Ip2RegionError {\n    #[error(\"Io error: {0}\")]\n    IoError(#[from] std::io::"
  },
  {
    "path": "binding/rust/ip2region/src/ip_value.rs",
    "chars": 1500,
    "preview": "use std::borrow::Cow;\nuse std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\nuse std::str::FromStr;\n\nuse crate::error::{Ip2RegionEr"
  },
  {
    "path": "binding/rust/ip2region/src/lib.rs",
    "chars": 113,
    "preview": "mod error;\nmod ip_value;\nmod searcher;\n\npub use ip_value::IpValueExt;\npub use searcher::{CachePolicy, Searcher};\n"
  },
  {
    "path": "binding/rust/ip2region/src/searcher.rs",
    "chars": 9273,
    "preview": "use std::borrow::Cow;\nuse std::fmt::Display;\nuse std::fs::File;\nuse std::io::{Read, Seek, SeekFrom};\nuse std::net::IpAdd"
  },
  {
    "path": "binding/typescript/README.md",
    "chars": 294,
    "preview": "# :cn: [中文简体]\n\n# ip2region typescript xdb 查询客户端\n\n请使用最新的 IPv6 兼容的 javascript binding:[javascript binding](../javascript/)"
  },
  {
    "path": "data/sample/github-issue-196.fix",
    "chars": 1849,
    "preview": "39.144.0.0|39.144.0.255|中国|山东省|菏泽市|移动\n39.144.1.0|39.144.9.255|中国|0|0|移动\n39.144.10.0|39.144.12.255|中国|北京|北京|移动\n39.144.13."
  },
  {
    "path": "data/sample/github-issue-200.fix",
    "chars": 1192,
    "preview": "112.224.0.0|112.224.63.255|中国|山东省|济南市|联通\n112.224.64.0|112.224.64.255|中国|山东省|青岛市|联通\n112.224.65.0|112.224.65.255|中国|山东省|0|"
  },
  {
    "path": "data/sample/github-issue-243.fix",
    "chars": 6653,
    "preview": "36.132.128.0|36.132.147.255|中国|黑龙江省|哈尔滨市|移动\n36.132.148.0|36.132.150.255|中国|黑龙江省|齐齐哈尔市|移动\n36.132.151.0|36.132.255.255|中国|"
  },
  {
    "path": "data/sample/github-issue-287.bug",
    "chars": 352,
    "preview": "# report by https://github.com/lionsoul2014/ip2region/issues/287\n# triggered the bug of getInt2\n193.150.116.0|193.150.11"
  },
  {
    "path": "data/sample/ip.test.txt",
    "chars": 871,
    "preview": "1.0.0.0|1.0.0.255|澳大利亚|0|0|0\n1.0.1.0|1.0.3.255|中国|福建省|福州市|电信\n1.0.4.0|1.0.7.255|澳大利亚|维多利亚|墨尔本|0\n1.0.8.0|1.0.15.255|中国|广东省"
  },
  {
    "path": "data/sample/segments.tests",
    "chars": 202,
    "preview": "192.168.2.1|192.168.2.20|0|0|内网IP|办公室A\n192.168.2.21|192.168.2.30|0|0|内网IP|办公室A\n192.168.2.31|192.168.2.60|0|0|内网IP|办公室B\n1"
  },
  {
    "path": "data/sample/segments.tests.mixed",
    "chars": 527,
    "preview": "192.168.2.1|192.168.2.20|0|0|内网IP|办公室A\n192.168.2.21|192.168.2.30|0|0|内网IP|办公室A\n192.168.2.31|192.168.2.60|0|0|内网IP|办公室B\n1"
  },
  {
    "path": "maker/c/ReadMe.md",
    "chars": 52,
    "preview": "# ip2region xdb c语言生成实现\n\n# 数据生成\n\n# 数据查询\n\n# bench 测试\n"
  },
  {
    "path": "maker/cpp/README.md",
    "chars": 259,
    "preview": "# :cn: [中文简体]\n\n1. [生成 xdb 文件](../../binding/cpp#4-生成-xdb-文件)\n2. [原始数据编辑](../../binding/cpp#5-原始数据编辑)\n\n# :globe_with_meri"
  },
  {
    "path": "maker/csharp/.gitignore",
    "chars": 340,
    "preview": "*.swp\n*.*~\nproject.lock.json\n.DS_Store\n*.pyc\nnupkg/\n\n# Visual Studio Code\n.vscode\n\n# Rider\n.idea\n\n# User-specific files\n"
  },
  {
    "path": "maker/csharp/IP2RegionMaker/IP2RegionMaker.csproj",
    "chars": 239,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</Targe"
  },
  {
    "path": "maker/csharp/IP2RegionMaker/Program.cs",
    "chars": 1587,
    "preview": "using IP2RegionMaker.XDB;\nusing System.Diagnostics;\n\n\nstring srcFile = \"\", dstFile = \"\";\nIndexPolicy indexPolicy = Inde"
  },
  {
    "path": "maker/csharp/IP2RegionMaker/Properties/PublishProfiles/FolderProfile.pubxml",
    "chars": 371,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project>\n  <PropertyGr"
  },
  {
    "path": "maker/csharp/IP2RegionMaker/XDB/IndexPolicy.cs",
    "chars": 253,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nna"
  },
  {
    "path": "maker/csharp/IP2RegionMaker/XDB/Maker.cs",
    "chars": 9095,
    "preview": "// Copyright 2022 The Ip2Region Authors. All rights reserved.\n// Use of this source code is governed by a Apache2.0-sty"
  }
]

// ... and 72 more files (download for full content)

About this extraction

This page contains the full source code of the lionsoul2014/ip2region GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 272 files (149.0 MB), approximately 281.0k tokens, and a symbol index with 980 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!