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() {
let filepath = "../../data/ip2region.xdb";
let searcher = lib::Searcher::new(filepath).expect("load file error");
let result = searcher.search_by_ip("1.2.165.128");
println!("{:?}", result);
println!("");
// let filepath = "../../data/ip2region.xdb";
// let searcher = lib::Searcher::new(filepath).expect("load file error");
// 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::io::Read;
use std::net::Ipv4Addr;
use std::str::FromStr;
use crate::ip_value::ToUIntIP;
const HEADER_INFO_LENGTH: u32 = 256;
// const VECTOR_INDEX_ROWS: u32 = 256;
@ -13,22 +16,22 @@ pub struct 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 buffer = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(Self { buffer })
}
pub fn search_by_ip(&self, ip: &'static str) -> Result<String, Box<dyn std::error::Error>> {
let ip = ip.parse::<Ipv4Addr>().unwrap_or_else(|_| {
let ip = ip
.parse::<u32>()
.expect("ip is not a valid ip or valid int");
Ipv4Addr::from(ip)
});
pub fn search_by_ip<T>(&self, ip: T) -> Result<String, Box<dyn Error>>
where
T: ToUIntIP
{
let changed_value = ip.to_u32_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 il1 = (ip >> 16) & 0xFF;
let idx = VECTOR_INDEX_SIZE * (il0 * VECTOR_INDEX_COLS + il1);
@ -80,13 +83,39 @@ fn get_u32(bytes: &[u8], offset: usize) -> u32 {
mod tests {
use super::*;
///test all types find correct
#[test]
fn test_search_by_ip() {
let filepath = "../../data/ip2region.xdb";
let searcher = Searcher::new(filepath).expect("load file error");
let result = searcher.search_by_ip("2.0.0.0");
println!("{:?}", result);
let searcher = Searcher::new(get_xdb_filepath()).expect("load file error");
searcher.search_by_ip("2.0.0.0").unwrap();
searcher.search_by_ip("32").unwrap();
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() {}