1use std::{
2 borrow::{Borrow, Cow},
3 cmp::Ordering,
4 ffi::OsStr,
5 fmt::{self, Debug, Display},
6 hash::{Hash, Hasher},
7 ops::{Deref, Index},
8 path::Path,
9 slice::SliceIndex,
10 str::Utf8Error,
11};
12
13use bytes::{Buf, Bytes};
14
15use crate::BytesString;
16
17#[derive(Clone, Default, PartialEq, Eq)]
37#[cfg_attr(
38 feature = "rkyv",
39 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
40)]
41pub struct BytesStr {
42 pub(crate) bytes: Bytes,
43}
44
45impl BytesStr {
46 pub fn new() -> Self {
58 Self {
59 bytes: Bytes::new(),
60 }
61 }
62
63 pub fn from_static(bytes: &'static str) -> Self {
74 Self {
75 bytes: Bytes::from_static(bytes.as_bytes()),
76 }
77 }
78
79 pub fn from_utf8(bytes: Bytes) -> Result<Self, Utf8Error> {
92 std::str::from_utf8(&bytes)?;
93
94 Ok(Self { bytes })
95 }
96
97 pub fn from_utf8_vec(bytes: Vec<u8>) -> Result<Self, Utf8Error> {
110 std::str::from_utf8(&bytes)?;
111
112 Ok(Self {
113 bytes: Bytes::from(bytes),
114 })
115 }
116
117 pub unsafe fn from_utf8_unchecked(bytes: Bytes) -> Self {
125 Self { bytes }
126 }
127
128 pub unsafe fn from_utf8_vec_unchecked(bytes: Vec<u8>) -> Self {
137 Self::from_utf8_unchecked(Bytes::from(bytes))
138 }
139
140 pub fn from_utf8_slice(bytes: &[u8]) -> Result<Self, Utf8Error> {
153 std::str::from_utf8(bytes)?;
154
155 Ok(Self {
156 bytes: Bytes::copy_from_slice(bytes),
157 })
158 }
159
160 pub unsafe fn from_utf8_slice_unchecked(bytes: &[u8]) -> Self {
169 Self {
170 bytes: Bytes::copy_from_slice(bytes),
171 }
172 }
173
174 pub fn from_static_utf8_slice(bytes: &'static [u8]) -> Result<Self, Utf8Error> {
186 std::str::from_utf8(bytes)?;
187
188 Ok(Self {
189 bytes: Bytes::from_static(bytes),
190 })
191 }
192
193 pub unsafe fn from_static_utf8_slice_unchecked(bytes: &'static [u8]) -> Self {
202 Self {
203 bytes: Bytes::from_static(bytes),
204 }
205 }
206
207 pub fn as_str(&self) -> &str {
219 unsafe { std::str::from_utf8_unchecked(&self.bytes) }
220 }
221
222 pub fn into_bytes(self) -> Bytes {
236 self.bytes
237 }
238
239 pub fn advance(&mut self, n: usize) {
258 if !self.is_char_boundary(n) {
259 panic!("n is not a character boundary");
260 }
261
262 self.bytes.advance(n);
263 }
264}
265
266impl Deref for BytesStr {
267 type Target = str;
268
269 fn deref(&self) -> &Self::Target {
270 self.as_ref()
271 }
272}
273
274impl AsRef<str> for BytesStr {
275 fn as_ref(&self) -> &str {
276 self.as_str()
277 }
278}
279
280impl From<String> for BytesStr {
281 fn from(s: String) -> Self {
282 Self {
283 bytes: Bytes::from(s),
284 }
285 }
286}
287
288impl From<&'static str> for BytesStr {
289 fn from(s: &'static str) -> Self {
290 Self {
291 bytes: Bytes::from_static(s.as_bytes()),
292 }
293 }
294}
295
296impl From<BytesStr> for BytesString {
297 fn from(s: BytesStr) -> Self {
298 Self {
299 bytes: s.bytes.into(),
300 }
301 }
302}
303
304impl From<BytesString> for BytesStr {
305 fn from(s: BytesString) -> Self {
306 Self {
307 bytes: s.bytes.into(),
308 }
309 }
310}
311
312impl AsRef<[u8]> for BytesStr {
313 fn as_ref(&self) -> &[u8] {
314 self.bytes.as_ref()
315 }
316}
317
318impl AsRef<Bytes> for BytesStr {
319 fn as_ref(&self) -> &Bytes {
320 &self.bytes
321 }
322}
323
324impl AsRef<OsStr> for BytesStr {
325 fn as_ref(&self) -> &OsStr {
326 OsStr::new(self.as_str())
327 }
328}
329
330impl AsRef<Path> for BytesStr {
331 fn as_ref(&self) -> &Path {
332 Path::new(self.as_str())
333 }
334}
335
336impl Borrow<str> for BytesStr {
337 fn borrow(&self) -> &str {
338 self.as_str()
339 }
340}
341
342impl Debug for BytesStr {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 Debug::fmt(self.as_str(), f)
345 }
346}
347
348impl Display for BytesStr {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 Display::fmt(self.as_str(), f)
351 }
352}
353
354impl Extend<BytesStr> for BytesString {
355 fn extend<T: IntoIterator<Item = BytesStr>>(&mut self, iter: T) {
356 self.bytes.extend(iter.into_iter().map(|s| s.bytes));
357 }
358}
359
360impl<I> Index<I> for BytesStr
361where
362 I: SliceIndex<str>,
363{
364 type Output = I::Output;
365
366 fn index(&self, index: I) -> &Self::Output {
367 self.as_str().index(index)
368 }
369}
370
371impl PartialEq<str> for BytesStr {
372 fn eq(&self, other: &str) -> bool {
373 self.as_str() == other
374 }
375}
376
377impl PartialEq<&'_ str> for BytesStr {
378 fn eq(&self, other: &&str) -> bool {
379 self.as_str() == *other
380 }
381}
382
383impl PartialEq<Cow<'_, str>> for BytesStr {
384 fn eq(&self, other: &Cow<'_, str>) -> bool {
385 self.as_str() == *other
386 }
387}
388
389impl PartialEq<BytesStr> for str {
390 fn eq(&self, other: &BytesStr) -> bool {
391 self == other.as_str()
392 }
393}
394
395impl PartialEq<BytesStr> for &'_ str {
396 fn eq(&self, other: &BytesStr) -> bool {
397 *self == other.as_str()
398 }
399}
400
401impl PartialEq<BytesStr> for Bytes {
402 fn eq(&self, other: &BytesStr) -> bool {
403 *self == other.bytes
404 }
405}
406
407impl PartialEq<String> for BytesStr {
408 fn eq(&self, other: &String) -> bool {
409 self.as_str() == other
410 }
411}
412
413impl PartialEq<BytesStr> for String {
414 fn eq(&self, other: &BytesStr) -> bool {
415 self == other.as_str()
416 }
417}
418
419impl Ord for BytesStr {
420 fn cmp(&self, other: &Self) -> Ordering {
421 self.as_str().cmp(other.as_str())
422 }
423}
424
425impl PartialOrd for BytesStr {
426 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
427 Some(self.cmp(other))
428 }
429}
430
431impl Hash for BytesStr {
433 fn hash<H: Hasher>(&self, state: &mut H) {
434 self.as_str().hash(state);
435 }
436}
437
438impl TryFrom<&'static [u8]> for BytesStr {
439 type Error = Utf8Error;
440
441 fn try_from(value: &'static [u8]) -> Result<Self, Self::Error> {
442 Self::from_static_utf8_slice(value)
443 }
444}
445
446#[cfg(feature = "serde")]
447mod serde_impl {
448 use serde::{Deserialize, Deserializer, Serialize, Serializer};
449
450 use super::*;
451
452 impl<'de> Deserialize<'de> for BytesStr {
453 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
454 where
455 D: Deserializer<'de>,
456 {
457 let s = String::deserialize(deserializer)?;
458 Ok(Self::from(s))
459 }
460 }
461
462 impl Serialize for BytesStr {
463 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
464 where
465 S: Serializer,
466 {
467 serializer.serialize_str(self.as_str())
468 }
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use std::{
475 borrow::{Borrow, Cow},
476 collections::{hash_map::DefaultHasher, HashMap},
477 ffi::OsStr,
478 hash::{Hash, Hasher},
479 path::Path,
480 };
481
482 use bytes::Bytes;
483
484 use super::*;
485 use crate::BytesString;
486
487 #[test]
488 fn test_new() {
489 let s = BytesStr::new();
490 assert_eq!(s.as_str(), "");
491 assert_eq!(s.len(), 0);
492 assert!(s.is_empty());
493 }
494
495 #[test]
496 fn test_default() {
497 let s: BytesStr = Default::default();
498 assert_eq!(s.as_str(), "");
499 assert_eq!(s.len(), 0);
500 assert!(s.is_empty());
501 }
502
503 #[test]
504 fn test_from_static() {
505 let s = BytesStr::from_static("hello world");
506 assert_eq!(s.as_str(), "hello world");
507 assert_eq!(s.len(), 11);
508 assert!(!s.is_empty());
509
510 let s = BytesStr::from_static("한국어 🌍");
512 assert_eq!(s.as_str(), "한국어 🌍");
513 }
514
515 #[test]
516 fn test_from_utf8() {
517 let bytes = Bytes::from_static(b"hello");
518 let s = BytesStr::from_utf8(bytes).unwrap();
519 assert_eq!(s.as_str(), "hello");
520
521 let bytes = Bytes::from("한국어".as_bytes());
523 let s = BytesStr::from_utf8(bytes).unwrap();
524 assert_eq!(s.as_str(), "한국어");
525
526 let invalid_bytes = Bytes::from_static(&[0xff, 0xfe]);
528 assert!(BytesStr::from_utf8(invalid_bytes).is_err());
529 }
530
531 #[test]
532 fn test_from_utf8_vec() {
533 let vec = b"hello world".to_vec();
534 let s = BytesStr::from_utf8_vec(vec).unwrap();
535 assert_eq!(s.as_str(), "hello world");
536
537 let vec = "한국어 🎉".as_bytes().to_vec();
539 let s = BytesStr::from_utf8_vec(vec).unwrap();
540 assert_eq!(s.as_str(), "한국어 🎉");
541
542 let invalid_vec = vec![0xff, 0xfe];
544 assert!(BytesStr::from_utf8_vec(invalid_vec).is_err());
545 }
546
547 #[test]
548 fn test_from_utf8_unchecked() {
549 let bytes = Bytes::from_static(b"hello");
550 let s = unsafe { BytesStr::from_utf8_unchecked(bytes) };
551 assert_eq!(s.as_str(), "hello");
552
553 let bytes = Bytes::from("한국어".as_bytes());
555 let s = unsafe { BytesStr::from_utf8_unchecked(bytes) };
556 assert_eq!(s.as_str(), "한국어");
557 }
558
559 #[test]
560 fn test_from_utf8_vec_unchecked() {
561 let vec = b"hello world".to_vec();
562 let s = unsafe { BytesStr::from_utf8_vec_unchecked(vec) };
563 assert_eq!(s.as_str(), "hello world");
564
565 let vec = "한국어 🎉".as_bytes().to_vec();
567 let s = unsafe { BytesStr::from_utf8_vec_unchecked(vec) };
568 assert_eq!(s.as_str(), "한국어 🎉");
569 }
570
571 #[test]
572 fn test_from_utf8_slice() {
573 let s = BytesStr::from_utf8_slice(b"hello").unwrap();
574 assert_eq!(s.as_str(), "hello");
575
576 let s = BytesStr::from_utf8_slice("한국어".as_bytes()).unwrap();
578 assert_eq!(s.as_str(), "한국어");
579
580 assert!(BytesStr::from_utf8_slice(&[0xff, 0xfe]).is_err());
582 }
583
584 #[test]
585 fn test_from_utf8_slice_unchecked() {
586 let s = unsafe { BytesStr::from_utf8_slice_unchecked(b"hello") };
587 assert_eq!(s.as_str(), "hello");
588
589 let s = unsafe { BytesStr::from_utf8_slice_unchecked("한국어".as_bytes()) };
591 assert_eq!(s.as_str(), "한국어");
592 }
593
594 #[test]
595 fn test_from_static_utf8_slice() {
596 let s = BytesStr::from_static_utf8_slice(b"hello").unwrap();
597 assert_eq!(s.as_str(), "hello");
598
599 let s = BytesStr::from_static_utf8_slice("한국어".as_bytes()).unwrap();
601 assert_eq!(s.as_str(), "한국어");
602
603 assert!(BytesStr::from_static_utf8_slice(&[0xff, 0xfe]).is_err());
605 }
606
607 #[test]
608 fn test_from_static_utf8_slice_unchecked() {
609 let s = unsafe { BytesStr::from_static_utf8_slice_unchecked(b"hello") };
610 assert_eq!(s.as_str(), "hello");
611
612 let s = unsafe { BytesStr::from_static_utf8_slice_unchecked("한국어".as_bytes()) };
614 assert_eq!(s.as_str(), "한국어");
615 }
616
617 #[test]
618 fn test_as_str() {
619 let s = BytesStr::from_static("hello world");
620 assert_eq!(s.as_str(), "hello world");
621
622 let s = BytesStr::from_static("한국어 🌍");
624 assert_eq!(s.as_str(), "한국어 🌍");
625 }
626
627 #[test]
628 fn test_deref() {
629 let s = BytesStr::from_static("hello world");
630
631 assert_eq!(s.len(), 11);
633 assert!(s.contains("world"));
634 assert!(s.starts_with("hello"));
635 assert!(s.ends_with("world"));
636 assert_eq!(&s[0..5], "hello");
637 }
638
639 #[test]
640 fn test_as_ref_str() {
641 let s = BytesStr::from_static("hello");
642 let str_ref: &str = s.as_ref();
643 assert_eq!(str_ref, "hello");
644 }
645
646 #[test]
647 fn test_as_ref_bytes() {
648 let s = BytesStr::from_static("hello");
649 let bytes_ref: &[u8] = s.as_ref();
650 assert_eq!(bytes_ref, b"hello");
651 }
652
653 #[test]
654 fn test_as_ref_bytes_type() {
655 let s = BytesStr::from_static("hello");
656 let bytes_ref: &Bytes = s.as_ref();
657 assert_eq!(bytes_ref.as_ref(), b"hello");
658 }
659
660 #[test]
661 fn test_as_ref_os_str() {
662 let s = BytesStr::from_static("hello/world");
663 let os_str_ref: &OsStr = s.as_ref();
664 assert_eq!(os_str_ref, OsStr::new("hello/world"));
665 }
666
667 #[test]
668 fn test_as_ref_path() {
669 let s = BytesStr::from_static("hello/world");
670 let path_ref: &Path = s.as_ref();
671 assert_eq!(path_ref, Path::new("hello/world"));
672 }
673
674 #[test]
675 fn test_borrow() {
676 let s = BytesStr::from_static("hello");
677 let borrowed: &str = s.borrow();
678 assert_eq!(borrowed, "hello");
679 }
680
681 #[test]
682 fn test_from_string() {
683 let original = String::from("hello world");
684 let s = BytesStr::from(original);
685 assert_eq!(s.as_str(), "hello world");
686 }
687
688 #[test]
689 fn test_from_static_str() {
690 let s = BytesStr::from("hello world");
691 assert_eq!(s.as_str(), "hello world");
692 }
693
694 #[test]
695 fn test_conversion_to_bytes_string() {
696 let s = BytesStr::from_static("hello");
697 let bytes_string: BytesString = s.into();
698 assert_eq!(bytes_string.as_str(), "hello");
699 }
700
701 #[test]
702 fn test_conversion_from_bytes_string() {
703 let mut bytes_string = BytesString::from("hello");
704 bytes_string.push_str(" world");
705 let s: BytesStr = bytes_string.into();
706 assert_eq!(s.as_str(), "hello world");
707 }
708
709 #[test]
710 fn test_try_from_static_slice() {
711 let s = BytesStr::try_from(b"hello" as &'static [u8]).unwrap();
712 assert_eq!(s.as_str(), "hello");
713
714 let invalid_slice: &'static [u8] = &[0xff, 0xfe];
716 assert!(BytesStr::try_from(invalid_slice).is_err());
717 }
718
719 #[test]
720 fn test_debug() {
721 let s = BytesStr::from_static("hello");
722 assert_eq!(format!("{:?}", s), "\"hello\"");
723
724 let s = BytesStr::from_static("hello\nworld");
725 assert_eq!(format!("{:?}", s), "\"hello\\nworld\"");
726 }
727
728 #[test]
729 fn test_display() {
730 let s = BytesStr::from_static("hello world");
731 assert_eq!(format!("{}", s), "hello world");
732
733 let s = BytesStr::from_static("한국어 🌍");
734 assert_eq!(format!("{}", s), "한국어 🌍");
735 }
736
737 #[test]
738 fn test_index() {
739 let s = BytesStr::from_static("hello world");
740 assert_eq!(&s[0..5], "hello");
741 assert_eq!(&s[6..], "world");
742 assert_eq!(&s[..5], "hello");
743 assert_eq!(&s[6..11], "world");
744
745 let s = BytesStr::from_static("한국어");
747 assert_eq!(&s[0..6], "한국");
748 }
749
750 #[test]
751 fn test_partial_eq_str() {
752 let s = BytesStr::from_static("hello");
753
754 assert_eq!(s, "hello");
756 assert_ne!(s, "world");
757
758 assert_eq!("hello", s);
760 assert_ne!("world", s);
761
762 let hello_str = "hello";
764 let world_str = "world";
765 assert_eq!(s, hello_str);
766 assert_ne!(s, world_str);
767
768 assert_eq!(hello_str, s);
770 assert_ne!(world_str, s);
771 }
772
773 #[test]
774 fn test_partial_eq_string() {
775 let s = BytesStr::from_static("hello");
776 let string = String::from("hello");
777 let other_string = String::from("world");
778
779 assert_eq!(s, string);
781 assert_ne!(s, other_string);
782
783 assert_eq!(string, s);
785 assert_ne!(other_string, s);
786 }
787
788 #[test]
789 fn test_partial_eq_cow() {
790 let s = BytesStr::from_static("hello");
791
792 assert_eq!(s, Cow::Borrowed("hello"));
793 assert_eq!(s, Cow::Owned(String::from("hello")));
794 assert_ne!(s, Cow::Borrowed("world"));
795 assert_ne!(s, Cow::Owned(String::from("world")));
796 }
797
798 #[test]
799 fn test_partial_eq_bytes() {
800 let s = BytesStr::from_static("hello");
801 let bytes = Bytes::from_static(b"hello");
802 let other_bytes = Bytes::from_static(b"world");
803
804 assert_eq!(bytes, s);
805 assert_ne!(other_bytes, s);
806 }
807
808 #[test]
809 fn test_partial_eq_bytes_str() {
810 let s1 = BytesStr::from_static("hello");
811 let s2 = BytesStr::from_static("hello");
812 let s3 = BytesStr::from_static("world");
813
814 assert_eq!(s1, s2);
815 assert_ne!(s1, s3);
816 }
817
818 #[test]
819 fn test_ordering() {
820 let s1 = BytesStr::from_static("apple");
821 let s2 = BytesStr::from_static("banana");
822 let s3 = BytesStr::from_static("apple");
823
824 assert!(s1 < s2);
825 assert!(s2 > s1);
826 assert_eq!(s1, s3);
827 assert!(s1 <= s3);
828 assert!(s1 >= s3);
829
830 assert_eq!(s1.partial_cmp(&s2), Some(std::cmp::Ordering::Less));
832 assert_eq!(s2.partial_cmp(&s1), Some(std::cmp::Ordering::Greater));
833 assert_eq!(s1.partial_cmp(&s3), Some(std::cmp::Ordering::Equal));
834 }
835
836 #[test]
837 fn test_hash() {
838 let s1 = BytesStr::from_static("hello");
839 let s2 = BytesStr::from_static("hello");
840 let s3 = BytesStr::from_static("world");
841
842 let mut hasher1 = DefaultHasher::new();
843 let mut hasher2 = DefaultHasher::new();
844 let mut hasher3 = DefaultHasher::new();
845
846 s1.hash(&mut hasher1);
847 s2.hash(&mut hasher2);
848 s3.hash(&mut hasher3);
849
850 assert_eq!(hasher1.finish(), hasher2.finish());
851 assert_ne!(hasher1.finish(), hasher3.finish());
852
853 let mut str_hasher = DefaultHasher::new();
855 "hello".hash(&mut str_hasher);
856 assert_eq!(hasher1.finish(), str_hasher.finish());
857 }
858
859 #[test]
860 fn test_clone() {
861 let s1 = BytesStr::from_static("hello world");
862 let s2 = s1.clone();
863
864 assert_eq!(s1, s2);
865 assert_eq!(s1.as_str(), s2.as_str());
866
867 }
870
871 #[test]
872 fn test_extend_bytes_string() {
873 let mut bytes_string = BytesString::from("hello");
874 let parts = vec![
875 BytesStr::from_static(" "),
876 BytesStr::from_static("world"),
877 BytesStr::from_static("!"),
878 ];
879
880 bytes_string.extend(parts);
881 assert_eq!(bytes_string.as_str(), "hello world!");
882 }
883
884 #[test]
885 fn test_unicode_handling() {
886 let s = BytesStr::from_static("Hello 🌍 한국어 🎉");
887 assert_eq!(s.as_str(), "Hello 🌍 한국어 🎉");
888 assert!(s.len() > 13); let korean = BytesStr::from_static("한국어");
892 assert_eq!(korean.len(), 9); assert_eq!(&korean[0..6], "한국"); }
895
896 #[test]
897 fn test_empty_strings() {
898 let s = BytesStr::new();
899 assert!(s.is_empty());
900 assert_eq!(s.len(), 0);
901 assert_eq!(s.as_str(), "");
902
903 let s = BytesStr::from_static("");
904 assert!(s.is_empty());
905 assert_eq!(s.len(), 0);
906 assert_eq!(s.as_str(), "");
907 }
908
909 #[test]
910 fn test_large_strings() {
911 let large_str = "a".repeat(10000);
912 let s = BytesStr::from(large_str.clone());
913 assert_eq!(s.len(), 10000);
914 assert_eq!(s.as_str(), large_str);
915 }
916
917 #[test]
918 fn test_hash_map_usage() {
919 let mut map = HashMap::new();
920 let key = BytesStr::from_static("key");
921 map.insert(key, "value");
922
923 let lookup_key = BytesStr::from_static("key");
924 assert_eq!(map.get(&lookup_key), Some(&"value"));
925
926 assert_eq!(map.get("key"), Some(&"value"));
928 }
929
930 #[test]
931 fn test_memory_efficiency() {
932 let original = BytesStr::from(String::from("hello world"));
934 let clone1 = original.clone();
935 let clone2 = original.clone();
936
937 assert_eq!(original.as_str(), "hello world");
939 assert_eq!(clone1.as_str(), "hello world");
940 assert_eq!(clone2.as_str(), "hello world");
941
942 assert_eq!(original, clone1);
944 assert_eq!(clone1, clone2);
945 }
946
947 #[test]
948 fn test_static_vs_owned() {
949 let static_str = BytesStr::from_static("hello");
951 assert_eq!(static_str.as_str(), "hello");
952
953 let owned_str = BytesStr::from(String::from("hello"));
955 assert_eq!(owned_str.as_str(), "hello");
956
957 assert_eq!(static_str, owned_str);
959 }
960
961 #[test]
962 fn test_error_cases() {
963 let invalid_sequences = vec![
965 vec![0xff], vec![0xfe, 0xff], vec![0xc0, 0x80], vec![0xe0, 0x80, 0x80], ];
970
971 for invalid in invalid_sequences {
972 assert!(BytesStr::from_utf8(Bytes::from(invalid.clone())).is_err());
973 assert!(BytesStr::from_utf8_vec(invalid.clone()).is_err());
974 assert!(BytesStr::from_utf8_slice(&invalid).is_err());
975 }
976 }
977
978 #[test]
979 fn test_boundary_conditions() {
980 let s = BytesStr::from_static("a");
982 assert_eq!(s.len(), 1);
983 assert_eq!(s.as_str(), "a");
984
985 let s = BytesStr::from_static("한");
987 assert_eq!(s.len(), 3); assert_eq!(s.as_str(), "한");
989
990 let s = BytesStr::from_static("🌍");
992 assert_eq!(s.len(), 4); assert_eq!(s.as_str(), "🌍");
994 }
995}