Skip to content

Commit

Permalink
MacAddress type instead of string; SIMD for MAC comparisons
Browse files Browse the repository at this point in the history
  • Loading branch information
Crypto-Spartan committed Jul 26, 2024
1 parent 932f52b commit a5feea1
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 48 deletions.
61 changes: 61 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ authors = ["Crypto-Spartan"]

[dependencies]
chrono = { version = "0.4", features=["serde"] }
constcat = "0.5"
egui = "0.28"
eframe = "0.28"
flume = { version = "0.11", default-features = false }
image = { version = "0.25", default-features = false, features=["ico"] }
multiversion = "0.7"
once_cell = "1.19"
regex-automata = { version = "0.4", default-features = false, features=["std", "perf", "dfa"] }
reqwest = {version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "blocking", "cookies", "json", "zstd"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_repr = "0.1"
serde_with = "3.7"
simd-itertools = "0.2.3"
simd-json = "0.13"
thiserror = "1.0"
zeroize = "1.6"
Expand Down
7 changes: 4 additions & 3 deletions src/gui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
popup::{GuiError, PopupWindow, WindowMeta},
{ChannelsGuiThread, ChannelsSearchThread},
},
mac_address::validation::text_is_valid_mac,
mac_address::{MacAddress, validation::text_is_valid_mac},
unifi::search::{find_unifi_device, UnifiSearchInfo},
};
use std::thread;
Expand Down Expand Up @@ -283,8 +283,9 @@ impl<'a> GuiApp<'a> {
p
}
};
let server_url = server_url_input.to_string();
let mac_to_search = mac_addr_input.replace("-", ":").to_lowercase();
let server_url = server_url_input.strip_suffix('/').unwrap_or(server_url_input).to_string();
let mac_to_search = MacAddress::try_from(mac_addr_input.as_ref())
.expect("Mac Address validation failed"); // SAFETY: this should never error due to the check above
let accept_invalid_certs = *invalid_certs_checked;

search_info_tx.send(
Expand Down
4 changes: 2 additions & 2 deletions src/gui/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<'a> GuiError<'a> {
}
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub(super) enum PopupWindow<'a> {
SearchProgress(f32),
SearchResult(UnifiDeviceBasic),
Expand Down Expand Up @@ -235,7 +235,7 @@ impl<'a> PopupWindow<'a> {

// add the MAC address of the device found
PopupWindow::create_search_result_row(
ui, "MAC Address:", mac.as_ref(),
ui, "MAC Address:", format!("{mac}"),
);

// add device status; ie if the device is connected, offline, or unknown
Expand Down
91 changes: 61 additions & 30 deletions src/mac_address/mod.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,84 @@
use constcat::concat;
use serde::{de::{self, Unexpected}, Deserialize};
use thiserror::Error;

pub mod validation;
use validation::MAC_ADDR_REGEX_STR;

/*pub(crate) struct MACAddress{
#[derive(Clone, Debug, Default)]//, PartialEq, Eq)]
pub(crate) struct MacAddress{
bytes: [u8; 6]
}

impl MACAddress {
pub fn new(bytes: [u8; 6]) -> MACAddress {
MACAddress{ bytes }
impl MacAddress {
pub fn new(bytes: [u8; 6]) -> MacAddress {
MacAddress{ bytes }
}

#[inline]
pub fn as_bytes(&self) -> &[u8; 6] {
&self.bytes
}

pub fn bytes(self) -> [u8; 6] {
#[inline]
pub fn into_bytes(self) -> [u8; 6] {
self.bytes
}
}

impl From<[u8; 6]> for MACAddress {
impl From<[u8; 6]> for MacAddress {
#[inline]
fn from(v: [u8; 6]) -> Self {
MACAddress::new(v)
MacAddress::new(v)
}
}

impl std::str::FromStr for MACAddress {
type Err = MACParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut array = [0u8; 6];
let mut nth = 0;
for byte in input.split(|c| c == ':' || c == '-') {
if nth == 6 {
return Err(MACParseError::InvalidLength);
}
#[derive(Error, Debug)]
pub(crate) enum MacParseError{
#[error("Invalid MAC Address: {invalid_mac:?}")]
InvalidMac{ invalid_mac: Box<str> },
}

array[nth] = u8::from_str_radix(byte, 16).map_err(|_| MACParseError::InvalidDigit)?;
impl std::str::FromStr for MacAddress {
type Err = MacParseError;

nth += 1;
fn from_str(input: &str) -> Result<Self, Self::Err> {
if !validation::text_is_valid_mac(input.as_bytes()) {
return Err(MacParseError::InvalidMac { invalid_mac: Box::from(input) });
}

if nth != 6 {
return Err(MACParseError::InvalidLength);
let mut array = [0u8; 6];
let mac_bytes_iter = input
.split([':', '-'])
.map(|b_str| {
u8::from_str_radix(b_str, 16).expect("mac validation failed")
});

for (idx, b) in array.iter_mut().zip(mac_bytes_iter) {
*idx = b
}

Ok(MACAddress::new(array))
Ok(MacAddress::new(array))
}
}

impl std::convert::TryFrom<&'_ str> for MACAddress {
type Error = MACParseError;
impl std::convert::TryFrom<&'_ str> for MacAddress {
type Error = MacParseError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}

impl std::convert::TryFrom<std::borrow::Cow<'_, str>> for MACAddress {
type Error = MACParseError;
impl std::convert::TryFrom<std::borrow::Cow<'_, str>> for MacAddress {
type Error = MacParseError;

fn try_from(value: std::borrow::Cow<'_, str>) -> Result<Self, Self::Error> {
value.parse()
}
}

impl std::fmt::Display for MACAddress {
impl std::fmt::Display for MacAddress {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let _ = write!(
f,
Expand All @@ -76,13 +93,27 @@ impl std::fmt::Display for MACAddress {

Ok(())
}
}*/
}

// impl serde::Serialize for MACAddress {
// impl serde::Serialize for MacAddress {
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
// where
// S: serde::Serializer,
// {
// serializer.collect_str(self)
// }
// }

impl<'de> Deserialize<'de> for MacAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let mac_str: &str = de::Deserialize::deserialize(deserializer)?;
MacAddress::try_from(mac_str).map_err(|_| {
let unexpected = Unexpected::Str(mac_str);
const EXPECTED: &str = concat!("MAC Address in string format matching regex: ", MAC_ADDR_REGEX_STR);
de::Error::invalid_value(unexpected, &EXPECTED)
})
}
}
5 changes: 3 additions & 2 deletions src/unifi/devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use chrono::serde::ts_seconds;
use chrono::{DateTime, Utc};
use serde::Deserialize;
use serde_repr::Deserialize_repr;
use crate::mac_address::MacAddress;

#[derive(Default, Debug, Clone, Deserialize)]
pub(crate) struct UnifiSite {
Expand Down Expand Up @@ -44,9 +45,9 @@ impl DeviceState {
}
}

#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct UnifiDeviceBasic {
pub(crate) mac: Box<str>,
pub(crate) mac: MacAddress,
pub(crate) state: DeviceState,
pub(crate) adopted: bool,
#[serde(rename(deserialize = "type"))]
Expand Down
Loading

0 comments on commit a5feea1

Please sign in to comment.