1#![no_std]
2#![cfg(target_pointer_width = "64")]
3
4extern crate alloc;
5
6use alloc::borrow::{Cow, ToOwned as _};
7use alloc::boxed::Box;
8use alloc::slice;
9use alloc::string::String;
10use alloc::sync::Arc;
11use core::{cmp, fmt, ptr};
12use core::alloc::Layout;
13use core::borrow::Borrow;
14use core::ops::Deref;
15use core::ptr::NonNull;
16use core::str::FromStr;
17
18pub const MAX_INLINE_BYTES: usize = 12;
21
22pub const MAX_LEN: usize = 2_usize.pow(32);
25
26const OWNED_PTR: usize = 0;
29
30const SHARED_PTR: usize = usize::MAX;
33
34#[repr(C)]
41pub struct GermanStr {
42 len: u32,
46
47 prefix: [u8; 4],
55
56 last8: Last8,
66}
67
68#[derive(Copy, Clone)]
69union Last8 {
71 ptr: ointers::NotNull<u8, 0, false, 1>,
73 buf: [u8; 8],
79}
80
81#[derive(Debug, Clone, Copy)]
82pub enum InitError {
84 TooLong,
87}
88
89impl GermanStr {
90 #[inline]
91 pub fn new(src: impl AsRef<str>) -> Result<Self, InitError> {
93 let src = src.as_ref();
94 if src.len() > MAX_LEN {
95 return Err(InitError::TooLong);
96 }
97 if src.len() <= MAX_INLINE_BYTES {
98 return Ok(GermanStr::new_inline(src));
99 }
100
101 let layout = Layout::array::<u8>(src.len())
102 .map_err(|_| InitError::TooLong)?;
103 let ptr = unsafe {
104 alloc::alloc::alloc(layout)
106 };
107 let Some(ptr) = NonNull::new(ptr) else {
108 alloc::alloc::handle_alloc_error(layout);
109 };
110 unsafe {
111 ptr::copy_nonoverlapping(
118 src.as_bytes().as_ptr(),
119 ptr.as_ptr(),
120 src.len(),
121 );
122 }
123 let ointer = unsafe {
124 ointers::NotNull::new_stealing(ptr, OWNED_PTR)
126 };
127 Ok(GermanStr {
128 len: src.len() as u32,
129 prefix: str_prefix::<&str>(&src),
130 last8: Last8 { ptr: ointer },
131 })
132 }
133
134 #[inline]
135 pub const fn new_inline(src: &str) -> GermanStr {
140 assert!(src.len() <= MAX_INLINE_BYTES);
141
142 let mut prefix = [0; 4];
143 let mut i = 0;
144 while i < src.len() && i < 4 {
145 prefix[i] = src.as_bytes()[i];
146 i += 1;
147 }
148
149 let mut buf = [0; 8];
150 let mut i = 4;
151 while i < src.len() && i < MAX_INLINE_BYTES {
152 buf[i - 4] = src.as_bytes()[i];
153 i += 1;
154 }
155
156 GermanStr {
157 len: src.len() as u32,
158 prefix,
159 last8: Last8 { buf },
160 }
161 }
162
163 #[inline(always)]
164 pub fn heap_ptr(&self) -> Option<NonNull<u8>> {
172 self.heap_ointer()
173 .map(|ointer| ointer.as_non_null())
174 }
175
176 #[inline(always)]
177 fn heap_ointer(&self) -> Option<ointers::NotNull<u8, 0, false, 1>> {
179 if self.len as usize > MAX_INLINE_BYTES {
180 Some(unsafe {
181 self.last8.ptr
183 })
184 } else {
185 None
186 }
187 }
188
189
190 #[inline(always)]
191 pub fn has_shared_buffer(&self) -> bool {
194 self.heap_ointer().is_some_and(|ptr| ptr.stolen() != OWNED_PTR)
195 }
196
197 #[inline]
198 pub fn leaky_shared_clone(&mut self) -> Self {
212 if self.is_heap_allocated() {
213 unsafe {
214 self.last8.ptr = self.last8.ptr.steal(SHARED_PTR);
215 }
216 }
217 GermanStr {
218 len: self.len,
219 prefix: self.prefix,
220 last8: self.last8,
221 }
222 }
223
224 pub unsafe fn free(mut self) {
258 unsafe {
259 self.last8.ptr = self.last8.ptr.steal(OWNED_PTR);
262 }
263 core::mem::drop(self)
264 }
265
266 #[inline]
267 pub fn prefix_bytes_slice(&self) -> &[u8] {
272 let prefix_len = self.len().min(4);
273 &self.prefix[..prefix_len]
274 }
275
276 #[inline(always)]
277 pub const fn prefix_bytes_array(&self) -> [u8; 4] {
283 self.prefix
284 }
285
286 #[inline]
287 pub fn suffix_bytes_slice(&self) -> &[u8] {
289 let suffix_len = self.len().saturating_sub(4);
290 if self.len as usize > MAX_INLINE_BYTES {
291 unsafe {
292 let ptr = self.last8.ptr.as_non_null().as_ptr();
295
296 slice::from_raw_parts(ptr.add(4), suffix_len)
303 }
304 } else {
305 unsafe {
306 &self.last8.buf[0..suffix_len]
307 }
308 }
309 }
310
311 #[inline(always)]
312 pub fn as_str(&self) -> &str {
313 Deref::deref(self)
314 }
315
316 #[allow(clippy::inherent_to_string_shadow_display)]
317 #[inline(always)]
318 pub fn to_string(&self) -> String {
319 self.as_str().to_owned()
320 }
321
322 #[inline(always)]
323 pub const fn len(&self) -> usize {
324 self.len as usize
325 }
326
327 #[inline(always)]
328 pub const fn is_empty(&self) -> bool {
329 self.len == 0
330 }
331
332 #[inline(always)]
333 pub const fn is_heap_allocated(&self) -> bool {
335 self.len as usize > MAX_INLINE_BYTES
336 }
337
338 #[inline(always)]
339 pub const fn is_inlined(&self) -> bool {
341 !self.is_heap_allocated()
342 }
343}
344
345impl Clone for GermanStr {
346 #[inline]
347 fn clone(&self) -> Self {
348 if let Some(self_ptr) = self.heap_ptr() {
349 let (ptr, layout) = unsafe {
350 let layout = Layout::array::<u8>(self.len()).unwrap_unchecked();
353
354 let ptr = alloc::alloc::alloc(layout);
356 (ptr, layout)
357 };
358 let Some(ptr) = NonNull::new(ptr) else {
359 alloc::alloc::handle_alloc_error(layout);
360 };
361 unsafe {
362 ptr::copy_nonoverlapping(
367 self_ptr.as_ptr(),
368 ptr.as_ptr(),
369 self.len(),
370 );
371 }
372 let ointer = unsafe {
373 ointers::NotNull::new_stealing(ptr, OWNED_PTR)
375 };
376 GermanStr {
377 prefix: self.prefix,
378 len: self.len,
379 last8: Last8 { ptr: ointer },
380 }
381 } else {
382 GermanStr {
383 len: self.len,
384 prefix: self.prefix,
385 last8: self.last8,
386 }
387 }
388 }
389}
390
391impl Drop for GermanStr {
392 #[inline]
393 fn drop(&mut self) {
394 let ptr = match self.heap_ptr() {
395 Some(ptr) if !self.has_shared_buffer() => ptr,
396 Some(_) | None => return,
397 };
400 unsafe {
401 let layout = Layout::array::<u8>(self.len as usize).unwrap_unchecked();
405 alloc::alloc::dealloc(ptr.as_ptr(), layout);
406 }
407 }
408}
409
410unsafe impl Send for GermanStr {}
414
415unsafe impl Sync for GermanStr {}
418
419impl Deref for GermanStr {
420 type Target = str;
421
422 #[inline(always)]
423 fn deref(&self) -> &str {
424 let ptr = self.heap_ptr()
425 .unwrap_or_else(|| unsafe {
426 NonNull::new_unchecked(self.prefix.as_ptr().cast_mut())
429 });
430 unsafe {
431 let slice = slice::from_raw_parts(ptr.as_ptr(), self.len as usize);
439
440 core::str::from_utf8_unchecked(slice)
445 }
446 }
447}
448
449impl AsRef<str> for GermanStr {
450 #[inline(always)]
451 fn as_ref(&self) -> &str {
452 Deref::deref(self)
453 }
454}
455
456impl PartialEq<GermanStr> for GermanStr {
457 #[inline(always)]
458 fn eq(&self, other: &GermanStr) -> bool {
459 let prefixes_equal = self.prefix == other.prefix;
460 if !prefixes_equal {
461 return false;
462 } else if self.len <= 4 && other.len <= 4 {
463 return true;
464 }
465
466 if self.is_inlined() && other.is_inlined() {
467 return unsafe {
468 self.last8.buf == other.last8.buf
470 };
471 }
472
473 return self.suffix_bytes_slice() == other.suffix_bytes_slice();
474 }
475}
476
477impl Eq for GermanStr {}
478
479impl Ord for GermanStr {
480 #[inline]
481 fn cmp(&self, other: &Self) -> cmp::Ordering {
482 self.prefix
483 .cmp(&other.prefix)
484 .then_with(||
485 if self.len <= 4 && other.len <= 4 {
486 cmp::Ordering::Equal
487 } else if self.is_inlined() && other.is_inlined() {
488 unsafe {
489 self.last8.buf.cmp(&other.last8.buf)
491 }
492 } else {
493 self.suffix_bytes_slice().cmp(other.suffix_bytes_slice())
494 }
495 )
496 }
497}
498
499impl Default for GermanStr {
500 #[inline(always)]
501 fn default() -> GermanStr {
502 GermanStr {
503 len: 0,
504 prefix: [0; 4],
505 last8: Last8 { buf: [0; 8] },
506 }
507 }
508}
509
510impl FromStr for GermanStr {
511 type Err = InitError;
512
513 #[inline]
514 fn from_str(s: &str) -> Result<GermanStr, Self::Err> {
515 GermanStr::new(s)
516 }
517}
518
519impl Borrow<str> for GermanStr {
520 #[inline(always)]
521 fn borrow(&self) -> &str {
522 self.as_str()
523 }
524}
525
526impl core::fmt::Debug for GermanStr {
527 #[inline]
528 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
529 core::fmt::Debug::fmt(self.as_str(), f)
530 }
531}
532
533
534impl core::fmt::Display for GermanStr {
535 #[inline]
536 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
537 core::fmt::Display::fmt(self.as_str(), f)
538 }
539}
540
541impl core::fmt::Display for InitError {
542 #[inline]
543 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
544 core::fmt::Display::fmt(
545 match self {
546 InitError::TooLong => "Tried to initialize a GermanStr longer than 4GB.",
547 },
548 f
549 )
550 }
551}
552
553impl core::hash::Hash for GermanStr {
554 #[inline(always)]
555 fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
556 self.as_str().hash(hasher);
557 }
558}
559
560
561impl PartialOrd for GermanStr {
562 #[inline(always)]
563 fn partial_cmp(&self, other: &GermanStr) -> Option<core::cmp::Ordering> {
564 Some(self.cmp(other))
565 }
566}
567
568impl PartialEq<str> for GermanStr {
569 #[inline(always)]
570 fn eq(&self, other: &str) -> bool {
571 self.prefix == str_prefix::<&str>(other) &&
572 self.suffix_bytes_slice() == str_suffix::<&str>(&other)
573 }
574}
575
576impl PartialEq<GermanStr> for str {
577 #[inline(always)]
578 fn eq(&self, other: &GermanStr) -> bool {
579 other.prefix == str_prefix::<&str>(self) &&
580 other.suffix_bytes_slice() == str_suffix::<&str>(&self)
581 }
582}
583
584impl<'a> PartialEq<&'a str> for GermanStr {
585 #[inline(always)]
586 fn eq(&self, other: &&'a str) -> bool {
587 self.prefix == str_prefix::<&str>(other) &&
588 self.suffix_bytes_slice() == str_suffix::<&str>(&other)
589 }
590}
591
592impl<'a> PartialEq<GermanStr> for &'a str {
593 #[inline(always)]
594 fn eq(&self, other: &GermanStr) -> bool {
595 other.prefix == str_prefix::<&str>(self) &&
596 other.suffix_bytes_slice() == str_suffix::<&str>(&self)
597 }
598}
599
600impl PartialEq<String> for GermanStr {
601 #[inline(always)]
602 fn eq(&self, other: &String) -> bool {
603 self.prefix == str_prefix::<&str>(other) &&
604 self.suffix_bytes_slice() == str_suffix::<&str>(&other)
605 }
606}
607
608impl PartialEq<GermanStr> for String {
609 #[inline(always)]
610 fn eq(&self, other: &GermanStr) -> bool {
611 other.prefix == str_prefix::<&str>(self) &&
612 other.suffix_bytes_slice() == str_suffix::<&str>(&self)
613 }
614}
615
616impl<'a> PartialEq<&'a String> for GermanStr {
617 #[inline(always)]
618 fn eq(&self, other: &&'a String) -> bool {
619 self.prefix == str_prefix::<&str>(other) &&
620 self.suffix_bytes_slice() == str_suffix::<&str>(&other)
621 }
622}
623
624impl<'a> PartialEq<GermanStr> for &'a String {
625 #[inline(always)]
626 fn eq(&self, other: &GermanStr) -> bool {
627 other.prefix == str_prefix::<&str>(self) &&
628 other.suffix_bytes_slice() == str_suffix::<&str>(&self)
629 }
630}
631
632impl TryFrom<&str> for GermanStr {
633 type Error = InitError;
634
635 #[inline]
636 fn try_from(s: &str) -> Result<GermanStr, InitError> {
637 GermanStr::new(s)
638 }
639 }
640
641impl TryFrom<&mut str> for GermanStr {
642 type Error = InitError;
643
644 #[inline]
645 fn try_from(s: &mut str) -> Result<GermanStr,InitError> {
646 GermanStr::new(s)
647 }
648}
649
650impl TryFrom<&String> for GermanStr {
651 type Error = InitError;
652
653 #[inline]
654 fn try_from(s: &String) -> Result<GermanStr,InitError> {
655 GermanStr::new(s)
656 }
657}
658
659impl TryFrom<String> for GermanStr {
660 type Error = InitError;
661
662 #[inline(always)]
663 fn try_from(text: String) -> Result<Self, Self::Error> {
664 Self::new(text)
665 }
666}
667
668impl TryFrom<Box<str>> for GermanStr {
669 type Error = InitError;
670
671 #[inline(always)]
672 fn try_from(s: Box<str>) -> Result<GermanStr, Self::Error> {
673 GermanStr::new(s)
674 }
675}
676
677impl TryFrom<Arc<str>> for GermanStr {
678 type Error = InitError;
679
680 #[inline(always)]
681 fn try_from(s: Arc<str>) -> Result<GermanStr, Self::Error> {
682 GermanStr::new(s)
683 }
684}
685
686impl From<GermanStr> for Arc<str> {
687 #[inline(always)]
688 fn from(text: GermanStr) -> Self {
689 text.as_str().into()
690 }
691}
692
693impl<'a> TryFrom<Cow<'a, str>> for GermanStr {
694 type Error = InitError;
695
696 #[inline]
697 fn try_from(s: Cow<'a, str>) -> Result<GermanStr,InitError> {
698 GermanStr::new(s)
699 }
700}
701
702impl From<GermanStr> for String {
703 #[inline(always)]
704 fn from(text: GermanStr) -> Self {
705 text.as_str().into()
706 }
707}
708
709#[inline]
710pub fn str_prefix<T>(src: impl AsRef<str>) -> [u8; 4] {
713 let src_bytes = src.as_ref().as_bytes();
714 let prefix_len = src_bytes.len().min(4);
715 let mut bytes = [0; 4];
716 bytes[..prefix_len].copy_from_slice(&src_bytes[..prefix_len]);
717 bytes
718}
719
720#[inline]
721pub fn str_suffix<T>(src: &impl AsRef<str>) -> &[u8] {
723 src.as_ref().as_bytes().get(4..).unwrap_or_default()
724}
725
726pub trait ToGermanStr {
728 fn to_german_str(&self) -> GermanStr;
729}
730
731#[doc(hidden)]
732pub struct Writer {
733 len: usize,
734 inline: [u8; MAX_INLINE_BYTES],
735 heap: String,
736}
737
738impl Writer {
739 #[must_use]
740 pub const fn new() -> Self {
741 Writer {
742 len: 0,
743 inline: [0; MAX_INLINE_BYTES],
744 heap: String::new(),
745 }
746 }
747
748 fn push_str(&mut self, s: &str) -> Result<(), InitError> {
749 let old_len = self.len;
750 self.len += s.len();
751 if self.len > MAX_LEN {
752 return Err(InitError::TooLong);
753 }
754 if self.len <= MAX_INLINE_BYTES {
755 self.inline[old_len..self.len].copy_from_slice(s.as_bytes());
757 } else if old_len <= MAX_INLINE_BYTES {
758 self.heap.reserve(self.len);
760 unsafe {
761 self.heap
765 .as_mut_vec()
766 .extend_from_slice(&self.inline[..old_len]);
767 }
768 self.heap.push_str(s);
769 } else {
770 self.heap.push_str(s);
771 }
772 Ok(())
773 }
774}
775
776impl fmt::Write for Writer {
777 #[inline]
778 fn write_str(&mut self, s: &str) -> fmt::Result {
779 self.push_str(s)
780 .map_err(|_| fmt::Error::default())
781 }
782}
783
784#[macro_export]
788macro_rules! format_german_str {
789 ($($tt:tt)*) => {{
790 use ::core::fmt::Write;
791 let mut w = $crate::Writer::new();
792 w.write_fmt(format_args!($($tt)*))
793 .expect("tried to format_german_str a GermanStr bigger than the maximum GermanStr size");
794 $crate::GermanStr::from(w)
795 }};
796}
797
798impl From<Writer> for GermanStr {
799 fn from(value: Writer) -> Self {
800 if value.len <= MAX_INLINE_BYTES {
801 let mut prefix = [0; 4];
802 prefix.clone_from_slice(&value.inline[0..4]);
803 let mut last8 = [0; 8];
804 last8.clone_from_slice(&value.inline[4..MAX_INLINE_BYTES]);
805 GermanStr {
806 len: value.len as u32,
807 prefix,
808 last8: Last8 { buf: last8 },
809 }
810 } else {
811 let heap_ref = value.heap.leak(); let non_null = unsafe {
813 NonNull::new_unchecked(heap_ref.as_mut_ptr())
814 };
815 let ointer = unsafe {
816 ointers::NotNull::new_stealing(non_null, OWNED_PTR)
818 };
819 GermanStr {
820 len: value.len as u32,
821 prefix: str_prefix::<&str>(heap_ref),
822 last8: Last8 { ptr: ointer },
823 }
824 }
825 }
826}
827
828impl<T> ToGermanStr for T
829where
830 T: fmt::Display + ?Sized,
831{
832 fn to_german_str(&self) -> GermanStr {
833 format_german_str!("{}", self)
834 }
835}
836
837#[cfg(feature = "arbitrary")]
838impl<'a> arbitrary::Arbitrary<'a> for GermanStr {
839 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> Result<Self, arbitrary::Error> {
840 let s = <&str>::arbitrary(u)?;
841 Ok(GermanStr::new(s).expect("BUG in arbitrary implementation of GermanStr. Please report it at github.com/ostnam/german-str/issues"))
842 }
843
844 fn size_hint(_: usize) -> (usize, Option<usize>) {
845 (0, Some(MAX_LEN))
846 }
847}
848
849#[cfg(feature = "serde")]
850mod serde {
851 use alloc::string::String;
852 use alloc::vec::Vec;
853 use core::fmt;
854
855 use serde::de::{Deserializer, Error, Unexpected, Visitor};
856
857 use crate::GermanStr;
858
859 fn deserialize<'de: 'a, 'a, D>(deserializer: D) -> Result<GermanStr, D::Error>
860 where
861 D: Deserializer<'de>,
862 {
863 struct GermanStrVisitor;
864
865 impl<'a> Visitor<'a> for GermanStrVisitor {
866 type Value = GermanStr;
867
868 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
869 formatter.write_str("a string")
870 }
871
872 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
873 where
874 E: Error,
875 {
876 GermanStr::new(v).map_err(Error::custom)
877 }
878
879 fn visit_borrowed_str<E>(self, v: &'a str) -> Result<Self::Value, E>
880 where
881 E: Error,
882 {
883 GermanStr::new(v).map_err(Error::custom)
884 }
885
886 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
887 where
888 E: Error,
889 {
890 GermanStr::new(v).map_err(Error::custom)
891 }
892
893 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
894 where
895 E: Error,
896 {
897 match core::str::from_utf8(v) {
898 Ok(s) => GermanStr::new(s).map_err(Error::custom),
899 Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
900 }
901 }
902
903 fn visit_borrowed_bytes<E>(self, v: &'a [u8]) -> Result<Self::Value, E>
904 where
905 E: Error,
906 {
907 match core::str::from_utf8(v) {
908 Ok(s) => GermanStr::new(s).map_err(Error::custom),
909 Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
910 }
911 }
912
913 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
914 where
915 E: Error,
916 {
917 match String::from_utf8(v) {
918 Ok(s) => GermanStr::new(s).map_err(Error::custom),
919 Err(e) => Err(Error::invalid_value(
920 Unexpected::Bytes(&e.into_bytes()),
921 &self,
922 )),
923 }
924 }
925 }
926
927 deserializer.deserialize_str(GermanStrVisitor)
928 }
929
930 impl serde::Serialize for GermanStr {
931 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
932 where
933 S: serde::Serializer,
934 {
935 self.as_str().serialize(serializer)
936 }
937 }
938
939 impl<'de> serde::Deserialize<'de> for GermanStr {
940 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
941 where
942 D: serde::Deserializer<'de>,
943 {
944 deserialize(deserializer)
945 }
946 }
947}