Rust: 0.2.0, use std::net::Ipaddr and update Error.

This commit is contained in:
biluohc 2018-07-03 15:49:08 +08:00
parent 94ed13cdd0
commit ea9a3ff7b8
7 changed files with 142 additions and 40 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ip2region" name = "ip2region"
version = "0.1.0" version = "0.2.0"
authors = ["biluohc <biluohc@qq.com>"] authors = ["biluohc <biluohc@qq.com>"]
include = ["./*", "../../data/ip2region.db", "../../Cargo.toml"] include = ["./*", "../../data/ip2region.db", "../../Cargo.toml"]

View File

@ -27,6 +27,19 @@ fn lazy() {
let res = memory_search(ip); let res = memory_search(ip);
let end = start.elapsed().subsec_micros(); let end = start.elapsed().subsec_micros();
println!("lazy__ {:06} microseconds: {:?}", end, res); 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(); let ip2o = ip2.to_owned().unwrap();
for ip in IPS { for ip in IPS {
// mem
let start = Instant::now(); let start = Instant::now();
let res = ip2o.memory_search(ip); let res = ip2o.memory_search(ip);
let end = start.elapsed().subsec_micros(); let end = start.elapsed().subsec_micros();
println!("memory {:06} microseconds: {:?}", end, res); 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 start = Instant::now();
let res = ip2.binary_search(ip); let res = ip2.binary_search(ip);
let end = start.elapsed().subsec_micros(); let end = start.elapsed().subsec_micros();
println!("binary {:06} microseconds: {:?}", end, res); 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 start = Instant::now();
let res = ip2.btree_search(ip); let res = ip2.btree_search(ip);
let end = start.elapsed().subsec_micros(); let end = start.elapsed().subsec_micros();
println!("btree {:06} microseconds: {:?}", end, res); 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!(); println!();
} }
} }

View File

@ -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 直接打包进二进制 ### `lazy` feature 把 DB 直接打包进二进制
取消上面 toml 的 `# features = ["lazy"]` 行的注释即可使用,其 api 是 `memory_search` 取消上面 toml 的 `# features = ["lazy"]` 行的注释即可使用,其 api 是 `memory_search``memory_search_ip`
只是目前 DB 足有3.2M,还是有些感人的。 只是目前 DB 足有3.2M,还是有些感人的。

View File

@ -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>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Str(&'static str),
Io(io::Error), Io(io::Error),
Utf8(str::Utf8Error), 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 { impl From<io::Error> for Error {
fn from(e: io::Error) -> Self { fn from(e: io::Error) -> Self {
Error::Io(e) Error::Io(e)
@ -25,8 +36,9 @@ impl From<str::Utf8Error> for Error {
Error::Utf8(e) Error::Utf8(e)
} }
} }
impl From<num::ParseIntError> for Error {
fn from(e: num::ParseIntError) -> Self { impl From<net::AddrParseError> for Error {
Error::Int(e) fn from(e: net::AddrParseError) -> Self {
Error::Addr(e)
} }
} }

View File

@ -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) 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)
}

View File

