use core::{cmp::Ordering, hash::Hash, ops::Add};
use crate::{
core::{Serial, SerialAdditionError},
emit::name::parse_dotted_name,
view::{Label, Name},
};
impl Hash for Name<'_> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
for label in self.labels_with_null() {
label.hash(state);
}
}
}
impl Ord for Name<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.labels_with_null().cmp(other.labels_with_null())
}
}
impl PartialOrd for Name<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Name<'_> {}
impl PartialEq<Name<'_>> for Name<'_> {
fn eq(&self, other: &Name<'_>) -> bool {
self.labels_with_null().eq(other.labels_with_null())
}
}
impl PartialEq<[u8]> for Name<'_> {
fn eq(&self, mut other: &[u8]) -> bool {
if self
.labels_with_null()
.next()
.is_some_and(|label| label.is_null())
{
return other == b".";
}
let mut self_labels = self.labels_with_null();
let mut other_label = [0u8; 63];
while let Ok(Some((other_label, rest))) = parse_dotted_name(&mut other_label, other) {
if !self_labels.next().is_some_and(|label| label == other_label) {
return false;
}
other = rest;
}
self_labels.next().is_some_and(|label| label.is_null())
}
}
impl PartialEq<&[u8]> for Name<'_> {
fn eq(&self, other: &&[u8]) -> bool {
*self == other[..]
}
}
impl<const N: usize> PartialEq<[u8; N]> for Name<'_> {
fn eq(&self, other: &[u8; N]) -> bool {
*self == other[..]
}
}
impl<const N: usize> PartialEq<&[u8; N]> for Name<'_> {
fn eq(&self, other: &&[u8; N]) -> bool {
*self == other[..]
}
}
impl PartialEq<str> for Name<'_> {
fn eq(&self, other: &str) -> bool {
*self == other.as_bytes()[..]
}
}
impl PartialEq<&str> for Name<'_> {
fn eq(&self, other: &&str) -> bool {
*self == other.as_bytes()[..]
}
}
#[cfg(feature = "alloc")]
impl PartialEq<alloc::string::String> for Name<'_> {
fn eq(&self, other: &alloc::string::String) -> bool {
*self == other.as_bytes()[..]
}
}
#[cfg(feature = "alloc")]
impl PartialEq<&alloc::string::String> for Name<'_> {
fn eq(&self, other: &&alloc::string::String) -> bool {
*self == other.as_bytes()[..]
}
}
impl Hash for Label<'_> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
if let Some(value) = self.value() {
state.write_u8(1);
for byte in value.iter() {
state.write_u8(byte.to_ascii_lowercase());
}
state.write_u8(0);
} else {
state.write_u8(0);
}
}
}
impl Ord for Label<'_> {
fn cmp(&self, other: &Self) -> Ordering {
if let Some((this, other)) = self.value().zip(other.value()) {
let this = this.iter().map(|byte| byte.to_ascii_lowercase());
let other = other.iter().map(|byte| byte.to_ascii_lowercase());
this.cmp(other)
} else {
self.value().cmp(&other.value())
}
}
}
impl PartialOrd for Label<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Label<'_> {}
impl PartialEq<Label<'_>> for Label<'_> {
fn eq(&self, other: &Label<'_>) -> bool {
if let Some((this, other)) = self.value().zip(other.value()) {
this.eq_ignore_ascii_case(other)
} else {
self.value() == other.value()
}
}
}
impl PartialEq<[u8]> for Label<'_> {
fn eq(&self, other: &[u8]) -> bool {
if let Some((this, other)) = self.value().zip(Some(other)) {
this.eq_ignore_ascii_case(other)
} else {
self.value() == Some(other)
}
}
}
impl PartialEq<&[u8]> for Label<'_> {
fn eq(&self, other: &&[u8]) -> bool {
*self == other[..]
}
}
impl<const N: usize> PartialEq<[u8; N]> for Label<'_> {
fn eq(&self, other: &[u8; N]) -> bool {
*self == other[..]
}
}
impl<const N: usize> PartialEq<&[u8; N]> for Label<'_> {
fn eq(&self, other: &&[u8; N]) -> bool {
*self == other[..]
}
}
impl PartialEq<str> for Label<'_> {
fn eq(&self, other: &str) -> bool {
*self == other.as_bytes()[..]
}
}
impl PartialEq<&str> for Label<'_> {
fn eq(&self, other: &&str) -> bool {
*self == other.as_bytes()[..]
}
}
#[cfg(feature = "alloc")]
impl PartialEq<alloc::string::String> for Label<'_> {
fn eq(&self, other: &alloc::string::String) -> bool {
*self == other.as_bytes()[..]
}
}
#[cfg(feature = "alloc")]
impl PartialEq<&alloc::string::String> for Label<'_> {
fn eq(&self, other: &&alloc::string::String) -> bool {
*self == other.as_bytes()[..]
}
}
impl PartialOrd<Serial> for Serial {
fn partial_cmp(&self, other: &Serial) -> Option<Ordering> {
const HALF: u32 = 1 << u32::BITS - 1;
match (self.value(), other.value()) {
(x, y) if x == y => Some(Ordering::Equal),
(x, y) if x < y && y - x < HALF => Some(Ordering::Less),
(x, y) if x > y && x - y > HALF => Some(Ordering::Less),
(x, y) if x < y && y - x > HALF => Some(Ordering::Greater),
(x, y) if x > y && x - y < HALF => Some(Ordering::Greater),
_ => None,
}
}
}
impl Add<u32> for Serial {
type Output = Result<Self, SerialAdditionError>;
fn add(self, rhs: u32) -> Self::Output {
if rhs > (1 << u32::BITS - 1) - 1 {
return Err(SerialAdditionError);
}
Ok(Self::new(self.value().wrapping_add(rhs)))
}
}
#[cfg(test)]
mod test {
use core::cmp::Ordering;
use crate::{
core::{Serial, SerialAdditionError},
view::{Label, LabelError, Name, NameError, View},
};
#[test]
fn serial() {
assert_eq!(
Serial::new(0xFFFFFFFF) + 0x7FFFFFFF,
Ok(Serial::new(0x7FFFFFFE))
);
assert_eq!(
Serial::new(0xFFFFFFFF) + 0x80000000,
Err(SerialAdditionError)
);
assert!(Serial::new(0x40000000) < Serial::new(0xBFFFFFFF));
assert!(Serial::new(0x40000000)
.partial_cmp(&Serial::new(0xC0000000))
.is_none());
assert!(Serial::new(0x40000000) > Serial::new(0xC0000001));
assert!(Serial::new(0xC0000000) < Serial::new(0x3FFFFFFF));
assert!(Serial::new(0xC0000000)
.partial_cmp(&Serial::new(0x40000000))
.is_none());
assert!(Serial::new(0xC0000000) > Serial::new(0x40000001));
}
#[test]
#[rustfmt::skip]
fn name_partial_eq() -> Result<(), NameError> {
let test_case = b"\x05daria\x03daz\x03cat\0";
assert_eq!(Name::view(test_case, ..)?.0, Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0);
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, &b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, *b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, *"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, "daria.daz.cat.");
let test_case = b"\x05daria\xC0\x11\x05charming\x03daz\x03cat\0";
assert_eq!(Name::view(test_case, ..)?.0, Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0);
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, &b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, *b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, *"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, "daria.daz.cat.");
let test_case = b"\x05DARIA\x03DAZ\x03CAT\0";
assert_eq!(Name::view(test_case, ..)?.0, Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0);
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, &b"daria.daz.cat."[..]);
assert_eq!(Name::view(test_case, ..)?.0, *b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, b"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, *"daria.daz.cat.");
assert_eq!(Name::view(test_case, ..)?.0, "daria.daz.cat.");
let test_case = b"\0";
assert_eq!(Name::view(test_case, ..)?.0, Name::view(b"\0", ..)?.0);
assert_eq!(Name::view(test_case, ..)?.0, b"."[..]);
assert_eq!(Name::view(test_case, ..)?.0, &b"."[..]);
assert_eq!(Name::view(test_case, ..)?.0, *b".");
assert_eq!(Name::view(test_case, ..)?.0, b".");
assert_eq!(Name::view(test_case, ..)?.0, *".");
assert_eq!(Name::view(test_case, ..)?.0, ".");
assert_ne!(Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0, Name::view(b"\0", ..)?.0);
assert_ne!(Name::view(b"\0", ..)?.0, Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0);
assert_ne!(Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0, "daria.daz.cat");
assert_ne!(Name::view(b"\x03daz\x03cat\0", ..)?.0, "daria.daz.cat");
assert_ne!(Name::view(b"\x03cat\0", ..)?.0, "daria.daz.cat");
assert_ne!(Name::view(b"\0", ..)?.0, "daria.daz.cat");
assert_ne!(Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0, "daz.cat");
assert_ne!(Name::view(b"\x03daz\x03cat\0", ..)?.0, "daz.cat");
assert_ne!(Name::view(b"\x03cat\0", ..)?.0, "daz.cat");
assert_ne!(Name::view(b"\0", ..)?.0, "daz.cat");
assert_ne!(Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0, "cat");
assert_ne!(Name::view(b"\x03daz\x03cat\0", ..)?.0, "cat");
assert_ne!(Name::view(b"\x03cat\0", ..)?.0, "cat");
assert_ne!(Name::view(b"\0", ..)?.0, "cat");
assert_ne!(Name::view(b"\x05daria\x03daz\x03cat\0", ..)?.0, "");
assert_ne!(Name::view(b"\x03daz\x03cat\0", ..)?.0, "");
assert_ne!(Name::view(b"\x03cat\0", ..)?.0, "");
assert_ne!(Name::view(b"\0", ..)?.0, "");
Ok(())
}
#[test]
#[rustfmt::skip]
fn name_ord() -> Result<(), NameError> {
fn assert_cmp<const L: usize, const R: usize>(
left: &[u8; L], right: &[u8; R], result: Ordering
) -> Result<(), NameError> {
let left = Name::view(left, ..)?.0;
let right = Name::view(right, ..)?.0;
Ok(assert_eq!(Ord::cmp(&left, &right), result))
}
assert_cmp(b"\x05daria\x03daz\x03cat\0", b"\x05daria\x03daz\x03cat\0", Ordering::Equal)?;
assert_cmp(b"\x05daria\x03daz\x03cat\0", b"\x07example\x03com\0", Ordering::Less)?;
assert_cmp(b"\x05daria\x03daz\x03cat\0", b"\x08codeberg\x03org\0", Ordering::Greater)?;
Ok(())
}
#[test]
#[rustfmt::skip]
#[cfg(feature = "std")]
fn name_hash_is_prefix_free() -> Result<(), NameError> {
fn hash<T: core::hash::Hash>(t: &T) -> u64 {
let mut s = std::hash::DefaultHasher::new();
t.hash(&mut s);
core::hash::Hasher::finish(&s)
}
fn hash_name(source: impl AsRef<str>) -> u64 {
hash(&Name::view(source.as_ref().as_bytes(), ..).expect("bad test case").0)
}
let mut collisions = 0;
for i in 1..128 {
let c = char::from_u32(i).expect("guaranteed by loop");
let two_three = format!("\x02{c}{c}\x03{c}{c}{c}\0");
let three_two = format!("\x03{c}{c}{c}\x02{c}{c}\0");
if hash_name(two_three) == hash_name(three_two) {
collisions += 1;
}
}
assert!(collisions < 10);
Ok(())
}
#[test]
#[rustfmt::skip]
fn label_partial_eq() -> Result<(), LabelError> {
assert_eq!(Label::view(b"\x03cat", ..)?.0, Label::view(b"\x03cat", ..)?.0);
assert_eq!(Label::view(b"\x03cat", ..)?.0, b"cat"[..]);
assert_eq!(Label::view(b"\x03cat", ..)?.0, &b"cat"[..]);
assert_eq!(Label::view(b"\x03cat", ..)?.0, *b"cat");
assert_eq!(Label::view(b"\x03cat", ..)?.0, b"cat");
assert_eq!(Label::view(b"\x03cat", ..)?.0, *"cat");
assert_eq!(Label::view(b"\x03cat", ..)?.0, "cat");
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, Label::view(b"\x03cat", ..)?.0);
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, b"cat"[..]);
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, &b"cat"[..]);
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, *b"cat");
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, b"cat");
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, *"cat");
assert_eq!(Label::view(b"\xC0\x02\x03cat", ..)?.0, "cat");
assert_eq!(Label::view(b"\x03CAT", ..)?.0, Label::view(b"\x03cat", ..)?.0);
assert_eq!(Label::view(b"\x03CAT", ..)?.0, b"cat"[..]);
assert_eq!(Label::view(b"\x03CAT", ..)?.0, &b"cat"[..]);
assert_eq!(Label::view(b"\x03CAT", ..)?.0, *b"cat");
assert_eq!(Label::view(b"\x03CAT", ..)?.0, b"cat");
assert_eq!(Label::view(b"\x03CAT", ..)?.0, *"cat");
assert_eq!(Label::view(b"\x03CAT", ..)?.0, "cat");
assert_eq!(Label::view(b"\0", ..)?.0, Label::view(b"\0", ..)?.0);
assert_eq!(Label::view(b"\0", ..)?.0, b""[..]);
assert_eq!(Label::view(b"\0", ..)?.0, &b""[..]);
assert_eq!(Label::view(b"\0", ..)?.0, *b"");
assert_eq!(Label::view(b"\0", ..)?.0, b"");
assert_eq!(Label::view(b"\0", ..)?.0, *"");
assert_eq!(Label::view(b"\0", ..)?.0, "");
assert_ne!(Label::view(b"\x03cat", ..)?.0, Label::view(b"\0", ..)?.0);
assert_ne!(Label::view(b"\0", ..)?.0, Label::view(b"\x03cat", ..)?.0);
Ok(())
}
#[test]
#[rustfmt::skip]
fn label_ord() -> Result<(), LabelError> {
fn assert_cmp<const L: usize, const R: usize>(
left: &[u8; L], right: &[u8; R], result: Ordering
) -> Result<(), LabelError> {
let left = Label::view(left, ..)?.0;
let right = Label::view(right, ..)?.0;
Ok(assert_eq!(Ord::cmp(&left, &right), result))
}
assert_cmp(b"\x05daria", b"\x05daria", Ordering::Equal)?;
assert_cmp(b"\x05daria", b"\x07example", Ordering::Less)?;
assert_cmp(b"\x05daria", b"\x08codeberg", Ordering::Greater)?;
Ok(())
}
}