# xdb-parse
A high-performance, zero-copy Rust library for querying xdb IP geolocation databases (ip2region-compatible). Supports IPv4 and IPv6 via an optimized two-level binary search.
[中文文档](README_zh.md)
## Features
- **Dual Protocol**: Full IPv4 and IPv6 support
- **Blazing Fast**: ~258ns per IPv4 `search_ip(u32)` lookup, ~527ns per `search_ip(&str)` (all-segment IP pool)
- **Zero-copy**: Returns `&str` borrowing from the loaded data — no heap allocation per query
- **Raw integer API**: `search_by_uint` / `search_by_u128` skip string parsing entirely
- **Thread Safe**: `Send + Sync`, load once and share via `Arc`
## Installation
```toml
[dependencies]
xdb-parse = "0.2.0"
```
## Quick Start
```rust
use xdb_parse::{load_file, search_ip};
fn main() -> anyhow::Result<()> {
let data = load_file("./assets/ip2region_v4.xdb".into())?;
let location = search_ip("73.24.63.66", &data)?;
println!("{}", location);
Ok(())
}
```
## API Reference
### Loading
```rust
pub fn load_file(path: PathBuf) -> Result<Vec<u8>, XdbError>
```
Pre-allocates the buffer with the exact file size.
### Searching
```rust
// Search by IP string (dotted-decimal, colon-hex, or numeric)
pub fn search_ip(ip: &str, data: &[u8]) -> Result<&str, XdbError>
// Search by raw u32 (IPv4) — fastest path
pub fn search_by_uint(ip: u32, data: &[u8]) -> Result<&str, XdbError>
// Search by raw u128 (IPv6) — fastest path
pub fn search_by_u128(ip: u128, data: &[u8]) -> Result<&str, XdbError>
// Search by std::net::IpAddr
pub fn search_by_ipaddr(ip: IpAddr, data: &[u8]) -> Result<&str, XdbError>
```
All search functions return `&str` borrowing from the loaded data — zero allocation per query.
### Supported IP Formats
```rust
search_ip("192.168.1.1", &data)?; // dotted-decimal IPv4
search_ip("2001:0db8:85a3::0370:7334", &data)?; // colon-hex IPv6
search_ip("3232235777", &data)?; // numeric IPv4
search_ip("42540766411282592856903984951653826560", &data)?; // numeric IPv6
```
### Header & Version Detection
```rust
use xdb_parse::{Header, IpVersion, detect_version};
let data = load_file("./assets/ip2region_v4.xdb".into())?;
let version = detect_version(&data)?;
assert_eq!(version, IpVersion::V4);
let header = Header::parse(&data)?;
println!("xdb v{}, generated at {}", header.version, header.generation_time);
```
## Multi-threaded Usage
```rust
use std::sync::Arc;
use xdb_parse::{load_file, search_ip};
let data = Arc::new(load_file("./assets/ip2region_v6.xdb".into())?);
let data_clone = Arc::clone(&data);
println!("{}", result);
}).join().unwrap();
```
## Performance
Measured with all-segment IP pool (524K IPv4 / 704K IPv6), criterion release profile:
| IPv4 `search_by_uint` | ~272 ns |
| IPv4 `search_ip(u32)` | ~258 ns |
| IPv4 `search_ip(&str)` | ~527 ns |
| IPv6 `search_ip(&str)` | ~476 ns |
Run benchmarks:
```bash
cargo bench
```
## Database File Format
```
┌─────────────────┐
│ Header │ 256 bytes
├─────────────────┤
│ Vector Index │ 256×256×8 bytes
├─────────────────┤
│ Segment Data │ Variable length
└─────────────────┘
```
IPv4 segments: 14 bytes (`u32` start, `u32` end, `u16` data_len, `u32` data_ptr).
IPv6 segments: 38 bytes (`u128` start, `u128` end, `u16` data_len, `u32` data_ptr).
## License
MIT OR Apache-2.0