Merge pull request #57 from biluohc/master
Rust: 0.2.0, use std::net::Ipaddr and update Error.
This commit is contained in:
commit
af12f97152
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ip2region"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["biluohc <biluohc@qq.com>"]
|
||||
|
||||
include = ["./*", "../../data/ip2region.db", "../../Cargo.toml"]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,19 @@ fn lazy() {
|
|||
let res = memory_search(ip);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("lazy__ {:06} microseconds: {:?}", end, res);
|
||||
|
||||
let start = Instant::now();
|
||||
let ip_addr = ip.parse().unwrap();
|
||||
let res2 = memory_search_ip(&ip_addr);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("lazy__ {:06} microseconds: {:?}", end, res2);
|
||||
|
||||
if res.is_ok() && res2.is_ok() {
|
||||
assert_eq!(res.unwrap(), res2.unwrap());
|
||||
} else if res.is_err() && res2.is_err() {
|
||||
} else {
|
||||
panic!("not EQ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,21 +51,64 @@ fn overview() {
|
|||
let ip2o = ip2.to_owned().unwrap();
|
||||
|
||||
for ip in IPS {
|
||||
// mem
|
||||
let start = Instant::now();
|
||||
let res = ip2o.memory_search(ip);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("memory {:06} microseconds: {:?}", end, res);
|
||||
|
||||
let start = Instant::now();
|
||||
let ip_addr = ip.parse().unwrap();
|
||||
let res2 = ip2o.memory_search_ip(&ip_addr);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("memory {:06} microseconds: {:?}", end, res2);
|
||||
|
||||
if res.is_ok() && res2.is_ok() {
|
||||
assert_eq!(res.unwrap(), res2.unwrap());
|
||||
} else if res.is_err() && res2.is_err() {
|
||||
} else {
|
||||
panic!("not EQ")
|
||||
}
|
||||
|
||||
// binary
|
||||
let start = Instant::now();
|
||||
let res = ip2.binary_search(ip);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("binary {:06} microseconds: {:?}", end, res);
|
||||
|
||||
let start = Instant::now();
|
||||
let ip_addr = ip.parse().unwrap();
|
||||
let res2 = ip2.binary_search_ip(&ip_addr);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("binary {:06} microseconds: {:?}", end, res2);
|
||||
|
||||
if res.is_ok() && res2.is_ok() {
|
||||
assert_eq!(res.unwrap(), res2.unwrap());
|
||||
} else if res.is_err() && res2.is_err() {
|
||||
} else {
|
||||
panic!("not EQ")
|
||||
}
|
||||
|
||||
// btree
|
||||
let start = Instant::now();
|
||||
let res = ip2.btree_search(ip);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("btree {:06} microseconds: {:?}", end, res);
|
||||
|
||||
let start = Instant::now();
|
||||
let ip_addr = ip.parse().unwrap();
|
||||
let res2 = ip2.btree_search_ip(&ip_addr);
|
||||
let end = start.elapsed().subsec_micros();
|
||||
println!("btree_ {:06} microseconds: {:?}", end, res2);
|
||||
|
||||
if res.is_ok() && res2.is_ok() {
|
||||
assert_eq!(res.unwrap(), res2.unwrap());
|
||||
} else if res.is_err() && res2.is_err() {
|
||||
} else {
|
||||
panic!("not EQ")
|
||||
}
|
||||
|
||||
// \n
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
## 用法
|
||||
|
||||
都在 `src/example` 里
|
||||
Demo: 在 `example` 目录里
|
||||
|
||||
另外 `cargo doc --features lazy` 可以看到所有 `API`。
|
||||
API文档: `cargo doc --features lazy --open` 可以看到所有。
|
||||
|
||||
运行测试: `cargo test --features lazy`
|
||||
|
||||
### 添加依赖
|
||||
|
||||
|
|
@ -20,11 +22,11 @@ version = "*"
|
|||
```
|
||||
|
||||
### 代码
|
||||
查看 `src/example/src/main.rs`
|
||||
查看 `example/src/main.rs`
|
||||
|
||||
### `lazy` feature 把 DB 直接打包进二进制
|
||||
|
||||
取消上面 toml 的 `# features = ["lazy"]` 行的注释即可使用,其 api 是 `memory_search`。
|
||||
取消上面 toml 的 `# features = ["lazy"]` 行的注释即可使用,其 api 是 `memory_search` 和 `memory_search_ip`。
|
||||
|
||||
只是目前 DB 足有3.2M,还是有些感人的。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,31 @@
|
|||
use std::{self, io, num, str};
|
||||
use std::{self, io, net, str};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Str(&'static str),
|
||||
Io(io::Error),
|
||||
Utf8(str::Utf8Error),
|
||||
Int(num::ParseIntError),
|
||||
Addr(net::AddrParseError),
|
||||
/// `224.0.0.0` ~ `239.255.255.255`
|
||||
///
|
||||
// `ff00::/8`
|
||||
IpIsMulticast,
|
||||
/// `0.0.0.0`
|
||||
IpIsUnspecified,
|
||||
/// `127.0.0.0/8`
|
||||
IpIsLoopback,
|
||||
///1. `10.0.0.0/8`
|
||||
///
|
||||
///2. `172.16.0.0/12`
|
||||
///
|
||||
///3. `192.168.0.0/16`
|
||||
IpIsPrivate,
|
||||
/// Unsupport Ipv6 Now
|
||||
UnsupportIpv6,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(e: &'static str) -> Self {
|
||||
Error::Str(e)
|
||||
}
|
||||
}
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
|
|
@ -25,8 +36,9 @@ impl From<str::Utf8Error> for Error {
|
|||
Error::Utf8(e)
|
||||
}
|
||||
}
|
||||
impl From<num::ParseIntError> for Error {
|
||||
fn from(e: num::ParseIntError) -> Self {
|
||||
Error::Int(e)
|
||||
|
||||
impl From<net::AddrParseError> for Error {
|
||||
fn from(e: net::AddrParseError) -> Self {
|
||||
Error::Addr(e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn memory_search(ip_str: &str) -> Result<IpInfo> {
|
||||
pub fn memory_search<S: AsRef<str>>(ip_str: S) -> Result<IpInfo<'static>> {
|
||||
OWNED_IP_2_REGION.memory_search(ip_str)
|
||||
}
|
||||
|
||||
pub fn memory_search_ip(ip_addr: &IpAddr) -> Result<IpInfo> {
|
||||
OWNED_IP_2_REGION.memory_search_ip(ip_addr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ extern crate lazy_static;
|
|||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, SeekFrom};
|
||||
use std::net::IpAddr;
|
||||
use std::{fmt, str};
|
||||
|
||||
mod db;
|
||||
|
|
@ -19,7 +20,7 @@ pub use owned::{OwnedIp2Region, OwnedIpInfo};
|
|||
#[cfg(feature = "lazy")]
|
||||
use db::DB_BYTES;
|
||||
#[cfg(feature = "lazy")]
|
||||
pub use owned::memory_search;
|
||||
pub use owned::{memory_search, memory_search_ip};
|
||||
|
||||
const INDEX_BLOCK_LENGTH: u32 = 12;
|
||||
const TOTAL_HEADER_LENGTH: usize = 8192;
|
||||
|
|
@ -89,20 +90,34 @@ fn get_u32(bytes: &[u8], offset: u32) -> u32 {
|
|||
tmp as u32
|
||||
}
|
||||
|
||||
fn ip2u32(ip_str: &str) -> Result<u32> {
|
||||
let bits = ip_str
|
||||
.split('.')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
if bits.len() != 4 {
|
||||
Err("ip format error(it does not have 4 parts, like 1.1.1.1)")?;
|
||||
fn ip2u32(ip: &IpAddr) -> Result<u32> {
|
||||
if ip.is_ipv6() {
|
||||
return Err(Error::UnsupportIpv6);
|
||||
}
|
||||
let mut sum: u32 = 0;
|
||||
for (i, n) in bits.iter().enumerate() {
|
||||
let bit = n.parse::<u32>()?;
|
||||
sum += bit << 24 - 8 * i;
|
||||
if ip.is_unspecified() {
|
||||
return Err(Error::IpIsUnspecified);
|
||||
}
|
||||
if ip.is_loopback() {
|
||||
return Err(Error::IpIsLoopback);
|
||||
}
|
||||
if ip.is_multicast() {
|
||||
return Err(Error::IpIsMulticast);
|
||||
}
|
||||
|
||||
match ip {
|
||||
IpAddr::V4(v4) => {
|
||||
if v4.is_private() {
|
||||
return Err(Error::IpIsPrivate);
|
||||
}
|
||||
|
||||
let mut sum: u32 = 0;
|
||||
for (i, n) in v4.octets().iter().enumerate() {
|
||||
sum += (*n as u32) << 24 - 8 * i;
|
||||
}
|
||||
return Ok(sum);
|
||||
}
|
||||
IpAddr::V6(_v6) => unreachable!(),
|
||||
}
|
||||
Ok(sum)
|
||||
}
|
||||
|
||||
pub struct Ip2Region {
|
||||
|
|
@ -138,7 +153,11 @@ impl Ip2Region {
|
|||
OwnedIp2Region::new2(&mut self.db_file).map_err(Error::Io)
|
||||
}
|
||||
|
||||
pub fn binary_search(&mut self, ip_str: &str) -> Result<OwnedIpInfo> {
|
||||
pub fn binary_search<S: AsRef<str>>(&mut self, ip_str: S) -> Result<OwnedIpInfo> {
|
||||
let ip = ip_str.as_ref().parse::<IpAddr>()?;
|
||||
self.binary_search_ip(&ip)
|
||||
}
|
||||
pub fn binary_search_ip(&mut self, ip_addr: &IpAddr) -> Result<OwnedIpInfo> {
|
||||
BUF.with(|buf| {
|
||||
let mut buf = buf.borrow_mut();
|
||||
|
||||
|
|
@ -150,7 +169,7 @@ impl Ip2Region {
|
|||
self.total_blocks =
|
||||
(self.last_index_ptr - self.first_index_ptr) / INDEX_BLOCK_LENGTH + 1;
|
||||
}
|
||||
let ip = ip2u32(ip_str)?;
|
||||
let ip = ip2u32(ip_addr)?;
|
||||
let mut h = self.total_blocks;
|
||||
let (mut data_ptr, mut l) = (0u32, 0u32);
|
||||
while l <= h {
|
||||
|
|
@ -173,7 +192,7 @@ impl Ip2Region {
|
|||
}
|
||||
}
|
||||
if data_ptr == 0 {
|
||||
Err("not found")?;
|
||||
Err(Error::NotFound)?;
|
||||
}
|
||||
|
||||
let data_len = (data_ptr >> 24) & 0xff;
|
||||
|
|
@ -189,7 +208,11 @@ impl Ip2Region {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn btree_search(&mut self, ip_str: &str) -> Result<OwnedIpInfo> {
|
||||
pub fn btree_search<S: AsRef<str>>(&mut self, ip_str: S) -> Result<OwnedIpInfo> {
|
||||
let ip = ip_str.as_ref().parse::<IpAddr>()?;
|
||||
self.btree_search_ip(&ip)
|
||||
}
|
||||
pub fn btree_search_ip(&mut self, ip_addr: &IpAddr) -> Result<OwnedIpInfo> {
|
||||
BUF_BTREE.with(|buf| {
|
||||
let mut buf = buf.borrow_mut();
|
||||
|
||||
|
|
@ -212,7 +235,7 @@ impl Ip2Region {
|
|||
self.header_len = idx
|
||||
}
|
||||
|
||||
let ip = ip2u32(ip_str)?;
|
||||
let ip = ip2u32(ip_addr)?;
|
||||
let mut h = self.header_len;
|
||||
let (mut sptr, mut eptr, mut l) = (0u32, 0u32, 0u32);
|
||||
|
||||
|
|
@ -257,7 +280,7 @@ impl Ip2Region {
|
|||
}
|
||||
|
||||
if sptr == 0 {
|
||||
Err("not found")?;
|
||||
Err(Error::NotFound)?;
|
||||
}
|
||||
let block_len = eptr - sptr;
|
||||
self.db_file.seek(SeekFrom::Start(sptr as u64))?;
|
||||
|
|
@ -285,7 +308,7 @@ impl Ip2Region {
|
|||
}
|
||||
}
|
||||
if data_ptr == 0 {
|
||||
Err("not found")?;
|
||||
Err(Error::NotFound)?;
|
||||
}
|
||||
|
||||
let data_len = (data_ptr >> 24) & 0xff;
|
||||
|
|
|
|||
|
|
@ -65,8 +65,13 @@ impl OwnedIp2Region {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn memory_search(&self, ip_str: &str) -> Result<IpInfo> {
|
||||
let ip = ip2u32(ip_str)?;
|
||||
pub fn memory_search<S: AsRef<str>>(&self, ip_str: S) -> Result<IpInfo> {
|
||||
let ip = ip_str.as_ref().parse()?;
|
||||
self.memory_search_ip(&ip)
|
||||
}
|
||||
|
||||
pub fn memory_search_ip(&self, ip_addr: &IpAddr) -> Result<IpInfo> {
|
||||
let ip = ip2u32(ip_addr)?;
|
||||
let mut h = self.total_blocks;
|
||||
let (mut data_ptr, mut l) = (0u32, 0u32);
|
||||
while l <= h {
|
||||
|
|
@ -88,7 +93,7 @@ impl OwnedIp2Region {
|
|||
}
|
||||
|
||||
if data_ptr == 0 {
|
||||
Err("not found")?;
|
||||
Err(Error::NotFound)?;
|
||||
}
|
||||
|
||||
let data_len = (data_ptr >> 24) & 0xff;
|
||||
|
|
|
|||
Loading…
Reference in New Issue