Skip to main content

static_collections/ffi/
wstring.rs

1// The static-wstring module.
2
3use core::{fmt, mem::MaybeUninit, ops::{Index, IndexMut}, slice::SliceIndex};
4
5use crate::vec::StaticVec;
6
7/// The `StaticWString` is a fixed-capacity UTF-16 string object.
8#[derive(Default, Debug, Clone)]
9pub struct StaticWString<const N:usize>
10{
11	internal:StaticVec<N,u16>
12}
13
14impl<const N:usize> StaticWString<N>
15{
16	/// Creates a new empty `StaticWString`.
17	/// 
18	/// Given that the string is empty, the buffer that contains the string isn't initialized.
19	/// This means the initial operation is very inexpensive.
20	pub const fn new()->Self
21	{
22		Self
23		{
24			internal:StaticVec::new()
25		}
26	}
27
28
29	/// Obtains the length of this string, in number of UTF-16 characters. \
30	/// If a character cannot fit in a single UTF-16 range (e.g.: emoji), it will be counted as 2 characters.
31	/// 
32	/// # Example
33	/// ```
34	/// use static_collections::ffi::wstring::StaticWString;
35	/// let mut s:StaticWString<32>=StaticWString::new();
36	/// s.push_char('a');
37	/// assert_eq!(s.len(),1);
38	/// s.push_char('😀');
39	/// assert_eq!(s.len(),3);
40	/// ```
41	pub const fn len(&self)->usize
42	{
43		self.internal.len()
44	}
45
46	/// Obtains the capacity of this string, in number of UTF-16 characters.
47	/// 
48	/// # Example
49	/// ```
50	/// use static_collections::ffi::wstring::StaticWString;
51	/// let s:StaticWString<32>=StaticWString::new();
52	/// assert_eq!(s.capacity(),32);
53	/// ```
54	pub const fn capacity(&self)->usize
55	{
56		N
57	}
58
59	/// Checks if this string is empty.
60	/// 
61	/// # Example
62	/// ```
63	/// use static_collections::ffi::wstring::StaticWString;
64	/// let mut s:StaticWString<32>=StaticWString::new();
65	/// assert!(s.is_empty());
66	/// s.push_char('a');
67	/// s.push_char('😀');
68	/// assert_eq!(s.is_empty(),false);
69	/// ```
70	pub const fn is_empty(&self)->bool
71	{
72		self.len()==0
73	}
74
75	/// Returns an immutable slice of this string in `&[u16]` form.
76	/// 
77	/// # Example
78	/// ```
79	/// use static_collections::ffi::wstring::StaticWString;
80	/// let mut s:StaticWString<32>=StaticWString::new();
81	/// s.push_char('😀');
82	/// assert_eq!(s.as_slice(),[0xD83D,0xDE00]);
83	/// ```
84	pub const fn as_slice(&self)->&[u16]
85	{
86		self.internal.as_slice()
87	}
88
89
90	/// Returns a mutable slice of this string in `&mut [u16]` form.
91	/// 
92	/// # Example
93	/// ```
94	/// use static_collections::ffi::wstring::StaticWString;
95	/// use utf16_lit::utf16;
96	/// let mut s:StaticWString<32>=StaticWString::new();
97	/// s.push_char('😀');
98	/// let x=s.as_mut_slice();
99	/// assert_eq!(x,[0xD83D,0xDE00]);
100	/// x[0]=b'1' as u16;
101	/// x[1]=b'0' as u16;
102	/// assert_eq!(s,utf16!("10"));
103	/// ```
104	pub const fn as_mut_slice(&mut self)->&mut [u16]
105	{
106		self.internal.as_mut_slice()
107	}
108
109	/// Returns an immutable pointer to the first character of this string.
110	pub const fn as_ptr(&self)->*const u16
111	{
112		self.internal.as_ptr()
113	}
114
115	/// Returns a mutable pointer to the first character of this string.
116	pub const fn as_mut_ptr(&mut self)->*mut u16
117	{
118		self.internal.as_mut_ptr()
119	}
120
121	/// Inserts a character to the end of the string.
122	/// 
123	/// # Example
124	/// ```
125	/// use static_collections::ffi::wstring::StaticWString;
126	/// let mut s:StaticWString<32>=StaticWString::new();
127	/// s.push_char('a');
128	/// assert_eq!(s.len(),1);
129	/// assert_eq!(s.as_slice(),[b'a' as u16]);
130	/// ```
131	pub fn push_char(&mut self,ch:char)
132	{
133		let rsvd_size=ch.len_utf16();
134		if self.capacity()-self.len()>rsvd_size
135		{
136			unsafe
137			{
138				let mut x:MaybeUninit<[u16;2]>=MaybeUninit::uninit();
139				let u=ch.encode_utf16(x.assume_init_mut());
140				for c in u
141				{
142					self.internal.push(*c);
143				}
144			}
145		}
146	}
147
148	/// Inserts a UTF-8 encoded string-slice to the end of the string.
149	/// 
150	/// # Example
151	/// ```
152	/// use static_collections::ffi::wstring::StaticWString;
153	/// use utf16_lit::utf16;
154	/// let mut s:StaticWString<32>=StaticWString::new();
155	/// s.push_str("Hello, World!");
156	/// assert_eq!(s.as_slice(),utf16!("Hello, World!"));
157	/// ```
158	pub fn push_str(&mut self,s:&str)
159	{
160		for c in s.encode_utf16()
161		{
162			self.internal.push(c)
163		}
164	}
165
166	/// Inserts a character to the position specifed by `index`.
167	/// 
168	/// # Example
169	/// ```
170	/// use static_collections::ffi::wstring::StaticWString;
171	/// use utf16_lit::utf16;
172	/// let mut s:StaticWString<32>=StaticWString::from("Hello World!");
173	/// s.insert_char(5,',');
174	/// assert_eq!(s.as_slice(),utf16!("Hello, World!"));
175	/// ```
176	pub fn insert_char(&mut self,index:usize,ch:char)
177	{
178		let mut x:MaybeUninit<[u16;2]>=MaybeUninit::uninit();
179		let rsvd_size=ch.len_utf16();
180		if self.capacity()-self.len()>rsvd_size
181		{
182			let copy_range=index..self.len();
183			let u=unsafe
184			{
185				self.internal.force_resize(self.len()+rsvd_size);
186				ch.encode_utf16(x.assume_init_mut())
187			};
188			self.internal.copy_within(copy_range,index+rsvd_size);
189			for (i,c) in u.iter().enumerate()
190			{
191				self[index+i]= *c;
192			}
193		}
194	}
195	/// Inserts a UTF-8-encoded string-slice to the position specified by `index`.
196	/// 
197	/// # Example
198	/// ```
199	/// use static_collections::ffi::wstring::StaticWString;
200	/// use utf16_lit::utf16;
201	/// let mut s:StaticWString<32>=StaticWString::from("123789");
202	/// s.insert_str(3,"456");
203	/// assert_eq!(s.as_slice(),utf16!("123456789"));
204	/// ```
205	pub fn insert_str(&mut self,index:usize,s:&str)
206	{
207		// Use `encode_utf16` iterator twice in order to avoid dynamic allocations.
208		// To avoid repeated memmoves, we need to count the number of UTF-16 characters.
209		let insert_len:usize=s.encode_utf16().count();
210		let copy_range=index..self.len();
211		// May-Panic: The `force_resize` will panic if overflow.
212		unsafe
213		{
214			self.internal.force_resize(self.len()+insert_len);
215		}
216		self.internal.copy_within(copy_range,index+insert_len);
217		for (i,c) in s.encode_utf16().enumerate()
218		{
219			self[index+i]=c;
220		}
221	}
222}
223
224impl<I:SliceIndex<[u16]>,const N:usize> Index<I> for StaticWString<N>
225{
226	type Output = I::Output;
227
228	fn index(&self, index: I) -> &Self::Output
229	{
230		&self.internal[index]
231	}
232}
233
234impl<I:SliceIndex<[u16]>,const N:usize> IndexMut<I> for StaticWString<N>
235{
236	fn index_mut(&mut self, index: I) -> &mut Self::Output
237	{
238		&mut self.internal[index]
239	}
240}
241
242impl<const N:usize> From<&str> for StaticWString<N>
243{
244	fn from(value: &str) -> Self
245	{
246		let mut s=Self::new();
247		s.push_str(value);
248		s
249	}
250}
251
252impl<const N:usize> PartialEq<[u16]> for StaticWString<N>
253{
254	fn eq(&self, other: &[u16]) -> bool
255	{
256		self.as_slice()==other
257	}
258}
259
260impl<const M:usize,const N:usize> PartialEq<[u16;M]> for StaticWString<N>
261{
262	fn eq(&self, other: &[u16;M]) -> bool
263	{
264		self.as_slice()==other
265	}
266}
267
268impl<const N:usize> PartialOrd<[u16]> for StaticWString<N>
269{
270	fn partial_cmp(&self, other: &[u16]) -> Option<core::cmp::Ordering>
271	{
272		self.as_slice().partial_cmp(other)
273	}
274}
275
276impl<const M:usize,const N:usize> PartialOrd<[u16;M]> for StaticWString<N>
277{
278	fn partial_cmp(&self, other: &[u16;M]) -> Option<core::cmp::Ordering>
279	{
280		self.as_slice().partial_cmp(other)
281	}
282}
283
284impl<const N:usize> fmt::Display for StaticWString<N>
285{
286	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
287	{
288		use fmt::Write;
289		for c in char::decode_utf16(self.iter())
290		{
291			match c
292			{
293				Ok(c)=>f.write_char(c),
294				Err(_)=>f.write_char(char::REPLACEMENT_CHARACTER)
295			}?;
296		}
297		Ok(())
298	}
299}
300
301impl<const N:usize> fmt::Write for StaticWString<N>
302{
303	fn write_char(&mut self, c: char) -> fmt::Result
304	{
305		self.push_char(c);
306		Ok(())
307	}
308
309	fn write_str(&mut self, s: &str) -> fmt::Result
310	{
311		self.push_str(s);
312		Ok(())
313	}
314}
315
316impl<'a,const N:usize> StaticWString<N>
317{
318	// Just to make sure `char::decode_utf16` can work without cloning the whole string.
319	fn iter(&'a self)->StaticWIter<'a,N>
320	{
321		StaticWIter
322		{
323			internal:StaticWIterator
324			{
325				index:0,
326				source:self
327			}
328		}
329	}
330}
331
332struct StaticWIterator<'a,const N:usize>
333{
334	index:usize,
335	source:&'a StaticWString<N>
336}
337
338impl<'a,const N:usize> Iterator for StaticWIterator<'a,N>
339{
340	type Item = u16;
341
342	fn next(&mut self) -> Option<Self::Item>
343	{
344		let i=self.index;
345		if i<self.source.len()
346		{
347			self.index+=1;
348			Some(self.source[i])
349		}
350		else
351		{
352			None
353		}
354	}
355}
356
357struct StaticWIter<'a,const N:usize>
358{
359	internal:StaticWIterator<'a,N>
360}
361
362impl<'a,const N:usize> IntoIterator for StaticWIter<'a,N>
363{
364	type IntoIter = StaticWIterator<'a,N>;
365	type Item = u16;
366
367	fn into_iter(self) -> Self::IntoIter
368	{
369		self.internal
370	}
371}
372
373#[cfg(test)]
374mod test
375{
376	extern crate std;
377	use std::format;
378	use super::StaticWString;
379
380	#[test] fn correct_fmt()
381	{
382		let s:StaticWString<32>=StaticWString::from("abcd魑魅魍魉1234😀🤣😅👍");
383		let ss=format!("This is {s}!");
384		assert_eq!(ss,"This is abcd魑魅魍魉1234😀🤣😅👍!");
385	}
386}