use super::super::wire::ParseError;
use super::absolute::Name;
use super::builder::{FromStrError, NameBuilder, PushError};
use super::chain::{Chain, LongChainError};
use super::label::{Label, LabelTypeError, SplitLabelError};
use super::traits::{ToLabelIter, ToRelativeName};
#[cfg(feature = "bytes")]
use bytes::Bytes;
use core::cmp::Ordering;
use core::ops::{Bound, RangeBounds};
use core::str::FromStr;
use core::{borrow, cmp, fmt, hash, mem};
use octseq::builder::{
EmptyBuilder, FreezeBuilder, FromBuilder, IntoBuilder, Truncate,
};
use octseq::octets::{Octets, OctetsFrom};
#[cfg(feature = "serde")]
use octseq::serde::{DeserializeOctets, SerializeOctets};
#[cfg(feature = "std")]
use std::vec::Vec;
#[derive(Clone)]
#[repr(transparent)]
pub struct RelativeName<Octs: ?Sized>(Octs);
impl<Octs> RelativeName<Octs> {
pub const unsafe fn from_octets_unchecked(octets: Octs) -> Self {
RelativeName(octets)
}
pub fn from_octets(octets: Octs) -> Result<Self, RelativeNameError>
where
Octs: AsRef<[u8]>,
{
RelativeName::check_slice(octets.as_ref())?;
Ok(unsafe { RelativeName::from_octets_unchecked(octets) })
}
#[must_use]
pub fn empty() -> Self
where
Octs: From<&'static [u8]>,
{
unsafe { RelativeName::from_octets_unchecked(b"".as_ref().into()) }
}
#[must_use]
pub fn wildcard() -> Self
where
Octs: From<&'static [u8]>,
{
unsafe {
RelativeName::from_octets_unchecked(b"\x01*".as_ref().into())
}
}
pub fn from_chars<C>(chars: C) -> Result<Self, RelativeFromStrError>
where
Octs: FromBuilder,
<Octs as FromBuilder>::Builder: EmptyBuilder
+ FreezeBuilder<Octets = Octs>
+ AsRef<[u8]>
+ AsMut<[u8]>,
C: IntoIterator<Item = char>,
{
let mut builder = NameBuilder::<Octs::Builder>::new();
builder.append_chars(chars)?;
if builder.in_label() || builder.is_empty() {
Ok(builder.finish())
} else {
Err(RelativeFromStrError::AbsoluteName)
}
}
}
impl RelativeName<[u8]> {
pub(super) unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
mem::transmute(slice)
}
pub fn from_slice(slice: &[u8]) -> Result<&Self, RelativeNameError> {
Self::check_slice(slice)?;
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
#[must_use]
pub fn empty_slice() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"") }
}
#[must_use]
pub fn wildcard_slice() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"\x01*") }
}
pub(super) fn check_slice(
mut slice: &[u8],
) -> Result<(), RelativeNameError> {
if slice.len() > 254 {
return Err(RelativeNameErrorEnum::LongName.into());
}
while !slice.is_empty() {
let (label, tail) = Label::split_from(slice)?;
if label.is_root() {
return Err(RelativeNameErrorEnum::AbsoluteName.into());
}
slice = tail;
}
Ok(())
}
}
impl RelativeName<&'static [u8]> {
#[must_use]
pub fn empty_ref() -> Self {
Self::empty()
}
#[must_use]
pub fn wildcard_ref() -> Self {
Self::wildcard()
}
}
#[cfg(feature = "std")]
impl RelativeName<Vec<u8>> {
#[must_use]
pub fn empty_vec() -> Self {
Self::empty()
}
#[must_use]
pub fn wildcard_vec() -> Self {
Self::wildcard()
}
pub fn vec_from_str(s: &str) -> Result<Self, RelativeFromStrError> {
FromStr::from_str(s)
}
}
#[cfg(feature = "bytes")]
impl RelativeName<Bytes> {
pub fn empty_bytes() -> Self {
Self::empty()
}
pub fn wildcard_bytes() -> Self {
Self::wildcard()
}
pub fn bytes_from_str(s: &str) -> Result<Self, RelativeFromStrError> {
FromStr::from_str(s)
}
}
impl<Octs: ?Sized> RelativeName<Octs> {
pub fn as_octets(&self) -> &Octs {
&self.0
}
pub fn into_octets(self) -> Octs
where
Octs: Sized,
{
self.0
}
pub fn for_ref(&self) -> RelativeName<&Octs> {
unsafe { RelativeName::from_octets_unchecked(&self.0) }
}
pub fn as_slice(&self) -> &[u8]
where
Octs: AsRef<[u8]>,
{
self.0.as_ref()
}
pub fn for_slice(&self) -> &RelativeName<[u8]>
where
Octs: AsRef<[u8]>,
{
unsafe { RelativeName::from_slice_unchecked(self.0.as_ref()) }
}
pub fn make_canonical(&mut self)
where
Octs: AsMut<[u8]>,
{
Label::make_slice_canonical(self.0.as_mut());
}
}
impl<Octs> RelativeName<Octs> {
pub fn into_builder(self) -> NameBuilder<<Octs as IntoBuilder>::Builder>
where
Octs: IntoBuilder,
{
unsafe { NameBuilder::from_builder_unchecked(self.0.into_builder()) }
}
pub fn into_absolute(self) -> Result<Name<Octs>, PushError>
where
Octs: IntoBuilder,
<Octs as IntoBuilder>::Builder:
FreezeBuilder<Octets = Octs> + AsRef<[u8]> + AsMut<[u8]>,
{
self.into_builder().into_name()
}
pub fn chain<N: ToLabelIter>(
self,
other: N,
) -> Result<Chain<Self, N>, LongChainError>
where
Octs: AsRef<[u8]>,
{
Chain::new(self, other)
}
pub fn chain_root(self) -> Chain<Self, Name<&'static [u8]>>
where
Octs: AsRef<[u8]>,
{
self.chain(Name::root()).unwrap()
}
}
impl<Octs: AsRef<[u8]> + ?Sized> RelativeName<Octs> {
pub fn len(&self) -> usize {
self.0.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.0.as_ref().is_empty()
}
}
impl<Octs: AsRef<[u8]> + ?Sized> RelativeName<Octs> {
pub fn iter(&self) -> NameIter {
NameIter::new(self.0.as_ref())
}
pub fn label_count(&self) -> usize {
self.iter().count()
}
pub fn first(&self) -> Option<&Label> {
self.iter().next()
}
pub fn last(&self) -> Option<&Label> {
self.iter().next_back()
}
pub fn ndots(&self) -> usize {
if self.0.as_ref().is_empty() {
0
} else {
self.label_count() - 1
}
}
pub fn starts_with<N: ToLabelIter>(&self, base: &N) -> bool {
<Self as ToLabelIter>::starts_with(self, base)
}
pub fn ends_with<N: ToLabelIter>(&self, base: &N) -> bool {
<Self as ToLabelIter>::ends_with(self, base)
}
pub fn is_label_start(&self, mut index: usize) -> bool {
if index == 0 {
return true;
}
let mut tmp = self.as_slice();
while !tmp.is_empty() {
let (label, tail) = Label::split_from(tmp).unwrap();
let len = label.len() + 1;
match index.cmp(&len) {
Ordering::Less => return false,
Ordering::Equal => return true,
_ => {}
}
index -= len;
tmp = tail;
}
false
}
fn check_index(&self, index: usize) {
if !self.is_label_start(index) {
panic!("index not at start of a label");
}
}
fn check_bounds(&self, bounds: &impl RangeBounds<usize>) {
match bounds.start_bound().cloned() {
Bound::Included(idx) => self.check_index(idx),
Bound::Excluded(_) => {
panic!("excluded lower bounds not supported");
}
Bound::Unbounded => {}
}
match bounds.end_bound().cloned() {
Bound::Included(idx) => self
.check_index(idx.checked_add(1).expect("end bound too big")),
Bound::Excluded(idx) => self.check_index(idx),
Bound::Unbounded => {}
}
}
pub fn slice(
&self,
range: impl RangeBounds<usize>,
) -> &RelativeName<[u8]> {
self.check_bounds(&range);
unsafe {
RelativeName::from_slice_unchecked(self.0.as_ref().range(range))
}
}
pub fn range(
&self,
range: impl RangeBounds<usize>,
) -> RelativeName<<Octs as Octets>::Range<'_>>
where
Octs: Octets,
{
self.check_bounds(&range);
unsafe { RelativeName::from_octets_unchecked(self.0.range(range)) }
}
}
impl<Octs: AsRef<[u8]> + ?Sized> RelativeName<Octs> {
pub fn split(
&self,
mid: usize,
) -> (RelativeName<Octs::Range<'_>>, RelativeName<Octs::Range<'_>>)
where
Octs: Octets,
{
self.check_index(mid);
unsafe {
(
RelativeName::from_octets_unchecked(self.0.range(..mid)),
RelativeName::from_octets_unchecked(self.0.range(mid..)),
)
}
}
pub fn truncate(&mut self, len: usize)
where
Octs: Truncate,
{
self.check_index(len);
self.0.truncate(len);
}
pub fn split_first(
&self,
) -> Option<(&Label, RelativeName<Octs::Range<'_>>)>
where
Octs: Octets,
{
if self.is_empty() {
return None;
}
let label = self.iter().next()?;
Some((label, self.split(label.len() + 1).1))
}
pub fn parent(&self) -> Option<RelativeName<Octs::Range<'_>>>
where
Octs: Octets,
{
self.split_first().map(|(_, parent)| parent)
}
pub fn strip_suffix<N: ToRelativeName>(
&mut self,
base: &N,
) -> Result<(), StripSuffixError>
where
Octs: Truncate,
{
if self.ends_with(base) {
let idx = self.0.as_ref().len() - usize::from(base.compose_len());
self.0.truncate(idx);
Ok(())
} else {
Err(StripSuffixError(()))
}
}
}
impl<Octs> AsRef<Octs> for RelativeName<Octs> {
fn as_ref(&self) -> &Octs {
&self.0
}
}
impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for RelativeName<Octs> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<Octs, SrcOcts> OctetsFrom<RelativeName<SrcOcts>> for RelativeName<Octs>
where
Octs: OctetsFrom<SrcOcts>,
{
type Error = Octs::Error;
fn try_octets_from(
source: RelativeName<SrcOcts>,
) -> Result<Self, Self::Error> {
Octs::try_octets_from(source.0)
.map(|octets| unsafe { Self::from_octets_unchecked(octets) })
}
}
impl<Octs> FromStr for RelativeName<Octs>
where
Octs: FromBuilder,
<Octs as FromBuilder>::Builder: EmptyBuilder
+ FreezeBuilder<Octets = Octs>
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Err = RelativeFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_chars(s.chars())
}
}
impl<Octs> ToLabelIter for RelativeName<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
{
type LabelIter<'a> = NameIter<'a> where Octs: 'a;
fn iter_labels(&self) -> Self::LabelIter<'_> {
self.iter()
}
fn compose_len(&self) -> u16 {
u16::try_from(self.0.as_ref().len()).expect("long domain name")
}
}
impl<Octs: AsRef<[u8]> + ?Sized> ToRelativeName for RelativeName<Octs> {
fn as_flat_slice(&self) -> Option<&[u8]> {
Some(self.0.as_ref())
}
fn is_empty(&self) -> bool {
self.0.as_ref().is_empty()
}
}
impl<'a, Octs> IntoIterator for &'a RelativeName<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
{
type Item = &'a Label;
type IntoIter = NameIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<Octs, N> PartialEq<N> for RelativeName<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
N: ToRelativeName + ?Sized,
{
fn eq(&self, other: &N) -> bool {
self.name_eq(other)
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Eq for RelativeName<Octs> {}
impl<Octs, N> PartialOrd<N> for RelativeName<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
N: ToRelativeName + ?Sized,
{
fn partial_cmp(&self, other: &N) -> Option<cmp::Ordering> {
Some(self.name_cmp(other))
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Ord for RelativeName<Octs> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.name_cmp(other)
}
}
impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for RelativeName<Octs> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
for item in self.iter() {
item.hash(state)
}
}
}
impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for RelativeName<Octs> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.iter();
match iter.next() {
Some(label) => label.fmt(f)?,
None => return Ok(()),
}
for label in iter {
f.write_str(".")?;
label.fmt(f)?;
}
Ok(())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for RelativeName<Octs> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RelativeName({})", self)
}
}
impl<Octs> AsRef<RelativeName<[u8]>> for RelativeName<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
{
fn as_ref(&self) -> &RelativeName<[u8]> {
self.for_slice()
}
}
impl<Octs> borrow::Borrow<RelativeName<[u8]>> for RelativeName<Octs>
where
Octs: AsRef<[u8]>,
{
fn borrow(&self) -> &RelativeName<[u8]> {
self.for_slice()
}
}
#[cfg(feature = "serde")]
impl<Octs> serde::Serialize for RelativeName<Octs>
where
Octs: AsRef<[u8]> + SerializeOctets + ?Sized,
{
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_newtype_struct(
"RelativeName",
&format_args!("{}", self),
)
} else {
serializer.serialize_newtype_struct(
"RelativeName",
&self.0.as_serialized_octets(),
)
}
}
}
#[cfg(feature = "serde")]
impl<'de, Octs> serde::Deserialize<'de> for RelativeName<Octs>
where
Octs: FromBuilder + DeserializeOctets<'de>,
<Octs as FromBuilder>::Builder: FreezeBuilder<Octets = Octs>
+ EmptyBuilder
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use core::marker::PhantomData;
struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
impl<'de, Octs> serde::de::Visitor<'de> for InnerVisitor<'de, Octs>
where
Octs: FromBuilder + DeserializeOctets<'de>,
<Octs as FromBuilder>::Builder: FreezeBuilder<Octets = Octs>
+ EmptyBuilder
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Value = RelativeName<Octs>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a relative domain name")
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> Result<Self::Value, E> {
let mut builder = NameBuilder::<Octs::Builder>::new();
builder.append_chars(v.chars()).map_err(E::custom)?;
Ok(builder.finish())
}
fn visit_borrowed_bytes<E: serde::de::Error>(
self,
value: &'de [u8],
) -> Result<Self::Value, E> {
self.0.visit_borrowed_bytes(value).and_then(|octets| {
RelativeName::from_octets(octets).map_err(E::custom)
})
}
#[cfg(feature = "std")]
fn visit_byte_buf<E: serde::de::Error>(
self,
value: std::vec::Vec<u8>,
) -> Result<Self::Value, E> {
self.0.visit_byte_buf(value).and_then(|octets| {
RelativeName::from_octets(octets).map_err(E::custom)
})
}
}
struct NewtypeVisitor<T>(PhantomData<T>);
impl<'de, Octs> serde::de::Visitor<'de> for NewtypeVisitor<Octs>
where
Octs: FromBuilder + DeserializeOctets<'de>,
<Octs as FromBuilder>::Builder: FreezeBuilder<Octets = Octs>
+ EmptyBuilder
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Value = RelativeName<Octs>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a relative domain name")
}
fn visit_newtype_struct<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
if deserializer.is_human_readable() {
deserializer
.deserialize_str(InnerVisitor(Octs::visitor()))
} else {
Octs::deserialize_with_visitor(
deserializer,
InnerVisitor(Octs::visitor()),
)
}
}
}
deserializer.deserialize_newtype_struct(
"RelativeName",
NewtypeVisitor(PhantomData),
)
}
}
#[derive(Clone, Debug)]
pub struct NameIter<'a> {
slice: &'a [u8],
}
impl<'a> NameIter<'a> {
pub(super) fn new(slice: &'a [u8]) -> Self {
NameIter { slice }
}
}
impl<'a> Iterator for NameIter<'a> {
type Item = &'a Label;
fn next(&mut self) -> Option<Self::Item> {
let (label, tail) = match Label::split_from(self.slice) {
Ok(res) => res,
Err(_) => return None,
};
self.slice = tail;
Some(label)
}
}
impl<'a> DoubleEndedIterator for NameIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.slice.is_empty() {
return None;
}
let mut tmp = self.slice;
loop {
let (label, tail) = Label::split_from(tmp).unwrap();
if tail.is_empty() {
let end = self.slice.len() - (label.len() + 1);
self.slice = &self.slice[..end];
return Some(label);
} else {
tmp = tail
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RelativeNameError(RelativeNameErrorEnum);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RelativeNameErrorEnum {
BadLabel(LabelTypeError),
CompressedName,
ShortInput,
LongName,
AbsoluteName,
}
impl From<LabelTypeError> for RelativeNameError {
fn from(err: LabelTypeError) -> Self {
Self(RelativeNameErrorEnum::BadLabel(err))
}
}
impl From<SplitLabelError> for RelativeNameError {
fn from(err: SplitLabelError) -> Self {
Self(match err {
SplitLabelError::Pointer(_) => {
RelativeNameErrorEnum::CompressedName
}
SplitLabelError::BadType(t) => RelativeNameErrorEnum::BadLabel(t),
SplitLabelError::ShortInput => RelativeNameErrorEnum::ShortInput,
})
}
}
impl From<RelativeNameErrorEnum> for RelativeNameError {
fn from(err: RelativeNameErrorEnum) -> Self {
Self(err)
}
}
impl fmt::Display for RelativeNameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
RelativeNameErrorEnum::BadLabel(err) => err.fmt(f),
RelativeNameErrorEnum::CompressedName => {
f.write_str("compressed domain name")
}
RelativeNameErrorEnum::ShortInput => {
ParseError::ShortInput.fmt(f)
}
RelativeNameErrorEnum::LongName => {
f.write_str("long domain name")
}
RelativeNameErrorEnum::AbsoluteName => {
f.write_str("absolute domain name")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for RelativeNameError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum RelativeFromStrError {
FromStr(FromStrError),
AbsoluteName,
}
impl From<FromStrError> for RelativeFromStrError {
fn from(src: FromStrError) -> Self {
Self::FromStr(src)
}
}
impl fmt::Display for RelativeFromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RelativeFromStrError::FromStr(err) => err.fmt(f),
RelativeFromStrError::AbsoluteName => {
f.write_str("absolute domain name")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for RelativeFromStrError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct StripSuffixError(());
impl fmt::Display for StripSuffixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("suffix not found")
}
}
#[cfg(feature = "std")]
impl std::error::Error for StripSuffixError {}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "std")]
macro_rules! assert_panic {
( $cond:expr ) => {{
let result = std::panic::catch_unwind(|| $cond);
assert!(result.is_err());
}};
}
#[test]
#[cfg(feature = "std")]
fn impls() {
fn assert_to_relative_name<T: ToRelativeName + ?Sized>(_: &T) {}
assert_to_relative_name(
RelativeName::from_slice(b"\x03www".as_ref()).unwrap(),
);
assert_to_relative_name(
&RelativeName::from_octets(b"\x03www").unwrap(),
);
assert_to_relative_name(
&RelativeName::from_octets(b"\x03www".as_ref()).unwrap(),
);
assert_to_relative_name(
&RelativeName::from_octets(Vec::from(b"\x03www".as_ref()))
.unwrap(),
);
}
#[cfg(feature = "bytes")]
#[test]
fn impl_bytes() {
fn assert_to_relative_name<T: ToRelativeName + ?Sized>(_: &T) {}
assert_to_relative_name(
&RelativeName::from_octets(Bytes::from(b"\x03www".as_ref()))
.unwrap(),
);
}
#[test]
fn empty() {
assert_eq!(RelativeName::empty_slice().as_slice(), b"");
assert_eq!(RelativeName::empty_ref().as_slice(), b"");
#[cfg(feature = "std")]
{
assert_eq!(RelativeName::empty_vec().as_slice(), b"");
}
}
#[test]
fn wildcard() {
assert_eq!(RelativeName::wildcard_slice().as_slice(), b"\x01*");
assert_eq!(RelativeName::wildcard_ref().as_slice(), b"\x01*");
#[cfg(feature = "std")]
{
assert_eq!(RelativeName::wildcard_vec().as_slice(), b"\x01*");
}
}
#[cfg(feature = "bytes")]
#[test]
fn literals_bytes() {
assert_eq!(RelativeName::empty_bytes().as_slice(), b"");
assert_eq!(RelativeName::wildcard_bytes().as_slice(), b"\x01*");
}
#[test]
#[cfg(feature = "std")]
fn from_slice() {
assert_eq!(RelativeName::from_slice(b"").unwrap().as_slice(), b"");
assert_eq!(
RelativeName::from_slice(b"\x03www").unwrap().as_slice(),
b"\x03www"
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example")
.unwrap()
.as_slice(),
b"\x03www\x07example"
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com\0"),
Err(RelativeNameError(RelativeNameErrorEnum::AbsoluteName))
);
assert_eq!(
RelativeName::from_slice(b"\0"),
Err(RelativeNameError(RelativeNameErrorEnum::AbsoluteName))
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07exa"),
Err(RelativeNameError(RelativeNameErrorEnum::ShortInput))
);
let mut slice = [0u8; 64];
slice[0] = 63;
assert!(RelativeName::from_slice(&slice[..]).is_ok());
let mut slice = [0u8; 65];
slice[0] = 64;
assert!(RelativeName::from_slice(&slice[..]).is_err());
let mut buf = Vec::new();
for _ in 0..25 {
buf.extend_from_slice(b"\x09123456789");
}
assert_eq!(buf.len(), 250);
let mut tmp = buf.clone();
tmp.extend_from_slice(b"\x03123");
assert_eq!(RelativeName::from_slice(&tmp).map(|_| ()), Ok(()));
buf.extend_from_slice(b"\x041234");
assert!(RelativeName::from_slice(&buf).is_err());
assert_eq!(
RelativeName::from_slice(b"\xa2asdasds"),
Err(LabelTypeError::Undefined.into())
);
assert_eq!(
RelativeName::from_slice(b"\x62asdasds"),
Err(LabelTypeError::Extended(0x62).into())
);
assert_eq!(
RelativeName::from_slice(b"\xccasdasds"),
Err(RelativeNameError(RelativeNameErrorEnum::CompressedName))
);
}
#[test]
#[cfg(feature = "std")]
fn from_str() {
assert_eq!(RelativeName::vec_from_str("").unwrap().as_slice(), b"");
assert_eq!(
RelativeName::vec_from_str("www.example")
.unwrap()
.as_slice(),
b"\x03www\x07example"
);
assert!(RelativeName::vec_from_str("www.example.com.").is_err());
}
#[test]
#[cfg(feature = "std")]
fn into_absolute() {
assert_eq!(
RelativeName::from_octets(Vec::from(
b"\x03www\x07example\x03com".as_ref()
))
.unwrap()
.into_absolute()
.unwrap()
.as_slice(),
b"\x03www\x07example\x03com\0"
);
let mut buf = Vec::new();
for _ in 0..25 {
buf.extend_from_slice(b"\x09123456789");
}
assert_eq!(buf.len(), 250);
let mut tmp = buf.clone();
tmp.extend_from_slice(b"\x03123");
RelativeName::from_octets(tmp)
.unwrap()
.into_absolute()
.unwrap();
}
#[test]
#[cfg(feature = "std")]
fn make_canonical() {
let mut name = Name::vec_from_str("wWw.exAmpLE.coM.").unwrap();
name.make_canonical();
assert_eq!(
name,
Name::from_octets(b"\x03www\x07example\x03com\0").unwrap()
);
}
#[test]
fn chain_root() {
assert_eq!(
Name::from_octets(b"\x03www\x07example\x03com\0").unwrap(),
RelativeName::from_octets(b"\x03www\x07example\x03com")
.unwrap()
.chain_root()
);
}
#[test]
fn iter() {
use crate::base::name::absolute::test::cmp_iter;
cmp_iter(RelativeName::empty_ref().iter(), &[]);
cmp_iter(RelativeName::wildcard_ref().iter(), &[b"*"]);
cmp_iter(
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap()
.iter(),
&[b"www", b"example", b"com"],
);
}
#[test]
fn iter_back() {
use crate::base::name::absolute::test::cmp_iter_back;
cmp_iter_back(RelativeName::empty_ref().iter(), &[]);
cmp_iter_back(RelativeName::wildcard_ref().iter(), &[b"*"]);
cmp_iter_back(
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap()
.iter(),
&[b"com", b"example", b"www"],
);
}
#[test]
fn label_count() {
assert_eq!(RelativeName::empty_ref().label_count(), 0);
assert_eq!(RelativeName::wildcard_slice().label_count(), 1);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap()
.label_count(),
3
);
}
#[test]
fn first() {
assert_eq!(RelativeName::empty_slice().first(), None);
assert_eq!(
RelativeName::from_slice(b"\x03www")
.unwrap()
.first()
.unwrap()
.as_slice(),
b"www"
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example")
.unwrap()
.first()
.unwrap()
.as_slice(),
b"www"
);
}
#[test]
fn last() {
assert_eq!(RelativeName::empty_slice().last(), None);
assert_eq!(
RelativeName::from_slice(b"\x03www")
.unwrap()
.last()
.unwrap()
.as_slice(),
b"www"
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example")
.unwrap()
.last()
.unwrap()
.as_slice(),
b"example"
);
}
#[test]
fn ndots() {
assert_eq!(RelativeName::empty_slice().ndots(), 0);
assert_eq!(RelativeName::from_slice(b"\x03www").unwrap().ndots(), 0);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example")
.unwrap()
.ndots(),
1
);
}
#[test]
fn starts_with() {
let matrix = [
(
RelativeName::empty_slice(),
[true, false, false, false, false, false],
),
(
RelativeName::from_slice(b"\x03www").unwrap(),
[true, true, false, false, false, false],
),
(
RelativeName::from_slice(b"\x03www\x07example").unwrap(),
[true, true, true, false, false, false],
),
(
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap(),
[true, true, true, true, false, false],
),
(
RelativeName::from_slice(b"\x07example\x03com").unwrap(),
[true, false, false, false, true, false],
),
(
RelativeName::from_slice(b"\x03com").unwrap(),
[true, false, false, false, false, true],
),
];
for i in 0..6 {
for j in 0..6 {
assert_eq!(
matrix[i].0.starts_with(&matrix[j].0),
matrix[i].1[j],
"i={}, j={}",
i,
j
)
}
}
}
#[test]
fn ends_with() {
let matrix = [
(
RelativeName::empty_slice(),
[true, false, false, false, false, false],
),
(
RelativeName::from_slice(b"\x03www").unwrap(),
[true, true, false, false, false, false],
),
(
RelativeName::from_slice(b"\x03www\x07example").unwrap(),
[true, false, true, false, false, false],
),
(
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap(),
[true, false, false, true, true, true],
),
(
RelativeName::from_slice(b"\x07example\x03com").unwrap(),
[true, false, false, false, true, true],
),
(
RelativeName::from_slice(b"\x03com").unwrap(),
[true, false, false, false, false, true],
),
];
for i in 0..matrix.len() {
for j in 0..matrix.len() {
assert_eq!(
matrix[i].0.ends_with(&matrix[j].0),
matrix[i].1[j],
"i={}, j={}",
i,
j
)
}
}
}
#[test]
fn is_label_start() {
let wec =
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap();
assert!(wec.is_label_start(0)); assert!(!wec.is_label_start(1)); assert!(!wec.is_label_start(2)); assert!(!wec.is_label_start(3)); assert!(wec.is_label_start(4)); assert!(!wec.is_label_start(5)); assert!(!wec.is_label_start(6)); assert!(!wec.is_label_start(7)); assert!(!wec.is_label_start(8)); assert!(!wec.is_label_start(9)); assert!(!wec.is_label_start(10)); assert!(!wec.is_label_start(11)); assert!(wec.is_label_start(12)); assert!(!wec.is_label_start(13)); assert!(!wec.is_label_start(14)); assert!(!wec.is_label_start(15)); assert!(wec.is_label_start(16)); assert!(!wec.is_label_start(17)); }
#[test]
#[cfg(feature = "std")]
fn slice() {
let wec =
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap();
assert_eq!(wec.slice(0..4).as_slice(), b"\x03www");
assert_eq!(wec.slice(0..12).as_slice(), b"\x03www\x07example");
assert_eq!(wec.slice(4..12).as_slice(), b"\x07example");
assert_eq!(wec.slice(4..16).as_slice(), b"\x07example\x03com");
assert_panic!(wec.slice(0..3));
assert_panic!(wec.slice(1..4));
assert_panic!(wec.slice(0..11));
assert_panic!(wec.slice(1..12));
assert_panic!(wec.slice(0..17));
assert_panic!(wec.slice(4..17));
assert_panic!(wec.slice(0..18));
}
#[test]
#[cfg(feature = "std")]
fn range() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
assert_eq!(wec.range(0..4).as_slice(), b"\x03www");
assert_eq!(wec.range(0..12).as_slice(), b"\x03www\x07example");
assert_eq!(wec.range(4..12).as_slice(), b"\x07example");
assert_eq!(wec.range(4..16).as_slice(), b"\x07example\x03com");
assert_panic!(wec.range(0..3));
assert_panic!(wec.range(1..4));
assert_panic!(wec.range(0..11));
assert_panic!(wec.range(1..12));
assert_panic!(wec.range(0..17));
assert_panic!(wec.range(4..17));
assert_panic!(wec.range(0..18));
}
#[test]
#[cfg(feature = "std")]
fn split() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
let (left, right) = wec.split(0);
assert_eq!(left.as_slice(), b"");
assert_eq!(right.as_slice(), b"\x03www\x07example\x03com");
let (left, right) = wec.split(4);
assert_eq!(left.as_slice(), b"\x03www");
assert_eq!(right.as_slice(), b"\x07example\x03com");
let (left, right) = wec.split(12);
assert_eq!(left.as_slice(), b"\x03www\x07example");
assert_eq!(right.as_slice(), b"\x03com");
let (left, right) = wec.split(16);
assert_eq!(left.as_slice(), b"\x03www\x07example\x03com");
assert_eq!(right.as_slice(), b"");
assert_panic!(wec.split(1));
assert_panic!(wec.split(14));
assert_panic!(wec.split(17));
assert_panic!(wec.split(18));
}
#[test]
#[cfg(feature = "std")]
fn truncate() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
let mut tmp = wec.clone();
tmp.truncate(0);
assert_eq!(tmp.as_slice(), b"");
let mut tmp = wec.clone();
tmp.truncate(4);
assert_eq!(tmp.as_slice(), b"\x03www");
let mut tmp = wec.clone();
tmp.truncate(12);
assert_eq!(tmp.as_slice(), b"\x03www\x07example");
let mut tmp = wec.clone();
tmp.truncate(16);
assert_eq!(tmp.as_slice(), b"\x03www\x07example\x03com");
assert_panic!(wec.clone().truncate(1));
assert_panic!(wec.clone().truncate(14));
assert_panic!(wec.clone().truncate(17));
assert_panic!(wec.clone().truncate(18));
}
#[test]
fn split_first() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
let (label, wec) = wec.split_first().unwrap();
assert_eq!(label.as_slice(), b"www");
assert_eq!(wec.as_slice(), b"\x07example\x03com");
let (label, wec) = wec.split_first().unwrap();
assert_eq!(label.as_slice(), b"example");
assert_eq!(wec.as_slice(), b"\x03com");
let (label, wec) = wec.split_first().unwrap();
assert_eq!(label.as_slice(), b"com");
assert_eq!(wec.as_slice(), b"");
assert!(wec.split_first().is_none());
}
#[test]
fn parent() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
let wec = wec.parent().unwrap();
assert_eq!(wec.as_slice(), b"\x07example\x03com");
let wec = wec.parent().unwrap();
assert_eq!(wec.as_slice(), b"\x03com");
let wec = wec.parent().unwrap();
assert_eq!(wec.as_slice(), b"");
assert!(wec.parent().is_none());
}
#[test]
fn strip_suffix() {
let wec =
RelativeName::from_octets(b"\x03www\x07example\x03com".as_ref())
.unwrap();
let ec = RelativeName::from_octets(b"\x07example\x03com".as_ref())
.unwrap();
let c = RelativeName::from_octets(b"\x03com".as_ref()).unwrap();
let wen =
RelativeName::from_octets(b"\x03www\x07example\x03net".as_ref())
.unwrap();
let en = RelativeName::from_octets(b"\x07example\x03net".as_ref())
.unwrap();
let n = RelativeName::from_slice(b"\x03net".as_ref()).unwrap();
let mut tmp = wec.clone();
assert_eq!(tmp.strip_suffix(&wec), Ok(()));
assert_eq!(tmp.as_slice(), b"");
let mut tmp = wec.clone();
assert_eq!(tmp.strip_suffix(&ec), Ok(()));
assert_eq!(tmp.as_slice(), b"\x03www");
let mut tmp = wec.clone();
assert_eq!(tmp.strip_suffix(&c), Ok(()));
assert_eq!(tmp.as_slice(), b"\x03www\x07example");
let mut tmp = wec.clone();
assert_eq!(tmp.strip_suffix(&RelativeName::empty_ref()), Ok(()));
assert_eq!(tmp.as_slice(), b"\x03www\x07example\x03com");
assert!(wec.clone().strip_suffix(&wen).is_err());
assert!(wec.clone().strip_suffix(&en).is_err());
assert!(wec.clone().strip_suffix(&n).is_err());
}
#[test]
fn eq() {
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap()
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
RelativeName::from_slice(b"\x03wWw\x07eXAMple\x03Com").unwrap()
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
&RelativeName::from_octets(b"\x03www")
.unwrap()
.chain(
RelativeName::from_octets(b"\x07example\x03com").unwrap()
)
.unwrap()
);
assert_eq!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
&RelativeName::from_octets(b"\x03wWw")
.unwrap()
.chain(
RelativeName::from_octets(b"\x07eXAMple\x03coM").unwrap()
)
.unwrap()
);
assert_ne!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
RelativeName::from_slice(b"\x03ww4\x07example\x03com").unwrap()
);
assert_ne!(
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap(),
&RelativeName::from_octets(b"\x03www")
.unwrap()
.chain(
RelativeName::from_octets(b"\x073xample\x03com").unwrap()
)
.unwrap()
);
}
#[test]
fn cmp() {
use core::cmp::Ordering;
let names = [
RelativeName::from_slice(b"\x07example").unwrap(),
RelativeName::from_slice(b"\x01a\x07example").unwrap(),
RelativeName::from_slice(b"\x08yljkjljk\x01a\x07example")
.unwrap(),
RelativeName::from_slice(b"\x01Z\x01a\x07example").unwrap(),
RelativeName::from_slice(b"\x04zABC\x01a\x07example").unwrap(),
RelativeName::from_slice(b"\x01z\x07example").unwrap(),
RelativeName::from_slice(b"\x01\x01\x01z\x07example").unwrap(),
RelativeName::from_slice(b"\x01*\x01z\x07example").unwrap(),
RelativeName::from_slice(b"\x01\xc8\x01z\x07example").unwrap(),
];
for i in 0..names.len() {
for j in 0..names.len() {
let ord = i.cmp(&j);
assert_eq!(names[i].partial_cmp(names[j]), Some(ord));
assert_eq!(names[i].cmp(names[j]), ord);
}
}
let n1 =
RelativeName::from_slice(b"\x03www\x07example\x03com").unwrap();
let n2 =
RelativeName::from_slice(b"\x03wWw\x07eXAMple\x03Com").unwrap();
assert_eq!(n1.partial_cmp(n2), Some(Ordering::Equal));
assert_eq!(n1.cmp(n2), Ordering::Equal);
}
#[test]
#[cfg(feature = "std")]
fn hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut s1 = DefaultHasher::new();
let mut s2 = DefaultHasher::new();
RelativeName::from_slice(b"\x03www\x07example\x03com")
.unwrap()
.hash(&mut s1);
RelativeName::from_slice(b"\x03wWw\x07eXAMple\x03Com")
.unwrap()
.hash(&mut s2);
assert_eq!(s1.finish(), s2.finish());
}
#[test]
#[cfg(feature = "std")]
fn display() {
use std::string::ToString;
fn cmp(bytes: &[u8], fmt: &str) {
let name = RelativeName::from_octets(bytes).unwrap();
assert_eq!(name.to_string(), fmt);
}
cmp(b"", "");
cmp(b"\x03com", "com");
cmp(b"\x07example\x03com", "example.com");
}
#[cfg(all(feature = "serde", feature = "std"))]
#[test]
fn ser_de() {
use serde_test::{assert_tokens, Configure, Token};
let name = RelativeName::from_octets(Vec::from(
b"\x03www\x07example\x03com".as_ref(),
))
.unwrap();
assert_tokens(
&name.clone().compact(),
&[
Token::NewtypeStruct {
name: "RelativeName",
},
Token::ByteBuf(b"\x03www\x07example\x03com"),
],
);
assert_tokens(
&name.readable(),
&[
Token::NewtypeStruct {
name: "RelativeName",
},
Token::Str("www.example.com"),
],
);
}
}