1use super::super::wire::{FormError, ParseError};
7use super::builder::{parse_escape, LabelFromStrError};
8use core::str::FromStr;
9use core::{borrow, cmp, fmt, hash, iter, ops, slice};
10use octseq::builder::OctetsBuilder;
11
12pub struct Label([u8]);
38
39impl Label {
42 pub const MAX_LEN: usize = 63;
44
45 pub(super) unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
51 &*(slice as *const [u8] as *const Self)
52 }
53
54 pub(super) unsafe fn from_slice_mut_unchecked(
60 slice: &mut [u8],
61 ) -> &mut Self {
62 &mut *(slice as *mut [u8] as *mut Self)
63 }
64
65 #[must_use]
69 pub fn root() -> &'static Self {
70 unsafe { Self::from_slice_unchecked(b"") }
71 }
72
73 #[must_use]
75 pub fn wildcard() -> &'static Self {
76 unsafe { Self::from_slice_unchecked(b"*") }
77 }
78
79 pub fn from_slice(slice: &[u8]) -> Result<&Self, LongLabelError> {
83 if slice.len() > Label::MAX_LEN {
84 Err(LongLabelError)
85 } else {
86 Ok(unsafe { Self::from_slice_unchecked(slice) })
87 }
88 }
89
90 pub fn from_slice_mut(
94 slice: &mut [u8],
95 ) -> Result<&mut Self, LongLabelError> {
96 if slice.len() > Label::MAX_LEN {
97 Err(LongLabelError)
98 } else {
99 Ok(unsafe { Self::from_slice_mut_unchecked(slice) })
100 }
101 }
102
103 pub fn split_from(
108 slice: &[u8],
109 ) -> Result<(&Self, &[u8]), SplitLabelError> {
110 let head = match slice.first() {
111 Some(ch) => *ch,
112 None => return Err(SplitLabelError::ShortInput),
113 };
114 let end = match head {
115 0..=0x3F => (head as usize) + 1,
116 0x40..=0x7F => {
117 return Err(SplitLabelError::BadType(
118 LabelTypeError::Extended(head),
119 ))
120 }
121 0xC0..=0xFF => {
122 let res = match slice.get(1) {
123 Some(ch) => u16::from(*ch),
124 None => return Err(SplitLabelError::ShortInput),
125 };
126 let res = res | ((u16::from(head) & 0x3F) << 8);
127 return Err(SplitLabelError::Pointer(res));
128 }
129 _ => {
130 return Err(SplitLabelError::BadType(
131 LabelTypeError::Undefined,
132 ))
133 }
134 };
135 if slice.len() < end {
136 return Err(SplitLabelError::ShortInput);
137 }
138 Ok((
139 unsafe { Self::from_slice_unchecked(&slice[1..end]) },
140 &slice[end..],
141 ))
142 }
143
144 pub fn split_from_mut(
149 slice: &mut [u8],
150 ) -> Result<(&mut Self, &mut [u8]), SplitLabelError> {
151 let head = match slice.first_mut() {
152 Some(ch) => *ch,
153 None => return Err(SplitLabelError::ShortInput),
154 };
155 let end = match head {
156 0..=0x3F => (head as usize) + 1,
157 0x40..=0x7F => {
158 return Err(SplitLabelError::BadType(
159 LabelTypeError::Extended(head),
160 ))
161 }
162 0xC0..=0xFF => {
163 let res = match slice.get(1) {
164 Some(ch) => u16::from(*ch),
165 None => return Err(SplitLabelError::ShortInput),
166 };
167 let res = res | ((u16::from(head) & 0x3F) << 8);
168 return Err(SplitLabelError::Pointer(res));
169 }
170 _ => {
171 return Err(SplitLabelError::BadType(
172 LabelTypeError::Undefined,
173 ))
174 }
175 };
176 if slice.len() < end {
177 return Err(SplitLabelError::ShortInput);
178 }
179 let (left, right) = slice.split_at_mut(end);
180 Ok((
181 unsafe { Self::from_slice_mut_unchecked(&mut left[1..]) },
182 right,
183 ))
184 }
185
186 pub fn iter(&self) -> iter::Copied<slice::Iter<u8>> {
188 self.as_slice().iter().copied()
189 }
190
191 #[must_use]
202 pub fn iter_slice(slice: &[u8], start: usize) -> SliceLabelsIter {
203 SliceLabelsIter { slice, start }
204 }
205
206 #[must_use]
208 pub fn as_slice(&self) -> &[u8] {
209 unsafe { &*(self as *const Self as *const [u8]) }
210 }
211
212 pub fn as_slice_mut(&mut self) -> &mut [u8] {
214 unsafe { &mut *(self as *mut Label as *mut [u8]) }
215 }
216
217 pub fn make_canonical(&mut self) {
222 self.0.make_ascii_lowercase()
223 }
224
225 pub(super) fn make_slice_canonical(mut slice: &mut [u8]) {
236 while !slice.is_empty() {
237 let (head, tail) =
238 Label::split_from_mut(slice).expect("invalid name");
239 head.make_canonical();
240 slice = tail;
241 }
242 }
243
244 #[must_use]
248 pub fn to_canonical(&self) -> OwnedLabel {
249 let mut res = OwnedLabel::from_label(self);
250 res.make_canonical();
251 res
252 }
253
254 #[must_use]
256 pub fn composed_cmp(&self, other: &Self) -> cmp::Ordering {
257 match self.0.len().cmp(&other.0.len()) {
258 cmp::Ordering::Equal => {}
259 other => return other,
260 }
261 self.0.cmp(other.as_ref())
262 }
263
264 #[must_use]
266 pub fn lowercase_composed_cmp(&self, other: &Self) -> cmp::Ordering {
267 match self.0.len().cmp(&other.0.len()) {
268 cmp::Ordering::Equal => {}
269 other => return other,
270 }
271 self.cmp(other)
272 }
273
274 pub fn compose<Builder: OctetsBuilder + ?Sized>(
279 &self,
280 target: &mut Builder,
281 ) -> Result<(), Builder::AppendError> {
282 target.append_slice(&[self.len() as u8])?;
283 target.append_slice(self.as_slice())
284 }
285
286 pub fn compose_canonical<Builder: OctetsBuilder + ?Sized>(
292 &self,
293 target: &mut Builder,
294 ) -> Result<(), Builder::AppendError> {
295 target.append_slice(&[self.len() as u8])?;
296 for ch in self.into_iter() {
297 target.append_slice(&[ch.to_ascii_lowercase()])?;
298 }
299 Ok(())
300 }
301}
302
303impl Label {
306 #[must_use]
311 pub fn len(&self) -> usize {
312 self.as_slice().len()
313 }
314
315 #[must_use]
317 pub fn is_empty(&self) -> bool {
318 self.as_slice().is_empty()
319 }
320
321 #[must_use]
323 pub fn is_root(&self) -> bool {
324 self.is_empty()
325 }
326
327 #[must_use]
329 pub fn is_wildcard(&self) -> bool {
330 self.0.len() == 1 && self.0[0] == b'*'
331 }
332
333 #[must_use]
338 pub fn compose_len(&self) -> u16 {
339 u16::try_from(self.len()).expect("long label") + 1
340 }
341}
342
343impl AsRef<[u8]> for Label {
346 fn as_ref(&self) -> &[u8] {
347 self.as_slice()
348 }
349}
350
351impl AsMut<[u8]> for Label {
352 fn as_mut(&mut self) -> &mut [u8] {
353 self.as_slice_mut()
354 }
355}
356
357#[cfg(feature = "std")]
360impl std::borrow::ToOwned for Label {
361 type Owned = OwnedLabel;
362
363 fn to_owned(&self) -> Self::Owned {
364 self.into()
365 }
366}
367
368impl<T: AsRef<[u8]> + ?Sized> PartialEq<T> for Label {
371 fn eq(&self, other: &T) -> bool {
372 self.as_slice().eq_ignore_ascii_case(other.as_ref())
373 }
374}
375
376impl Eq for Label {}
377
378impl PartialOrd for Label {
381 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
391 Some(self.cmp(other))
392 }
393}
394
395impl Ord for Label {
396 fn cmp(&self, other: &Self) -> cmp::Ordering {
397 self.as_slice()
398 .iter()
399 .map(u8::to_ascii_lowercase)
400 .cmp(other.as_slice().iter().map(u8::to_ascii_lowercase))
401 }
402}
403
404impl hash::Hash for Label {
407 fn hash<H: hash::Hasher>(&self, state: &mut H) {
408 (self.len() as u8).hash(state);
411 for c in self.iter() {
412 c.to_ascii_lowercase().hash(state)
413 }
414 }
415}
416
417impl<'a> IntoIterator for &'a Label {
420 type Item = u8;
421 type IntoIter = iter::Copied<slice::Iter<'a, u8>>;
422
423 fn into_iter(self) -> Self::IntoIter {
424 self.iter()
425 }
426}
427
428impl fmt::Display for Label {
431 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
432 for ch in self.iter() {
433 if ch == b' ' || ch == b'.' || ch == b'\\' {
434 write!(f, "\\{}", ch as char)?;
435 } else if !(0x20..0x7F).contains(&ch) {
436 write!(f, "\\{:03}", ch)?;
437 } else {
438 write!(f, "{}", (ch as char))?;
439 }
440 }
441 Ok(())
442 }
443}
444
445impl fmt::Debug for Label {
446 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
447 f.write_str("Label(")?;
448 fmt::Display::fmt(self, f)?;
449 f.write_str(")")
450 }
451}
452
453#[derive(Clone, Copy)]
463pub struct OwnedLabel([u8; 64]);
464
465impl OwnedLabel {
466 #[must_use]
468 pub fn from_label(label: &Label) -> Self {
469 let mut res = [0; 64];
470 res[0] = label.len() as u8;
471 res[1..=label.len()].copy_from_slice(label.as_slice());
472 OwnedLabel(res)
473 }
474
475 pub fn from_chars(
477 mut chars: impl Iterator<Item = char>,
478 ) -> Result<Self, LabelFromStrError> {
479 let mut res = [0u8; 64];
480 while let Some(ch) = chars.next() {
481 if res[0] as usize >= Label::MAX_LEN {
482 return Err(LabelFromStrError::LongLabel);
483 }
484 let ch = match ch {
485 ' '..='-' | '/'..='[' | ']'..='~' => ch as u8,
486 '\\' => parse_escape(&mut chars, res[0] > 0)?,
487 _ => return Err(LabelFromStrError::IllegalCharacter(ch)),
488 };
489 res[(res[0] as usize) + 1] = ch;
490 res[0] += 1;
491 }
492 Ok(OwnedLabel(res))
493 }
494
495 pub fn make_canonical(&mut self) {
500 self.0[1..].make_ascii_lowercase()
501 }
502
503 #[must_use]
505 pub fn as_label(&self) -> &Label {
506 unsafe {
507 Label::from_slice_unchecked(&self.0[1..=(self.0[0] as usize)])
508 }
509 }
510
511 pub fn as_label_mut(&mut self) -> &mut Label {
513 let len = self.0[0] as usize;
514 unsafe { Label::from_slice_mut_unchecked(&mut self.0[1..=len]) }
515 }
516
517 #[must_use]
519 pub fn as_wire_slice(&self) -> &[u8] {
520 let len = self.0[0] as usize;
521 &self.0[..=len]
522 }
523}
524
525impl<'a> From<&'a Label> for OwnedLabel {
528 fn from(label: &'a Label) -> Self {
529 Self::from_label(label)
530 }
531}
532
533impl FromStr for OwnedLabel {
536 type Err = LabelFromStrError;
537
538 fn from_str(s: &str) -> Result<Self, Self::Err> {
539 Self::from_chars(s.chars())
540 }
541}
542
543impl ops::Deref for OwnedLabel {
546 type Target = Label;
547
548 fn deref(&self) -> &Label {
549 self.as_label()
550 }
551}
552
553impl ops::DerefMut for OwnedLabel {
554 fn deref_mut(&mut self) -> &mut Label {
555 self.as_label_mut()
556 }
557}
558
559impl AsRef<Label> for OwnedLabel {
560 fn as_ref(&self) -> &Label {
561 self.as_label()
562 }
563}
564
565impl AsRef<[u8]> for OwnedLabel {
566 fn as_ref(&self) -> &[u8] {
567 self.as_label().as_slice()
568 }
569}
570
571impl AsMut<Label> for OwnedLabel {
572 fn as_mut(&mut self) -> &mut Label {
573 self.as_label_mut()
574 }
575}
576
577impl AsMut<[u8]> for OwnedLabel {
578 fn as_mut(&mut self) -> &mut [u8] {
579 self.as_label_mut().as_slice_mut()
580 }
581}
582
583impl borrow::Borrow<Label> for OwnedLabel {
584 fn borrow(&self) -> &Label {
585 self.as_label()
586 }
587}
588
589impl borrow::BorrowMut<Label> for OwnedLabel {
590 fn borrow_mut(&mut self) -> &mut Label {
591 self.as_label_mut()
592 }
593}
594
595impl<T: AsRef<Label>> PartialEq<T> for OwnedLabel {
598 fn eq(&self, other: &T) -> bool {
599 self.as_label().eq(other.as_ref())
600 }
601}
602
603impl Eq for OwnedLabel {}
604
605impl<T: AsRef<Label>> PartialOrd<T> for OwnedLabel {
608 fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
609 self.as_label().partial_cmp(other.as_ref())
610 }
611}
612
613impl Ord for OwnedLabel {
614 fn cmp(&self, other: &Self) -> cmp::Ordering {
615 self.as_label().cmp(other.as_ref())
616 }
617}
618
619impl hash::Hash for OwnedLabel {
622 fn hash<H: hash::Hasher>(&self, state: &mut H) {
623 self.as_label().hash(state)
624 }
625}
626
627impl fmt::Display for OwnedLabel {
630 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
631 self.as_label().fmt(f)
632 }
633}
634
635impl fmt::Debug for OwnedLabel {
636 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637 f.debug_tuple("OwnedLabel").field(&self.as_label()).finish()
638 }
639}
640
641#[cfg(feature = "serde")]
644impl serde::Serialize for OwnedLabel {
645 fn serialize<S: serde::Serializer>(
646 &self,
647 serializer: S,
648 ) -> Result<S::Ok, S::Error> {
649 use octseq::serde::SerializeOctets;
650
651 if serializer.is_human_readable() {
652 serializer.serialize_newtype_struct(
653 "OwnedLabel",
654 &format_args!("{}", self),
655 )
656 } else {
657 serializer.serialize_newtype_struct(
658 "OwnedLabel",
659 &self.as_label().as_slice().as_serialized_octets(),
660 )
661 }
662 }
663}
664
665#[cfg(feature = "serde")]
666impl<'de> serde::Deserialize<'de> for OwnedLabel {
667 fn deserialize<D: serde::Deserializer<'de>>(
668 deserializer: D,
669 ) -> Result<Self, D::Error> {
670 use serde::de::Error;
671
672 struct InnerVisitor;
673
674 impl<'de> serde::de::Visitor<'de> for InnerVisitor {
675 type Value = OwnedLabel;
676
677 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
678 f.write_str("an domain name label")
679 }
680
681 fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
682 OwnedLabel::from_str(v).map_err(E::custom)
683 }
684
685 fn visit_borrowed_bytes<E: serde::de::Error>(
686 self,
687 value: &'de [u8],
688 ) -> Result<Self::Value, E> {
689 Label::from_slice(value)
690 .map(OwnedLabel::from_label)
691 .map_err(E::custom)
692 }
693 }
694
695 struct NewtypeVisitor;
696
697 impl<'de> serde::de::Visitor<'de> for NewtypeVisitor {
698 type Value = OwnedLabel;
699
700 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
701 f.write_str("an domain name label")
702 }
703
704 fn visit_newtype_struct<D: serde::Deserializer<'de>>(
705 self,
706 deserializer: D,
707 ) -> Result<Self::Value, D::Error> {
708 if deserializer.is_human_readable() {
709 deserializer.deserialize_str(InnerVisitor)
710 } else {
711 deserializer.deserialize_bytes(InnerVisitor)
712 }
713 }
714 }
715
716 deserializer.deserialize_newtype_struct("OwnedLabel", NewtypeVisitor)
717 }
718}
719
720pub struct SliceLabelsIter<'a> {
730 slice: &'a [u8],
732
733 start: usize,
737}
738
739impl<'a> Iterator for SliceLabelsIter<'a> {
740 type Item = &'a Label;
741
742 fn next(&mut self) -> Option<Self::Item> {
743 if self.start == usize::max_value() {
744 return None;
745 }
746 loop {
747 match Label::split_from(&self.slice[self.start..]) {
748 Ok((label, _)) => {
749 if label.is_root() {
750 self.start = usize::max_value();
751 } else {
752 self.start += label.len();
753 }
754 return Some(label);
755 }
756 Err(SplitLabelError::Pointer(pos)) => {
757 let pos = pos as usize;
758 if pos > self.start {
759 self.start = usize::max_value();
762 return None;
763 }
764 self.start = pos;
765 continue;
766 }
767 Err(_) => {
768 self.start = usize::max_value();
769 return None;
770 }
771 }
772 }
773 }
774}
775
776#[derive(Clone, Copy, Debug, Eq, PartialEq)]
782pub enum LabelTypeError {
783 Undefined,
785
786 Extended(u8),
791}
792
793impl fmt::Display for LabelTypeError {
796 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
797 match *self {
798 LabelTypeError::Undefined => f.write_str("undefined label type"),
799 LabelTypeError::Extended(value) => {
800 write!(f, "unknown extended label 0x{:02x}", value)
801 }
802 }
803 }
804}
805
806#[cfg(feature = "std")]
807impl std::error::Error for LabelTypeError {}
808
809#[derive(Clone, Copy, Debug, Eq, PartialEq)]
813pub struct LongLabelError;
814
815impl fmt::Display for LongLabelError {
818 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
819 f.write_str("long label")
820 }
821}
822
823#[cfg(feature = "std")]
824impl std::error::Error for LongLabelError {}
825
826#[derive(Clone, Copy, Debug, Eq, PartialEq)]
830pub enum SplitLabelError {
831 Pointer(u16),
833
834 BadType(LabelTypeError),
836
837 ShortInput,
839}
840
841impl From<LabelTypeError> for SplitLabelError {
844 fn from(err: LabelTypeError) -> SplitLabelError {
845 SplitLabelError::BadType(err)
846 }
847}
848
849impl From<SplitLabelError> for ParseError {
850 fn from(err: SplitLabelError) -> ParseError {
851 match err {
852 SplitLabelError::Pointer(_) => {
853 ParseError::Form(FormError::new("compressed domain name"))
854 }
855 SplitLabelError::BadType(_) => {
856 ParseError::Form(FormError::new("invalid label type"))
857 }
858 SplitLabelError::ShortInput => ParseError::ShortInput,
859 }
860 }
861}
862
863impl fmt::Display for SplitLabelError {
866 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
867 match *self {
868 SplitLabelError::Pointer(_) => {
869 f.write_str("compressed domain name")
870 }
871 SplitLabelError::BadType(ltype) => ltype.fmt(f),
872 SplitLabelError::ShortInput => ParseError::ShortInput.fmt(f),
873 }
874 }
875}
876
877#[cfg(feature = "std")]
878impl std::error::Error for SplitLabelError {}
879
880#[cfg(test)]
883mod test {
884 use super::*;
885
886 #[test]
887 fn from_slice() {
888 let x = [0u8; 10];
889 assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
890 let x = [0u8; 63];
891 assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
892 let x = [0u8; 64];
893 assert!(Label::from_slice(&x[..]).is_err());
894 }
895
896 #[test]
897 fn split_from() {
898 assert_eq!(
900 Label::split_from(b"\x03www\x07example\x03com\0").unwrap(),
901 (
902 Label::from_slice(b"www").unwrap(),
903 &b"\x07example\x03com\0"[..]
904 )
905 );
906
907 assert_eq!(
909 Label::split_from(b"\x03www").unwrap(),
910 (Label::from_slice(b"www").unwrap(), &b""[..])
911 );
912
913 assert_eq!(
915 Label::split_from(b"\0some").unwrap(),
916 (Label::from_slice(b"").unwrap(), &b"some"[..])
917 );
918
919 assert_eq!(
921 Label::split_from(b"\x03ww"),
922 Err(SplitLabelError::ShortInput)
923 );
924
925 assert_eq!(Label::split_from(b""), Err(SplitLabelError::ShortInput));
927
928 assert_eq!(
930 Label::split_from(b"\xc0\x05foo"),
931 Err(SplitLabelError::Pointer(5))
932 );
933
934 assert_eq!(
936 Label::split_from(b"\xb3foo"),
937 Err(LabelTypeError::Undefined.into())
938 );
939
940 assert_eq!(
942 Label::split_from(b"\x66foo"),
943 Err(LabelTypeError::Extended(0x66).into())
944 );
945 }
946
947 #[test]
948 #[cfg(feature = "std")]
949 fn compose() {
950 use octseq::builder::infallible;
951 use std::vec::Vec;
952
953 let mut buf = Vec::new();
954 infallible(Label::root().compose(&mut buf));
955 assert_eq!(buf, &b"\0"[..]);
956
957 let mut buf = Vec::new();
958 let label = Label::from_slice(b"123").unwrap();
959 infallible(label.compose(&mut buf));
960 assert_eq!(buf, &b"\x03123"[..]);
961 }
962
963 #[test]
964 fn eq() {
965 assert_eq!(
966 Label::from_slice(b"example").unwrap(),
967 Label::from_slice(b"eXAMple").unwrap()
968 );
969 assert_ne!(
970 Label::from_slice(b"example").unwrap(),
971 Label::from_slice(b"e4ample").unwrap()
972 );
973 }
974
975 #[test]
976 fn cmp() {
977 use core::cmp::Ordering;
978
979 let labels = [
980 Label::root(),
981 Label::from_slice(b"\x01").unwrap(),
982 Label::from_slice(b"*").unwrap(),
983 Label::from_slice(b"\xc8").unwrap(),
984 ];
985 for i in 0..labels.len() {
986 for j in 0..labels.len() {
987 let ord = i.cmp(&j);
988 assert_eq!(labels[i].partial_cmp(labels[j]), Some(ord));
989 assert_eq!(labels[i].cmp(labels[j]), ord);
990 }
991 }
992
993 let l1 = Label::from_slice(b"example").unwrap();
994 let l2 = Label::from_slice(b"eXAMple").unwrap();
995 assert_eq!(l1.partial_cmp(l2), Some(Ordering::Equal));
996 assert_eq!(l1.cmp(l2), Ordering::Equal);
997 }
998
999 #[test]
1000 #[cfg(feature = "std")]
1001 fn hash() {
1002 use std::collections::hash_map::DefaultHasher;
1003 use std::hash::{Hash, Hasher};
1004
1005 let mut s1 = DefaultHasher::new();
1006 let mut s2 = DefaultHasher::new();
1007 Label::from_slice(b"example").unwrap().hash(&mut s1);
1008 Label::from_slice(b"eXAMple").unwrap().hash(&mut s2);
1009 assert_eq!(s1.finish(), s2.finish());
1010 }
1011
1012 #[cfg(feature = "serde")]
1015 #[test]
1016 fn owned_label_ser_de() {
1017 use serde_test::{assert_tokens, Configure, Token};
1018
1019 let label =
1020 OwnedLabel::from_label(Label::from_slice(b"fo.").unwrap());
1021 assert_tokens(
1022 &label.compact(),
1023 &[
1024 Token::NewtypeStruct { name: "OwnedLabel" },
1025 Token::BorrowedBytes(b"fo."),
1026 ],
1027 );
1028 assert_tokens(
1029 &label.readable(),
1030 &[
1031 Token::NewtypeStruct { name: "OwnedLabel" },
1032 Token::Str("fo\\."),
1033 ],
1034 );
1035 }
1036}