From 1605a3d9a57d6329e36eede43c99ec9b1826e29b Mon Sep 17 00:00:00 2001 From: biluohc Date: Tue, 3 Jul 2018 15:49:08 +0800 Subject: [PATCH] Rust: 0.2.0, use std::net::Ipaddr and update Error. --- binding/rust/Cargo.toml | 2 +- binding/rust/example/src/main.rs | 56 ++++++++++++++++++++++++++++ binding/rust/readme.md | 10 +++-- binding/rust/src/error.rs | 34 +++++++++++------ binding/rust/src/lazy.rs | 6 ++- binding/rust/src/lib.rs | 63 ++++++++++++++++++++++---------- binding/rust/src/owned.rs | 11 ++++-- 7 files changed, 142 insertions(+), 40 deletions(-) diff --git a/binding/rust/Cargo.toml b/binding/rust/Cargo.toml index 03239da..8967155 100644 --- a/binding/rust/Cargo.toml +++ b/binding/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ip2region" -version = "0.1.0" +version = "0.2.0" authors = ["biluohc "] include = ["./*", "../../data/ip2region.db", "../../Cargo.toml"] diff --git a/binding/rust/example/src/main.rs b/binding/rust/example/src/main.rs index 6e0fe61..597f22f 100644 --- a/binding/rust/example/src/main.rs +++ b/binding/rust/example/src/main.rs @@ -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!(); } } diff --git a/binding/rust/readme.md b/binding/rust/readme.md index 5e13a2b..700624c 100644 --- a/binding/rust/readme.md +++ b/binding/rust/readme.md @@ -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,还是有些感人的。 diff --git a/binding/rust/src/error.rs b/binding/rust/src/error.rs index f5505ee..2978379 100644 --- a/binding/rust/src/error.rs +++ b/binding/rust/src/error.rs @@ -1,20 +1,31 @@ -use std::{self, io, num, str}; +use std::{self, io, net, str}; pub type Result = std::result::Result; #[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 for Error { fn from(e: io::Error) -> Self { Error::Io(e) @@ -25,8 +36,9 @@ impl From for Error { Error::Utf8(e) } } -impl From for Error { - fn from(e: num::ParseIntError) -> Self { - Error::Int(e) + +impl From for Error { + fn from(e: net::AddrParseError) -> Self { + Error::Addr(e) } } diff --git a/binding/rust/src/lazy.rs b/binding/rust/src/lazy.rs index 7a576fd..48d3fd2 100644 --- a/binding/rust/src/lazy.rs +++ b/binding/rust/src/lazy.rs @@ -11,6 +11,10 @@ lazy_static! { }; } -pub fn memory_search(ip_str: &str) -> Result { +pub fn memory_search>(ip_str: S) -> Result> { OWNED_IP_2_REGION.memory_search(ip_str) } + +pub fn memory_search_ip(ip_addr: &IpAddr) -> Result { + OWNED_IP_2_REGION.memory_search_ip(ip_addr) +} diff --git a/binding/rust/src/lib.rs b/binding/rust/src/lib.rs index efcde97..aa19c58 100644 --- a/binding/rust/src/lib.rs +++ b/binding/rust/src/lib.rs @@ -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 { - let bits = ip_str - .split('.') - .filter(|s| !s.is_empty()) - .collect::>(); - if bits.len() != 4 { - Err("ip format error(it does not have 4 parts, like 1.1.1.1)")?; +fn ip2u32(ip: &IpAddr) -> Result { + if ip.is_ipv6() { + return Err(Error::UnsupportIpv6); } - let mut sum: u32 = 0; - for (i, n) in bits.iter().enumerate() { - let bit = n.parse::()?; - 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 { + pub fn binary_search>(&mut self, ip_str: S) -> Result { + let ip = ip_str.as_ref().parse::()?; + self.binary_search_ip(&ip) + } + pub fn binary_search_ip(&mut self, ip_addr: &IpAddr) -> Result { 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 { + pub fn btree_search>(&mut self, ip_str: S) -> Result { + let ip = ip_str.as_ref().parse::()?; + self.btree_search_ip(&ip) + } + pub fn btree_search_ip(&mut self, ip_addr: &IpAddr) -> Result { 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; diff --git a/binding/rust/src/owned.rs b/binding/rust/src/owned.rs index 9117e15..0744d27 100644 --- a/binding/rust/src/owned.rs +++ b/binding/rust/src/owned.rs @@ -65,8 +65,13 @@ impl OwnedIp2Region { }) } - pub fn memory_search(&self, ip_str: &str) -> Result { - let ip = ip2u32(ip_str)?; + pub fn memory_search>(&self, ip_str: S) -> Result { + let ip = ip_str.as_ref().parse()?; + self.memory_search_ip(&ip) + } + + pub fn memory_search_ip(&self, ip_addr: &IpAddr) -> Result { + 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;