1#![allow(unsafe_code)]
7#![warn(missing_docs)]
8
9use crate::SharedVector;
10use alloc::string::String;
11use core::fmt::{Debug, Display, Write};
12use core::ops::Deref;
13#[cfg(not(feature = "std"))]
14#[allow(unused)]
15use num_traits::Float;
16
17#[macro_export]
25macro_rules! format {
26 ($($arg:tt)*) => {{
27 $crate::string::format(core::format_args!($($arg)*))
28 }}
29}
30
31#[derive(Clone, Default)]
42#[repr(C)]
43pub struct SharedString {
44 inner: SharedVector<u8>,
46}
47
48impl SharedString {
49 pub fn new() -> Self {
53 Self::default()
54 }
55
56 fn as_ptr(&self) -> *const u8 {
57 self.inner.as_ptr()
58 }
59
60 pub fn len(&self) -> usize {
62 self.inner.len().saturating_sub(1)
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.len() == 0
68 }
69
70 pub fn as_str(&self) -> &str {
72 unsafe {
74 core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.as_ptr(), self.len()))
75 }
76 }
77
78 pub fn push_str(&mut self, x: &str) {
89 let mut iter = x.as_bytes().iter().copied();
90 if self.inner.is_empty() {
91 self.inner.extend(iter.chain(core::iter::once(0)));
92 } else if let Some(first) = iter.next() {
93 let prev_len = self.len();
97 self.inner.extend(iter.chain(core::iter::once(0)));
98 self.inner.make_mut_slice()[prev_len] = first;
99 }
100 }
101}
102
103impl Deref for SharedString {
104 type Target = str;
105 fn deref(&self) -> &Self::Target {
106 self.as_str()
107 }
108}
109
110impl From<&str> for SharedString {
111 fn from(value: &str) -> Self {
112 SharedString {
113 inner: SharedVector::from_iter(
114 value.as_bytes().iter().cloned().chain(core::iter::once(0)),
115 ),
116 }
117 }
118}
119
120impl Debug for SharedString {
121 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122 Debug::fmt(self.as_str(), f)
123 }
124}
125
126impl Display for SharedString {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 Display::fmt(self.as_str(), f)
129 }
130}
131
132impl AsRef<str> for SharedString {
133 #[inline]
134 fn as_ref(&self) -> &str {
135 self.as_str()
136 }
137}
138
139#[cfg(feature = "serde")]
140impl serde::Serialize for SharedString {
141 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142 where
143 S: serde::Serializer,
144 {
145 let string = self.as_str();
146 serializer.serialize_str(string)
147 }
148}
149
150#[cfg(feature = "serde")]
151impl<'de> serde::Deserialize<'de> for SharedString {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::Deserializer<'de>,
155 {
156 let string = String::deserialize(deserializer)?;
157 Ok(SharedString::from(string))
158 }
159}
160
161#[cfg(feature = "std")]
162impl AsRef<std::ffi::CStr> for SharedString {
163 #[inline]
164 fn as_ref(&self) -> &std::ffi::CStr {
165 if self.inner.is_empty() {
166 return Default::default();
167 }
168 debug_assert_eq!(self.inner.as_slice()[self.inner.len() - 1], 0);
170 unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(self.inner.as_slice()) }
171 }
172}
173
174#[cfg(feature = "std")]
175impl AsRef<std::path::Path> for SharedString {
176 #[inline]
177 fn as_ref(&self) -> &std::path::Path {
178 self.as_str().as_ref()
179 }
180}
181
182#[cfg(feature = "std")]
183impl AsRef<std::ffi::OsStr> for SharedString {
184 #[inline]
185 fn as_ref(&self) -> &std::ffi::OsStr {
186 self.as_str().as_ref()
187 }
188}
189
190impl AsRef<[u8]> for SharedString {
191 #[inline]
192 fn as_ref(&self) -> &[u8] {
193 self.as_str().as_bytes()
194 }
195}
196
197impl<T> PartialEq<T> for SharedString
198where
199 T: ?Sized + AsRef<str>,
200{
201 fn eq(&self, other: &T) -> bool {
202 self.as_str() == other.as_ref()
203 }
204}
205impl Eq for SharedString {}
206
207impl<T> PartialOrd<T> for SharedString
208where
209 T: ?Sized + AsRef<str>,
210{
211 fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
212 PartialOrd::partial_cmp(self.as_str(), other.as_ref())
213 }
214}
215impl Ord for SharedString {
216 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
217 Ord::cmp(self.as_str(), other.as_str())
218 }
219}
220
221impl From<String> for SharedString {
222 fn from(s: String) -> Self {
223 s.as_str().into()
224 }
225}
226
227impl From<&String> for SharedString {
228 fn from(s: &String) -> Self {
229 s.as_str().into()
230 }
231}
232
233impl From<char> for SharedString {
234 fn from(c: char) -> Self {
235 SharedString::from(c.encode_utf8(&mut [0; 6]) as &str)
236 }
237}
238
239impl From<SharedString> for String {
240 fn from(s: SharedString) -> String {
241 s.as_str().into()
242 }
243}
244
245impl From<&SharedString> for String {
246 fn from(s: &SharedString) -> String {
247 s.as_str().into()
248 }
249}
250
251impl core::ops::AddAssign<&str> for SharedString {
252 fn add_assign(&mut self, other: &str) {
253 self.push_str(other);
254 }
255}
256
257impl core::ops::Add<&str> for SharedString {
258 type Output = SharedString;
259 fn add(mut self, other: &str) -> SharedString {
260 self.push_str(other);
261 self
262 }
263}
264
265impl core::hash::Hash for SharedString {
266 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
267 self.as_str().hash(state)
268 }
269}
270
271impl Write for SharedString {
272 fn write_str(&mut self, s: &str) -> core::fmt::Result {
273 self.push_str(s);
274 Ok(())
275 }
276}
277
278impl core::borrow::Borrow<str> for SharedString {
279 fn borrow(&self) -> &str {
280 self.as_str()
281 }
282}
283
284impl Extend<char> for SharedString {
285 fn extend<X: IntoIterator<Item = char>>(&mut self, iter: X) {
286 let iter = iter.into_iter();
287 self.inner.reserve(iter.size_hint().0);
288 let mut buf = [0; 4];
289 for ch in iter {
290 let utf8 = ch.encode_utf8(&mut buf);
291 self.push_str(utf8);
292 }
293 }
294}
295
296impl FromIterator<char> for SharedString {
297 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
298 let mut str = Self::new();
299 str.extend(iter);
300 str
301 }
302}
303
304pub fn format(args: core::fmt::Arguments<'_>) -> SharedString {
306 let mut output = SharedString::default();
309 output.write_fmt(args).unwrap();
310 output
311}
312
313pub trait ToSharedString {
318 fn to_shared_string(&self) -> SharedString;
320}
321
322impl<T> ToSharedString for T
323where
324 T: Display + ?Sized,
325{
326 fn to_shared_string(&self) -> SharedString {
327 format!("{}", self)
328 }
329}
330
331pub fn shared_string_from_number(n: f64) -> SharedString {
333 if n < 16777216. { crate::format!("{}", n as f32) } else { crate::format!("{}", n) }
335}
336
337pub fn shared_string_from_number_fixed(n: f64, digits: usize) -> SharedString {
339 crate::format!("{number:.digits$}", number = n, digits = digits)
340}
341
342pub fn shared_string_from_number_precision(n: f64, precision: usize) -> SharedString {
344 let exponent = f64::log10(n.abs()).floor() as isize;
345 if precision == 0 {
346 shared_string_from_number(n)
347 } else if exponent < -6 || (exponent >= 0 && exponent as usize >= precision) {
348 crate::format!(
349 "{number:.digits$e}",
350 number = n,
351 digits = precision.saturating_add_signed(-1)
352 )
353 } else {
354 shared_string_from_number_fixed(n, precision.saturating_add_signed(-(exponent + 1)))
355 }
356}
357
358#[test]
359fn simple_test() {
360 use std::string::ToString;
361 let x = SharedString::from("hello world!");
362 assert_eq!(x, "hello world!");
363 assert_ne!(x, "hello world?");
364 assert_eq!(x, x.clone());
365 assert_eq!("hello world!", x.as_str());
366 let string = String::from("hello world!");
367 assert_eq!(x, string);
368 assert_eq!(x.to_string(), string);
369 let def = SharedString::default();
370 assert_eq!(def, SharedString::default());
371 assert_eq!(def, SharedString::new());
372 assert_ne!(def, x);
373 assert_eq!(
374 (&x as &dyn AsRef<std::ffi::CStr>).as_ref(),
375 &*std::ffi::CString::new("hello world!").unwrap()
376 );
377 assert_eq!(SharedString::from('h'), "h");
378 assert_eq!(SharedString::from('😎'), "😎");
379}
380
381#[test]
382fn threading() {
383 let shared_cst = SharedString::from("Hello there!");
384 let shared_mtx = std::sync::Arc::new(std::sync::Mutex::new(SharedString::from("Shared:")));
385 let mut handles = std::vec::Vec::new();
386 for _ in 0..20 {
387 let cst = shared_cst.clone();
388 let mtx = shared_mtx.clone();
389 handles.push(std::thread::spawn(move || {
390 assert_eq!(cst, "Hello there!");
391 let mut cst2 = cst.clone();
392 cst2.push_str(" ... or not?");
393 assert_eq!(cst2, "Hello there! ... or not?");
394 assert_eq!(cst.clone(), "Hello there!");
395
396 let shared = {
397 let mut lock = mtx.lock().unwrap();
398 assert!(lock.starts_with("Shared:"));
399 lock.push_str("!");
400 lock.clone()
401 };
402 assert!(shared.clone().starts_with("Shared:"));
403 }));
404 }
405 for j in handles {
406 j.join().unwrap();
407 }
408 assert_eq!(shared_cst.clone(), "Hello there!");
409 assert_eq!(shared_mtx.lock().unwrap().as_str(), "Shared:!!!!!!!!!!!!!!!!!!!!");
410 }
412
413#[test]
414fn to_shared_string() {
415 let i = 5.1;
416 let five = SharedString::from("5.1");
417
418 assert_eq!(five, i.to_shared_string());
419}
420
421#[cfg(feature = "ffi")]
422pub(crate) mod ffi {
423 use super::*;
424
425 #[allow(non_camel_case_types)]
427 type c_char = u8;
428
429 #[unsafe(no_mangle)]
430 pub extern "C" fn slint_shared_string_bytes(ss: &SharedString) -> *const c_char {
434 if ss.is_empty() { "\0".as_ptr() } else { ss.as_ptr() }
435 }
436
437 #[unsafe(no_mangle)]
438 pub unsafe extern "C" fn slint_shared_string_drop(ss: *const SharedString) {
440 unsafe {
441 core::ptr::read(ss);
442 }
443 }
444
445 #[unsafe(no_mangle)]
446 pub unsafe extern "C" fn slint_shared_string_clone(out: *mut SharedString, ss: &SharedString) {
449 unsafe { core::ptr::write(out, ss.clone()) }
450 }
451
452 #[unsafe(no_mangle)]
453 pub unsafe extern "C" fn slint_shared_string_from_bytes(
456 out: *mut SharedString,
457 bytes: *const c_char,
458 len: usize,
459 ) {
460 unsafe {
461 let str = core::str::from_utf8(core::slice::from_raw_parts(bytes, len)).unwrap();
462 core::ptr::write(out, SharedString::from(str));
463 }
464 }
465
466 #[unsafe(no_mangle)]
469 pub unsafe extern "C" fn slint_shared_string_from_number(out: *mut SharedString, n: f64) {
470 let str = shared_string_from_number(n);
471 unsafe { core::ptr::write(out, str) };
472 }
473
474 #[test]
475 fn test_slint_shared_string_from_number() {
476 unsafe {
477 let mut s = core::mem::MaybeUninit::uninit();
478 slint_shared_string_from_number(s.as_mut_ptr(), 45.);
479 assert_eq!(s.assume_init(), "45");
480
481 let mut s = core::mem::MaybeUninit::uninit();
482 slint_shared_string_from_number(s.as_mut_ptr(), 45.12);
483 assert_eq!(s.assume_init(), "45.12");
484
485 let mut s = core::mem::MaybeUninit::uninit();
486 slint_shared_string_from_number(s.as_mut_ptr(), -1325466.);
487 assert_eq!(s.assume_init(), "-1325466");
488
489 let mut s = core::mem::MaybeUninit::uninit();
490 slint_shared_string_from_number(s.as_mut_ptr(), 0.);
491 assert_eq!(s.assume_init(), "0");
492
493 let mut s = core::mem::MaybeUninit::uninit();
494 slint_shared_string_from_number(
495 s.as_mut_ptr(),
496 ((1235.82756f32 * 1000f32).round() / 1000f32) as _,
497 );
498 assert_eq!(s.assume_init(), "1235.828");
499 }
500 }
501
502 #[unsafe(no_mangle)]
503 pub extern "C" fn slint_shared_string_from_number_fixed(
504 out: &mut SharedString,
505 n: f64,
506 digits: usize,
507 ) {
508 *out = shared_string_from_number_fixed(n, digits);
509 }
510
511 #[test]
512 fn test_slint_shared_string_from_number_fixed() {
513 let mut s = SharedString::default();
514
515 let num = 12345.6789;
516
517 slint_shared_string_from_number_fixed(&mut s, num, 0);
518 assert_eq!(s.as_str(), "12346");
519
520 slint_shared_string_from_number_fixed(&mut s, num, 1);
521 assert_eq!(s.as_str(), "12345.7");
522
523 slint_shared_string_from_number_fixed(&mut s, num, 6);
524 assert_eq!(s.as_str(), "12345.678900");
525
526 let num = -12345.6789;
527
528 slint_shared_string_from_number_fixed(&mut s, num, 0);
529 assert_eq!(s.as_str(), "-12346");
530
531 slint_shared_string_from_number_fixed(&mut s, num, 1);
532 assert_eq!(s.as_str(), "-12345.7");
533
534 slint_shared_string_from_number_fixed(&mut s, num, 6);
535 assert_eq!(s.as_str(), "-12345.678900");
536
537 slint_shared_string_from_number_fixed(&mut s, 1.23E+20_f64, 2);
538 assert_eq!(s.as_str(), "123000000000000000000.00");
539
540 slint_shared_string_from_number_fixed(&mut s, 1.23E-10_f64, 2);
541 assert_eq!(s.as_str(), "0.00");
542
543 slint_shared_string_from_number_fixed(&mut s, 2.34, 1);
544 assert_eq!(s.as_str(), "2.3");
545
546 slint_shared_string_from_number_fixed(&mut s, 2.35, 1);
547 assert_eq!(s.as_str(), "2.4");
548
549 slint_shared_string_from_number_fixed(&mut s, 2.55, 1);
550 assert_eq!(s.as_str(), "2.5");
551 }
552
553 #[unsafe(no_mangle)]
554 pub extern "C" fn slint_shared_string_from_number_precision(
555 out: &mut SharedString,
556 n: f64,
557 precision: usize,
558 ) {
559 *out = shared_string_from_number_precision(n, precision);
560 }
561
562 #[test]
563 fn test_slint_shared_string_from_number_precision() {
564 let mut s = SharedString::default();
565
566 let num = 5.123456;
567
568 slint_shared_string_from_number_precision(&mut s, num, 0);
569 assert_eq!(s.as_str(), "5.123456");
570
571 slint_shared_string_from_number_precision(&mut s, num, 5);
572 assert_eq!(s.as_str(), "5.1235");
573
574 slint_shared_string_from_number_precision(&mut s, num, 2);
575 assert_eq!(s.as_str(), "5.1");
576
577 slint_shared_string_from_number_precision(&mut s, num, 1);
578 assert_eq!(s.as_str(), "5");
579
580 let num = 0.000123;
581
582 slint_shared_string_from_number_precision(&mut s, num, 0);
583 assert_eq!(s.as_str(), "0.000123");
584
585 slint_shared_string_from_number_precision(&mut s, num, 5);
586 assert_eq!(s.as_str(), "0.00012300");
587
588 slint_shared_string_from_number_precision(&mut s, num, 2);
589 assert_eq!(s.as_str(), "0.00012");
590
591 slint_shared_string_from_number_precision(&mut s, num, 1);
592 assert_eq!(s.as_str(), "0.0001");
593
594 let num = 1234.5;
595
596 slint_shared_string_from_number_precision(&mut s, num, 1);
597 assert_eq!(s.as_str(), "1e3");
598
599 slint_shared_string_from_number_precision(&mut s, num, 2);
600 assert_eq!(s.as_str(), "1.2e3");
601
602 slint_shared_string_from_number_precision(&mut s, num, 6);
603 assert_eq!(s.as_str(), "1234.50");
604
605 let num = -1234.5;
606
607 slint_shared_string_from_number_precision(&mut s, num, 1);
608 assert_eq!(s.as_str(), "-1e3");
609
610 slint_shared_string_from_number_precision(&mut s, num, 2);
611 assert_eq!(s.as_str(), "-1.2e3");
612
613 slint_shared_string_from_number_precision(&mut s, num, 6);
614 assert_eq!(s.as_str(), "-1234.50");
615
616 let num = 0.00000012345;
617
618 slint_shared_string_from_number_precision(&mut s, num, 1);
619 assert_eq!(s.as_str(), "1e-7");
620
621 slint_shared_string_from_number_precision(&mut s, num, 10);
622 assert_eq!(s.as_str(), "1.234500000e-7");
623 }
624
625 #[unsafe(no_mangle)]
629 pub unsafe extern "C" fn slint_shared_string_append(
630 self_: &mut SharedString,
631 bytes: *const c_char,
632 len: usize,
633 ) {
634 let str = core::str::from_utf8(unsafe { core::slice::from_raw_parts(bytes, len) }).unwrap();
635 self_.push_str(str);
636 }
637 #[test]
638 fn test_slint_shared_string_append() {
639 let mut s = SharedString::default();
640 let mut append = |x: &str| unsafe {
641 slint_shared_string_append(&mut s, x.as_bytes().as_ptr(), x.len());
642 };
643 append("Hello");
644 append(", ");
645 append("world");
646 append("");
647 append("!");
648 assert_eq!(s.as_str(), "Hello, world!");
649 }
650
651 #[unsafe(no_mangle)]
652 pub unsafe extern "C" fn slint_shared_string_to_lowercase(
653 out: &mut SharedString,
654 ss: &SharedString,
655 ) {
656 *out = SharedString::from(ss.to_lowercase());
657 }
658 #[test]
659 fn test_slint_shared_string_to_lowercase() {
660 let s = SharedString::from("Hello");
661 let mut out = SharedString::default();
662
663 unsafe {
664 slint_shared_string_to_lowercase(&mut out, &s);
665 }
666 assert_eq!(out.as_str(), "hello");
667 }
668
669 #[unsafe(no_mangle)]
670 pub unsafe extern "C" fn slint_shared_string_to_uppercase(
671 out: &mut SharedString,
672 ss: &SharedString,
673 ) {
674 *out = SharedString::from(ss.to_uppercase());
675 }
676 #[test]
677 fn test_slint_shared_string_to_uppercase() {
678 let s = SharedString::from("Hello");
679 let mut out = SharedString::default();
680
681 unsafe {
682 slint_shared_string_to_uppercase(&mut out, &s);
683 }
684 assert_eq!(out.as_str(), "HELLO");
685 }
686}
687
688#[cfg(feature = "serde")]
689#[test]
690fn test_serialize_deserialize_sharedstring() {
691 let v = SharedString::from("data");
692 let serialized = serde_json::to_string(&v).unwrap();
693 let deserialized: SharedString = serde_json::from_str(&serialized).unwrap();
694 assert_eq!(v, deserialized);
695}
696
697#[test]
698fn test_extend_from_chars() {
699 let mut s = SharedString::from("x");
700 s.extend(core::iter::repeat_n('a', 4).chain(core::iter::once('🍌')));
701 assert_eq!(s.as_str(), "xaaaa🍌");
702}
703
704#[test]
705fn test_collect_from_chars() {
706 let s: SharedString = core::iter::repeat_n('a', 4).chain(core::iter::once('🍌')).collect();
707 assert_eq!(s.as_str(), "aaaa🍌");
708}