1pub struct String {
13 ptr: ptr::NonNull<u8>,
14 len: usize,
16 capacity: usize,
17}
18
19unsafe impl Send for String {}
20unsafe impl Sync for String {}
21
22impl String {
23 #[must_use]
25 pub const fn new() -> Self {
26 Self {
27 ptr: unsafe { ptr::NonNull::new_unchecked(PTR_TO_NULL as *mut u8) },
28 len: 0,
29 capacity: 0,
30 }
31 }
32
33 #[must_use]
35 pub fn with_capacity(capacity: usize) -> Self {
36 let mut this = Self::new();
37 this.reserve(capacity);
38 this
39 }
40
41 #[must_use]
44 pub const fn len(&self) -> usize {
45 self.len
46 }
47
48 #[must_use]
50 pub const fn is_empty(&self) -> bool {
51 self.len() == 0
52 }
53
54 #[must_use]
58 pub const fn capacity(&self) -> usize {
59 self.capacity
60 }
61
62 #[must_use]
67 pub fn as_str(&self) -> &str {
68 unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.ptr.as_ptr(), self.len)) }
69 }
70
71 #[must_use]
74 pub fn as_str_nul(&self) -> &str {
75 unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.ptr.as_ptr(), self.len + 1)) }
76 }
77
78 #[must_use]
93 pub unsafe fn from_raw_parts(ptr: *mut u8, len: usize, capacity: usize) -> Self {
94 debug_assert!(!ptr.is_null());
95 debug_assert_ne!(capacity, 0);
96 debug_assert!(len < capacity);
97 Self {
98 ptr: unsafe { ptr::NonNull::new_unchecked(ptr) },
99 len,
100 capacity,
101 }
102 }
103
104 #[must_use]
110 pub fn into_raw(self) -> *mut u8 {
111 let this = ManuallyDrop::new(self);
112 if this.capacity == 0 {
113 unsafe { calloc(1, 1) }.cast()
114 } else {
115 this.ptr.as_ptr()
116 }
117 }
118
119 pub fn reserve(&mut self, n: usize) {
130 const MIN_NON_ZERO_CAP: usize = 8;
132
133 if n < self.capacity - self.len {
135 return;
136 }
137
138 let min_capacity =
139 (|| self.len.checked_add(n)?.checked_add(1))().expect("string length overflowed");
140 let new_capacity = Ord::max(self.capacity * 2, min_capacity);
141 let new_capacity = Ord::max(MIN_NON_ZERO_CAP, new_capacity);
142
143 let ptr = if self.capacity == 0 {
144 let ptr = unsafe { malloc(new_capacity) }.cast::<u8>();
145 unsafe { *ptr = b'\0' };
147 ptr
148 } else {
149 unsafe { realloc(self.ptr.as_ptr().cast(), new_capacity) }.cast::<u8>()
150 };
151
152 self.ptr = ptr::NonNull::new(ptr).expect("glib allocation returned NULL");
154
155 self.capacity = new_capacity;
156 }
157
158 pub fn push_str(&mut self, s: &str) {
164 assert!(
165 !s.as_bytes().contains(&b'\0'),
166 "push_str called on string with nuls"
167 );
168
169 if s.is_empty() {
170 return;
171 }
172
173 self.reserve(s.len());
174 unsafe {
175 ptr::copy_nonoverlapping(s.as_ptr(), self.ptr.as_ptr().add(self.len), s.len());
176 self.len += s.len();
177 *self.ptr.as_ptr().add(self.len) = b'\0';
178 }
179 }
180
181 #[allow(clippy::missing_panics_doc)]
187 pub fn shrink_to(&mut self, min_capacity: usize) {
188 let min_capacity = Ord::max(self.len + 1, min_capacity);
189 if self.capacity <= min_capacity {
190 return;
191 }
192
193 let ptr = unsafe { realloc(self.ptr.as_ptr().cast(), min_capacity) }.cast::<u8>();
197
198 self.ptr = ptr::NonNull::new(ptr).expect("glib allocation returned NULL");
200
201 self.capacity = min_capacity;
202 }
203
204 pub fn shrink_to_fit(&mut self) {
207 self.shrink_to(0);
208 }
209
210 pub fn clear(&mut self) {
214 if !self.is_empty() {
215 unsafe { *self.ptr.as_ptr() = b'\0' };
216 self.len = 0;
217 }
218 }
219
220 pub fn push(&mut self, char: char) {
222 self.push_str(char.encode_utf8(&mut [0; 4]));
223 }
224
225 pub fn truncate(&mut self, new_len: usize) {
236 if new_len < self.len() {
237 assert!(self.is_char_boundary(new_len));
238 self.len = new_len;
239 unsafe { *self.ptr.as_ptr().add(self.len) = b'\0' };
240 }
241 }
242
243 pub fn pop(&mut self) -> Option<char> {
247 let last = self.chars().next_back()?;
248 self.truncate(self.len() - last.len_utf8());
249 Some(last)
250 }
251}
252
253impl Drop for String {
254 fn drop(&mut self) {
255 if self.capacity != 0 {
256 unsafe { free(self.ptr.as_ptr().cast()) };
257 }
258 }
259}
260
261impl Deref for String {
262 type Target = str;
263 fn deref(&self) -> &Self::Target {
264 self.as_str()
265 }
266}
267
268impl AsRef<str> for String {
271 fn as_ref(&self) -> &str {
272 self.as_str()
273 }
274}
275
276impl AsRef<CStr> for String {
277 fn as_ref(&self) -> &CStr {
278 let bytes = self.as_str_nul().as_bytes();
279 if cfg!(debug_assertions) {
280 CStr::from_bytes_with_nul(bytes).unwrap()
281 } else {
282 unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
283 }
284 }
285}
286
287impl Debug for String {
288 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
289 Debug::fmt(self.as_str(), f)
290 }
291}
292
293impl Display for String {
294 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
295 Display::fmt(self.as_str(), f)
296 }
297}
298
299impl fmt::Write for String {
300 fn write_str(&mut self, s: &str) -> fmt::Result {
301 self.push_str(s);
302 Ok(())
303 }
304}
305
306macro_rules! impl_from_stringlike {
307 ($($t:ty,)*) => { $(
308 impl From<$t> for String {
309 fn from(s: $t) -> Self {
310 let mut this = Self::new();
311 this.push_str(&*s);
312 this
313 }
314 }
315 )* };
316}
317impl_from_stringlike!(
318 &String,
319 &str,
320 &mut str,
321 std::string::String,
322 &std::string::String,
323 Cow<'_, str>,
324 &Cow<'_, str>,
325);
326
327macro_rules! impl_into_std_string {
328 ($($t:ty),*) => { $(
329 impl From<$t> for std::string::String {
330 fn from(string: $t) -> Self {
331 std::string::String::from(string.as_str())
332 }
333 }
334 )* }
335}
336impl_into_std_string!(String, &String, &mut String);
337
338impl From<GString> for String {
339 fn from(s: GString) -> Self {
340 let len = s.len();
341 let capacity = len + 1;
345
346 unsafe { Self::from_raw_parts(s.into_glib_ptr().cast(), len, capacity) }
347 }
348}
349
350impl Default for String {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356impl Clone for String {
357 fn clone(&self) -> Self {
358 Self::from(self.as_str())
359 }
360}
361
362impl PartialEq for String {
363 fn eq(&self, other: &Self) -> bool {
364 self.as_str() == other.as_str()
365 }
366}
367impl Eq for String {}
368
369impl PartialOrd for String {
370 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
371 Some(self.cmp(other))
372 }
373}
374impl Ord for String {
375 fn cmp(&self, other: &Self) -> cmp::Ordering {
376 self.as_str().cmp(other.as_str())
377 }
378}
379
380impl Hash for String {
381 fn hash<H: Hasher>(&self, state: &mut H) {
382 self.as_str().hash(state);
383 }
384}
385
386impl Borrow<str> for String {
387 fn borrow(&self) -> &str {
388 self.as_str()
389 }
390}
391
392impl Extend<char> for String {
393 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
394 for c in iter {
395 self.push_str(c.encode_utf8(&mut [0; 4]));
396 }
397 }
398}
399
400impl<'a> Extend<&'a char> for String {
401 fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
402 for c in iter {
403 self.push_str(c.encode_utf8(&mut [0; 4]));
404 }
405 }
406}
407
408impl<'a> Extend<&'a str> for String {
409 fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
410 for s in iter {
411 self.push_str(s);
412 }
413 }
414}
415
416impl FromIterator<char> for String {
417 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
418 let mut this = Self::new();
419 this.extend(iter);
420 this
421 }
422}
423
424impl<'a> FromIterator<&'a char> for String {
425 fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
426 let mut this = Self::new();
427 this.extend(iter);
428 this
429 }
430}
431
432impl<'a> FromIterator<&'a str> for String {
433 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
434 let mut this = Self::new();
435 this.extend(iter);
436 this
437 }
438}
439
440#[macro_export]
444macro_rules! format {
445 ($($tt:tt)*) => { $crate::format(::core::format_args!($($tt)*)) };
446}
447
448#[must_use]
456pub fn format(args: fmt::Arguments<'_>) -> String {
457 let mut s = String::new();
458 s.write_fmt(args)
459 .expect("a formatting trait implementation returned an error");
460 s
461}
462
463const PTR_TO_NULL: *const u8 = &0;
464
465#[cfg(test)]
466mod tests {
467 #[test]
468 fn empty() {
469 let s = String::new();
470 assert_eq!(unsafe { *s.ptr.as_ptr() }, b'\0');
471 assert_eq!(s.len, 0);
472 assert_eq!(s.len(), 0);
473 assert!(s.is_empty());
474 assert_eq!(s.capacity, 0);
475 assert_eq!(s.capacity(), 0);
476 assert_eq!(s.as_str(), "");
477 assert_eq!(s.as_str_nul(), "\0");
478 }
479
480 #[test]
481 fn into_raw_allocates() {
482 unsafe { super::free(String::new().into_raw().cast()) };
483 }
484
485 #[test]
486 fn reserve_none() {
487 let mut s = String::new();
488 s.reserve(0);
489 assert!(s.is_empty());
490 assert_eq!(s.as_str_nul(), "\0");
491 assert_eq!(s.capacity(), 8);
492 }
493
494 #[test]
495 fn reserve() {
496 let mut s = String::new();
497 s.reserve(2);
498
499 assert!(s.is_empty());
500 assert_eq!(s.as_str_nul(), "\0");
501 assert_eq!(s.capacity(), 8);
502
503 s.reserve(7);
504 assert_eq!(s.as_str_nul(), "\0");
505 assert_eq!(s.capacity(), 8);
506
507 s.reserve(8);
508 assert_eq!(s.as_str_nul(), "\0");
509 assert_eq!(s.capacity(), 16);
510 }
511
512 #[test]
513 fn push_str() {
514 let mut s = String::new();
515
516 s.push_str("a");
517 assert_eq!(s.as_str_nul(), "a\0");
518 assert_eq!(s.capacity(), 8);
519
520 s.push_str("bcdefg");
521 assert_eq!(s.as_str_nul(), "abcdefg\0");
522 assert_eq!(s.capacity(), 8);
523
524 s.push_str("h");
525 assert_eq!(s.as_str_nul(), "abcdefgh\0");
526 assert_eq!(s.capacity(), 16);
527 }
528
529 #[test]
530 fn shrink() {
531 let mut s = String::new();
532 s.shrink_to_fit();
533 s.shrink_to(0);
534 s.shrink_to(400);
535 assert_eq!(s.capacity(), 0);
536
537 s.push_str("foo");
538
539 s.shrink_to(5);
540 assert_eq!(s.capacity(), 5);
541 assert_eq!(s.as_str_nul(), "foo\0");
542
543 s.shrink_to_fit();
544 assert_eq!(s.capacity(), 4);
545 assert_eq!(s.as_str_nul(), "foo\0");
546 }
547
548 #[test]
549 fn clear() {
550 let mut s = String::new();
551 assert_eq!(s.as_str_nul(), "\0");
552 s.clear();
553 assert_eq!(s.as_str_nul(), "\0");
554 assert_eq!(s.capacity(), 0);
555
556 s.push_str("hello world!");
557 s.clear();
558 assert_eq!(s.as_str_nul(), "\0");
559 assert_eq!(s.capacity(), 13);
560 }
561
562 #[test]
563 fn truncate() {
564 let mut s = String::new();
565
566 s.truncate(10);
567 s.truncate(0);
568
569 s.push_str("foobar");
570
571 s.truncate(10);
572 assert_eq!(s.as_str(), "foobar");
573 assert_eq!(s.as_str_nul(), "foobar\0");
574
575 s.truncate(6);
576 assert_eq!(s.as_str(), "foobar");
577 assert_eq!(s.as_str_nul(), "foobar\0");
578
579 s.truncate(3);
580 assert_eq!(s.as_str(), "foo");
581 assert_eq!(s.as_str_nul(), "foo\0");
582
583 s.truncate(0);
584 assert_eq!(s.as_str(), "");
585 assert_eq!(s.as_str_nul(), "\0");
586 }
587
588 #[test]
589 #[cfg(not(miri))]
590 fn from_gstring() {
591 let s = String::from(GString::from("hello world"));
592 assert_eq!(s.as_str(), "hello world");
593 assert_eq!(s.as_str_nul(), "hello world\0");
594 }
595
596 #[test]
597 fn formatting() {
598 assert_eq!(format!("PI = {}", 3).as_str_nul(), "PI = 3\0");
599 }
600
601 use super::String;
602 use cairo::glib::GString;
603}
604
605#[cfg(not(miri))]
606mod allocator {
607 pub(crate) use crate::glib_sys::g_free as free;
608 pub(crate) use crate::glib_sys::g_malloc as malloc;
609 pub(crate) use crate::glib_sys::g_malloc0_n as calloc;
610 pub(crate) use crate::glib_sys::g_realloc as realloc;
611}
612#[cfg(miri)]
613mod allocator {
614 pub(crate) use libc::calloc;
615 pub(crate) use libc::free;
616 pub(crate) use libc::malloc;
617 pub(crate) use libc::realloc;
618}
619use allocator::calloc;
620use allocator::free;
621use allocator::malloc;
622use allocator::realloc;
623
624use cairo::glib::translate::IntoGlibPtr;
625use cairo::glib::GString;
626use std::borrow::Borrow;
627use std::borrow::Cow;
628use std::cmp;
629use std::ffi::CStr;
630use std::fmt;
631use std::fmt::Debug;
632use std::fmt::Display;
633use std::fmt::Formatter;
634use std::fmt::Write as _;
635use std::hash::Hash;
636use std::hash::Hasher;
637use std::mem::ManuallyDrop;
638use std::ops::Deref;
639use std::ptr;
640use std::slice;
641use std::str;