smol_bytes/utf8_bytes_mut.rs
1use core::{borrow::Borrow, convert::Infallible, ops::RangeBounds, str};
2
3use super::{
4 BytesMut,
5 error::*,
6 utf8_buf::{Utf8Buf, Utf8BufMut, range_bounds_to_start_end, sealed, validate_char_boundary},
7 utf8_drain::Utf8Drain,
8 utf8_edit::{RawUtf8Storage, RetainGuard, replace_bytes as raw_replace_bytes},
9};
10use bytes::BufMut;
11
12mod cmp;
13mod fmt;
14mod from;
15mod iter;
16mod ops;
17
18#[cfg(feature = "arbitrary")]
19mod arbitrary;
20#[cfg(feature = "borsh")]
21mod borsh;
22#[cfg(feature = "quickcheck")]
23mod quickcheck;
24#[cfg(feature = "serde")]
25mod serde;
26
27#[cfg(feature = "pyo3")]
28mod python;
29
30#[cfg(feature = "wasm")]
31mod wasm;
32
33#[doc = "Growable mutable UTF-8 string with inline/heap storage."]
34#[doc = ""]
35#[doc = "Guarantees valid UTF-8. Split operations check char boundaries."]
36#[cfg_attr(not(feature = "wasm"), doc = "")]
37#[cfg_attr(not(feature = "wasm"), doc = "# Examples")]
38#[cfg_attr(not(feature = "wasm"), doc = "")]
39#[cfg_attr(not(feature = "wasm"), doc = "```")]
40#[cfg_attr(not(feature = "wasm"), doc = "use smol_bytes::Utf8BytesMut;")]
41#[cfg_attr(not(feature = "wasm"), doc = "")]
42#[cfg_attr(not(feature = "wasm"), doc = "let mut buf = Utf8BytesMut::new();")]
43#[cfg_attr(not(feature = "wasm"), doc = "buf.push_str(\"Hello\");")]
44#[cfg_attr(not(feature = "wasm"), doc = "buf.push(' ');")]
45#[cfg_attr(not(feature = "wasm"), doc = "buf.push_str(\"world!\");")]
46#[cfg_attr(
47 not(feature = "wasm"),
48 doc = "assert_eq!(buf.as_str(), \"Hello world!\");"
49)]
50#[cfg_attr(not(feature = "wasm"), doc = "```")]
51#[cfg_attr(feature = "wasm", doc = "")]
52#[cfg_attr(feature = "wasm", doc = "@example")]
53#[cfg_attr(feature = "wasm", doc = "```typescript")]
54#[cfg_attr(feature = "wasm", doc = "import { Utf8BytesMut } from 'smol-bytes';")]
55#[cfg_attr(feature = "wasm", doc = "const buf = new Utf8BytesMut();")]
56#[cfg_attr(feature = "wasm", doc = "buf.pushStr('hello world');")]
57#[cfg_attr(
58 feature = "wasm",
59 doc = "console.log(buf.toString()); // 'hello world'"
60)]
61#[cfg_attr(feature = "wasm", doc = "```")]
62#[derive(Clone)]
63#[cfg_attr(feature = "pyo3", ::pyo3::prelude::pyclass(from_py_object))]
64#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
65pub struct Utf8BytesMut {
66 pub(crate) inner: BytesMut,
67}
68
69impl Default for Utf8BytesMut {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl sealed::Sealed for Utf8BytesMut {}
76
77impl core::fmt::Write for Utf8BytesMut {
78 #[inline(always)]
79 fn write_str(&mut self, s: &str) -> core::fmt::Result {
80 self.push_str(s);
81 Ok(())
82 }
83}
84
85impl Utf8BytesMut {
86 /// Creates a new, empty `Utf8BytesMut`.
87 ///
88 /// # Examples
89 ///
90 /// ```
91 /// use smol_bytes::Utf8BytesMut;
92 ///
93 /// const EMPTY: Utf8BytesMut = Utf8BytesMut::new();
94 /// assert!(EMPTY.is_empty());
95 /// ```
96 #[inline(always)]
97 pub const fn new() -> Self {
98 Self {
99 inner: BytesMut::new(),
100 }
101 }
102
103 /// Validates owned mutable bytes as UTF-8 without discarding them on
104 /// failure.
105 pub fn from_utf8(bytes: BytesMut) -> Result<Self, FromUtf8Error<BytesMut>> {
106 match core::str::from_utf8(bytes.as_ref()) {
107 Ok(_) => Ok(Self { inner: bytes }),
108 Err(error) => Err(FromUtf8Error::new(bytes, error)),
109 }
110 }
111
112 /// Decodes owned bytes as UTF-8, replacing malformed sequences with
113 /// `U+FFFD`.
114 ///
115 /// Valid input keeps its original storage and current view. Invalid input is
116 /// converted through an owned [`String`](std::string::String).
117 ///
118 /// ```
119 /// use smol_bytes::{BytesMut, Utf8BytesMut};
120 ///
121 /// let text = Utf8BytesMut::from_utf8_lossy(BytesMut::from(&b"hi\xff"[..]));
122 /// assert_eq!(text.as_str(), "hi\u{FFFD}");
123 /// ```
124 pub fn from_utf8_lossy(bytes: BytesMut) -> Self {
125 match Self::from_utf8(bytes) {
126 Ok(value) => value,
127 Err(error) => Self::from(std::string::String::from_utf8_lossy(error.as_bytes()).into_owned()),
128 }
129 }
130
131 /// Creates a new `Utf8BytesMut` with the specified capacity.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use smol_bytes::Utf8BytesMut;
137 ///
138 /// let buf = Utf8BytesMut::with_capacity(100);
139 /// assert!(buf.capacity() >= 100);
140 /// ```
141 pub fn with_capacity(capacity: usize) -> Self {
142 Self {
143 inner: BytesMut::with_capacity(capacity),
144 }
145 }
146
147 /// Returns a reference to the inner `BytesMut`.
148 ///
149 /// Note: named `as_inner` to avoid confusion with `str::as_bytes`/
150 /// `String::as_bytes_mut`, which users might expect to return `&[u8]`.
151 #[inline(always)]
152 pub const fn as_inner(&self) -> &BytesMut {
153 &self.inner
154 }
155
156 /// Consumes `self` and returns the inner `BytesMut`.
157 #[inline(always)]
158 pub fn into_inner(self) -> BytesMut {
159 self.inner
160 }
161
162 /// Returns the contents as a string slice.
163 ///
164 /// # Examples
165 ///
166 /// ```
167 /// use smol_bytes::Utf8BytesMut;
168 ///
169 /// let buf = Utf8BytesMut::from("hello");
170 /// assert_eq!(buf.as_str(), "hello");
171 /// ```
172 #[inline(always)]
173 pub fn as_str(&self) -> &str {
174 // SAFETY: Utf8BytesMut guarantees valid UTF-8
175 unsafe { str::from_utf8_unchecked(self.inner.as_ref()) }
176 }
177
178 /// Returns the contents as a mutable string slice.
179 ///
180 /// Mutations must preserve UTF-8 validity and the byte length of edited
181 /// ranges, as required by [`prim@str`].
182 pub fn as_mut_str(&mut self) -> &mut str {
183 str::from_utf8_mut(self.inner.as_mut_slice()).expect("UTF-8 invariant")
184 }
185
186 /// Returns the length in bytes.
187 #[inline(always)]
188 pub fn len(&self) -> usize {
189 self.inner.len()
190 }
191
192 /// Returns `true` if the buffer has length 0.
193 #[cfg_attr(not(coverage), inline(always))]
194 pub fn is_empty(&self) -> bool {
195 self.inner.is_empty()
196 }
197
198 /// Returns the number of bytes the buffer can hold without reallocating.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use smol_bytes::Utf8BytesMut;
204 ///
205 /// let buf = Utf8BytesMut::with_capacity(128);
206 /// assert!(buf.capacity() >= 128);
207 /// ```
208 #[inline]
209 pub fn capacity(&self) -> usize {
210 self.inner.capacity()
211 }
212
213 /// Returns `true` if the underlying bytes are stored inline (≤ 62 bytes).
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use smol_bytes::Utf8BytesMut;
219 ///
220 /// let small = Utf8BytesMut::from("hi");
221 /// assert!(small.is_inline());
222 /// ```
223 #[inline]
224 pub fn is_inline(&self) -> bool {
225 self.inner.is_inline()
226 }
227
228 /// Returns `true` if the underlying bytes are heap-allocated.
229 ///
230 /// # Examples
231 ///
232 /// ```
233 /// use smol_bytes::Utf8BytesMut;
234 ///
235 /// let big = Utf8BytesMut::with_capacity(128);
236 /// assert!(big.is_heap());
237 /// ```
238 #[inline]
239 pub fn is_heap(&self) -> bool {
240 self.inner.is_heap()
241 }
242
243 /// Appends a character to the buffer.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use smol_bytes::Utf8BytesMut;
249 ///
250 /// let mut buf = Utf8BytesMut::new();
251 /// buf.push('a');
252 /// buf.push('b');
253 /// assert_eq!(buf.as_str(), "ab");
254 /// ```
255 pub fn push(&mut self, ch: char) {
256 <Self as Utf8BufMut>::push(self, ch)
257 }
258
259 /// Appends a character without allocation failure.
260 pub fn try_push(&mut self, ch: char) -> Result<(), Infallible> {
261 <Self as Utf8BufMut>::try_push(self, ch)
262 }
263
264 /// Appends a string slice to the buffer.
265 ///
266 /// # Examples
267 ///
268 /// ```
269 /// use smol_bytes::Utf8BytesMut;
270 ///
271 /// let mut buf = Utf8BytesMut::new();
272 /// buf.push_str("hello");
273 /// assert_eq!(buf.as_str(), "hello");
274 /// ```
275 pub fn push_str(&mut self, s: &str) {
276 <Self as Utf8BufMut>::push_str(self, s)
277 }
278
279 /// Appends a string slice without allocation failure.
280 pub fn try_push_str(&mut self, s: &str) -> Result<(), Infallible> {
281 <Self as Utf8BufMut>::try_push_str(self, s)
282 }
283
284 /// Clears the buffer, removing all contents.
285 pub fn clear(&mut self) {
286 <Self as Utf8BufMut>::clear(self)
287 }
288
289 /// Truncates the buffer to the specified length.
290 ///
291 /// # Panics
292 ///
293 /// Panics if `new_len` does not lie on a UTF-8 character boundary.
294 pub fn truncate(&mut self, new_len: usize) {
295 self
296 .try_truncate(new_len)
297 .expect("new_len must lie on a UTF-8 character boundary");
298 }
299
300 /// Truncates the buffer at a UTF-8 character boundary.
301 pub fn try_truncate(&mut self, new_len: usize) -> Result<(), Utf8IndexError> {
302 <Self as Utf8BufMut>::try_truncate(self, new_len)
303 }
304
305 /// Removes and returns the last Unicode scalar value.
306 ///
307 /// This operates on Unicode scalar values, not grapheme clusters, and never
308 /// allocates.
309 ///
310 /// ```
311 /// use smol_bytes::Utf8BytesMut;
312 ///
313 /// let mut text = Utf8BytesMut::from("aé");
314 /// assert_eq!(text.pop(), Some('é'));
315 /// assert_eq!(text.as_str(), "a");
316 /// ```
317 pub fn pop(&mut self) -> Option<char> {
318 let ch = self.as_str().chars().next_back()?;
319 self.truncate(self.len() - ch.len_utf8());
320 Some(ch)
321 }
322
323 /// Freezes this mutable value using the default shared immutable strategy.
324 ///
325 /// Heap-backed values retain their allocation. [`freeze_compact`](Self::freeze_compact)
326 /// also transfers existing heap storage without copying, including a short current view;
327 /// its compact strategy applies to later shortening or view operations.
328 ///
329 /// ```
330 /// use smol_bytes::Utf8BytesMut;
331 ///
332 /// let text = Utf8BytesMut::from("hello").freeze();
333 /// assert_eq!(text.as_str(), "hello");
334 /// ```
335 pub fn freeze(self) -> crate::Utf8Bytes {
336 crate::utf8_bytes::Utf8Bytes {
337 inner: self.inner.freeze(),
338 }
339 }
340
341 /// Freezes this mutable value using the shared immutable strategy.
342 pub fn freeze_shared(self) -> crate::shared::Utf8Bytes {
343 crate::utf8_bytes::Utf8Bytes {
344 inner: self.inner.freeze_with(),
345 }
346 }
347
348 /// Freezes this mutable value using the compact immutable strategy.
349 ///
350 /// Existing heap storage is transferred without copying. In particular, a
351 /// short heap view is not immediately canonicalized; the compact strategy
352 /// applies to subsequent shortening or view operations.
353 pub fn freeze_compact(self) -> crate::compact::Utf8Bytes {
354 crate::utf8_bytes::Utf8Bytes {
355 inner: self.inner.freeze_with(),
356 }
357 }
358
359 /// Reserves capacity for at least `additional` more bytes.
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use smol_bytes::Utf8BytesMut;
365 ///
366 /// let mut buf = Utf8BytesMut::new();
367 /// buf.reserve(100);
368 /// assert!(buf.capacity() >= 100);
369 /// ```
370 pub fn reserve(&mut self, additional: usize) {
371 self.inner.reserve(additional);
372 }
373
374 /// Attempts to reclaim consumed storage for `additional` more bytes without
375 /// allocating.
376 ///
377 /// Returns `true` when the inner [`BytesMut`](crate::BytesMut) can provide
378 /// that writable space, and leaves this value unchanged on `false`.
379 ///
380 /// ```
381 /// use smol_bytes::Utf8BytesMut;
382 ///
383 /// let mut text = Utf8BytesMut::with_capacity(64);
384 /// text.push_str("abc");
385 /// assert!(text.try_reclaim(61));
386 /// assert_eq!(text.as_str(), "abc");
387 /// ```
388 pub fn try_reclaim(&mut self, additional: usize) -> bool {
389 self.inner.try_reclaim(additional)
390 }
391
392 /// Copies the current heap view into inline storage when it fits.
393 ///
394 /// This forwards to [`BytesMut::try_make_inline`](crate::BytesMut::try_make_inline)
395 /// and preserves UTF-8 validity.
396 ///
397 /// ```
398 /// use smol_bytes::{Utf8BytesMut, INLINE_CAP};
399 ///
400 /// let mut text = Utf8BytesMut::with_capacity(128);
401 /// text.push_str("café");
402 /// assert!(text.try_make_inline());
403 /// assert_eq!(text.capacity(), INLINE_CAP);
404 /// ```
405 pub fn try_make_inline(&mut self) -> bool {
406 self.inner.try_make_inline()
407 }
408
409 /// Inserts `s` at a UTF-8 character boundary.
410 ///
411 /// # Panics
412 ///
413 /// Panics if `idx` is invalid or growth overflows the address space.
414 pub fn insert_str(&mut self, idx: usize, s: &str) {
415 self
416 .try_insert_str(idx, s)
417 .unwrap_or_else(|error| panic!("insert_str failed: {error}"));
418 }
419
420 /// Tries to insert `s` at a UTF-8 character boundary.
421 ///
422 /// Invalid indices leave the buffer unchanged. Growth follows
423 /// [`BytesMut::reserve`](crate::BytesMut::reserve) behavior.
424 pub fn try_insert_str(&mut self, idx: usize, s: &str) -> Result<(), Utf8IndexError> {
425 validate_char_boundary(self.as_str(), idx)?;
426 self.replace_range_validated(idx, idx, s);
427 Ok(())
428 }
429
430 /// Inserts a Unicode scalar value at a UTF-8 character boundary.
431 ///
432 /// # Panics
433 ///
434 /// Panics if `idx` is invalid or growth overflows the address space.
435 pub fn insert(&mut self, idx: usize, ch: char) {
436 self
437 .try_insert(idx, ch)
438 .unwrap_or_else(|error| panic!("insert failed: {error}"));
439 }
440
441 /// Tries to insert a Unicode scalar value at a UTF-8 character boundary.
442 pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), Utf8IndexError> {
443 let mut encoded = [0_u8; 4];
444 self.try_insert_str(idx, ch.encode_utf8(&mut encoded))
445 }
446
447 /// Replaces a UTF-8 character-boundary range with `replacement`.
448 ///
449 /// # Panics
450 ///
451 /// Panics if the range is invalid or growth overflows the address space.
452 pub fn replace_range(&mut self, range: impl RangeBounds<usize>, replacement: &str) {
453 self
454 .try_replace_range(range, replacement)
455 .unwrap_or_else(|error| panic!("replace_range failed: {error}"));
456 }
457
458 /// Tries to replace a UTF-8 character-boundary range with `replacement`.
459 ///
460 /// Invalid ranges leave the buffer unchanged. Growth follows
461 /// [`BytesMut::reserve`](crate::BytesMut::reserve) behavior.
462 pub fn try_replace_range(
463 &mut self,
464 range: impl RangeBounds<usize>,
465 replacement: &str,
466 ) -> Result<(), Utf8IndexError> {
467 let (start, end) = range_bounds_to_start_end(self.as_str(), range)?;
468 self.replace_range_validated(start, end, replacement);
469 Ok(())
470 }
471
472 /// Appends a copy of the specified UTF-8 character-boundary range.
473 ///
474 /// # Panics
475 ///
476 /// Panics if the range is invalid or growing the buffer overflows the
477 /// address space.
478 pub fn extend_from_within(&mut self, range: impl RangeBounds<usize>) {
479 self
480 .try_extend_from_within(range)
481 .unwrap_or_else(|error| panic!("extend_from_within failed: {error}"));
482 }
483
484 /// Tries to append a copy of the specified UTF-8 character-boundary range.
485 ///
486 /// The range is normalized and both boundaries are validated before this
487 /// growable buffer reserves storage.
488 pub fn try_extend_from_within(
489 &mut self,
490 range: impl RangeBounds<usize>,
491 ) -> Result<(), Utf8IndexError> {
492 let old_len = self.len();
493 let (start, end) = range_bounds_to_start_end(self.as_str(), range)?;
494 let count = end - start;
495 let new_len = old_len.checked_add(count).expect("buffer length overflow");
496 if count == 0 {
497 return Ok(());
498 }
499
500 self.inner.resize(new_len, 0);
501 self.inner.as_mut_slice().copy_within(start..end, old_len);
502 Ok(())
503 }
504
505 fn replace_range_validated(&mut self, start: usize, end: usize, replacement: &str) {
506 let mut storage = RawUtf8Storage::growable(&mut self.inner);
507 raw_replace_bytes(&mut storage, start, end, replacement.as_bytes());
508 }
509
510 /// Removes and returns the Unicode scalar value at `idx`.
511 ///
512 /// # Panics
513 ///
514 /// Panics if `idx` is outside the visible string or is not a UTF-8
515 /// character boundary.
516 pub fn remove(&mut self, idx: usize) -> char {
517 self
518 .try_remove(idx)
519 .unwrap_or_else(|error| panic!("remove failed: {error}"))
520 }
521
522 /// Tries to remove and return the Unicode scalar value at `idx`.
523 pub fn try_remove(&mut self, idx: usize) -> Result<char, Utf8IndexError> {
524 let len = self.len();
525 if idx >= len {
526 return Err(OutOfBounds::new(idx, len).into());
527 }
528 validate_char_boundary(self.as_str(), idx)?;
529 let ch = self.as_str()[idx..]
530 .chars()
531 .next()
532 .expect("index below string length has a scalar");
533 self.replace_range_validated(idx, idx + ch.len_utf8(), "");
534 Ok(ch)
535 }
536
537 /// Retains only the characters for which `predicate` returns `true`.
538 ///
539 /// The predicate runs once per character in original order without
540 /// allocating. If it panics, decisions already returned from the predicate
541 /// are kept, while the current character and unvisited suffix remain.
542 pub fn retain<F>(&mut self, mut predicate: F)
543 where
544 F: FnMut(char) -> bool,
545 {
546 let mut guard = RetainGuard::new(RawUtf8Storage::growable(&mut self.inner));
547 guard.retain(&mut predicate);
548 }
549
550 /// Returns an iterator over a range that is removed when the iterator drops.
551 ///
552 /// # Panics
553 ///
554 /// Panics if the range is out of bounds or not on UTF-8 character boundaries.
555 pub fn drain(&mut self, range: impl RangeBounds<usize>) -> Utf8Drain<'_> {
556 self.try_drain(range).expect("drain range failed")
557 }
558
559 /// Tries to return an iterator over a range removed when it drops.
560 ///
561 /// The selected range is not removed eagerly. Dropping this iterator removes
562 /// the entire original range, even if it was only partly consumed.
563 pub fn try_drain(
564 &mut self,
565 range: impl RangeBounds<usize>,
566 ) -> Result<Utf8Drain<'_>, Utf8IndexError> {
567 let (start, end) = range_bounds_to_start_end(self.as_str(), range)?;
568 Ok(Utf8Drain::new(
569 RawUtf8Storage::growable(&mut self.inner),
570 start,
571 end,
572 ))
573 }
574
575 /// Splits the buffer at the given index.
576 ///
577 /// # Panics
578 ///
579 /// Panics if `at` is not on a character boundary or is out of bounds.
580 ///
581 /// # Examples
582 ///
583 /// ```
584 /// use smol_bytes::Utf8BytesMut;
585 ///
586 /// let mut buf = Utf8BytesMut::from("hello world");
587 /// let head = buf.split_to(5);
588 ///
589 /// assert_eq!(head.as_str(), "hello");
590 /// assert_eq!(buf.as_str(), " world");
591 /// ```
592 pub fn split_to(&mut self, at: usize) -> Self {
593 self.try_split_to(at).expect("split_to failed")
594 }
595
596 /// Tries to split the buffer at the given index.
597 ///
598 /// # Errors
599 ///
600 /// Returns an error if `at` is out of bounds or not on a character boundary.
601 pub fn try_split_to(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
602 <Self as Utf8Buf>::try_split_to(self, at)
603 }
604
605 /// Splits the buffer at the given index, returning the tail.
606 ///
607 /// # Panics
608 ///
609 /// Panics if `at` is not on a character boundary or is out of bounds.
610 ///
611 /// # Examples
612 ///
613 /// ```
614 /// use smol_bytes::Utf8BytesMut;
615 ///
616 /// let mut buf = Utf8BytesMut::from("hello world");
617 /// let tail = buf.split_off(6);
618 ///
619 /// assert_eq!(buf.as_str(), "hello ");
620 /// assert_eq!(tail.as_str(), "world");
621 /// ```
622 pub fn split_off(&mut self, at: usize) -> Self {
623 self.try_split_off(at).expect("split_off failed")
624 }
625
626 /// Tries to split the buffer at the given index, returning the tail.
627 ///
628 /// # Errors
629 ///
630 /// Returns an error if `at` is out of bounds or not on a character boundary.
631 pub fn try_split_off(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
632 <Self as Utf8Buf>::try_split_off(self, at)
633 }
634
635 /// Removes all bytes from the buffer and returns them in a new buffer.
636 ///
637 /// Afterwards, `self` will be empty, retaining any additional capacity
638 /// it had before the split (see [`bytes::BytesMut::split`]).
639 ///
640 /// # Examples
641 ///
642 /// ```
643 /// use smol_bytes::Utf8BytesMut;
644 ///
645 /// let mut buf = Utf8BytesMut::with_capacity(128);
646 /// buf.push_str("hello");
647 /// let data = buf.split();
648 ///
649 /// assert_eq!(data.as_str(), "hello");
650 /// assert_eq!(buf.len(), 0);
651 /// ```
652 pub fn split(&mut self) -> Self {
653 let inner = self.inner.split();
654 Self { inner }
655 }
656
657 /// Merges another buffer back into this one.
658 ///
659 /// Heap-backed contiguous halves merge without copying. Inline and mixed
660 /// pairs append their contents, promoting storage when necessary.
661 ///
662 /// # Examples
663 ///
664 /// ```
665 /// use smol_bytes::Utf8BytesMut;
666 ///
667 /// let mut buf = Utf8BytesMut::with_capacity(128);
668 /// buf.push_str("hello world");
669 /// let tail = buf.split_off(5);
670 /// buf.unsplit(tail);
671 /// assert_eq!(buf.as_str(), "hello world");
672 /// ```
673 ///
674 /// Inline buffers append too:
675 ///
676 /// ```
677 /// use smol_bytes::Utf8BytesMut;
678 ///
679 /// let mut buf = Utf8BytesMut::from("hi"); // inline
680 /// let tail = Utf8BytesMut::from("!");
681 /// buf.unsplit(tail);
682 /// assert_eq!(buf.as_str(), "hi!");
683 /// ```
684 pub fn unsplit(&mut self, other: Self) {
685 self.inner.unsplit(other.inner)
686 }
687
688 /// Returns a slice of this buffer.
689 ///
690 /// # Panics
691 ///
692 /// Panics if the range is not on character boundaries or is out of bounds.
693 ///
694 /// # Examples
695 ///
696 /// ```
697 /// use smol_bytes::Utf8BytesMut;
698 ///
699 /// let buf = Utf8BytesMut::from("hello world");
700 /// let slice = buf.slice(0..5);
701 ///
702 /// assert_eq!(slice.as_str(), "hello");
703 /// ```
704 pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
705 self.try_slice(range).expect("slice failed")
706 }
707
708 /// Tries to return a slice of this buffer.
709 ///
710 /// # Errors
711 ///
712 /// Returns an error if the range is out of bounds or not on character boundaries.
713 pub fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, Utf8IndexError> {
714 <Self as Utf8Buf>::try_slice(self, range)
715 }
716}
717
718impl AsRef<str> for Utf8BytesMut {
719 fn as_ref(&self) -> &str {
720 self.as_str()
721 }
722}
723
724impl AsRef<[u8]> for Utf8BytesMut {
725 fn as_ref(&self) -> &[u8] {
726 self.as_str().as_bytes()
727 }
728}
729
730impl Borrow<str> for Utf8BytesMut {
731 fn borrow(&self) -> &str {
732 self.as_str()
733 }
734}
735
736impl core::ops::Deref for Utf8BytesMut {
737 type Target = str;
738
739 fn deref(&self) -> &Self::Target {
740 self.as_str()
741 }
742}
743
744#[cfg(feature = "std")]
745impl AsRef<std::ffi::OsStr> for Utf8BytesMut {
746 fn as_ref(&self) -> &std::ffi::OsStr {
747 std::ffi::OsStr::new(self.as_str())
748 }
749}
750
751#[cfg(feature = "std")]
752impl AsRef<std::path::Path> for Utf8BytesMut {
753 fn as_ref(&self) -> &std::path::Path {
754 std::path::Path::new(self.as_str())
755 }
756}
757
758// The trait impls hold the real bodies. Inherent methods (above) forward
759// here via explicit `<Self as Utf8Buf>::method` / `<Self as Utf8BufMut>::method`
760// UFCS so there is no ambiguity about which impl is called and no fragile
761// reliance on the inherent-over-trait method resolution rule.
762impl Utf8Buf for Utf8BytesMut {
763 fn try_split_to(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
764 validate_char_boundary(self.as_str(), at)?;
765 let inner = self.inner.split_to(at);
766 Ok(Self { inner })
767 }
768
769 fn try_split_off(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
770 validate_char_boundary(self.as_str(), at)?;
771 let inner = self.inner.split_off(at);
772 Ok(Self { inner })
773 }
774
775 fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, Utf8IndexError> {
776 let (start, end) = range_bounds_to_start_end(self.as_str(), range)?;
777 Ok(Self {
778 inner: BytesMut::from(&self.inner.as_slice()[start..end]),
779 })
780 }
781}
782
783impl Utf8BufMut for Utf8BytesMut {
784 type CapacityError = Infallible;
785
786 fn try_truncate(&mut self, new_len: usize) -> Result<(), Utf8IndexError> {
787 if new_len >= self.len() {
788 return Ok(());
789 }
790 if !self.as_str().is_char_boundary(new_len) {
791 return Err(Utf8IndexError::InvalidCharBoundary { index: new_len });
792 }
793 self.inner.truncate(new_len);
794 Ok(())
795 }
796
797 fn try_push(&mut self, ch: char) -> Result<(), Self::CapacityError> {
798 let mut buf = [0u8; 4];
799 let s = ch.encode_utf8(&mut buf);
800 self.inner.put_slice(s.as_bytes());
801 Ok(())
802 }
803
804 fn try_push_str(&mut self, s: &str) -> Result<(), Self::CapacityError> {
805 self.inner.put_slice(s.as_bytes());
806 Ok(())
807 }
808
809 fn clear(&mut self) {
810 self.inner.clear();
811 }
812}