@ -5,6 +5,7 @@ extern crate lazy_static;
use std::cell::RefCell; use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom}; use std::io::{self, Read, Seek, SeekFrom};
use std::net::IpAddr;
use std::{fmt, str}; use std::{fmt, str};
mod db; mod db;
@ -19,7 +20,7 @@ pub use owned::{OwnedIp2Region, OwnedIpInfo};
#[cfg(feature = "lazy")] #[cfg(feature = "lazy")]
use db::DB_BYTES; use db::DB_BYTES;
#[cfg(feature = "lazy")] #[cfg(feature = "lazy")]
pub use owned::memory_search; pub use owned::{memory_search, memory_search_ip};
const INDEX_BLOCK_LENGTH: u32 = 12; const INDEX_BLOCK_LENGTH: u32 = 12;
const TOTAL_HEADER_LENGTH: usize = 8192; const TOTAL_HEADER_LENGTH: usize = 8192;
@ -89,20 +90,34 @@ fn get_u32(bytes: &[u8], offset: u32) -> u32 {
tmp as u32 tmp as u32
} }
fn ip2u32(ip_str: &str) -> Result<u32> { fn ip2u32(ip: &IpAddr) -> Result<u32> {
let bits = ip_str if ip.is_ipv6() {
.split('.') return Err(Error::UnsupportIpv6);
.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)")?;
} }
let mut sum: u32 = 0; if ip.is_unspecified() {
for (i, n) in bits.iter().enumerate() { return Err(Error::IpIsUnspecified);
let bit = n.parse::<u32>()?; }
sum += bit << 24 - 8 * i; 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 { pub struct Ip2Region {
@ -138,7 +153,11 @@ impl Ip2Region {
OwnedIp2Region::new2(&mut self.db_file).map_err(Error::Io) 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| { BUF.with(|buf| {
let mut buf = buf.borrow_mut(); let mut buf = buf.borrow_mut();
@ -150,7 +169,7 @@ impl Ip2Region {
self.total_blocks = self.total_blocks =
(self.last_index_ptr - self.first_index_ptr) / INDEX_BLOCK_LENGTH + 1; (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 h = self.total_blocks;
let (mut data_ptr, mut l) = (0u32, 0u32); let (mut data_ptr, mut l) = (0u32, 0u32);
while l <= h { while l <= h {
@ -173,7 +192,7 @@ impl Ip2Region {
} }
} }
if data_ptr == 0 { if data_ptr == 0 {
Err("not found")?; Err(Error::NotFound)?;
} }
let data_len = (data_ptr >> 24) & 0xff; 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| { BUF_BTREE.with(|buf| {
let mut buf = buf.borrow_mut(); let mut buf = buf.borrow_mut();
@ -212,7 +235,7 @@ impl Ip2Region {
self.header_len = idx self.header_len = idx
} }
let ip = ip2u32(ip_str)?; let ip = ip2u32(ip_addr)?;
let mut h = self.header_len; let mut h = self.header_len;
let (mut sptr, mut eptr, mut l) = (0u32, 0u32, 0u32); let (mut sptr, mut eptr, mut l) = (0u32, 0u32, 0u32);
@ -257,7 +280,7 @@ impl Ip2Region {
} }
if sptr == 0 { if sptr == 0 {
Err("not found")?; Err(Error::NotFound)?;
} }
let block_len = eptr - sptr; let block_len = eptr - sptr;
self.db_file.seek(SeekFrom::Start(sptr as u64))?; self.db_file.seek(SeekFrom::Start(sptr as u64))?;
@ -285,7 +308,7 @@ impl Ip2Region {
} }
} }
if data_ptr == 0 { if data_ptr == 0 {
Err("not found")?; Err(Error::NotFound)?;
} }
let data_len = (data_ptr >> 24) & 0xff; let data_len = (data_ptr >> 24) & 0xff;

View File

@ -65,8 +65,13 @@ impl OwnedIp2Region {
}) })
} }
pub fn memory_search(&self, ip_str: &str) -> Result<IpInfo> { pub fn memory_search<S: AsRef<str>>(&self, ip_str: S) -> Result<IpInfo> {
let ip = ip2u32(ip_str)?; 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 h = self.total_blocks;
let (mut data_ptr, mut l) = (0u32, 0u32); let (mut data_ptr, mut l) = (0u32, 0u32);
while l <= h { while l <= h {
@ -88,7 +93,7 @@ impl OwnedIp2Region {
} }
if data_ptr == 0 { if data_ptr == 0 {
Err("not found")?; Err(Error::NotFound)?;
} }
let data_len = (data_ptr >> 24) & 0xff; let data_len = (data_ptr >> 24) & 0xff;