varlen/str.rs
1# representation of `"hello"` is:
6 //!
7 //! ```svgbob
8 //! "VBox"
9 //! +----------+
10 //! | ptr |
11 //! +----------+
12 //! |
13 //! | Str
14 //! | +-----------+-----------+
15 //! '-> | 3: usize | ""hello"" |
16 //! +-----------+-----------+
17 //! ```
18 //!
19 //! Once allocated, the string size may not be modified.
20 //!
21 //! # Examples
22 //!
23 //! ```
24 //! use varlen::prelude::*;
25 //! use varlen::Layout;
26 //! let s = VBox::new(Str::copy("hello"));
27 //! assert_eq!(&s[..], "hello");
28 //! // Layout is as specified above:
29 //! assert_eq!(s.calculate_layout().size(), std::mem::size_of::<usize>() + 5)
30 //! ```
31)]
32
33use crate::array::{Array, ArrayCloner, ArrayLen};
34use crate::newtype::define_varlen_newtype;
35use crate::{impl_initializer_as_newtype, Initializer, VClone, VCopy};
36use core::pin::Pin;
37
38define_varlen_newtype! {
39 #[repr(transparent)]
40 #[doc = crate::doc_macro::make_svgbobdoc!(
41 /// A string with inline storage.
42 ///
43 /// This consists of an integer `length` field, followed immediately by the utf8 string payload.
44 /// For example, the [`VBox<String>`](crate::VBox) representation of `"hello"` is:
45 ///
46 /// ```svgbob
47 /// "VBox"
48 /// +----------+
49 /// | ptr |
50 /// +----------+
51 /// |
52 /// | Str
53 /// | +-----------+------+------+------+------+------+
54 /// '-> | 3: usize | "'h'"| "'e'"| "'l'"| "'l'"| "'o'"|
55 /// +-----------+------+------+------+------+------+
56 /// ```
57 ///
58 /// Once allocated, the string size may not be modified.
59 ///
60 /// # Examples
61 ///
62 /// ```
63 /// use varlen::prelude::*;
64 /// use varlen::Layout;
65 /// let s = VBox::new(Str::copy("hello"));
66 /// assert_eq!(&s[..], "hello");
67 /// // Layout is as specified above:
68 /// assert_eq!(s.calculate_layout().size(), std::mem::size_of::<usize>() + 5)
69 /// ```
70 ///
71 /// # Smaller length field
72 ///
73 /// You may choose to store the length of the string in a smaller integer type than [`usize`],
74 /// just like in [`Array<T>`](crate::Array). Construction will fail if the string is too long for the
75 /// integer type to express:
76 ///
77 /// ```
78 /// use varlen::prelude::*;
79 /// // Short string fits:
80 /// let s: VBox<Str<u8>> = VBox::new(Str::try_copy("hello").unwrap());
81 /// // Long string doesn't fit:
82 /// assert!(Str::<u8>::try_copy(std::str::from_utf8(&[b'a'; 257]).unwrap()).is_none());
83 /// ```
84 )]
85 pub struct Str<(Len: ArrayLen = usize)>(Array<u8, Len>);
86
87 with signature: impl<(Len: ArrayLen)> Str<(Len)> { _ }
88
89 with init: struct StrInit<_>(_);
90 with inner_ref: fn inner(&self) -> &_;
91 with inner_mut: fn inner_mut(self: _) -> _;
92}
93
94impl<Len: ArrayLen> core::ops::Deref for Str<Len> {
95 type Target = str;
96 fn deref(&self) -> &Self::Target {
97 let slice: &[u8] = &self.inner();
98 unsafe {
99 // Safety: we only allow initialization from valid utf8
100 core::str::from_utf8_unchecked(slice)
101 }
102 }
103}
104
105#[allow(rustdoc::missing_doc_code_examples)]
106impl<Len: ArrayLen> Str<Len> {
107 /// Mutable access to the underlying string.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use varlen::prelude::*;
113 /// let mut s = VBox::new(Str::copy("hello"));
114 /// s.as_mut().mut_slice().make_ascii_uppercase();
115 /// assert_eq!(&s[..], "HELLO");
116 /// ```
117 pub fn mut_slice(self: Pin<&mut Self>) -> &mut str {
118 let slice = self.inner_mut().mut_slice();
119 unsafe {
120 // Safety: we only allow initialization from valid utf8
121 core::str::from_utf8_unchecked_mut(slice)
122 }
123 }
124}
125
126#[allow(rustdoc::missing_doc_code_examples)]
127impl Str {
128 /// Initializes a [`Str`] by copying from an existing [`&str`].
129 ///
130 /// # Examples
131 ///
132 /// ```
133 /// use varlen::prelude::*;
134 /// let s = VBox::new(Str::copy("hello"));
135 /// assert_eq!(&s[..], "hello");
136 /// ```
137 pub fn copy(s: &str) -> impl Initializer<Self> + '_ {
138 Str::try_copy(s).unwrap()
139 }
140}
141#[allow(rustdoc::missing_doc_code_examples)]
142impl<Len: ArrayLen> Str<Len> {
143 /// Initializes a [`Str`] from an existing [`&str`], or returns `None` if it doesn't fit in the length field.
144 ///
145 /// ```
146 /// use varlen::prelude::*;
147 /// // Short string fits:
148 /// let s: VBox<Str<u8>> = VBox::new(Str::try_copy("hello").unwrap());
149 /// // Long string doesn't fit:
150 /// assert!(Str::<u8>::try_copy(std::str::from_utf8(&[b'a'; 257]).unwrap()).is_none());
151 /// ```
152 pub fn try_copy(s: &str) -> Option<impl Initializer<Self> + '_> {
153 Some(StrInit(Array::try_copy(s.as_bytes())?))
154 }
155}
156
157/// Initializer type for cloning an array.
158///
159/// Prefer to use `Str::vcopy()` over `Str::vclone()` where possible.
160///
161/// # Examples
162///
163/// ```
164/// use varlen::prelude::*;
165/// let arr = VBox::new(Str::copy("hello"));
166/// let seq: Seq<Str> = seq![arr.vclone(), arr.vclone(), arr.vclone()]; // Clones the string
167/// for a in seq.iter() {
168/// assert_eq!(&a[..], "hello");
169/// }
170/// ```
171pub struct StrCloner<'a, Len: ArrayLen>(StrInit<ArrayCloner<'a, u8, Len>>);
172
173impl_initializer_as_newtype! {
174 impl<('a), (Len: ArrayLen)> Initializer<Str<Len>> for StrCloner<'a, Len> { _ }
175}
176
177impl<'a, Len: ArrayLen> VClone<'a> for Str<Len> {
178 type Cloner = StrCloner<'a, Len>;
179 fn vclone(&'a self) -> Self::Cloner {
180 StrCloner(StrInit(self.0.vclone()))
181 }
182}
183
184/// Strings can be copied with fast memcpy.
185///
186/// # Examples
187///
188/// ```
189/// use varlen::prelude::*;
190/// let arr = VBox::new(Str::copy("hello"));
191/// let seq: Seq<Str> = seq![arr.vcopy(), arr.vcopy(), arr.vcopy()];
192/// for a in seq.iter() {
193/// assert_eq!(&a[..], "hello");
194/// }
195/// ```
196// Safety: Len is Copy (because of ArrayLen), and so is the [u8] payload.
197unsafe impl<'a, Len: ArrayLen> VCopy<'a> for Str<Len> {}