coca/string.rs
1//! UTF-8 encoded, growable string types with constant capacity.
2
3use core::fmt::{self, Display};
4use core::ops::{RangeBounds, Range};
5use core::str::{self, Utf8Error, FromStr};
6
7use crate::CapacityError;
8use crate::collections::vec::Vec;
9use crate::storage::{ArrayLayout, Storage, Capacity, InlineStorage, ArenaStorage, normalize_range};
10
11/// A possible error value when converting a UTF-8 byte vector into a [`String`].
12///
13/// This is the error type for the [`from_utf8`] method on `String`.
14///
15/// [`from_utf8`]: String::from_utf8
16#[derive(Debug, PartialEq, Eq)]
17pub struct FromUtf8Error<S: Storage<ArrayLayout<u8>>, I: Capacity> {
18 bytes: Vec<u8, S, I>,
19 error: Utf8Error,
20}
21
22impl<S: Storage<ArrayLayout<u8>>, I: Capacity> FromUtf8Error<S, I> {
23 /// Returns a slice of bytes that were attempted to convert to a [`String`].
24 pub fn as_bytes(&self) -> &[u8] {
25 self.bytes.as_slice()
26 }
27
28 /// Returns the byte vector that was attempted to convert to a [`String`].
29 pub fn into_bytes(self) -> Vec<u8, S, I> {
30 self.bytes
31 }
32
33 /// Returns a [`Utf8Error`] that provides more details about the conversion failure.
34 pub fn utf8_error(&self) -> Utf8Error {
35 self.error
36 }
37}
38
39impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Clone for FromUtf8Error<S, I> where Vec<u8, S, I>: Clone {
40 fn clone(&self) -> Self {
41 FromUtf8Error {
42 bytes: self.bytes.clone(),
43 error: self.error,
44 }
45 }
46}
47
48impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Display for FromUtf8Error<S, I> {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result {
50 fmt::Display::fmt(&self.error, f)
51 }
52}
53
54/// A UTF-8 encoded, growable string.
55///
56/// Generic over the storage buffer type `S` and the index type `I`.
57pub struct String<S: Storage<ArrayLayout<u8>>, I: Capacity = usize> {
58 vec: Vec<u8, S, I>,
59}
60
61impl<S: Storage<ArrayLayout<u8>>, I: Capacity> From<S> for String<S, I> {
62 fn from(buf: S) -> Self {
63 String { vec: Vec::from(buf) }
64 }
65}
66
67impl<S: Storage<ArrayLayout<u8>>, I: Capacity> String<S, I> {
68 /// Converts a vector of bytes into a `String` without copying.
69 ///
70 /// If you are sure that the byte vector is valid UTF-8, and don't want to
71 /// incur the overhead of the validity check, consider [`from_utf8_unchecked`],
72 /// which has the same behavior but skips the check.
73 ///
74 /// If you need a [`&str`] instead of a `String`, consider [`str::from_utf8`].
75 ///
76 /// The inverse of this method is [`into_bytes`].
77 ///
78 /// [`from_utf8_unchecked`]: String::from_utf8_unchecked
79 /// [`&str`]: prim@str "&str"
80 /// [`into_bytes`]: String::into_bytes
81 ///
82 /// # Errors
83 /// Returns [`Err`] if the slice is not UTF-8 with a description as to why
84 /// the provided bytes are not UTF-8. The moved vector is also included.
85 ///
86 /// # Examples
87 /// ```
88 /// // Basic Usage:
89 /// let mut bytes = coca::collections::InlineVec::<u8, 8>::new();
90 /// bytes.extend_from_slice(&[240, 159, 146, 150]);
91 ///
92 /// let sparkle_heart = coca::InlineString::from_utf8(bytes).unwrap();
93 /// assert_eq!(sparkle_heart, "💖");
94 ///
95 /// // Invalid Bytes:
96 /// let mut bytes = coca::collections::InlineVec::<u8, 8>::new();
97 /// bytes.extend_from_slice(&[0, 159, 146, 150]);
98 /// assert!(coca::InlineString::from_utf8(bytes).is_err());
99 /// ```
100 #[inline]
101 pub fn from_utf8(vec: Vec<u8, S, I>) -> Result<Self, FromUtf8Error<S, I>> {
102 match core::str::from_utf8(&vec) {
103 Ok(_) => Ok(String { vec }),
104 Err(e) => Err(FromUtf8Error { bytes: vec, error: e }),
105 }
106 }
107
108 /// Decomposes a `String` into its raw parts.
109 ///
110 /// Returns the raw storage type and the length of the string (in bytes).
111 /// These are the same arguments in the same order as the arguments to
112 /// [`from_raw_parts`](String::from_raw_parts).
113 ///
114 /// # Examples
115 /// ```
116 /// use core::str::FromStr;
117 /// let s = coca::InlineString::<8>::from_str("hello").unwrap();
118 /// let (storage, len): ([core::mem::MaybeUninit<u8>; 8], usize) = s.into_raw_parts();
119 /// assert_eq!(len, 5);
120 /// ```
121 #[inline]
122 pub fn into_raw_parts(self) -> (S, I) {
123 self.vec.into_raw_parts()
124 }
125
126 /// Creates a new `String` from a length and raw storage buffer.
127 ///
128 /// # Safety
129 /// Callers must ensure that
130 ///
131 /// * `length` is less than or equal to `buf.capacity()`, and
132 /// * the first `length` bytes stored in `buf` are valid UTF-8.
133 ///
134 /// # Examples
135 /// ```
136 /// use core::str::FromStr;
137 /// use coca::InlineString;
138 ///
139 /// let s = InlineString::<8>::from_str("hello").unwrap();
140 /// let (storage, len) = s.into_raw_parts();
141 ///
142 /// let rebuilt = unsafe { InlineString::from_raw_parts(storage, len) };
143 /// assert_eq!(rebuilt, "hello");
144 /// ```
145 #[inline]
146 pub unsafe fn from_raw_parts(buf: S, length: I) -> Self {
147 String { vec: Vec::from_raw_parts(buf, length) }
148 }
149
150 /// Converts a vector of bytes into a `String` without copying or checking
151 /// that the bytes are valid UTF-8.
152 ///
153 /// See the safe version, [`from_utf8`], for more details.
154 ///
155 /// [`from_utf8`]: String::from_utf8
156 ///
157 /// # Safety
158 ///
159 /// Callers must ensure that the passed bytes are valid UTF-8. If this
160 /// constraint is violated, it may cause memory unsafety issues with future
161 /// users of the `String`, as the rest of `coca` assumes `String`s to be
162 /// valid UTF-8.
163 ///
164 /// # Examples
165 /// ```
166 /// let mut bytes = coca::collections::InlineVec::<u8, 8>::new();
167 /// bytes.extend_from_slice(&[240, 159, 146, 150]);
168 ///
169 /// let sparkle_heart = unsafe { coca::InlineString::from_utf8_unchecked(bytes) };
170 /// assert_eq!(sparkle_heart, "💖");
171 /// ```
172 #[inline]
173 pub unsafe fn from_utf8_unchecked(bytes: Vec<u8, S, I>) -> Self {
174 String { vec: bytes }
175 }
176
177 /// Converts a `String` into a vector of bytes without copying, consuming the string.
178 ///
179 /// # Examples
180 /// ```
181 /// use core::str::FromStr;
182 /// let s = coca::InlineString::<8>::from_str("hello").unwrap();
183 /// let bytes = s.into_bytes();
184 ///
185 /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
186 /// ```
187 #[inline]
188 pub fn into_bytes(self) -> Vec<u8, S, I> {
189 self.vec
190 }
191
192 /// Extracts a string slice containing the entire `String`.
193 pub fn as_str(&self) -> &str {
194 self
195 }
196
197 /// Extracts a mutable string slice containing the entire `String`.
198 pub fn as_mut_str(&mut self) -> &mut str {
199 self
200 }
201
202 /// Appends a given string slice onto the end of the `String`, returning
203 /// [`Err`] if the remaining space is insufficient.
204 ///
205 /// # Examples
206 /// ```
207 /// use core::str::FromStr;
208 /// let mut s = coca::InlineString::<8>::from_str("foo").unwrap();
209 ///
210 /// assert!(s.try_push_str("bar").is_ok());
211 ///
212 /// assert!(s.try_push_str("bazz").is_err());
213 /// assert_eq!(s, "foobar");
214 /// ```
215 #[inline]
216 pub fn try_push_str(&mut self, string: &str) -> crate::Result<()> {
217 self.vec.try_extend_from_slice(string.as_bytes())
218 }
219
220 /// Appends a given string slice onto the end of the `String`.
221 ///
222 /// # Panics
223 /// Panics if the space remaining in the string is insufficient.
224 /// See [`try_push_str`](String::try_push_str) for a checked version
225 /// that never panics.
226 #[inline]
227 pub fn push_str(&mut self, string: &str) {
228 #[cold]
229 #[inline(never)]
230 fn assert_failed() -> ! {
231 panic!("space remaining in string is insufficient")
232 }
233
234 if self.try_push_str(string).is_err() {
235 assert_failed();
236 }
237 }
238
239 /// Copies characters from the `src` range to the end of the string.
240 ///
241 /// Returns [`Err`] if the remaining space is insufficient.
242 ///
243 /// # Panics
244 /// Panics if the starting point is greater than the end point, if the end
245 /// point is greater than the length of the `String`, or if either one does
246 /// not lie on a [`char`] boundary.
247 ///
248 /// # Examples
249 /// ```
250 /// use core::str::FromStr;
251 /// let mut s = coca::InlineString::<10>::from_str("abcde").unwrap();
252 ///
253 /// assert!(s.try_extend_from_within(2..).is_ok());
254 /// assert_eq!(s, "abcdecde");
255 ///
256 /// assert!(s.try_extend_from_within(..2).is_ok());
257 /// assert_eq!(s, "abcdecdeab");
258 ///
259 /// assert!(s.try_extend_from_within(4..8).is_err());
260 /// ```
261 pub fn try_extend_from_within<R: RangeBounds<usize>>(&mut self, src: R) -> crate::Result<()> {
262 let Range { start, end } = normalize_range(src, self.len());
263
264 assert!(self.is_char_boundary(start));
265 assert!(self.is_char_boundary(end));
266
267 let src = I::from_usize(start)..I::from_usize(end);
268 self.vec.try_extend_from_within(src)
269 }
270
271 /// Copies the characters from the `src` range to the end of the string.
272 ///
273 /// # Panics
274 /// Panics if the starting point is greater than the end point, if the end
275 /// point is greater than the length of the `String`, if either one does
276 /// not lie on a [`char`] boundary, or if the remaining space is insufficient.
277 pub fn extend_from_within<R: RangeBounds<usize>>(&mut self, src: R) {
278 self.try_extend_from_within(src).expect("space remaining in string is insufficient");
279 }
280
281 /// Returns the `String`'s capacity, in bytes.
282 #[inline]
283 pub fn capacity(&self) -> usize {
284 self.vec.capacity()
285 }
286
287 /// Returns the current length of the `String`, in bytes, not [`char`]s or
288 /// graphemes. This might not be what a human considers the length of the string.
289 #[inline]
290 pub fn len(&self) -> usize {
291 self.vec.len()
292 }
293
294 /// Returns `true` if the `String` has a length of zero, and `false` otherwise.
295 #[inline]
296 pub fn is_empty(&self) -> bool {
297 self.len() == 0
298 }
299
300 /// Returns `true` if the `String` has a length equal to its capacity, and `false` otherwise.
301 #[inline]
302 pub fn is_full(&self) -> bool {
303 self.len() == self.capacity()
304 }
305
306 /// Appends the given [`char`] to the end of the `String`, returning
307 /// `Err(ch)` if the remaining space is insufficient.
308 ///
309 /// # Examples
310 /// ```
311 /// use core::str::FromStr;
312 /// let mut s = coca::InlineString::<4>::from_str("ab").unwrap();
313 ///
314 /// assert!(s.try_push('c').is_ok());
315 /// assert!(s.try_push('d').is_ok());
316 /// assert!(s.try_push('e').is_err());
317 ///
318 /// assert_eq!(s, "abcd");
319 /// ```
320 #[inline]
321 pub fn try_push(&mut self, ch: char) -> crate::Result<()> {
322 match ch.len_utf8() {
323 1 => self.vec.try_push(ch as u8).map_err(|_| CapacityError),
324 _ => self.vec.try_extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes())
325 }
326 }
327
328 /// Appends the given [`char`] to the end of the `String`.
329 ///
330 /// # Panics
331 /// Panics if the space remaining in the string is insufficient.
332 /// See [`try_push`](String::try_push_str) for a checked version
333 /// that never panics.
334 #[inline]
335 pub fn push(&mut self, ch: char) {
336 #[cold]
337 #[inline(never)]
338 fn assert_failed() -> ! {
339 panic!("space remaining in string is insufficient")
340 }
341
342 if self.try_push(ch).is_err() {
343 assert_failed();
344 }
345 }
346
347 /// Returns a byte slice of this `String`'s contents.
348 ///
349 /// The inverse of this method is [`from_utf8`](String::from_utf8).
350 ///
351 /// # Examples
352 /// ```
353 /// use core::str::FromStr;
354 /// let s = coca::InlineString::<8>::from_str("hello").unwrap();
355 /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes());
356 /// ```
357 #[inline]
358 pub fn as_bytes(&self) -> &[u8] {
359 &self.vec
360 }
361
362 /// Shortens the `String` to the specified length.
363 ///
364 /// If `new_len` is greater than the string's current length, this has no effect.
365 ///
366 /// # Panics
367 /// Panics if `new_len` does not lie on a [`char`] boundary.
368 ///
369 /// # Examples
370 /// ```
371 /// use core::str::FromStr;
372 /// let mut s = coca::InlineString::<8>::from_str("hello").unwrap();
373 ///
374 /// s.truncate(2);
375 ///
376 /// assert_eq!(s, "he");
377 /// ```
378 #[inline]
379 pub fn truncate(&mut self, new_len: usize) {
380 if new_len <= self.len() {
381 assert!(self.is_char_boundary(new_len));
382 self.vec.truncate(I::from_usize(new_len));
383 }
384 }
385
386 /// Truncates the `String`, removing all contents.
387 #[inline]
388 pub fn clear(&mut self) {
389 self.truncate(0);
390 }
391
392 /// Removes the last [`char`] from the `String` and returns it,
393 /// or [`None`] if it is empty.
394 ///
395 /// # Examples
396 /// ```
397 /// use core::str::FromStr;
398 /// let mut s = coca::InlineString::<4>::from_str("foo").unwrap();
399 ///
400 /// assert_eq!(s.pop(), Some('o'));
401 /// assert_eq!(s.pop(), Some('o'));
402 /// assert_eq!(s.pop(), Some('f'));
403 ///
404 /// assert_eq!(s.pop(), None);
405 /// ```
406 #[inline]
407 pub fn pop(&mut self) -> Option<char> {
408 let ch = self.chars().rev().next()?;
409 let newlen = I::from_usize(self.len() - ch.len_utf8());
410 unsafe { self.vec.set_len(newlen); }
411 Some(ch)
412 }
413
414 /// Removes a [`char`] at the given byte position from the `String` and returns it.
415 ///
416 /// This is an *O*(*n*) operation, as it requires copying every character
417 /// after the removed element in the string.
418 ///
419 /// # Panics
420 /// Panics if `idx` is larger than or equal to the string's length,
421 /// or if it does not lie on a [`char`] boundary.
422 ///
423 /// # Examples
424 /// ```
425 /// use core::str::FromStr;
426 /// let mut s = coca::InlineString::<4>::from_str("foo").unwrap();
427 ///
428 /// assert_eq!(s.remove(0), 'f');
429 /// assert_eq!(s.remove(1), 'o');
430 /// assert_eq!(s.remove(0), 'o');
431 ///
432 /// assert!(s.is_empty());
433 /// ```
434 pub fn remove(&mut self, idx: usize) -> char {
435 let result = self[idx..].chars().next().expect("cannot remove a char from the end of a string");
436
437 let next = idx + result.len_utf8();
438 let len = self.len();
439 let new_len = I::from_usize(len - (next - idx));
440
441 unsafe {
442 core::ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next);
443 self.vec.set_len(new_len);
444 }
445
446 result
447 }
448
449 /// Retains only the characters specified by the predicate.
450 ///
451 /// In other words, removes all characters `c` such that `f(c)` returns `false`.
452 /// This method operates in place, visiting each character exactly once in the
453 /// original order, and preserves the order of the retained characters.
454 ///
455 /// # Examples
456 /// ```
457 /// use core::str::FromStr;
458 /// let mut s = coca::InlineString::<12>::from_str("f_o_o_b_a_r").unwrap();
459 ///
460 /// s.retain(|ch| ch != '_');
461 ///
462 /// assert_eq!(s, "foobar");
463 /// ```
464 /// Because the elements are visited exactly once in the original order,
465 /// external state may be used to decide which characters to keep:
466 /// ```
467 /// use core::str::FromStr;
468 /// let mut s = coca::InlineString::<8>::from_str("abcde").unwrap();
469 /// let keep = [false, true, true, false, true];
470 /// let mut iter = keep.iter();
471 /// s.retain(|_| *iter.next().unwrap());
472 /// assert_eq!(s, "bce");
473 /// ```
474 pub fn retain<F: FnMut(char) -> bool>(&mut self, mut f: F) {
475 let len = self.len();
476 let mut idx = 0;
477 let mut deleted_bytes = 0;
478
479 while idx < len {
480 let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap_unchecked() };
481 let ch_len = ch.len_utf8();
482
483 if !f(ch) {
484 deleted_bytes += ch_len;
485 } else if deleted_bytes > 0 {
486 unsafe {
487 let src_ptr = self.vec.as_ptr().add(idx);
488 let dst_ptr = self.vec.as_mut_ptr().add(idx - deleted_bytes);
489 core::ptr::copy(src_ptr, dst_ptr, ch_len);
490 }
491 }
492
493 idx += ch_len;
494 }
495
496 unsafe { self.vec.set_len(I::from_usize(len - deleted_bytes)) };
497 }
498
499 /// Inserts a character into the `String` at a given byte position.
500 ///
501 /// Returns [`Err`] if the remaining space is insufficient.
502 ///
503 /// This is an *O*(*n*) operation as it requires copying every element in the buffer.
504 ///
505 /// # Panics
506 /// Panics if `idx` is larger than the `String`'s length, or if it does not lie
507 /// on a [`char`] boundary.
508 ///
509 /// # Examples
510 /// ```
511 /// let mut s = coca::InlineString::<3>::new();
512 ///
513 /// assert!(s.try_insert(0, 'o').is_ok());
514 /// assert!(s.try_insert(1, 'o').is_ok());
515 /// assert!(s.try_insert(0, 'f').is_ok());
516 ///
517 /// assert!(s.try_insert(3, 'b').is_err());
518 ///
519 /// assert_eq!(s, "foo");
520 /// ```
521 pub fn try_insert(&mut self, idx: usize, ch: char) -> crate::Result<()> {
522 assert!(self.is_char_boundary(idx));
523 let mut bits = [0; 4];
524 let bits = ch.encode_utf8(&mut bits).as_bytes();
525 self.vec.try_insert_slice(I::from_usize(idx), bits)
526 }
527
528 /// Inserts a character into the `String` at a given byte position.
529 ///
530 /// This is an *O*(*n*) operation as it requires copying every element in the buffer.
531 ///
532 /// # Panics
533 /// Panics if `idx` is larger than the `String`'s length, if it does not lie
534 /// on a [`char`] boundary, or if the remaining space is insufficient.
535 pub fn insert(&mut self, idx: usize, ch: char) {
536 self.try_insert(idx, ch).expect("remaining space is insufficient");
537 }
538
539 /// Inserts a string slice into the `String` at a given byte position.
540 ///
541 /// Returns [`Err`] if the remaining space is insufficient.
542 ///
543 /// This is an *O*(*n*) operation as it requires copying every element in the buffer.
544 ///
545 /// # Panics
546 /// Panics if `idx` is larger than the `String`'s length, or if it does not
547 /// lie on a [`char`] boundary.
548 ///
549 /// # Examples
550 /// ```
551 /// use core::str::FromStr;
552 /// let mut s = coca::InlineString::<8>::from_str("bar").unwrap();
553 ///
554 /// assert!(s.try_insert_str(0, "foo").is_ok());
555 /// assert!(s.try_insert_str(6, "bazz").is_err());
556 ///
557 /// assert_eq!(s, "foobar");
558 /// ```
559 pub fn try_insert_str(&mut self, idx: usize, string: &str) -> crate::Result<()> {
560 assert!(self.is_char_boundary(idx));
561 self.vec.try_insert_slice(I::from_usize(idx), string.as_bytes())
562 }
563
564 /// Inserts a string slice into the `String` at a given byte position.
565 ///
566 /// This is an *O*(*n*) operation as it requires copying every element in the buffer.
567 ///
568 /// # Panics
569 /// Panics if `idx` is larger than the `String`'s length, if it does not
570 /// lie on a [`char`] boundary, or if the remaining space is insufficient.
571 pub fn insert_str(&mut self, idx: usize, string: &str) {
572 self.try_insert_str(idx, string).expect("remaining space is insufficient");
573 }
574
575 /// Returns a mutable reference to the raw byte contents of this `String`.
576 ///
577 /// # Safety
578 /// This function is unsafe because the returned `&mut Vec` allows writing
579 /// bytes which are not valid UTF-8. If this constraint is violated, using
580 /// the original `String` after dropping the `&mut Vec` may violate memory
581 /// safety, as the rest of `coca` assumes that `String`s are valid UTF-8.
582 ///
583 /// # Examples
584 /// ```
585 /// use core::str::FromStr;
586 /// let mut s = coca::InlineString::<8>::from_str("hello").unwrap();
587 ///
588 /// unsafe {
589 /// let mut vec = s.as_mut_vec();
590 /// assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]);
591 ///
592 /// vec.iter_mut().for_each(|b| *b += 1);
593 /// }
594 ///
595 /// assert_eq!(s, "ifmmp");
596 /// ```
597 #[inline]
598 pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8, S, I> {
599 &mut self.vec
600 }
601
602 /// Creates a draining iterator that removes the specified range from the `String`
603 /// and yields the removed [`char`]s.
604 ///
605 /// Note: No matter how many elements of the iterator are consumed,
606 /// the full range is removed when the iterator **is** dropped;
607 /// if the iterator **is not** dropped, the `String` remains unchanged.
608 ///
609 /// # Panics
610 /// Panics if the starting point is greater than the end point, if the end
611 /// point is greater than the length of the `String`, or if either one does
612 /// not lie on a [`char`] boundary.
613 ///
614 /// # Examples
615 /// ```
616 /// use core::str::FromStr;
617 /// let mut s = coca::InlineString::<32>::from_str("α is alpha, β is beta").unwrap();
618 /// let beta_offset = s.find('β').unwrap();
619 ///
620 /// let mut drain_iter = s.drain(..beta_offset);
621 /// assert_eq!(drain_iter.next(), Some('α'));
622 /// assert_eq!(drain_iter.next_back(), Some(' '));
623 ///
624 /// drop(drain_iter);
625 /// assert_eq!(s, "β is beta");
626 ///
627 /// s.drain(..);
628 /// assert!(s.is_empty());
629 /// ```
630 pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> Drain<'_, S, I> {
631 let Range { start, end } = normalize_range(range, self.len());
632 assert!(self.is_char_boundary(start));
633 assert!(self.is_char_boundary(end));
634 let target_range = I::from_usize(start)..I::from_usize(end);
635
636 let self_ptr = self as *mut _;
637 let iter = unsafe { self.get_unchecked(start..end) }.chars();
638
639 Drain { parent: self_ptr, target_range, iter }
640 }
641
642 /// Removes the specified range in the `String` and replaces it with the given string.
643 /// The given string doesn't need to be the same length as the range.
644 ///
645 /// Returns [`Err`] if the remaining space is insufficient.
646 ///
647 /// # Panics
648 /// Panics if the starting point is greater than the end point, if the end
649 /// point is greater than the length of the `String`, or if either one does
650 /// not lie on a [`char`] boundary.
651 ///
652 /// # Examples
653 /// ```
654 /// use core::str::FromStr;
655 /// let mut s = coca::InlineString::<32>::from_str("α is alpha, β is beta").unwrap();
656 /// let beta_offset = s.find('β').unwrap();
657 ///
658 /// assert!(s.try_replace_range(..beta_offset, "A is capital alpha; ").is_ok());
659 /// assert_eq!(s, "A is capital alpha; β is beta");
660 ///
661 /// let beta_offset = s.find('β').unwrap();
662 /// assert!(s.try_replace_range(beta_offset.., "B is capital beta.").is_err());
663 /// ```
664 pub fn try_replace_range<R: RangeBounds<usize>>(&mut self, range: R, replace_with: &str) -> crate::Result<()> {
665 let Range { start, end } = normalize_range(range, self.len());
666
667 assert!(self.is_char_boundary(start));
668 assert!(self.is_char_boundary(end));
669
670 let range = I::from_usize(start)..I::from_usize(end);
671 self.vec.try_replace_range(range, replace_with.as_bytes())
672 }
673
674 /// Removes the specified range in the `String` and replaces it with the given string.
675 /// The given string doesn't need to be the same length as the range.
676 ///
677 /// # Panics
678 /// Panics if the starting point is greater than the end point, if the end
679 /// point is greater than the length of the `String`, if either one does
680 /// not lie on a [`char`] boundary, or if the remaining space is insufficient.
681 pub fn replace_range<R: RangeBounds<usize>>(&mut self, range: R, replace_with: &str) {
682 self.try_replace_range(range, replace_with).expect("remaining space is insufficient");
683 }
684}
685
686impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::ops::Deref for String<S, I> {
687 type Target = str;
688
689 #[inline]
690 fn deref(&self) -> &str {
691 unsafe { str::from_utf8_unchecked(&self.vec) }
692 }
693}
694
695impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::ops::DerefMut for String<S, I> {
696 #[inline]
697 fn deref_mut(&mut self) -> &mut str {
698 unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) }
699 }
700}
701
702impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::convert::AsRef<str> for String<S, I> {
703 #[inline]
704 fn as_ref(&self) -> &str {
705 self.as_str()
706 }
707}
708
709impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::convert::AsMut<str> for String<S, I> {
710 #[inline]
711 fn as_mut(&mut self) -> &mut str {
712 self.as_mut_str()
713 }
714}
715
716impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::convert::AsRef<[u8]> for String<S, I> {
717 #[inline]
718 fn as_ref(&self) -> &[u8] {
719 self.vec.as_slice()
720 }
721}
722
723impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Clone for String<S, I> where Vec<u8, S, I>: Clone {
724 #[inline]
725 fn clone(&self) -> Self {
726 String { vec: self.vec.clone() }
727 }
728}
729
730impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::iter::Extend<char> for String<S, I> {
731 #[inline]
732 fn extend<It: IntoIterator<Item = char>>(&mut self, iter: It) {
733 for ch in iter {
734 self.push(ch);
735 }
736 }
737}
738
739impl<'a, S: Storage<ArrayLayout<u8>>, I: Capacity> core::iter::Extend<&'a char> for String<S, I> {
740 #[inline]
741 fn extend<It: IntoIterator<Item = &'a char>>(&mut self, iter: It) {
742 for ch in iter {
743 self.push(*ch);
744 }
745 }
746}
747
748impl<'a, S: Storage<ArrayLayout<u8>>, I: Capacity> core::iter::Extend<&'a str> for String<S, I> {
749 #[inline]
750 fn extend<It: IntoIterator<Item = &'a str>>(&mut self, iter: It) {
751 for s in iter {
752 self.push_str(s);
753 }
754 }
755}
756
757impl<S1: Storage<ArrayLayout<u8>>, I1: Capacity, S2: Storage<ArrayLayout<u8>>, I2: Capacity> PartialEq<String<S2, I2>> for String<S1, I1> {
758 #[inline]
759 fn eq(&self, other: &String<S2, I2>) -> bool {
760 self.as_str() == other.as_str()
761 }
762}
763
764impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Eq for String<S, I> {}
765
766impl<S: Storage<ArrayLayout<u8>>, I: Capacity> PartialEq<str> for String<S, I> {
767 #[inline]
768 fn eq(&self, other: &str) -> bool {
769 self.as_str() == other
770 }
771}
772
773impl<S: Storage<ArrayLayout<u8>>, I: Capacity> PartialEq<&str> for String<S, I> {
774 #[inline]
775 fn eq(&self, other: &&str) -> bool {
776 self.as_str() == *other
777 }
778}
779
780impl<S1: Storage<ArrayLayout<u8>>, I1: Capacity, S2: Storage<ArrayLayout<u8>>, I2: Capacity> PartialOrd<String<S2, I2>> for String<S1, I1> {
781 #[inline]
782 fn partial_cmp(&self, other: &String<S2, I2>) -> Option<core::cmp::Ordering> {
783 self.as_str().partial_cmp(other.as_str())
784 }
785}
786
787impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Ord for String<S, I> {
788 #[inline]
789 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
790 self.as_str().cmp(other.as_str())
791 }
792}
793
794impl<S: Storage<ArrayLayout<u8>>, I: Capacity> fmt::Display for String<S, I> {
795 #[inline]
796 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
797 fmt::Display::fmt(&**self, f)
798 }
799}
800
801impl<S: Storage<ArrayLayout<u8>>, I: Capacity> fmt::Debug for String<S, I> {
802 #[inline]
803 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804 fmt::Debug::fmt(&**self, f)
805 }
806}
807
808impl<S: Storage<ArrayLayout<u8>>, I: Capacity> fmt::Write for String<S, I> {
809 #[inline]
810 fn write_str(&mut self, s: &str) -> fmt::Result {
811 self.try_push_str(s).map_err(|_| fmt::Error)
812 }
813
814 #[inline]
815 fn write_char(&mut self, c: char) -> fmt::Result {
816 self.try_push(c).map_err(|_| fmt::Error)
817 }
818}
819
820impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::hash::Hash for String<S, I> {
821 #[inline]
822 fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
823 (**self).hash(hasher);
824 }
825}
826
827impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::ops::Add<&str> for String<S, I> {
828 type Output = Self;
829
830 #[inline]
831 fn add(mut self, other: &str) -> Self {
832 self.push_str(other);
833 self
834 }
835}
836
837impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::ops::AddAssign<&str> for String<S, I> {
838 #[inline]
839 fn add_assign(&mut self, rhs: &str) {
840 self.push_str(rhs);
841 }
842}
843
844impl<'a, I: Capacity> crate::ArenaString<'a, I> {
845 /// Converts the given boxed `str` slice into a `String`.
846 ///
847 /// # Examples
848 /// ```
849 /// use coca::arena::Arena;
850 /// use core::mem::MaybeUninit;
851 ///
852 /// # fn test() -> Option<()> {
853 /// let mut backing_region = [MaybeUninit::uninit(); 1024];
854 /// let mut arena = Arena::from(&mut backing_region[..]);
855 ///
856 /// let boxed_str = coca::fmt!(arena, "{}", 1234567890)?;
857 /// let mut string = coca::ArenaString::<'_, usize>::from_boxed_str(boxed_str);
858 ///
859 /// string.retain(|c| (c as u8) % 2 == 1);
860 /// assert_eq!(string, "13579");
861 /// # Some(()) }
862 /// # assert!(test().is_some());
863 /// ```
864 pub fn from_boxed_str(mut string: crate::arena::Box<'a, str>) -> Self {
865 let length = string.len();
866 unsafe {
867 let buf = ArenaStorage::from_raw_parts(string.as_mut_ptr(), length).unwrap_unchecked();
868 Self::from_raw_parts(buf, I::from_usize(length))
869 }
870 }
871
872 /// Converts the `ArenaString` into an owned `str` slice.
873 ///
874 /// # Examples
875 /// ```
876 /// use coca::arena::Arena;
877 /// use core::mem::MaybeUninit;
878 ///
879 /// # fn test() -> Option<()> {
880 /// let mut backing_region = [MaybeUninit::uninit(); 1024];
881 /// let mut arena = Arena::from(&mut backing_region[..]);
882 ///
883 /// let mut string = arena.string_with_capacity_from(16usize, "Hello, ");
884 /// string.push('W');
885 /// string.push_str("orld!");
886 ///
887 /// let boxed_str = string.into_boxed_str();
888 /// assert_eq!(boxed_str.as_ref(), "Hello, World!");
889 /// # Some(()) }
890 /// # assert!(test().is_some());
891 /// ```
892 pub fn into_boxed_str(self) -> crate::arena::Box<'a, str> {
893 let (mut buf, len) = self.into_raw_parts();
894 let ptr = core::ptr::slice_from_raw_parts_mut(buf.get_mut_ptr(), len.as_usize());
895 unsafe {
896 let str_ptr = core::str::from_utf8_unchecked_mut(&mut *ptr);
897 crate::arena::Box::new_unchecked(str_ptr as *mut str)
898 }
899 }
900}
901
902#[cfg(feature = "alloc")]
903#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))]
904impl<I: Capacity> FromStr for String::<crate::storage::AllocStorage<ArrayLayout<u8>>, I> {
905 type Err = core::convert::Infallible;
906
907 /// Creates a new `AllocString` with the given contents, and zero excess capacity.
908 ///
909 /// # Examples
910 /// ```
911 /// use core::str::FromStr;
912 /// let s = coca::AllocString::<usize>::from_str("abcde").unwrap();
913 /// assert_eq!(s.capacity(), s.len());
914 /// ```
915 fn from_str(string: &str) -> Result<Self, Self::Err> {
916 let mut buf = Self::with_capacity(I::from_usize(string.len()));
917 buf.push_str(string);
918 Ok(buf)
919 }
920}
921
922#[cfg(feature = "alloc")]
923#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))]
924impl<I: Capacity> String<crate::storage::AllocStorage<ArrayLayout<u8>>, I> {
925 /// Creates a new, empty `AllocString` with the specified capacity.
926 pub fn with_capacity(capacity: I) -> Self {
927 Self::from(crate::storage::AllocStorage::with_capacity(capacity.as_usize()))
928 }
929
930 /// Creates a new `AllocString` with the given capacity, and initializes it with the given content.
931 ///
932 /// Returns [`Err`] if the given string is longer (in bytes) than `capacity`.
933 ///
934 /// # Examples
935 /// ```
936 /// let s = coca::AllocString::try_from_str_with_capacity("abcde", 8usize).unwrap();
937 /// assert_eq!(s.capacity(), 8);
938 /// assert_eq!(s, "abcde");
939 ///
940 /// assert!(coca::AllocString::try_from_str_with_capacity("abcde", 4usize).is_err());
941 /// ```
942 pub fn try_from_str_with_capacity(string: &str, capacity: I) -> crate::Result<Self> {
943 let cap = capacity.as_usize();
944 if string.len() > cap { return CapacityError::new(); }
945
946 let mut buf = Self::with_capacity(capacity);
947 buf.push_str(string);
948 Ok(buf)
949 }
950
951 /// Creates a new `AllocString` with the given capacity, and initializes it with the given content.
952 ///
953 /// # Panics
954 /// Panics if the given string is longer (in bytes) than `capacity`.
955 /// See [`try_from_str_with_capacity`](String::try_from_str_with_capacity)
956 /// for a checked version that never panics.
957 pub fn from_str_with_capacity(string: &str, capacity: I) -> Self {
958 Self::try_from_str_with_capacity(string, capacity).expect("given string is longer than capacity")
959 }
960}
961
962impl<I: Capacity, const C: usize> String<InlineStorage<u8, C>, I> {
963 /// Constructs a new, empty `String` backed by an inline array.
964 pub fn new() -> Self {
965 String { vec: Vec::new() }
966 }
967}
968
969impl<I: Capacity, const C: usize> FromStr for String<InlineStorage<u8, C>, I> {
970 type Err = CapacityError;
971 /// Constructs a new `String` backed by an inline array, initialized with the given contents.
972 ///
973 /// Returns [`Err`] if the given string is longer than `C` bytes.
974 ///
975 /// # Examples
976 /// ```
977 /// use core::str::FromStr;
978 /// let s = coca::InlineString::<8>::from_str("abcde").unwrap();
979 /// assert_eq!(s.capacity(), 8);
980 /// assert_eq!(s, "abcde");
981 ///
982 /// assert!(coca::InlineString::<4>::from_str("abcde").is_err());
983 /// ```
984 fn from_str(string: &str) -> Result<Self, Self::Err> {
985 if string.len() <= C {
986 let mut result = Self::new();
987 result.push_str(string);
988 Ok(result)
989 } else {
990 CapacityError::new()
991 }
992 }
993}
994
995impl<I: Capacity, const C: usize> Default for String<InlineStorage<u8, C>, I> {
996 fn default() -> Self {
997 Self::new()
998 }
999}
1000
1001/// A draining iterator for [`String`].
1002///
1003/// This struct is created by the [`drain`](String::drain) method on `String`.
1004/// See its documentation for more.
1005pub struct Drain<'a, S: Storage<ArrayLayout<u8>>, I: Capacity> {
1006 parent: *mut String<S, I>,
1007 target_range: Range<I>,
1008 iter: str::Chars<'a>,
1009}
1010
1011impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Drain<'_, S, I> {
1012 /// Returns the remaining (sub)string of this iterator as a slice.
1013 pub fn as_str(&self) -> &str {
1014 self.iter.as_str()
1015 }
1016}
1017
1018impl<S: Storage<ArrayLayout<u8>>, I: Capacity> fmt::Debug for Drain<'_, S, I> {
1019 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1020 f.debug_tuple("Drain").field(&self.as_str()).finish()
1021 }
1022}
1023
1024impl<S: Storage<ArrayLayout<u8>>, I: Capacity> AsRef<str> for Drain<'_, S, I> {
1025 fn as_ref(&self) -> &str {
1026 self.as_str()
1027 }
1028}
1029
1030impl<S: Storage<ArrayLayout<u8>>, I: Capacity> AsRef<[u8]> for Drain<'_, S, I> {
1031 fn as_ref(&self) -> &[u8] {
1032 self.iter.as_str().as_bytes()
1033 }
1034}
1035
1036impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Iterator for Drain<'_, S, I> {
1037 type Item = char;
1038
1039 #[inline]
1040 fn next(&mut self) -> Option<char> {
1041 self.iter.next()
1042 }
1043
1044 #[inline]
1045 fn size_hint(&self) -> (usize, Option<usize>) {
1046 self.iter.size_hint()
1047 }
1048
1049 #[inline]
1050 fn last(mut self) -> Option<char> {
1051 self.next_back()
1052 }
1053}
1054
1055impl<S: Storage<ArrayLayout<u8>>, I: Capacity> DoubleEndedIterator for Drain<'_, S, I> {
1056 #[inline]
1057 fn next_back(&mut self) -> Option<char> {
1058 self.iter.next_back()
1059 }
1060}
1061
1062impl<S: Storage<ArrayLayout<u8>>, I: Capacity> core::iter::FusedIterator for Drain<'_, S, I> {}
1063
1064impl<S: Storage<ArrayLayout<u8>>, I: Capacity> Drop for Drain<'_, S, I> {
1065 fn drop(&mut self) {
1066 unsafe {
1067 let vec = (*self.parent).as_mut_vec();
1068 vec.drain(self.target_range.clone());
1069 }
1070 }
1071}