1use super::super::scan::BadSymbol;
7use super::super::wire::{FormError, ParseError};
8use super::builder::{
9 parse_escape, LabelFromStrError, LabelFromStrErrorEnum,
10};
11use core::str::FromStr;
12use core::{borrow, cmp, fmt, hash, iter, mem, ops, slice};
13use octseq::builder::OctetsBuilder;
14
15#[repr(transparent)]
41pub struct Label([u8]);
42
43impl Label {
46 pub const MAX_LEN: usize = 63;
48
49 pub(super) unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
55 mem::transmute(slice)
57 }
58
59 pub(super) unsafe fn from_slice_mut_unchecked(
65 slice: &mut [u8],
66 ) -> &mut Self {
67 mem::transmute(slice)
69 }
70
71 #[must_use]
75 pub fn root() -> &'static Self {
76 unsafe { Self::from_slice_unchecked(b"") }
77 }
78
79 #[must_use]
81 pub fn wildcard() -> &'static Self {
82 unsafe { Self::from_slice_unchecked(b"*") }
83 }
84
85 pub fn from_slice(slice: &[u8]) -> Result<&Self, LongLabelError> {
89 if slice.len() > Label::MAX_LEN {
90 Err(LongLabelError(()))
91 } else {
92 Ok(unsafe { Self::from_slice_unchecked(slice) })
93 }
94 }
95
96 pub fn from_slice_mut(
100 slice: &mut [u8],
101 ) -> Result<&mut Self, LongLabelError> {
102 if slice.len() > Label::MAX_LEN {
103 Err(LongLabelError(()))
104 } else {
105 Ok(unsafe { Self::from_slice_mut_unchecked(slice) })
106 }
107 }
108
109 pub fn split_from(
114 slice: &[u8],
115 ) -> Result<(&Self, &[u8]), SplitLabelError> {
116 let head = match slice.first() {
117 Some(ch) => *ch,
118 None => return Err(SplitLabelError::ShortInput),
119 };
120 let end = match head {
121 0..=0x3F => (head as usize) + 1,
122 0x40..=0x7F => {
123 return Err(SplitLabelError::BadType(
124 LabelTypeError::Extended(head),
125 ))
126 }
127 0xC0..=0xFF => {
128 let res = match slice.get(1) {
129 Some(ch) => u16::from(*ch),
130 None => return Err(SplitLabelError::ShortInput),
131 };
132 let res = res | ((u16::from(head) & 0x3F) << 8);
133 return Err(SplitLabelError::Pointer(res));
134 }
135 _ => {
136 return Err(SplitLabelError::BadType(
137 LabelTypeError::Undefined,
138 ))
139 }
140 };
141 if slice.len() < end {
142 return Err(SplitLabelError::ShortInput);
143 }
144 Ok((
145 unsafe { Self::from_slice_unchecked(&slice[1..end]) },
146 &slice[end..],
147 ))
148 }
149
150 pub fn split_from_mut(
155 slice: &mut [u8],
156 ) -> Result<(&mut Self, &mut [u8]), SplitLabelError> {
157 let head = match slice.first_mut() {
158 Some(ch) => *ch,
159 None => return Err(SplitLabelError::ShortInput),
160 };
161 let end = match head {
162 0..=0x3F => (head as usize) + 1,
163 0x40..=0x7F => {
164 return Err(SplitLabelError::BadType(
165 LabelTypeError::Extended(head),
166 ))
167 }
168 0xC0..=0xFF => {
169 let res = match slice.get(1) {
170 Some(ch) => u16::from(*ch),
171 None => return Err(SplitLabelError::ShortInput),
172 };
173 let res = res | ((u16::from(head) & 0x3F) << 8);
174 return Err(SplitLabelError::Pointer(res));
175 }
176 _ => {
177 return Err(SplitLabelError::BadType(
178 LabelTypeError::Undefined,
179 ))
180 }
181 };
182 if slice.len() < end {
183 return Err(SplitLabelError::ShortInput);
184 }
185 let (left, right) = slice.split_at_mut(end);
186 Ok((
187 unsafe { Self::from_slice_mut_unchecked(&mut left[1..]) },
188 right,
189 ))
190 }
191
192 pub fn iter(&self) -> iter::Copied<slice::Iter<u8>> {
194 self.as_slice().iter().copied()
195 }
196
197 #[must_use]
208 pub fn iter_slice(slice: &[u8], start: usize) -> SliceLabelsIter {
209 SliceLabelsIter { slice, start }
210 }
211
212 #[must_use]
214 pub fn as_slice(&self) -> &[u8] {
215 &self.0
216 }
217
218 pub fn as_slice_mut(&mut self) -> &mut [u8] {
220 &mut self.0
221 }
222
223 pub fn make_canonical(&mut self) {
228 self.0.make_ascii_lowercase()
229 }
230
231 pub(super) fn make_slice_canonical(mut slice: &mut [u8]) {
242 while !slice.is_empty() {
243 let (head, tail) =
244 Label::split_from_mut(slice).expect("invalid name");
245 head.make_canonical();
246 slice = tail;
247 }
248 }
249
250 #[must_use]
254 pub fn to_canonical(&self) -> OwnedLabel {
255 let mut res = OwnedLabel::from_label(self);
256 res.make_canonical();
257 res
258 }
259
260 #[must_use]
262 pub fn composed_cmp(&self, other: &Self) -> cmp::Ordering {
263 match self.0.len().cmp(&other.0.len()) {
264 cmp::Ordering::Equal => {}
265 other => return other,
266 }
267 self.0.cmp(other.as_ref())
268 }
269
270 #[must_use]
272 pub fn lowercase_composed_cmp(&self, other: &Self) -> cmp::Ordering {
273 match self.0.len().cmp(&other.0.len()) {
274 cmp::Ordering::Equal => {}
275 other => return other,
276 }
277 self.cmp(other)
278 }
279
280 pub fn compose<Builder: OctetsBuilder + ?Sized>(
285 &self,
286 target: &mut Builder,
287 ) -> Result<(), Builder::AppendError> {
288 target.append_slice(&[self.len() as u8])?;
289 target.append_slice(self.as_slice())
290 }
291
292 pub fn compose_canonical<Builder: OctetsBuilder + ?Sized>(
298 &self,
299 target: &mut Builder,
300 ) -> Result<(), Builder::AppendError> {
301 target.append_slice(&[self.len() as u8])?;
302 for ch in self.into_iter() {
303 target.append_slice(&[ch.to_ascii_lowercase()])?;
304 }
305 Ok(())
306 }
307}
308
309impl Label {
312 #[must_use]
317 pub fn len(&self) -> usize {
318 self.as_slice().len()
319 }
320
321 #[must_use]
323 pub fn is_empty(&self) -> bool {
324 self.as_slice().is_empty()
325 }
326
327 #[must_use]
329 pub fn is_root(&self) -> bool {
330 self.is_empty()
331 }
332
333 #[must_use]
335 pub fn is_wildcard(&self) -> bool {
336 self.0.len() == 1 && self.0[0] == b'*'
337 }
338
339 #[must_use]
344 pub fn compose_len(&self) -> u16 {
345 u16::try_from(self.len()).expect("long label") + 1
346 }
347}
348
349impl AsRef<[u8]> for Label {
352 fn as_ref(&self) -> &[u8] {
353 self.as_slice()
354 }
355}
356
357impl AsMut<[u8]> for Label {
358 fn as_mut(&mut self) -> &mut [u8] {
359 self.as_slice_mut()
360 }
361}
362
363#[cfg(feature = "std")]
366impl std::borrow::ToOwned for Label {
367 type Owned = OwnedLabel;
368
369 fn to_owned(&self) -> Self::Owned {
370 self.into()
371 }
372}
373
374impl<T: AsRef<[u8]> + ?Sized> PartialEq<T> for Label {
377 fn eq(&self, other: &T) -> bool {
378 self.as_slice().eq_ignore_ascii_case(other.as_ref())
379 }
380}
381
382impl Eq for Label {}
383
384impl PartialOrd for Label {
387 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
397 Some(self.cmp(other))
398 }
399}
400
401impl Ord for Label {
402 fn cmp(&self, other: &Self) -> cmp::Ordering {
403 self.as_slice()
404 .iter()
405 .map(u8::to_ascii_lowercase)
406 .cmp(other.as_slice().iter().map(u8::to_ascii_lowercase))
407 }
408}
409
410impl hash::Hash for Label {
413 fn hash<H: hash::Hasher>(&self, state: &mut H) {
414 (self.len() as u8).hash(state);
417 for c in self.iter() {
418 c.to_ascii_lowercase().hash(state)
419 }
420 }
421}
422
423impl<'a> IntoIterator for &'a Label {
426 type Item = u8;
427 type IntoIter = iter::Copied<slice::Iter<'a, u8>>;
428
429 fn into_iter(self) -> Self::IntoIter {
430 self.iter()
431 }
432}
433
434impl fmt::Display for Label {
437 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438 for ch in self.iter() {
439 if ch == b' ' || ch == b'.' || ch == b'\\' {
440 write!(f, "\\{}", ch as char)?;
441 } else if !(0x20..0x7F).contains(&ch) {
442 write!(f, "\\{:03}", ch)?;
443 } else {
444 write!(f, "{}", (ch as char))?;
445 }
446 }
447 Ok(())
448 }
449}
450
451impl fmt::Debug for Label {
452 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
453 f.write_str("Label(")?;
454 fmt::Display::fmt(self, f)?;
455 f.write_str(")")
456 }
457}
458
459#[derive(Clone, Copy)]
469pub struct OwnedLabel([u8; 64]);
470
471impl OwnedLabel {
472 #[must_use]
474 pub fn from_label(label: &Label) -> Self {
475 let mut res = [0; 64];
476 res[0] = label.len() as u8;
477 res[1..=label.len()].copy_from_slice(label.as_slice());
478 OwnedLabel(res)
479 }
480
481 pub fn from_chars(
483 mut chars: impl Iterator<Item = char>,
484 ) -> Result<Self, LabelFromStrError> {
485 let mut res = [0u8; 64];
486 while let Some(ch) = chars.next() {
487 if res[0] as usize >= Label::MAX_LEN {
488 return Err(LabelFromStrErrorEnum::LongLabel.into());
489 }
490 let ch = match ch {
491 ' '..='-' | '/'..='[' | ']'..='~' => ch as u8,
492 '\\' => parse_escape(&mut chars, res[0] > 0)?,
493 _ => return Err(BadSymbol::non_ascii().into()),
494 };
495 res[(res[0] as usize) + 1] = ch;
496 res[0] += 1;
497 }
498 Ok(OwnedLabel(res))
499 }
500
501 pub fn make_canonical(&mut self) {
506 self.0[1..].make_ascii_lowercase()
507 }
508
509 #[must_use]
511 pub fn as_label(&self) -> &Label {
512 unsafe {
513 Label::from_slice_unchecked(&self.0[1..=(self.0[0] as usize)])
514 }
515 }
516
517 pub fn as_label_mut(&mut self) -> &mut Label {
519 let len = self.0[0] as usize;
520 unsafe { Label::from_slice_mut_unchecked(&mut self.0[1..=len]) }
521 }
522
523 #[must_use]
525 pub fn as_wire_slice(&self) -> &[u8] {
526 let len = self.0[0] as usize;
527 &self.0[..=len]
528 }
529}
530
531impl<'a> From<&'a Label> for OwnedLabel {
534 fn from(label: &'a Label) -> Self {
535 Self::from_label(label)
536 }
537}
538
539impl FromStr for OwnedLabel {
542 type Err = LabelFromStrError;
543
544 fn from_str(s: &str) -> Result<Self, Self::Err> {
545 Self::from_chars(s.chars())
546 }
547}
548
549impl ops::Deref for OwnedLabel {
552 type Target = Label;
553
554 fn deref(&self) -> &Label {
555 self.as_label()
556 }
557}
558
559impl ops::DerefMut for OwnedLabel {
560 fn deref_mut(&mut self) -> &mut Label {
561 self.as_label_mut()
562 }
563}
564
565impl AsRef<Label> for OwnedLabel {
566 fn as_ref(&self) -> &Label {
567 self.as_label()
568 }
569}
570
571impl AsRef<[u8]> for OwnedLabel {
572 fn as_ref(&self) -> &[u8] {
573 self.as_label().as_slice()
574 }
575}
576
577impl AsMut<Label> for OwnedLabel {
578 fn as_mut(&mut self) -> &mut Label {
579 self.as_label_mut()
580 }
581}
582
583impl AsMut<[u8]> for OwnedLabel {
584 fn as_mut(&mut self) -> &mut [u8] {
585 self.as_label_mut().as_slice_mut()
586 }
587}
588
589impl borrow::Borrow<Label> for OwnedLabel {
590 fn borrow(&self) -> &Label {
591 self.as_label()
592 }
593}
594
595impl borrow::BorrowMut<Label> for OwnedLabel {
596 fn borrow_mut(&mut self) -> &mut Label {
597 self.as_label_mut()
598 }
599}
600
601impl<T: AsRef<Label>> PartialEq<T> for OwnedLabel {
604 fn eq(&self, other: &T) -> bool {
605 self.as_label().eq(other.as_ref())
606 }
607}
608
609impl Eq for OwnedLabel {}
610
611impl<T: AsRef<Label>> PartialOrd<T> for OwnedLabel {
614 fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
615 self.as_label().partial_cmp(other.as_ref())
616 }
617}
618
619impl Ord for OwnedLabel {
620 fn cmp(&self, other: &Self) -> cmp::Ordering {
621 self.as_label().cmp(other.as_ref())
622 }
623}
624
625impl hash::Hash for OwnedLabel {
628 fn hash<H: hash::Hasher>(&self, state: &mut H) {
629 self.as_label().hash(state)
630 }
631}
632
633impl fmt::Display for OwnedLabel {
636 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637 self.as_label().fmt(f)
638 }
639}
640
641impl fmt::Debug for OwnedLabel {
642 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643 f.debug_tuple("OwnedLabel").field(&self.as_label()).finish()
644 }
645}
646
647#[cfg(feature = "serde")]
650impl serde::Serialize for OwnedLabel {
651 fn serialize<S: serde::Serializer>(
652 &self,
653 serializer: S,
654 ) -> Result<S::Ok, S::Error> {
655 use octseq::serde::SerializeOctets;
656
657 if serializer.is_human_readable() {
658 serializer.serialize_newtype_struct(
659 "OwnedLabel",
660 &format_args!("{}", self),
661 )
662 } else {
663 serializer.serialize_newtype_struct(
664 "OwnedLabel",
665 &self.as_label().as_slice().as_serialized_octets(),
666 )
667 }
668 }
669}
670
671#[cfg(feature = "serde")]
672impl<'de> serde::Deserialize<'de> for OwnedLabel {
673 fn deserialize<D: serde::Deserializer<'de>>(
674 deserializer: D,
675 ) -> Result<Self, D::Error> {
676 use serde::de::Error;
677
678 struct InnerVisitor;
679
680 impl<'de> serde::de::Visitor<'de> for InnerVisitor {
681 type Value = OwnedLabel;
682
683 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
684 f.write_str("an domain name label")
685 }
686
687 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
688 OwnedLabel::from_str(v).map_err(E::custom)
689 }
690
691 fn visit_borrowed_bytes<E: serde::de::Error>(
692 self,
693 value: &'de [u8],
694 ) -> Result<Self::Value, E> {
695 Label::from_slice(value)
696 .map(OwnedLabel::from_label)
697 .map_err(E::custom)
698 }
699 }
700
701 struct NewtypeVisitor;
702
703 impl<'de> serde::de::Visitor<'de> for NewtypeVisitor {
704 type Value = OwnedLabel;
705
706 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
707 f.write_str("an domain name label")
708 }
709
710 fn visit_newtype_struct<D: serde::Deserializer<'de>>(
711 self,
712 deserializer: D,
713 ) -> Result<Self::Value, D::Error> {
714 if deserializer.is_human_readable() {
715 deserializer.deserialize_str(InnerVisitor)
716 } else {
717 deserializer.deserialize_bytes(InnerVisitor)
718 }
719 }
720 }
721
722 deserializer.deserialize_newtype_struct("OwnedLabel", NewtypeVisitor)
723 }
724}
725
726pub struct SliceLabelsIter<'a> {
734 slice: &'a [u8],
736
737 start: usize,
741}
742
743impl<'a> Iterator for SliceLabelsIter<'a> {
744 type Item = &'a Label;
745
746 fn next(&mut self) -> Option<Self::Item> {
747 if self.start >= self.slice.len() {
748 return None;
749 }
750
751 loop {
752 match Label::split_from(&self.slice[self.start..]) {
753 Ok((label, _)) => {
754 if label.is_root() {
755 self.start = usize::MAX;
756 } else {
757 self.start += label.len() + 1;
758 }
759 return Some(label);
760 }
761 Err(SplitLabelError::Pointer(pos)) => {
762 let pos = pos as usize;
763 if pos > self.start {
764 self.start = usize::MAX;
767 return None;
768 }
769 self.start = pos;
770 continue;
771 }
772 Err(_) => {
773 self.start = usize::MAX;
774 return None;
775 }
776 }
777 }
778 }
779}
780
781#[derive(Clone, Copy, Debug, Eq, PartialEq)]
787pub enum LabelTypeError {
788 Undefined,
790
791 Extended(u8),
796}
797
798impl fmt::Display for LabelTypeError {
801 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802 match *self {
803 LabelTypeError::Undefined => f.write_str("undefined label type"),
804 LabelTypeError::Extended(value) => {
805 write!(f, "unknown extended label 0x{:02x}", value)
806 }
807 }
808 }
809}
810
811#[cfg(feature = "std")]
812impl std::error::Error for LabelTypeError {}
813
814#[derive(Clone, Copy, Debug, Eq, PartialEq)]
818pub struct LongLabelError(());
819
820impl fmt::Display for LongLabelError {
823 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
824 f.write_str("long label")
825 }
826}
827
828#[cfg(feature = "std")]
829impl std::error::Error for LongLabelError {}
830
831#[derive(Clone, Copy, Debug, Eq, PartialEq)]
835pub enum SplitLabelError {
836 Pointer(u16),
838
839 BadType(LabelTypeError),
841
842 ShortInput,
844}
845
846impl From<LabelTypeError> for SplitLabelError {
849 fn from(err: LabelTypeError) -> SplitLabelError {
850 SplitLabelError::BadType(err)
851 }
852}
853
854impl From<SplitLabelError> for ParseError {
855 fn from(err: SplitLabelError) -> ParseError {
856 match err {
857 SplitLabelError::Pointer(_) => {
858 ParseError::Form(FormError::new("compressed domain name"))
859 }
860 SplitLabelError::BadType(_) => {
861 ParseError::Form(FormError::new("invalid label type"))
862 }
863 SplitLabelError::ShortInput => ParseError::ShortInput,
864 }
865 }
866}
867
868impl fmt::Display for SplitLabelError {
871 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
872 match *self {
873 SplitLabelError::Pointer(_) => {
874 f.write_str("compressed domain name")
875 }
876 SplitLabelError::BadType(ltype) => ltype.fmt(f),
877 SplitLabelError::ShortInput => ParseError::ShortInput.fmt(f),
878 }
879 }
880}
881
882#[cfg(feature = "std")]
883impl std::error::Error for SplitLabelError {}
884
885#[cfg(test)]
888mod test {
889 use super::*;
890
891 #[test]
892 fn from_slice() {
893 let x = [0u8; 10];
894 assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
895 let x = [0u8; 63];
896 assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
897 let x = [0u8; 64];
898 assert!(Label::from_slice(&x[..]).is_err());
899 }
900
901 #[test]
902 fn split_from() {
903 assert_eq!(
905 Label::split_from(b"\x03www\x07example\x03com\0").unwrap(),
906 (
907 Label::from_slice(b"www").unwrap(),
908 &b"\x07example\x03com\0"[..]
909 )
910 );
911
912 assert_eq!(
914 Label::split_from(b"\x03www").unwrap(),
915 (Label::from_slice(b"www").unwrap(), &b""[..])
916 );
917
918 assert_eq!(
920 Label::split_from(b"\0some").unwrap(),
921 (Label::from_slice(b"").unwrap(), &b"some"[..])
922 );
923
924 assert_eq!(
926 Label::split_from(b"\x03ww"),
927 Err(SplitLabelError::ShortInput)
928 );
929
930 assert_eq!(Label::split_from(b""), Err(SplitLabelError::ShortInput));
932
933 assert_eq!(
935 Label::split_from(b"\xc0\x05foo"),
936 Err(SplitLabelError::Pointer(5))
937 );
938
939 assert_eq!(
941 Label::split_from(b"\xb3foo"),
942 Err(LabelTypeError::Undefined.into())
943 );
944
945 assert_eq!(
947 Label::split_from(b"\x66foo"),
948 Err(LabelTypeError::Extended(0x66).into())
949 );
950 }
951
952 #[test]
953 #[cfg(feature = "std")]
954 fn compose() {
955 use octseq::builder::infallible;
956 use std::vec::Vec;
957
958 let mut buf = Vec::new();
959 infallible(Label::root().compose(&mut buf));
960 assert_eq!(buf, &b"\0"[..]);
961
962 let mut buf = Vec::new();
963 let label = Label::from_slice(b"123").unwrap();
964 infallible(label.compose(&mut buf));
965 assert_eq!(buf, &b"\x03123"[..]);
966 }
967
968 #[test]
969 fn eq() {
970 assert_eq!(
971 Label::from_slice(b"example").unwrap(),
972 Label::from_slice(b"eXAMple").unwrap()
973 );
974 assert_ne!(
975 Label::from_slice(b"example").unwrap(),
976 Label::from_slice(b"e4ample").unwrap()
977 );
978 }
979
980 #[test]
981 fn cmp() {
982 use core::cmp::Ordering;
983
984 let labels = [
985 Label::root(),
986 Label::from_slice(b"\x01").unwrap(),
987 Label::from_slice(b"*").unwrap(),
988 Label::from_slice(b"\xc8").unwrap(),
989 ];
990 for i in 0..labels.len() {
991 for j in 0..labels.len() {
992 let ord = i.cmp(&j);
993 assert_eq!(labels[i].partial_cmp(labels[j]), Some(ord));
994 assert_eq!(labels[i].cmp(labels[j]), ord);
995 }
996 }
997
998 let l1 = Label::from_slice(b"example").unwrap();
999 let l2 = Label::from_slice(b"eXAMple").unwrap();
1000 assert_eq!(l1.partial_cmp(l2), Some(Ordering::Equal));
1001 assert_eq!(l1.cmp(l2), Ordering::Equal);
1002 }
1003
1004 #[test]
1005 #[cfg(feature = "std")]
1006 fn hash() {
1007 use std::collections::hash_map::DefaultHasher;
1008 use std::hash::{Hash, Hasher};
1009
1010 let mut s1 = DefaultHasher::new();
1011 let mut s2 = DefaultHasher::new();
1012 Label::from_slice(b"example").unwrap().hash(&mut s1);
1013 Label::from_slice(b"eXAMple").unwrap().hash(&mut s2);
1014 assert_eq!(s1.finish(), s2.finish());
1015 }
1016
1017 #[cfg(feature = "serde")]
1020 #[test]
1021 fn owned_label_ser_de() {
1022 use serde_test::{assert_tokens, Configure, Token};
1023
1024 let label =
1025 OwnedLabel::from_label(Label::from_slice(b"fo.").unwrap());
1026 assert_tokens(
1027 &label.compact(),
1028 &[
1029 Token::NewtypeStruct { name: "OwnedLabel" },
1030 Token::BorrowedBytes(b"fo."),
1031 ],
1032 );
1033 assert_tokens(
1034 &label.readable(),
1035 &[
1036 Token::NewtypeStruct { name: "OwnedLabel" },
1037 Token::Str("fo\\."),
1038 ],
1039 );
1040 }
1041
1042 #[test]
1043 fn iter_slice() {
1044 assert_eq!(None, Label::iter_slice(&[], 0).next());
1045 assert_eq!(None, Label::iter_slice(&[], 1).next());
1046
1047 let buf = [
1049 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f,
1050 0x6d, 0x00,
1051 ];
1052
1053 let mut it = Label::iter_slice(&buf, 0);
1054 assert_eq!(Label::from_slice(b"example").ok(), it.next());
1055 assert_eq!(Label::from_slice(b"com").ok(), it.next());
1056 assert_eq!(Some(Label::root()), it.next());
1057 assert_eq!(None, it.next());
1058
1059 let mut it = Label::iter_slice(&buf, b"example".len() + 1);
1060 assert_eq!(
1061 Label::from_slice(b"com").ok(),
1062 it.next(),
1063 "should jump to 2nd label"
1064 );
1065
1066 let mut it = Label::iter_slice(&buf, buf.len() - 1);
1067 assert_eq!(
1068 Some(Label::root()),
1069 it.next(),
1070 "should jump to last/root label"
1071 );
1072 }
1073}