xdb-parse 0.2.0

High-performance, zero-copy xdb IP geolocation parser (ip2region-compatible)
Documentation

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.

中文文档

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

[dependencies]
xdb-parse = "0.2.0"

Quick Start

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

pub fn load_file(path: PathBuf) -> Result<Vec<u8>, XdbError>

Pre-allocates the buffer with the exact file size.

Searching

// 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

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

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

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);
std::thread::spawn(move || {
    let result = search_ip("2408:8352:da10:1ad:c283:c9ff:fec6:4046", &data_clone).unwrap();
    println!("{}", result);
}).join().unwrap();

Performance

Measured with all-segment IP pool (524K IPv4 / 704K IPv6), criterion release profile:

Scenario Time
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:

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