1use crate::{
2 bytes_buffer::BytesBuffer,
3 lib::{
4 fmt::{Debug, Display, Formatter, Result as FmtResult},
5 format, Cow, Hash, Hasher, Iter, Seek, String, ToString, TryFrom, Vec, Write,
6 },
7};
8
9use super::{WireFormat, MAX_LABEL_LENGTH, MAX_NAME_LENGTH};
10
11const POINTER_MASK: u8 = 0b1100_0000;
12const POINTER_MASK_U16: u16 = 0b1100_0000_0000_0000;
13const MAX_COMPRESSION_OFFSET: u64 = !POINTER_MASK_U16 as u64;
14
15#[derive(Eq, Clone)]
30pub struct Name<'a> {
31 labels: Vec<Label<'a>>,
32}
33
34impl<'a> Name<'a> {
35 pub fn new(name: &'a str) -> crate::Result<Self> {
37 let labels = LabelsIter::new(name.as_bytes())
38 .map(Label::new)
39 .collect::<Result<Vec<Label>, _>>()?;
40
41 let name = Self { labels };
42
43 if name.len() > MAX_NAME_LENGTH {
44 Err(crate::SimpleDnsError::InvalidServiceName)
45 } else {
46 Ok(name)
47 }
48 }
49
50 pub fn new_unchecked(name: &'a str) -> Self {
52 let labels = LabelsIter::new(name.as_bytes())
53 .map(Label::new_unchecked)
54 .collect();
55
56 Self { labels }
57 }
58
59 pub fn new_with_labels(labels: &[Label<'a>]) -> Self {
63 Self {
64 labels: labels.to_vec(),
65 }
66 }
67
68 pub fn is_link_local(&self) -> bool {
70 match self.iter().last() {
71 Some(label) => b"local".eq_ignore_ascii_case(&label.data),
72 None => false,
73 }
74 }
75
76 pub fn iter(&'a self) -> Iter<'a, Label<'a>> {
78 self.labels.iter()
79 }
80
81 pub fn is_subdomain_of(&self, other: &Name) -> bool {
83 self.labels.len() > other.labels.len()
84 && other
85 .iter()
86 .rev()
87 .zip(self.iter().rev())
88 .all(|(o, s)| *o == *s)
89 }
90
91 pub fn into_owned<'b>(self) -> Name<'b> {
93 Name {
94 labels: self.labels.into_iter().map(|l| l.into_owned()).collect(),
95 }
96 }
97
98 pub fn without(&'_ self, domain: &Name) -> Option<Name<'_>> {
113 if self.is_subdomain_of(domain) {
114 let labels = self.labels[..self.labels.len() - domain.labels.len()].to_vec();
115
116 Some(Name { labels })
117 } else {
118 None
119 }
120 }
121
122 pub fn get_labels(&'_ self) -> &'_ [Label<'a>] {
124 &self.labels[..]
125 }
126
127 fn plain_append<T: Write>(&self, out: &mut T) -> crate::Result<()> {
128 for label in self.iter() {
129 out.write_all(&[label.len() as u8])?;
130 out.write_all(&label.data)?;
131 }
132
133 out.write_all(&[0])?;
134 Ok(())
135 }
136
137 fn compress_append<T: Write + Seek>(
138 &'a self,
139 out: &mut T,
140 name_refs: &mut crate::lib::BTreeMap<&[Label<'a>], u16>,
141 ) -> crate::Result<()> {
142 for (i, label) in self.iter().enumerate() {
143 match name_refs.entry(&self.labels[i..]) {
144 crate::lib::BTreeEntry::Occupied(e) => {
145 let p = *e.get();
146 out.write_all(&(p | POINTER_MASK_U16).to_be_bytes())?;
147
148 return Ok(());
149 }
150 crate::lib::BTreeEntry::Vacant(e) => {
151 let pos = out.stream_position()?;
152 if pos <= MAX_COMPRESSION_OFFSET {
153 e.insert(pos as u16);
154 }
155 out.write_all(&[label.len() as u8])?;
156 out.write_all(&label.data)?;
157 }
158 }
159 }
160
161 out.write_all(&[0])?;
162 Ok(())
163 }
164
165 pub fn is_valid(&self) -> bool {
167 self.labels.iter().all(|label| label.is_valid())
168 }
169
170 pub fn as_bytes(&self) -> impl Iterator<Item = &[u8]> {
172 self.labels.iter().map(|label| label.as_ref())
173 }
174}
175
176impl<'a> WireFormat<'a> for Name<'a> {
177 const MINIMUM_LEN: usize = 1;
178
179 fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
180 where
181 Self: Sized,
182 {
183 fn parse_labels<'a>(
186 data: &mut BytesBuffer<'a>,
187 name_len: &mut usize,
188 labels: &mut Vec<Label<'a>>,
189 ) -> crate::Result<Option<usize>> {
190 loop {
191 match data.get_u8()? {
192 0 => break Ok(None),
193 len if len & POINTER_MASK == POINTER_MASK => {
194 let mut pointer = len as u16;
195 pointer <<= 8;
196 pointer += data.get_u8()? as u16;
197 pointer &= !POINTER_MASK_U16;
198
199 break Ok(Some(pointer as usize));
200 }
201 len => {
202 *name_len += 1 + len as usize;
203
204 if *name_len >= MAX_NAME_LENGTH {
206 return Err(crate::SimpleDnsError::InvalidDnsPacket);
207 }
208
209 if len as usize > MAX_LABEL_LENGTH {
210 return Err(crate::SimpleDnsError::InvalidServiceLabel);
211 }
212
213 labels.push(Label::new_unchecked(data.get_slice(len as usize)?));
216 }
217 }
218 }
219 }
220
221 let mut labels = Vec::new();
222 let mut name_len = 0usize;
223
224 let mut pointer = parse_labels(data, &mut name_len, &mut labels)?;
225
226 let mut data = data.clone();
227 while let Some(p) = pointer {
228 data = data.new_at(p)?;
232 pointer = parse_labels(&mut data, &mut name_len, &mut labels)?;
233 }
234
235 Ok(Self { labels })
236 }
237
238 fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
239 self.plain_append(out)
240 }
241
242 fn write_compressed_to<T: Write + Seek>(
243 &'a self,
244 out: &mut T,
245 name_refs: &mut crate::lib::BTreeMap<&[Label<'a>], u16>,
246 ) -> crate::Result<()> {
247 self.compress_append(out, name_refs)
248 }
249
250 fn len(&self) -> usize {
251 self.labels
252 .iter()
253 .map(|label| label.len() + 1)
254 .sum::<usize>()
255 + Self::MINIMUM_LEN
256 }
257}
258
259impl<'a> TryFrom<&'a str> for Name<'a> {
260 type Error = crate::SimpleDnsError;
261
262 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
263 Name::new(value)
264 }
265}
266
267impl<'a> From<&'a [Label<'a>]> for Name<'a> {
268 fn from(labels: &'a [Label<'a>]) -> Self {
269 Name::new_with_labels(labels)
270 }
271}
272
273impl<'a, const N: usize> From<[Label<'a>; N]> for Name<'a> {
274 fn from(labels: [Label<'a>; N]) -> Self {
275 Name::new_with_labels(&labels)
276 }
277}
278
279impl Display for Name<'_> {
280 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
281 let mut labels = self.labels.iter();
282
283 if let Some(label) = labels.next() {
284 f.write_fmt(format_args!("{label}"))?;
285 }
286
287 for label in labels {
288 f.write_fmt(format_args!(".{label}"))?;
289 }
290
291 Ok(())
292 }
293}
294
295impl Debug for Name<'_> {
296 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
297 f.debug_tuple("Name")
298 .field(&format!("{self}"))
299 .field(&format!("{}", self.len()))
300 .finish()
301 }
302}
303
304impl PartialEq for Name<'_> {
305 fn eq(&self, other: &Self) -> bool {
306 self.labels == other.labels
307 }
308}
309
310impl Hash for Name<'_> {
311 fn hash<H: Hasher>(&self, state: &mut H) {
312 self.labels.hash(state);
313 }
314}
315
316struct LabelsIter<'a> {
318 bytes: &'a [u8],
319 current: usize,
320}
321
322impl<'a> LabelsIter<'a> {
323 fn new(bytes: &'a [u8]) -> Self {
324 Self { bytes, current: 0 }
325 }
326}
327
328impl<'a> Iterator for LabelsIter<'a> {
329 type Item = Cow<'a, [u8]>;
330
331 fn next(&mut self) -> Option<Self::Item> {
332 for i in self.current..self.bytes.len() {
333 if self.bytes[i] == b'.' {
334 let current = crate::lib::mem::replace(&mut self.current, i + 1);
335 if i - current == 0 {
336 continue;
337 }
338 return Some(self.bytes[current..i].into());
339 }
340 }
341
342 if self.current < self.bytes.len() {
343 let current = crate::lib::mem::replace(&mut self.current, self.bytes.len());
344 Some(self.bytes[current..].into())
345 } else {
346 None
347 }
348 }
349}
350
351#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord)]
363pub struct Label<'a> {
364 data: Cow<'a, [u8]>,
365}
366
367impl<'a> Label<'a> {
368 pub fn new<T: Into<Cow<'a, [u8]>>>(data: T) -> crate::Result<Self> {
370 let label = Self::new_unchecked(data);
371 if !label.is_valid() {
372 return Err(crate::SimpleDnsError::InvalidServiceLabel);
373 }
374
375 Ok(label)
376 }
377
378 pub fn new_unchecked<T: Into<Cow<'a, [u8]>>>(data: T) -> Self {
381 Self { data: data.into() }
382 }
383
384 pub fn len(&self) -> usize {
386 self.data.len()
387 }
388
389 pub fn is_empty(&self) -> bool {
391 self.data.is_empty()
392 }
393
394 pub fn into_owned<'b>(self) -> Label<'b> {
396 Label {
397 data: self.data.into_owned().into(),
398 }
399 }
400
401 pub fn is_valid(&self) -> bool {
403 if self.data.is_empty() || self.data.len() > MAX_LABEL_LENGTH {
404 return false;
405 }
406
407 if let Some(first) = self.data.first() {
408 if !first.is_ascii_alphanumeric() && *first != b'_' {
409 return false;
410 }
411 }
412
413 if !self
414 .data
415 .iter()
416 .skip(1)
417 .all(|c| c.is_ascii_alphanumeric() || *c == b'-' || *c == b'_')
418 {
419 return false;
420 }
421
422 if let Some(last) = self.data.last() {
423 if !last.is_ascii_alphanumeric() {
424 return false;
425 }
426 }
427
428 true
429 }
430}
431
432impl Display for Label<'_> {
433 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
434 let s = String::from_utf8_lossy(&self.data);
435 f.write_str(&s)
436 }
437}
438
439impl Debug for Label<'_> {
440 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
441 f.debug_struct("Label")
442 .field("data", &self.to_string())
443 .finish()
444 }
445}
446
447impl AsRef<[u8]> for Label<'_> {
448 fn as_ref(&self) -> &[u8] {
449 self.data.as_ref()
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456 use crate::lib::Cursor;
457 use crate::{lib::Vec, SimpleDnsError};
458
459 #[test]
460 fn construct_valid_names() {
461 assert!(Name::new("some").is_ok());
462 assert!(Name::new("some.local").is_ok());
463 assert!(Name::new("some.local.").is_ok());
464 assert!(Name::new("some-dash.local.").is_ok());
465 assert!(Name::new("_sync_miss._tcp.local").is_ok());
466 assert!(Name::new("1sync_miss._tcp.local").is_ok());
467
468 assert_eq!(Name::new_unchecked("\u{1F600}.local.").labels.len(), 2);
469 }
470
471 #[test]
472 fn label_validate() {
473 assert!(Name::new("\u{1F600}.local.").is_err());
474 assert!(Name::new("@.local.").is_err());
475 assert!(Name::new("\\.local.").is_err());
476 }
477
478 #[test]
479 fn is_link_local() {
480 assert!(!Name::new("some.example.com").unwrap().is_link_local());
481 assert!(Name::new("some.example.local.").unwrap().is_link_local());
482 }
483
484 #[test]
485 fn parse_without_compression() {
486 let mut data = BytesBuffer::new(
487 b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\x01F\x03ISI\x04ARPA\x00\x04ARPA\x00",
488 );
489 data.advance(3).unwrap();
490 let name = Name::parse(&mut data).unwrap();
491 assert_eq!("F.ISI.ARPA", name.to_string());
492
493 let name = Name::parse(&mut data).unwrap();
494 assert_eq!("FOO.F.ISI.ARPA", name.to_string());
495 }
496
497 #[test]
498 fn parse_with_compression() {
499 let mut data = BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03\x07INVALID\xc0\x1b" );
500 data.advance(3).unwrap();
501
502 let name = Name::parse(&mut data).unwrap();
503 assert_eq!("F.ISI.ARPA", name.to_string());
504
505 let name = Name::parse(&mut data).unwrap();
506 assert_eq!("FOO.F.ISI.ARPA", name.to_string());
507
508 let name = Name::parse(&mut data).unwrap();
509 assert_eq!("BAR.F.ISI.ARPA", name.to_string());
510
511 assert!(Name::parse(&mut data).is_err());
512 }
513
514 #[test]
515 fn parse_handle_circular_pointers() {
516 let mut data = BytesBuffer::new(&[249, 0, 37, 1, 1, 139, 192, 6, 1, 1, 1, 139, 192, 6]);
517 data.advance(12).unwrap();
518
519 assert_eq!(
520 Name::parse(&mut data),
521 Err(SimpleDnsError::InvalidDnsPacket)
522 );
523 }
524
525 #[test]
526 fn test_write() {
527 let mut bytes = Vec::with_capacity(30);
528 Name::new_unchecked("_srv._udp.local")
529 .write_to(&mut bytes)
530 .unwrap();
531
532 assert_eq!(b"\x04_srv\x04_udp\x05local\x00", &bytes[..]);
533
534 let mut bytes = Vec::with_capacity(30);
535 Name::new_unchecked("_srv._udp.local2.")
536 .write_to(&mut bytes)
537 .unwrap();
538
539 assert_eq!(b"\x04_srv\x04_udp\x06local2\x00", &bytes[..]);
540 }
541
542 #[test]
543 fn root_name_should_generate_no_labels() {
544 assert_eq!(Name::new_unchecked("").labels.len(), 0);
545 assert_eq!(Name::new_unchecked(".").labels.len(), 0);
546 }
547
548 #[test]
549 fn dot_sequence_should_generate_no_labels() {
550 assert_eq!(Name::new_unchecked(".....").labels.len(), 0);
551 assert_eq!(Name::new_unchecked("example.....com").labels.len(), 2);
552 }
553
554 #[test]
555 fn root_name_should_write_zero() {
556 let mut bytes = Vec::with_capacity(30);
557 Name::new_unchecked(".").write_to(&mut bytes).unwrap();
558
559 assert_eq!(b"\x00", &bytes[..]);
560 }
561
562 #[test]
563 fn append_to_vec_with_compression() {
564 let mut buf = Cursor::new(crate::lib::vec![0, 0, 0]);
565 buf.set_position(3);
566
567 let mut name_refs = Default::default();
568
569 let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
570 f_isi_arpa
571 .write_compressed_to(&mut buf, &mut name_refs)
572 .expect("failed to add F.ISI.ARPA");
573 let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
574 foo_f_isi_arpa
575 .write_compressed_to(&mut buf, &mut name_refs)
576 .expect("failed to add FOO.F.ISI.ARPA");
577
578 Name::new_unchecked("BAR.F.ISI.ARPA")
579 .write_compressed_to(&mut buf, &mut name_refs)
580 .expect("failed to add FOO.F.ISI.ARPA");
581
582 let data = b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03";
583 assert_eq!(data[..], buf.get_ref()[..]);
584 }
585
586 #[test]
587 fn append_to_vec_with_compression_mult_names() {
588 let mut buf = Cursor::new(Vec::new());
589 let mut name_refs = Default::default();
590
591 let isi_arpa = Name::new_unchecked("ISI.ARPA");
592 isi_arpa
593 .write_compressed_to(&mut buf, &mut name_refs)
594 .expect("failed to add ISI.ARPA");
595
596 let f_isi_arpa = Name::new_unchecked("F.ISI.ARPA");
597 f_isi_arpa
598 .write_compressed_to(&mut buf, &mut name_refs)
599 .expect("failed to add F.ISI.ARPA");
600 let foo_f_isi_arpa = Name::new_unchecked("FOO.F.ISI.ARPA");
601 foo_f_isi_arpa
602 .write_compressed_to(&mut buf, &mut name_refs)
603 .expect("failed to add F.ISI.ARPA");
604 Name::new_unchecked("BAR.F.ISI.ARPA")
605 .write_compressed_to(&mut buf, &mut name_refs)
606 .expect("failed to add F.ISI.ARPA");
607
608 let expected = b"\x03ISI\x04ARPA\x00\x01F\xc0\x00\x03FOO\xc0\x0a\x03BAR\xc0\x0a";
609 assert_eq!(expected[..], buf.get_ref()[..]);
610
611 let mut data = BytesBuffer::new(buf.get_ref());
612
613 let first = Name::parse(&mut data).unwrap();
614 assert_eq!("ISI.ARPA", first.to_string());
615 let second = Name::parse(&mut data).unwrap();
616 assert_eq!("F.ISI.ARPA", second.to_string());
617 let third = Name::parse(&mut data).unwrap();
618 assert_eq!("FOO.F.ISI.ARPA", third.to_string());
619 let fourth = Name::parse(&mut data).unwrap();
620 assert_eq!("BAR.F.ISI.ARPA", fourth.to_string());
621 }
622
623 #[test]
624 fn ensure_different_domains_are_not_compressed() {
625 let mut buf = Cursor::new(Vec::new());
626 let mut name_refs = Default::default();
627
628 let foo_bar_baz = Name::new_unchecked("FOO.BAR.BAZ");
629 foo_bar_baz
630 .write_compressed_to(&mut buf, &mut name_refs)
631 .expect("failed to add FOO.BAR.BAZ");
632
633 let foo_bar_buz = Name::new_unchecked("FOO.BAR.BUZ");
634 foo_bar_buz
635 .write_compressed_to(&mut buf, &mut name_refs)
636 .expect("failed to add FOO.BAR.BUZ");
637
638 Name::new_unchecked("FOO.BAR")
639 .write_compressed_to(&mut buf, &mut name_refs)
640 .expect("failed to add FOO.BAR");
641
642 let expected = b"\x03FOO\x03BAR\x03BAZ\x00\x03FOO\x03BAR\x03BUZ\x00\x03FOO\x03BAR\x00";
643 assert_eq!(expected[..], buf.get_ref()[..]);
644 }
645
646 #[test]
647 fn eq_other_name() -> Result<(), SimpleDnsError> {
648 assert_eq!(Name::new("example.com")?, Name::new("example.com")?);
649 assert_ne!(Name::new("some.example.com")?, Name::new("example.com")?);
650 assert_ne!(Name::new("example.co")?, Name::new("example.com")?);
651 assert_ne!(Name::new("example.com.org")?, Name::new("example.com")?);
652
653 let mut data =
654 BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
655 data.advance(3)?;
656 assert_eq!(Name::new("F.ISI.ARPA")?, Name::parse(&mut data)?);
657 assert_eq!(Name::new("FOO.F.ISI.ARPA")?, Name::parse(&mut data)?);
658 Ok(())
659 }
660
661 #[test]
662 fn len() -> crate::Result<()> {
663 let mut bytes = Vec::new();
664 let name_one = Name::new_unchecked("ex.com.");
665 name_one.write_to(&mut bytes)?;
666
667 assert_eq!(8, bytes.len());
668 assert_eq!(bytes.len(), name_one.len());
669 assert_eq!(8, Name::parse(&mut BytesBuffer::new(&bytes))?.len());
670
671 Ok(())
672 }
673
674 #[test]
675 fn len_compressed() -> crate::Result<()> {
676 let name_one = Name::new_unchecked("ex.com.");
677 let mut name_refs = Default::default();
678 let mut bytes = Cursor::new(Vec::new());
679 name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
680 name_one.write_compressed_to(&mut bytes, &mut name_refs)?;
681
682 assert_eq!(10, bytes.get_ref().len());
683 Ok(())
684 }
685
686 #[test]
687 #[cfg(feature = "std")]
688 fn hash() -> crate::Result<()> {
689 fn get_hash(name: &Name) -> u64 {
690 let mut hasher = std::hash::DefaultHasher::default();
691 name.hash(&mut hasher);
692 hasher.finish()
693 }
694
695 let mut data =
696 BytesBuffer::new(b"\x00\x00\x00\x01F\x03ISI\x04ARPA\x00\x03FOO\xc0\x03\x03BAR\xc0\x03");
697 data.advance(3)?;
698
699 assert_eq!(
700 get_hash(&Name::new("F.ISI.ARPA")?),
701 get_hash(&Name::parse(&mut data)?)
702 );
703
704 assert_eq!(
705 get_hash(&Name::new("FOO.F.ISI.ARPA")?),
706 get_hash(&Name::parse(&mut data)?)
707 );
708
709 Ok(())
710 }
711
712 #[test]
713 fn is_subdomain_of() {
714 assert!(Name::new_unchecked("sub.example.com")
715 .is_subdomain_of(&Name::new_unchecked("example.com")));
716
717 assert!(!Name::new_unchecked("example.com")
718 .is_subdomain_of(&Name::new_unchecked("example.com")));
719
720 assert!(Name::new_unchecked("foo.sub.example.com")
721 .is_subdomain_of(&Name::new_unchecked("example.com")));
722
723 assert!(!Name::new_unchecked("example.com")
724 .is_subdomain_of(&Name::new_unchecked("example.xom")));
725
726 assert!(!Name::new_unchecked("domain.com")
727 .is_subdomain_of(&Name::new_unchecked("other.domain")));
728
729 assert!(!Name::new_unchecked("domain.com")
730 .is_subdomain_of(&Name::new_unchecked("domain.com.br")));
731 }
732
733 #[test]
734 fn subtract_domain() {
735 let domain = Name::new_unchecked("_srv3._tcp.local");
736 assert_eq!(
737 Name::new_unchecked("a._srv3._tcp.local")
738 .without(&domain)
739 .unwrap()
740 .to_string(),
741 "a"
742 );
743
744 assert!(Name::new_unchecked("unrelated").without(&domain).is_none(),);
745
746 assert_eq!(
747 Name::new_unchecked("some.longer.domain._srv3._tcp.local")
748 .without(&domain)
749 .unwrap()
750 .to_string(),
751 "some.longer.domain"
752 );
753 }
754
755 #[test]
756 fn display_invalid_label() {
757 let input = b"invalid\xF0\x90\x80label";
758 let label = Label::new_unchecked(input);
759
760 assert_eq!(label.to_string(), "invalid�label");
761 }
762
763 #[test]
764 fn test_compress_append_near_boundary() -> crate::Result<()> {
765 let mut buf = Cursor::new(Vec::new());
766 let mut name_refs = Default::default();
767
768 let before_boundary_pos = (MAX_COMPRESSION_OFFSET - 5) as usize;
769 let padding = vec![0u8; before_boundary_pos];
770 buf.write_all(&padding)?;
771
772 let name1 = Name::new_unchecked("foo.example.com");
773 let name2 = Name::new_unchecked("bar.test.net");
774
775 let old_pos = buf.position();
777 name1.write_compressed_to(&mut buf, &mut name_refs)?;
778 assert_eq!(buf.position() - old_pos, name1.len() as u64);
779
780 let old_pos = buf.position();
782 name2.write_compressed_to(&mut buf, &mut name_refs)?;
783 assert_eq!(buf.position() - old_pos, name2.len() as u64);
784
785 let old_pos = buf.position();
787 name1.write_compressed_to(&mut buf, &mut name_refs)?;
788 assert_eq!(buf.position() - old_pos, 2);
789
790 let old_pos = buf.position();
792 name2.write_compressed_to(&mut buf, &mut name_refs)?;
793 assert_eq!(buf.position() - old_pos, name2.len() as u64);
794
795 Ok(())
796 }
797}