arraystring/arraystring.rs
1//! `ArrayString` definition and Api implementation
2
3use crate::utils::{encode_char_utf8_unchecked, is_char_boundary, is_inside_boundary, never};
4use crate::utils::{shift_left_unchecked, shift_right_unchecked, truncate_str, IntoLossy};
5use crate::{error::Error, generic::ArraySlice, prelude::*};
6use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
7use core::str::{from_utf8, from_utf8_unchecked};
8use core::{cmp::min, ops::*, ptr::copy_nonoverlapping};
9#[cfg(feature = "logs")]
10use log::{debug, trace};
11
12use crate::generic::Capacity;
13
14/// String based on a generic array (size defined at compile time through `typenum`)
15///
16/// Can't outgrow capacity (defined at compile time), always occupies [`capacity`] `+ 1` bytes of memory
17///
18/// *Doesn't allocate memory on the heap and never panics (all panic branches are stripped at compile time)*
19///
20/// [`capacity`]: ./struct.ArrayString.html#method.capacity
21#[derive(Clone)]
22pub struct ArrayString<SIZE: Capacity> {
23 /// Array type corresponding to specified `SIZE`
24 pub(crate) array: SIZE::Array,
25 /// Current string size
26 pub(crate) size: u8,
27}
28
29impl<SIZE: Capacity> ArrayString<SIZE> {
30 /// Creates new empty string.
31 ///
32 /// ```rust
33 /// # use arraystring::prelude::*;
34 /// # let _ = env_logger::try_init();
35 /// let string = SmallString::new();
36 /// assert!(string.is_empty());
37 /// ```
38 #[inline]
39 pub fn new() -> Self {
40 trace!("New empty ArrayString");
41 Self::default()
42 }
43
44 /// Creates new `ArrayString` from string slice if length is lower or equal to [`capacity`], otherwise returns an error.
45 ///
46 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
47 /// ```rust
48 /// # use arraystring::{error::Error, prelude::*};
49 /// # fn main() -> Result<(), Error> {
50 /// # let _ = env_logger::try_init();
51 /// let string = SmallString::try_from_str("My String")?;
52 /// assert_eq!(string.as_str(), "My String");
53 ///
54 /// assert_eq!(SmallString::try_from_str("")?.as_str(), "");
55 ///
56 /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
57 /// assert!(SmallString::try_from_str(out_of_bounds).is_err());
58 /// # Ok(())
59 /// # }
60 /// ```
61 #[inline]
62 pub fn try_from_str<S>(s: S) -> Result<Self, OutOfBounds>
63 where
64 S: AsRef<str>,
65 {
66 trace!("Try from str: {:?}", s.as_ref());
67 is_inside_boundary(s.as_ref().len(), Self::capacity())?;
68 unsafe { Ok(Self::from_str_unchecked(s.as_ref())) }
69 }
70
71 /// Creates new `ArrayString` from string slice truncating size if bigger than [`capacity`].
72 ///
73 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
74 ///
75 /// ```rust
76 /// # use arraystring::prelude::*;
77 /// # let _ = env_logger::try_init();
78 /// let string = SmallString::from_str_truncate("My String");
79 /// # assert_eq!(string.as_str(), "My String");
80 /// println!("{}", string);
81 ///
82 /// let truncate = "0".repeat(SmallString::capacity() as usize + 1);
83 /// let truncated = "0".repeat(SmallString::capacity().into());
84 /// let string = SmallString::from_str_truncate(&truncate);
85 /// assert_eq!(string.as_str(), truncated);
86 /// ```
87 #[inline]
88 pub fn from_str_truncate<S>(string: S) -> Self
89 where
90 S: AsRef<str>,
91 {
92 trace!("FromStr truncate");
93 unsafe { Self::from_str_unchecked(truncate_str(string.as_ref(), Self::capacity())) }
94 }
95
96 /// Creates new `ArrayString` from string slice assuming length is appropriate.
97 ///
98 /// # Safety
99 ///
100 /// It's UB if `string.len()` > [`capacity`].
101 ///
102 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
103 ///
104 /// ```rust
105 /// # use arraystring::prelude::*;
106 /// let filled = "0".repeat(SmallString::capacity().into());
107 /// let string = unsafe {
108 /// SmallString::from_str_unchecked(&filled)
109 /// };
110 /// assert_eq!(string.as_str(), filled.as_str());
111 ///
112 /// // Undefined behavior, don't do it
113 /// // let out_of_bounds = "0".repeat(SmallString::capacity().into() + 1);
114 /// // let ub = unsafe { SmallString::from_str_unchecked(out_of_bounds) };
115 /// ```
116 #[inline]
117 pub unsafe fn from_str_unchecked<S>(string: S) -> Self
118 where
119 S: AsRef<str>,
120 {
121 trace!("FromStr unchecked");
122 let mut out = Self::default();
123 out.push_str_unchecked(string);
124 out
125 }
126
127 /// Creates new `ArrayString` from string slice iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
128 ///
129 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
130 ///
131 /// ```rust
132 /// # use arraystring::prelude::*;
133 /// # fn main() -> Result<(), OutOfBounds> {
134 /// let string = MaxString::try_from_iterator(&["My String", " My Other String"][..])?;
135 /// assert_eq!(string.as_str(), "My String My Other String");
136 ///
137 /// let out_of_bounds = (0..100).map(|_| "000");
138 /// assert!(SmallString::try_from_iterator(out_of_bounds).is_err());
139 /// # Ok(())
140 /// # }
141 /// ```
142 #[inline]
143 pub fn try_from_iterator<U, I>(iter: I) -> Result<Self, OutOfBounds>
144 where
145 U: AsRef<str>,
146 I: IntoIterator<Item = U>,
147 {
148 trace!("FromIterator");
149 let mut out = Self::default();
150 for s in iter {
151 out.try_push_str(s)?;
152 }
153 Ok(out)
154 }
155
156 /// Creates new `ArrayString` from string slice iterator truncating size if bigger than [`capacity`].
157 ///
158 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
159 ///
160 /// ```rust
161 /// # use arraystring::prelude::*;
162 /// # fn main() -> Result<(), OutOfBounds> {
163 /// # let _ = env_logger::try_init();
164 /// let string = MaxString::from_iterator(&["My String", " Other String"][..]);
165 /// assert_eq!(string.as_str(), "My String Other String");
166 ///
167 /// let out_of_bounds = (0..400).map(|_| "000");
168 /// let truncated = "0".repeat(SmallString::capacity().into());
169 ///
170 /// let truncate = SmallString::from_iterator(out_of_bounds);
171 /// assert_eq!(truncate.as_str(), truncated.as_str());
172 /// # Ok(())
173 /// # }
174 /// ```
175 #[inline]
176 pub fn from_iterator<U, I>(iter: I) -> Self
177 where
178 U: AsRef<str>,
179 I: IntoIterator<Item = U>,
180 {
181 trace!("FromIterator truncate");
182 let mut out = Self::default();
183 for s in iter {
184 if out.try_push_str(s.as_ref()).is_err() {
185 out.push_str(s);
186 break;
187 }
188 }
189 out
190 }
191
192 /// Creates new `ArrayString` from string slice iterator assuming length is appropriate.
193 ///
194 /// # Safety
195 ///
196 /// It's UB if `iter.map(|c| c.len()).sum()` > [`capacity`].
197 ///
198 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
199 ///
200 /// ```rust
201 /// # use arraystring::prelude::*;
202 /// let string = unsafe {
203 /// MaxString::from_iterator_unchecked(&["My String", " My Other String"][..])
204 /// };
205 /// assert_eq!(string.as_str(), "My String My Other String");
206 ///
207 /// // Undefined behavior, don't do it
208 /// // let out_of_bounds = (0..400).map(|_| "000");
209 /// // let undefined_behavior = unsafe {
210 /// // SmallString::from_iterator_unchecked(out_of_bounds)
211 /// // };
212 /// ```
213 #[inline]
214 pub unsafe fn from_iterator_unchecked<U, I>(iter: I) -> Self
215 where
216 U: AsRef<str>,
217 I: IntoIterator<Item = U>,
218 {
219 trace!("FromIterator unchecked");
220 let mut out = Self::default();
221 for s in iter {
222 out.push_str_unchecked(s);
223 }
224 out
225 }
226
227 /// Creates new `ArrayString` from char iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
228 ///
229 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
230 ///
231 /// ```rust
232 /// # use arraystring::{error::Error, prelude::*};
233 /// # fn main() -> Result<(), Error> {
234 /// # let _ = env_logger::try_init();
235 /// let string = SmallString::try_from_chars("My String".chars())?;
236 /// assert_eq!(string.as_str(), "My String");
237 ///
238 /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
239 /// assert!(SmallString::try_from_chars(out_of_bounds.chars()).is_err());
240 /// # Ok(())
241 /// # }
242 /// ```
243 #[inline]
244 pub fn try_from_chars<I>(iter: I) -> Result<Self, OutOfBounds>
245 where
246 I: IntoIterator<Item = char>,
247 {
248 trace!("TryFrom chars");
249 let mut out = Self::default();
250 for c in iter {
251 out.try_push(c)?;
252 }
253 Ok(out)
254 }
255
256 /// Creates new `ArrayString` from char iterator truncating size if bigger than [`capacity`].
257 ///
258 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
259 ///
260 /// ```rust
261 /// # use arraystring::prelude::*;
262 /// # let _ = env_logger::try_init();
263 /// let string = SmallString::from_chars("My String".chars());
264 /// assert_eq!(string.as_str(), "My String");
265 ///
266 /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
267 /// let truncated = "0".repeat(SmallString::capacity().into());
268 ///
269 /// let truncate = SmallString::from_chars(out_of_bounds.chars());
270 /// assert_eq!(truncate.as_str(), truncated.as_str());
271 /// ```
272 #[inline]
273 pub fn from_chars<I>(iter: I) -> Self
274 where
275 I: IntoIterator<Item = char>,
276 {
277 trace!("From chars truncate");
278 let mut out = Self::default();
279 for c in iter {
280 if out.try_push(c).is_err() {
281 break;
282 }
283 }
284 out
285 }
286
287 /// Creates new `ArrayString` from char iterator assuming length is appropriate.
288 ///
289 /// # Safety
290 ///
291 /// It's UB if `iter.map(|c| c.len_utf8()).sum()` > [`capacity`].
292 ///
293 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
294 ///
295 /// ```rust
296 /// # use arraystring::prelude::*;
297 /// let string = unsafe { SmallString::from_chars_unchecked("My String".chars()) };
298 /// assert_eq!(string.as_str(), "My String");
299 ///
300 /// // Undefined behavior, don't do it
301 /// // let out_of_bounds = "000".repeat(400);
302 /// // let undefined_behavior = unsafe { SmallString::from_chars_unchecked(out_of_bounds.chars()) };
303 /// ```
304 #[inline]
305 pub unsafe fn from_chars_unchecked<I>(iter: I) -> Self
306 where
307 I: IntoIterator<Item = char>,
308 {
309 trace!("From chars unchecked");
310 let mut out = Self::default();
311 for c in iter {
312 out.push_unchecked(c)
313 }
314 out
315 }
316
317 /// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data or [`OutOfBounds`] if bigger than [`capacity`]
318 ///
319 /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
320 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
321 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
322 ///
323 /// ```rust
324 /// # use arraystring::{error::Error, prelude::*};
325 /// # fn main() -> Result<(), Error> {
326 /// # let _ = env_logger::try_init();
327 /// let string = SmallString::try_from_utf8("My String")?;
328 /// assert_eq!(string.as_str(), "My String");
329 ///
330 /// let invalid_utf8 = [0, 159, 146, 150];
331 /// assert_eq!(SmallString::try_from_utf8(invalid_utf8), Err(Error::Utf8));
332 ///
333 /// let out_of_bounds = "0000".repeat(400);
334 /// assert_eq!(SmallString::try_from_utf8(out_of_bounds.as_bytes()), Err(Error::OutOfBounds));
335 /// # Ok(())
336 /// # }
337 /// ```
338 #[inline]
339 pub fn try_from_utf8<B>(slice: B) -> Result<Self, Error>
340 where
341 B: AsRef<[u8]>,
342 {
343 debug!("From utf8: {:?}", slice.as_ref());
344 Ok(Self::try_from_str(from_utf8(slice.as_ref())?)?)
345 }
346
347 /// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data, truncating if bigger than [`capacity`].
348 ///
349 /// [`Utf8`]: ./error/struct.Utf8.html
350 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
351 ///
352 /// ```rust
353 /// # use arraystring::{error::Error, prelude::*};
354 /// # fn main() -> Result<(), Error> {
355 /// # let _ = env_logger::try_init();
356 /// let string = SmallString::from_utf8("My String")?;
357 /// assert_eq!(string.as_str(), "My String");
358 ///
359 /// let invalid_utf8 = [0, 159, 146, 150];
360 /// assert_eq!(SmallString::from_utf8(invalid_utf8), Err(Utf8));
361 ///
362 /// let out_of_bounds = "0".repeat(300);
363 /// assert_eq!(SmallString::from_utf8(out_of_bounds.as_bytes())?.as_str(),
364 /// "0".repeat(SmallString::capacity().into()).as_str());
365 /// # Ok(())
366 /// # }
367 /// ```
368 #[inline]
369 pub fn from_utf8<B>(slice: B) -> Result<Self, Utf8>
370 where
371 B: AsRef<[u8]>,
372 {
373 debug!("From utf8: {:?}", slice.as_ref());
374 Ok(Self::from_str_truncate(from_utf8(slice.as_ref())?))
375 }
376
377 /// Creates new `ArrayString` from byte slice assuming it's utf-8 and of a appropriate size.
378 ///
379 /// # Safety
380 ///
381 /// It's UB if `slice` is not a valid utf-8 string or `slice.len()` > [`capacity`].
382 ///
383 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
384 ///
385 /// ```rust
386 /// # use arraystring::prelude::*;
387 /// let string = unsafe { SmallString::from_utf8_unchecked("My String") };
388 /// assert_eq!(string.as_str(), "My String");
389 ///
390 /// // Undefined behavior, don't do it
391 /// // let out_of_bounds = "0".repeat(300);
392 /// // let ub = unsafe { SmallString::from_utf8_unchecked(out_of_bounds)) };
393 /// ```
394 #[inline]
395 pub unsafe fn from_utf8_unchecked<B>(slice: B) -> Self
396 where
397 B: AsRef<[u8]>,
398 {
399 trace!("From utf8 unchecked");
400 debug_assert!(from_utf8(slice.as_ref()).is_ok());
401 Self::from_str_unchecked(from_utf8_unchecked(slice.as_ref()))
402 }
403
404 /// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data or [`OutOfBounds`] if bigger than [`capacity`]
405 ///
406 /// [`Utf16`]: ./error/enum.Error.html#variant.Utf16
407 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
408 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
409 ///
410 /// ```rust
411 /// # use arraystring::{error::Error, prelude::*};
412 /// # fn main() -> Result<(), Error> {
413 /// # let _ = env_logger::try_init();
414 /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
415 /// let string = SmallString::try_from_utf16(music)?;
416 /// assert_eq!(string.as_str(), "𝄞music");
417 ///
418 /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
419 /// assert_eq!(SmallString::try_from_utf16(invalid_utf16), Err(Error::Utf16));
420 ///
421 /// let out_of_bounds: Vec<_> = (0..300).map(|_| 0).collect();
422 /// assert_eq!(SmallString::try_from_utf16(out_of_bounds), Err(Error::OutOfBounds));
423 /// # Ok(())
424 /// # }
425 /// ```
426 #[inline]
427 pub fn try_from_utf16<B>(slice: B) -> Result<Self, Error>
428 where
429 B: AsRef<[u16]>,
430 {
431 debug!("From utf16: {:?}", slice.as_ref());
432 let mut out = Self::default();
433 for c in decode_utf16(slice.as_ref().iter().cloned()) {
434 out.try_push(c?)?;
435 }
436 Ok(out)
437 }
438
439 /// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data, truncating if bigger than [`capacity`].
440 ///
441 /// [`Utf16`]: ./error/struct.Utf16.html
442 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
443 ///
444 /// ```rust
445 /// # use arraystring::{error::Error, prelude::*};
446 /// # fn main() -> Result<(), Error> {
447 /// # let _ = env_logger::try_init();
448 /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
449 /// let string = SmallString::from_utf16(music)?;
450 /// assert_eq!(string.as_str(), "𝄞music");
451 ///
452 /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
453 /// assert_eq!(SmallString::from_utf16(invalid_utf16), Err(Utf16));
454 ///
455 /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
456 /// assert_eq!(SmallString::from_utf16(out_of_bounds)?.as_str(),
457 /// "\0".repeat(SmallString::capacity().into()).as_str());
458 /// # Ok(())
459 /// # }
460 /// ```
461 #[inline]
462 pub fn from_utf16<B>(slice: B) -> Result<Self, Utf16>
463 where
464 B: AsRef<[u16]>,
465 {
466 debug!("From utf16: {:?}", slice.as_ref());
467 let mut out = Self::default();
468 for c in decode_utf16(slice.as_ref().iter().cloned()) {
469 if out.try_push(c?).is_err() {
470 break;
471 }
472 }
473 Ok(out)
474 }
475
476 /// Creates new `ArrayString` from `u16` slice, replacing invalid utf-16 data with `REPLACEMENT_CHARACTER` (\u{FFFD}) and truncating size if bigger than [`capacity`]
477 ///
478 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
479 ///
480 /// ```rust
481 /// # use arraystring::{error::Error, prelude::*};
482 /// # fn main() -> Result<(), Error> {
483 /// # let _ = env_logger::try_init();
484 /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
485 /// let string = SmallString::from_utf16_lossy(music);
486 /// assert_eq!(string.as_str(), "𝄞music");
487 ///
488 /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
489 /// assert_eq!(SmallString::from_utf16_lossy(invalid_utf16).as_str(), "𝄞mu\u{FFFD}ic");
490 ///
491 /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
492 /// assert_eq!(SmallString::from_utf16_lossy(&out_of_bounds).as_str(),
493 /// "\0".repeat(SmallString::capacity().into()).as_str());
494 /// # Ok(())
495 /// # }
496 /// ```
497 #[inline]
498 pub fn from_utf16_lossy<B>(slice: B) -> Self
499 where
500 B: AsRef<[u16]>,
501 {
502 debug!("From utf16 lossy: {:?}", slice.as_ref());
503 let mut out = Self::default();
504 for c in decode_utf16(slice.as_ref().iter().cloned()) {
505 if out.try_push(c.unwrap_or(REPLACEMENT_CHARACTER)).is_err() {
506 break;
507 }
508 }
509 out
510 }
511
512 /// Extracts a string slice containing the entire `ArrayString`
513 ///
514 /// ```rust
515 /// # use arraystring::{error::Error, prelude::*};
516 /// # fn main() -> Result<(), Error> {
517 /// # let _ = env_logger::try_init();
518 /// let s = SmallString::try_from_str("My String")?;
519 /// assert_eq!(s.as_str(), "My String");
520 /// # Ok(())
521 /// # }
522 /// ```
523 #[inline]
524 pub fn as_str(&self) -> &str {
525 trace!("As str: {}", <Self as AsRef<str>>::as_ref(self));
526 self.as_ref()
527 }
528
529 /// Extracts a mutable string slice containing the entire `ArrayString`
530 ///
531 /// ```rust
532 /// # use arraystring::{error::Error, prelude::*};
533 /// # fn main() -> Result<(), Error> {
534 /// # let _ = env_logger::try_init();
535 /// let mut s = SmallString::try_from_str("My String")?;
536 /// assert_eq!(s.as_mut_str(), "My String");
537 /// # Ok(())
538 /// # }
539 /// ```
540 #[inline]
541 pub fn as_mut_str(&mut self) -> &mut str {
542 trace!("As mut str: {}", self.as_mut());
543 self.as_mut()
544 }
545
546 /// Extracts a byte slice containing the entire `ArrayString`
547 ///
548 /// ```rust
549 /// # use arraystring::{error::Error, prelude::*};
550 /// # fn main() -> Result<(), Error> {
551 /// # let _ = env_logger::try_init();
552 /// let s = SmallString::try_from_str("My String")?;
553 /// assert_eq!(s.as_bytes(), "My String".as_bytes());
554 /// # Ok(())
555 /// # }
556 /// ```
557 #[inline]
558 pub fn as_bytes(&self) -> &[u8] {
559 trace!("As str: {}", self.as_str());
560 self.as_ref()
561 }
562
563 /// Extracts a mutable string slice containing the entire `ArrayString`
564 ///
565 /// ```rust
566 /// # use arraystring::{error::Error, prelude::*};
567 /// # fn main() -> Result<(), Error> {
568 /// let mut s = SmallString::try_from_str("My String")?;
569 /// assert_eq!(unsafe { s.as_mut_bytes() }, "My String".as_bytes());
570 /// # Ok(())
571 /// # }
572 /// ```
573 #[inline]
574 pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
575 trace!("As mut str: {}", self.as_str());
576 let len = self.len();
577 self.array.as_mut_slice().get_unchecked_mut(..len.into())
578 }
579
580 /// Returns maximum string capacity, defined at compile time, it will never change
581 ///
582 /// ```rust
583 /// # use arraystring::prelude::*;
584 /// # let _ = env_logger::try_init();
585 /// assert_eq!(ArrayString::<typenum::U32>::capacity(), 32);
586 /// ```
587 #[inline]
588 pub fn capacity() -> u8 {
589 SIZE::to_u8()
590 }
591
592 /// Pushes string slice to the end of the `ArrayString` if total size is lower or equal to [`capacity`], otherwise returns an error.
593 ///
594 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
595 ///
596 /// ```rust
597 /// # use arraystring::{error::Error, prelude::*};
598 /// # fn main() -> Result<(), Error> {
599 /// # let _ = env_logger::try_init();
600 /// let mut s = MaxString::try_from_str("My String")?;
601 /// s.try_push_str(" My other String")?;
602 /// assert_eq!(s.as_str(), "My String My other String");
603 ///
604 /// assert!(s.try_push_str("0".repeat(MaxString::capacity().into())).is_err());
605 /// # Ok(())
606 /// # }
607 /// ```
608 #[inline]
609 pub fn try_push_str<S>(&mut self, string: S) -> Result<(), OutOfBounds>
610 where
611 S: AsRef<str>,
612 {
613 trace!("Push str");
614 let new_end = string.as_ref().len().saturating_add(self.len().into());
615 is_inside_boundary(new_end, Self::capacity())?;
616 unsafe { self.push_str_unchecked(string) };
617 Ok(())
618 }
619
620 /// Pushes string slice to the end of the `ArrayString` truncating total size if bigger than [`capacity`].
621 ///
622 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
623 ///
624 /// ```rust
625 /// # use arraystring::{error::Error, prelude::*};
626 /// # fn main() -> Result<(), Error> {
627 /// # let _ = env_logger::try_init();
628 /// let mut s = MaxString::try_from_str("My String")?;
629 /// s.push_str(" My other String");
630 /// assert_eq!(s.as_str(), "My String My other String");
631 ///
632 /// let mut s = SmallString::default();
633 /// s.push_str("0".repeat(SmallString::capacity() as usize + 1));
634 /// assert_eq!(s.as_str(), "0".repeat(SmallString::capacity().into()).as_str());
635 /// # Ok(())
636 /// # }
637 /// ```
638 #[inline]
639 pub fn push_str<S>(&mut self, string: S)
640 where
641 S: AsRef<str>,
642 {
643 trace!("Push str truncate");
644 let size = Self::capacity().saturating_sub(self.len());
645 unsafe { self.push_str_unchecked(truncate_str(string.as_ref(), size)) }
646 }
647
648 /// Pushes string slice to the end of the `ArrayString` assuming total size is appropriate.
649 ///
650 /// # Safety
651 ///
652 /// It's UB if `self.len() + string.len()` > [`capacity`].
653 ///
654 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
655 ///
656 /// ```rust
657 /// # use arraystring::{error::Error, prelude::*};
658 /// # fn main() -> Result<(), Error> {
659 /// let mut s = MaxString::try_from_str("My String")?;
660 /// unsafe { s.push_str_unchecked(" My other String") };
661 /// assert_eq!(s.as_str(), "My String My other String");
662 ///
663 /// // Undefined behavior, don't do it
664 /// // let mut undefined_behavior = SmallString::default();
665 /// // undefined_behavior.push_str_unchecked("0".repeat(SmallString::capacity().into() + 1));
666 /// # Ok(())
667 /// # }
668 /// ```
669 #[inline]
670 pub unsafe fn push_str_unchecked<S>(&mut self, string: S)
671 where
672 S: AsRef<str>,
673 {
674 let (s, len) = (string.as_ref(), string.as_ref().len());
675 debug!("Push str unchecked: {} ({} + {})", s, self.len(), len);
676 debug_assert!(len.saturating_add(self.len().into()) <= Self::capacity() as usize);
677
678 let dest = self.as_mut_bytes().as_mut_ptr().add(self.len().into());
679 copy_nonoverlapping(s.as_ptr(), dest, len);
680 self.size = self.size.saturating_add(len.into_lossy());
681 }
682
683 /// Inserts character to the end of the `ArrayString` erroring if total size if bigger than [`capacity`].
684 ///
685 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
686 ///
687 /// ```rust
688 /// # use arraystring::{error::Error, prelude::*};
689 /// # fn main() -> Result<(), Error> {
690 /// # let _ = env_logger::try_init();
691 /// let mut s = SmallString::try_from_str("My String")?;
692 /// s.try_push('!')?;
693 /// assert_eq!(s.as_str(), "My String!");
694 ///
695 /// let mut s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
696 /// assert!(s.try_push('!').is_err());
697 /// # Ok(())
698 /// # }
699 /// ```
700 #[inline]
701 pub fn try_push(&mut self, character: char) -> Result<(), OutOfBounds> {
702 trace!("Push: {}", character);
703 let new_end = character.len_utf8().saturating_add(self.len().into());
704 is_inside_boundary(new_end, Self::capacity())?;
705 unsafe { self.push_unchecked(character) };
706 Ok(())
707 }
708
709 /// Inserts character to the end of the `ArrayString` assuming length is appropriate
710 ///
711 /// # Safety
712 ///
713 /// It's UB if `self.len() + character.len_utf8()` > [`capacity`]
714 ///
715 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
716 ///
717 /// ```rust
718 /// # use arraystring::{error::Error, prelude::*};
719 /// # fn main() -> Result<(), Error> {
720 /// let mut s = SmallString::try_from_str("My String")?;
721 /// unsafe { s.push_unchecked('!') };
722 /// assert_eq!(s.as_str(), "My String!");
723 ///
724 /// // s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
725 /// // Undefined behavior, don't do it
726 /// // s.push_unchecked('!');
727 /// # Ok(())
728 /// # }
729 /// ```
730 #[inline]
731 pub unsafe fn push_unchecked(&mut self, ch: char) {
732 let (len, chlen) = (self.len(), ch.len_utf8().into_lossy());
733 debug!("Push unchecked (len: {}): {} (len: {})", len, ch, chlen);
734 encode_char_utf8_unchecked(self, ch, len);
735 self.size = self.size.saturating_add(chlen);
736 }
737
738 /// Truncates `ArrayString` to specified size (if smaller than current size and a valid utf-8 char index).
739 ///
740 /// ```rust
741 /// # use arraystring::{error::Error, prelude::*};
742 /// # fn main() -> Result<(), Error> {
743 /// # let _ = env_logger::try_init();
744 /// let mut s = SmallString::try_from_str("My String")?;
745 /// s.truncate(5)?;
746 /// assert_eq!(s.as_str(), "My St");
747 ///
748 /// // Does nothing
749 /// s.truncate(6)?;
750 /// assert_eq!(s.as_str(), "My St");
751 ///
752 /// // Index is not at a valid char
753 /// let mut s = SmallString::try_from_str("🤔")?;
754 /// assert!(s.truncate(1).is_err());
755 /// # Ok(())
756 /// # }
757 /// ```
758 #[inline]
759 pub fn truncate(&mut self, size: u8) -> Result<(), Utf8> {
760 debug!("Truncate: {}", size);
761 let len = min(self.len(), size);
762 is_char_boundary(self, len).map(|()| self.size = len)
763 }
764
765 /// Removes last character from `ArrayString`, if any.
766 ///
767 /// ```rust
768 /// # use arraystring::{error::Error, prelude::*};
769 /// # fn main() -> Result<(), Error> {
770 /// # let _ = env_logger::try_init();
771 /// let mut s = SmallString::try_from_str("A🤔")?;
772 /// assert_eq!(s.pop(), Some('🤔'));
773 /// assert_eq!(s.pop(), Some('A'));
774 /// assert_eq!(s.pop(), None);
775 /// # Ok(())
776 /// # }
777 /// ```
778 #[inline]
779 pub fn pop(&mut self) -> Option<char> {
780 debug!("Pop");
781 self.as_str().chars().last().map(|ch| {
782 self.size = self.size.saturating_sub(ch.len_utf8().into_lossy());
783 ch
784 })
785 }
786
787 /// Removes spaces from the beggining and end of the string
788 ///
789 /// ```rust
790 /// # use arraystring::prelude::*;
791 /// # fn main() -> Result<(), OutOfBounds> {
792 /// # let _ = env_logger::try_init();
793 /// let mut string = MaxString::try_from_str(" to be trimmed ")?;
794 /// string.trim();
795 /// assert_eq!(string.as_str(), "to be trimmed");
796 ///
797 /// let mut string = SmallString::try_from_str(" 🤔")?;
798 /// string.trim();
799 /// assert_eq!(string.as_str(), "🤔");
800 /// # Ok(())
801 /// # }
802 /// ```
803 #[inline]
804 pub fn trim(&mut self) {
805 trace!("Trim");
806 let is_whitespace = |s: &[u8], index: usize| {
807 debug_assert!(index < s.len());
808 unsafe { s.get_unchecked(index) == &b' ' }
809 };
810 let (mut start, mut end, mut leave) = (0_u8, self.len(), 0_u8);
811 while start < end && leave < 2 {
812 leave = 0;
813
814 if is_whitespace(self.as_bytes(), start.into()) {
815 start = start.saturating_add(1);
816 if start >= end {
817 continue;
818 };
819 } else {
820 leave = leave.saturating_add(1);
821 }
822
823 if start < end && is_whitespace(self.as_bytes(), end.saturating_sub(1).into()) {
824 end = end.saturating_sub(1);
825 } else {
826 leave = leave.saturating_add(1);
827 }
828 }
829
830 unsafe { shift_left_unchecked(self, start, 0u8) };
831 self.size = end.saturating_sub(start);
832 }
833
834 /// Removes specified char from `ArrayString`
835 ///
836 /// ```rust
837 /// # use arraystring::{error::Error, prelude::*};
838 /// # fn main() -> Result<(), Error> {
839 /// # let _ = env_logger::try_init();
840 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
841 /// assert_eq!(s.remove("ABCD🤔".len() as u8), Err(Error::OutOfBounds));
842 /// assert_eq!(s.remove(10), Err(Error::OutOfBounds));
843 /// assert_eq!(s.remove(6), Err(Error::Utf8));
844 /// assert_eq!(s.remove(0), Ok('A'));
845 /// assert_eq!(s.as_str(), "BCD🤔");
846 /// assert_eq!(s.remove(2), Ok('D'));
847 /// assert_eq!(s.as_str(), "BC🤔");
848 /// # Ok(())
849 /// # }
850 /// ```
851 #[inline]
852 pub fn remove(&mut self, idx: u8) -> Result<char, Error> {
853 debug!("Remove: {}", idx);
854 is_inside_boundary(idx, self.len().saturating_sub(1))?;
855 is_char_boundary(self, idx)?;
856 debug_assert!(idx < self.len() && self.as_str().is_char_boundary(idx.into()));
857 let ch = unsafe { self.as_str().get_unchecked(idx.into()..).chars().next() };
858 let ch = ch.unwrap_or_else(|| unsafe { never("Missing char") });
859 unsafe { shift_left_unchecked(self, idx.saturating_add(ch.len_utf8().into_lossy()), idx) };
860 self.size = self.size.saturating_sub(ch.len_utf8().into_lossy());
861 Ok(ch)
862 }
863
864 /// Retains only the characters specified by the predicate.
865 ///
866 /// ```rust
867 /// # use arraystring::{error::Error, prelude::*};
868 /// # fn main() -> Result<(), Error> {
869 /// # let _ = env_logger::try_init();
870 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
871 /// s.retain(|c| c != '🤔');
872 /// assert_eq!(s.as_str(), "ABCD");
873 /// # Ok(())
874 /// # }
875 /// ```
876 #[inline]
877 pub fn retain<F: FnMut(char) -> bool>(&mut self, mut f: F) {
878 trace!("Retain");
879 // Not the most efficient solution, we could shift left during batch mismatch
880 *self = unsafe { Self::from_chars_unchecked(self.as_str().chars().filter(|c| f(*c))) };
881 }
882
883 /// Inserts character at specified index, returning error if total length is bigger than [`capacity`].
884 ///
885 /// Returns [`OutOfBounds`] if `idx` is out of bounds and [`Utf8`] if `idx` is not a char position
886 ///
887 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
888 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
889 /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
890 ///
891 /// ```rust
892 /// # use arraystring::{error::Error, prelude::*};
893 /// # fn main() -> Result<(), Error> {
894 /// # let _ = env_logger::try_init();
895 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
896 /// s.try_insert(1, 'A')?;
897 /// s.try_insert(2, 'B')?;
898 /// assert_eq!(s.as_str(), "AABBCD🤔");
899 /// assert_eq!(s.try_insert(20, 'C'), Err(Error::OutOfBounds));
900 /// assert_eq!(s.try_insert(8, 'D'), Err(Error::Utf8));
901 ///
902 /// let mut s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
903 /// assert_eq!(s.try_insert(0, 'C'), Err(Error::OutOfBounds));
904 /// # Ok(())
905 /// # }
906 /// ```
907 #[inline]
908 pub fn try_insert(&mut self, idx: u8, ch: char) -> Result<(), Error> {
909 trace!("Insert {} to {}", ch, idx);
910 is_inside_boundary(idx, self.len())?;
911 let new_end = ch.len_utf8().saturating_add(self.len().into());
912 is_inside_boundary(new_end, Self::capacity())?;
913 is_char_boundary(self, idx)?;
914 unsafe { self.insert_unchecked(idx, ch) };
915 Ok(())
916 }
917
918 /// Inserts character at specified index assuming length is appropriate
919 ///
920 /// # Safety
921 ///
922 /// It's UB if `idx` does not lie on a utf-8 `char` boundary
923 ///
924 /// It's UB if `self.len() + character.len_utf8()` > [`capacity`]
925 ///
926 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
927 ///
928 /// ```rust
929 /// # use arraystring::{error::Error, prelude::*};
930 /// # fn main() -> Result<(), Error> {
931 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
932 /// unsafe { s.insert_unchecked(1, 'A') };
933 /// unsafe { s.insert_unchecked(1, 'B') };
934 /// assert_eq!(s.as_str(), "ABABCD🤔");
935 ///
936 /// // Undefined behavior, don't do it
937 /// // s.insert(20, 'C');
938 /// // s.insert(8, 'D');
939 /// # Ok(())
940 /// # }
941 /// ```
942 #[inline]
943 pub unsafe fn insert_unchecked(&mut self, idx: u8, ch: char) {
944 let clen = ch.len_utf8().into_lossy();
945 debug!("Insert uncheck ({}+{}) {} at {}", self.len(), clen, ch, idx);
946 shift_right_unchecked(self, idx, idx.saturating_add(clen));
947 encode_char_utf8_unchecked(self, ch, idx);
948 self.size = self.size.saturating_add(clen);
949 }
950
951 /// Inserts string slice at specified index, returning error if total length is bigger than [`capacity`].
952 ///
953 /// Returns [`OutOfBounds`] if `idx` is out of bounds
954 /// Returns [`Utf8`] if `idx` is not a char position
955 ///
956 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
957 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
958 /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
959 ///
960 /// ```rust
961 /// # use arraystring::{error::Error, prelude::*};
962 /// # fn main() -> Result<(), Error> {
963 /// # let _ = env_logger::try_init();
964 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
965 /// s.try_insert_str(1, "AB")?;
966 /// s.try_insert_str(1, "BC")?;
967 /// assert_eq!(s.try_insert_str(1, "0".repeat(SmallString::capacity().into())),
968 /// Err(Error::OutOfBounds));
969 /// assert_eq!(s.as_str(), "ABCABBCD🤔");
970 /// assert_eq!(s.try_insert_str(20, "C"), Err(Error::OutOfBounds));
971 /// assert_eq!(s.try_insert_str(10, "D"), Err(Error::Utf8));
972 /// # Ok(())
973 /// # }
974 /// ```
975 #[inline]
976 pub fn try_insert_str<S>(&mut self, idx: u8, s: S) -> Result<(), Error>
977 where
978 S: AsRef<str>,
979 {
980 trace!("Try insert str");
981 is_inside_boundary(idx, self.len())?;
982 let new_end = s.as_ref().len().saturating_add(self.len().into());
983 is_inside_boundary(new_end, Self::capacity())?;
984 is_char_boundary(self, idx)?;
985 unsafe { self.insert_str_unchecked(idx, s.as_ref()) };
986 Ok(())
987 }
988
989 /// Inserts string slice at specified index, truncating size if bigger than [`capacity`].
990 ///
991 /// Returns [`OutOfBounds`] if `idx` is out of bounds and [`Utf8`] if `idx` is not a char position
992 ///
993 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
994 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
995 /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
996 ///
997 /// ```rust
998 /// # use arraystring::{error::Error, prelude::*};
999 /// # fn main() -> Result<(), Error> {
1000 /// # let _ = env_logger::try_init();
1001 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1002 /// s.insert_str(1, "AB")?;
1003 /// s.insert_str(1, "BC")?;
1004 /// assert_eq!(s.as_str(), "ABCABBCD🤔");
1005 ///
1006 /// assert_eq!(s.insert_str(20, "C"), Err(Error::OutOfBounds));
1007 /// assert_eq!(s.insert_str(10, "D"), Err(Error::Utf8));
1008 ///
1009 /// s.clear();
1010 /// s.insert_str(0, "0".repeat(SmallString::capacity() as usize + 10))?;
1011 /// assert_eq!(s.as_str(), "0".repeat(SmallString::capacity().into()).as_str());
1012 /// # Ok(())
1013 /// # }
1014 /// ```
1015 #[inline]
1016 pub fn insert_str<S>(&mut self, idx: u8, string: S) -> Result<(), Error>
1017 where
1018 S: AsRef<str>,
1019 {
1020 trace!("Insert str");
1021 is_inside_boundary(idx, self.len())?;
1022 is_char_boundary(self, idx)?;
1023 let size = Self::capacity().saturating_sub(self.len());
1024 unsafe { self.insert_str_unchecked(idx, truncate_str(string.as_ref(), size)) };
1025 Ok(())
1026 }
1027
1028 /// Inserts string slice at specified index, assuming total length is appropriate.
1029 ///
1030 /// # Safety
1031 ///
1032 /// It's UB if `idx` does not lie on a utf-8 `char` boundary
1033 ///
1034 /// It's UB if `self.len() + string.len()` > [`capacity`]
1035 ///
1036 /// [`capacity`]: ./struct.ArrayString.html#method.capacity
1037 ///
1038 /// ```rust
1039 /// # use arraystring::{error::Error, prelude::*};
1040 /// # fn main() -> Result<(), Error> {
1041 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1042 /// unsafe { s.insert_str_unchecked(1, "AB") };
1043 /// unsafe { s.insert_str_unchecked(1, "BC") };
1044 /// assert_eq!(s.as_str(), "ABCABBCD🤔");
1045 ///
1046 /// // Undefined behavior, don't do it
1047 /// // unsafe { s.insert_str_unchecked(20, "C") };
1048 /// // unsafe { s.insert_str_unchecked(10, "D") };
1049 /// // unsafe { s.insert_str_unchecked(1, "0".repeat(SmallString::capacity().into())) };
1050 /// # Ok(())
1051 /// # }
1052 /// ```
1053 #[inline]
1054 pub unsafe fn insert_str_unchecked<S>(&mut self, idx: u8, string: S)
1055 where
1056 S: AsRef<str>,
1057 {
1058 let (s, slen) = (string.as_ref(), string.as_ref().len().into_lossy());
1059 let ptr = s.as_ptr();
1060 trace!("InsertStr uncheck {}+{} {} at {}", self.len(), slen, s, idx);
1061 debug_assert!(self.len().saturating_add(slen) <= Self::capacity());
1062 debug_assert!(idx <= self.len());
1063 debug_assert!(self.as_str().is_char_boundary(idx.into()));
1064
1065 shift_right_unchecked(self, idx, idx.saturating_add(slen));
1066 let dest = self.as_mut_bytes().as_mut_ptr().add(idx.into());
1067 copy_nonoverlapping(ptr, dest, slen.into());
1068 self.size = self.size.saturating_add(slen);
1069 }
1070
1071 /// Returns `ArrayString` length.
1072 ///
1073 /// ```rust
1074 /// # use arraystring::{error::Error, prelude::*};
1075 /// # fn main() -> Result<(), Error> {
1076 /// # let _ = env_logger::try_init();
1077 /// let mut s = SmallString::try_from_str("ABCD")?;
1078 /// assert_eq!(s.len(), 4);
1079 /// s.try_push('🤔')?;
1080 /// // Emojis use 4 bytes (this is the default rust behavior, length of u8)
1081 /// assert_eq!(s.len(), 8);
1082 /// # Ok(())
1083 /// # }
1084 /// ```
1085 #[inline]
1086 pub fn len(&self) -> u8 {
1087 trace!("Len");
1088 self.size
1089 }
1090
1091 /// Checks if `ArrayString` is empty.
1092 ///
1093 /// ```rust
1094 /// # use arraystring::{error::Error, prelude::*};
1095 /// # fn main() -> Result<(), Error> {
1096 /// # let _ = env_logger::try_init();
1097 /// let mut s = SmallString::try_from_str("ABCD")?;
1098 /// assert!(!s.is_empty());
1099 /// s.clear();
1100 /// assert!(s.is_empty());
1101 /// # Ok(())
1102 /// # }
1103 /// ```
1104 #[inline]
1105 pub fn is_empty(&self) -> bool {
1106 trace!("Is empty");
1107 self.len() == 0
1108 }
1109
1110 /// Splits `ArrayString` in two if `at` is smaller than `self.len()`.
1111 ///
1112 /// Returns [`Utf8`] if `at` does not lie at a valid utf-8 char boundary and [`OutOfBounds`] if it's out of bounds
1113 ///
1114 /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
1115 /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
1116 ///
1117 /// ```rust
1118 /// # use arraystring::{error::Error, prelude::*};
1119 /// # fn main() -> Result<(), Error> {
1120 /// # let _ = env_logger::try_init();
1121 /// let mut s = SmallString::try_from_str("AB🤔CD")?;
1122 /// assert_eq!(s.split_off(6)?.as_str(), "CD");
1123 /// assert_eq!(s.as_str(), "AB🤔");
1124 /// assert_eq!(s.split_off(20), Err(Error::OutOfBounds));
1125 /// assert_eq!(s.split_off(4), Err(Error::Utf8));
1126 /// # Ok(())
1127 /// # }
1128 /// ```
1129 #[inline]
1130 pub fn split_off(&mut self, at: u8) -> Result<Self, Error> {
1131 debug!("Split off");
1132 is_inside_boundary(at, self.len())?;
1133 is_char_boundary(self, at)?;
1134 debug_assert!(at <= self.len() && self.as_str().is_char_boundary(at.into()));
1135 let new = unsafe { Self::from_utf8_unchecked(self.as_str().get_unchecked(at.into()..)) };
1136 self.size = at;
1137 Ok(new)
1138 }
1139
1140 /// Empties `ArrayString`
1141 ///
1142 /// ```rust
1143 /// # use arraystring::{error::Error, prelude::*};
1144 /// # fn main() -> Result<(), Error> {
1145 /// # let _ = env_logger::try_init();
1146 /// let mut s = SmallString::try_from_str("ABCD")?;
1147 /// assert!(!s.is_empty());
1148 /// s.clear();
1149 /// assert!(s.is_empty());
1150 /// # Ok(())
1151 /// # }
1152 /// ```
1153 #[inline]
1154 pub fn clear(&mut self) {
1155 trace!("Clear");
1156 self.size = 0;
1157 }
1158
1159 /// Creates a draining iterator that removes the specified range in the `ArrayString` and yields the removed chars.
1160 ///
1161 /// Note: The element range is removed even if the iterator is not consumed until the end.
1162 ///
1163 /// ```rust
1164 /// # use arraystring::{error::Error, prelude::*};
1165 /// # fn main() -> Result<(), Error> {
1166 /// # let _ = env_logger::try_init();
1167 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1168 /// assert_eq!(s.drain(..3)?.collect::<Vec<_>>(), vec!['A', 'B', 'C']);
1169 /// assert_eq!(s.as_str(), "D🤔");
1170 ///
1171 /// assert_eq!(s.drain(3..), Err(Error::Utf8));
1172 /// assert_eq!(s.drain(10..), Err(Error::OutOfBounds));
1173 /// # Ok(())
1174 /// # }
1175 /// ```
1176 #[inline]
1177 pub fn drain<R>(&mut self, range: R) -> Result<Drain<SIZE>, Error>
1178 where
1179 R: RangeBounds<u8>,
1180 {
1181 let start = match range.start_bound() {
1182 Bound::Included(t) => *t,
1183 Bound::Excluded(t) => t.saturating_add(1),
1184 Bound::Unbounded => 0,
1185 };
1186 let end = match range.end_bound() {
1187 Bound::Included(t) => t.saturating_add(1),
1188 Bound::Excluded(t) => *t,
1189 Bound::Unbounded => self.len(),
1190 };
1191
1192 debug!("Drain iterator (len: {}): {}..{}", self.len(), start, end);
1193 is_inside_boundary(start, end)?;
1194 is_inside_boundary(end, self.len())?;
1195 is_char_boundary(self, start)?;
1196 is_char_boundary(self, end)?;
1197 debug_assert!(start <= end && end <= self.len());
1198 debug_assert!(self.as_str().is_char_boundary(start.into()));
1199 debug_assert!(self.as_str().is_char_boundary(end.into()));
1200
1201 let drain = unsafe {
1202 let slice = self.as_str().get_unchecked(start.into()..end.into());
1203 Self::from_str_unchecked(slice)
1204 };
1205 unsafe { shift_left_unchecked(self, end, start) };
1206 self.size = self.size.saturating_sub(end.saturating_sub(start));
1207 Ok(Drain(drain, 0))
1208 }
1209
1210 /// Removes the specified range of the `ArrayString`, and replaces it with the given string. The given string doesn't need to have the same length as the range.
1211 ///
1212 /// ```rust
1213 /// # use arraystring::{error::Error, prelude::*};
1214 /// # fn main() -> Result<(), Error> {
1215 /// # let _ = env_logger::try_init();
1216 /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1217 /// s.replace_range(2..4, "EFGHI")?;
1218 /// assert_eq!(s.as_str(), "ABEFGHI🤔");
1219 ///
1220 /// assert_eq!(s.replace_range(9.., "J"), Err(Error::Utf8));
1221 /// assert_eq!(s.replace_range(..90, "K"), Err(Error::OutOfBounds));
1222 /// assert_eq!(s.replace_range(0..1, "0".repeat(SmallString::capacity().into())),
1223 /// Err(Error::OutOfBounds));
1224 /// # Ok(())
1225 /// # }
1226 /// ```
1227 #[inline]
1228 pub fn replace_range<S, R>(&mut self, r: R, with: S) -> Result<(), Error>
1229 where
1230 S: AsRef<str>,
1231 R: RangeBounds<u8>,
1232 {
1233 let replace_with = with.as_ref();
1234 let start = match r.start_bound() {
1235 Bound::Included(t) => *t,
1236 Bound::Excluded(t) => t.saturating_add(1),
1237 Bound::Unbounded => 0,
1238 };
1239 let end = match r.end_bound() {
1240 Bound::Included(t) => t.saturating_add(1),
1241 Bound::Excluded(t) => *t,
1242 Bound::Unbounded => self.len(),
1243 };
1244
1245 let len = replace_with.len().into_lossy();
1246 debug!(
1247 "Replace range (len: {}) ({}..{}) with (len: {}) {}",
1248 self.len(),
1249 start,
1250 end,
1251 len,
1252 replace_with
1253 );
1254
1255 is_inside_boundary(start, end)?;
1256 is_inside_boundary(end, self.len())?;
1257 let replaced = (end as usize).saturating_sub(start.into());
1258 is_inside_boundary(replaced.saturating_add(len.into()), Self::capacity())?;
1259 is_char_boundary(self, start)?;
1260 is_char_boundary(self, end)?;
1261
1262 debug_assert!(start <= end && end <= self.len());
1263 debug_assert!(len.saturating_sub(end).saturating_add(start) <= Self::capacity());
1264 debug_assert!(self.as_str().is_char_boundary(start.into()));
1265 debug_assert!(self.as_str().is_char_boundary(end.into()));
1266
1267 if start.saturating_add(len) > end {
1268 unsafe { shift_right_unchecked(self, end, start.saturating_add(len)) };
1269 } else {
1270 unsafe { shift_left_unchecked(self, end, start.saturating_add(len)) };
1271 }
1272
1273 let grow = len.saturating_sub(replaced.into_lossy());
1274 self.size = self.size.saturating_add(grow);
1275 let ptr = replace_with.as_ptr();
1276 let dest = unsafe { self.as_mut_bytes().as_mut_ptr().add(start.into()) };
1277 unsafe { copy_nonoverlapping(ptr, dest, len.into()) };
1278 Ok(())
1279 }
1280}