feat: add ToUIntIP for types ip value

This commit is contained in:
gongzhengyang 2022-12-20 09:56:51 +08:00
parent 09c05be3f2
commit a5f0837bb5
3 changed files with 112 additions and 20 deletions

View File

@ -0,0 +1,61 @@
use std::error::Error;
use std::net::Ipv4Addr;
use std::str::FromStr;
pub trait ToUIntIP {
fn to_u32_ip(&self) -> Result<u32, Box<dyn Error>>;
}
impl ToUIntIP for u32 {
fn to_u32_ip(&self) -> Result<u32, Box<dyn Error>> {
Ok(self.to_owned())
}
}
impl ToUIntIP for &str {
fn to_u32_ip(&self) -> Result<u32, Box<dyn Error>> {
if let Ok(ip_addr) = Ipv4Addr::from_str(self) {
return Ok(u32::from(ip_addr));
}
Ok(self.parse::<u32>()?)
}
}
impl ToUIntIP for Ipv4Addr {
fn to_u32_ip(&self) -> Result<u32, Box<dyn Error>> {
Ok(u32::from(*self))
}
}
#[cfg(test)]
mod test_ip {
use super::*;
#[test]
fn test_ip_str_2_u32() {
let ip_str = "1.1.1.1";
let result = ip_str.to_u32_ip().unwrap();
assert_eq!(result, 1 << 24 | 1 << 16 | 1 << 8 | 1);
}
#[test]
fn test_ip_u32_str() {
let ip = "12";
let result = ip.to_u32_ip().unwrap();
assert_eq!(result, 12);
}
#[test]
fn test_ip_u32() {
let ip: u32 = 33;
let result = ip.to_u32_ip().unwrap();
assert_eq!(result, 33);
}
#[test]
fn test_ip_addr() {
let ip = Ipv4Addr::from_str("0.0.3.12").unwrap();
let result = ip.to_u32_ip().unwrap();
assert_eq!(result, 3 << 8 | 12)
}
}

View File

@ -1,8 +1,10 @@
mod lib; mod ip_value;
mod search;
fn main() { fn main() {
let filepath = "../../data/ip2region.xdb"; println!("");
let searcher = lib::Searcher::new(filepath).expect("load file error"); // let filepath = "../../data/ip2region.xdb";
let result = searcher.search_by_ip("1.2.165.128"); // let searcher = lib::Searcher::new(filepath).expect("load file error");
println!("{:?}", result); // let result = searcher.search_by_ip("1.2.165.128");
// println!("{:?}", result);
} }

View File

@ -1,6 +1,9 @@
use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::str::FromStr;
use crate::ip_value::ToUIntIP;
const HEADER_INFO_LENGTH: u32 = 256; const HEADER_INFO_LENGTH: u32 = 256;
// const VECTOR_INDEX_ROWS: u32 = 256; // const VECTOR_INDEX_ROWS: u32 = 256;
@ -13,22 +16,22 @@ pub struct Searcher {
} }
impl Searcher { impl Searcher {
pub fn new(filepath: &'static str) -> Result<Self, Box<dyn std::error::Error>> { pub fn new(filepath: &'static str) -> Result<Self, Box<dyn Error>> {
let mut f = File::open(filepath)?; let mut f = File::open(filepath)?;
let mut buffer = Vec::new(); let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?; f.read_to_end(&mut buffer)?;
Ok(Self { buffer }) Ok(Self { buffer })
} }
pub fn search_by_ip(&self, ip: &'static str) -> Result<String, Box<dyn std::error::Error>> { pub fn search_by_ip<T>(&self, ip: T) -> Result<String, Box<dyn Error>>
let ip = ip.parse::<Ipv4Addr>().unwrap_or_else(|_| { where
let ip = ip T: ToUIntIP
.parse::<u32>() {
.expect("ip is not a valid ip or valid int"); let changed_value = ip.to_u32_ip()?;
Ipv4Addr::from(ip) self.search_by_ip_u32(changed_value)
}); }
let ip = u32::from(ip); pub fn search_by_ip_u32(&self, ip: u32) -> Result<String, Box<dyn Error>> {
let il0 = (ip >> 24) & 0xFF; let il0 = (ip >> 24) & 0xFF;
let il1 = (ip >> 16) & 0xFF; let il1 = (ip >> 16) & 0xFF;
let idx = VECTOR_INDEX_SIZE * (il0 * VECTOR_INDEX_COLS + il1); let idx = VECTOR_INDEX_SIZE * (il0 * VECTOR_INDEX_COLS + il1);
@ -80,13 +83,39 @@ fn get_u32(bytes: &[u8], offset: usize) -> u32 {
mod tests { mod tests {
use super::*; use super::*;
///test all types find correct
#[test] #[test]
fn test_search_by_ip() { fn test_search_by_ip() {
let filepath = "../../data/ip2region.xdb"; let searcher = Searcher::new(get_xdb_filepath()).expect("load file error");
let searcher = Searcher::new(filepath).expect("load file error"); searcher.search_by_ip("2.0.0.0").unwrap();
let result = searcher.search_by_ip("2.0.0.0"); searcher.search_by_ip("32").unwrap();
println!("{:?}", result); searcher.search_by_ip(32).unwrap();
searcher.search_by_ip(Ipv4Addr::from_str("1.1.1.1").unwrap()).unwrap();
}
fn get_xdb_filepath() -> &'static str {
"../../data/ip2region.xdb"
}
/// test find ip correct use the file ip.test.txt in ../../data
#[test]
fn test_random_choose_ip() {
let searcher = Searcher::new(get_xdb_filepath()).unwrap();
let mut file = File::open("../../data/ip.test.txt").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
for line in contents.split("\n") {
if !line.contains("|") {
continue
}
let ip_test_line = line.splitn(3, "|").collect::<Vec<&str>>();
let start_ip = Ipv4Addr::from_str(ip_test_line[0]).unwrap();
let end_ip = Ipv4Addr::from_str(ip_test_line[1]).unwrap();
for value in u32::from(start_ip)..u32::from(end_ip) + 1 {
let result = searcher.search_by_ip(value).unwrap();
assert_eq!(result.as_str(), ip_test_line[2])
}
}
} }
} }
fn main() {}