Rust: 0.2.0, use std::net::Ipaddr and update Error.
This commit is contained in:
parent
94ed13cdd0
commit
ea9a3ff7b8
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,还是有些感人的。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue