Skip to main content

trim_in_place/
lib.rs

1/*!
2# Trim in-place
3
4This crate is used for extending `String` in order to do in-place trimming.
5
6## Usage
7
8```rust
9use trim_in_place::TrimInPlace;
10
11let mut s = String::from(" 1234 abcd  ");
12
13s.trim_in_place();
14
15assert_eq!("1234 abcd", s);
16```
17
18## Benchmark
19
20```bash
21cargo bench
22```
23*/
24
25#![no_std]
26
27extern crate alloc;
28
29mod pattern;
30
31use alloc::string::String;
32use core::ptr::copy;
33
34pub use pattern::*;
35
36#[inline]
37fn move_to_front(string: &mut String, source: *const u8, len: usize) -> &str {
38    unsafe {
39        let v = string.as_mut_vec();
40
41        // SAFETY: `source` and `len` come from a valid subslice of this string.
42        // `ptr::copy` permits overlap, which is required for in-place trimming.
43        copy(source, v.as_mut_ptr(), len);
44
45        v.set_len(len);
46    }
47
48    string.as_str()
49}
50
51#[inline]
52fn set_len(string: &mut String, len: usize) -> &str {
53    unsafe {
54        // SAFETY: callers pass lengths produced by `str` trim methods, so they are always valid UTF-8 boundaries within the current string.
55        string.as_mut_vec().set_len(len);
56    }
57
58    string.as_str()
59}
60
61pub trait TrimInPlace {
62    /// Trims Unicode whitespace from both ends of this string without allocating a new string.
63    fn trim_in_place(&mut self) -> &str;
64
65    /// Trims Unicode whitespace from the start of this string without allocating a new string.
66    fn trim_start_in_place(&mut self) -> &str;
67
68    /// Trims Unicode whitespace from the end of this string without allocating a new string.
69    fn trim_end_in_place(&mut self) -> &str;
70
71    /// Trims ASCII whitespace from both ends of this string without allocating a new string.
72    fn trim_ascii_in_place(&mut self) -> &str;
73
74    /// Trims ASCII whitespace from the start of this string without allocating a new string.
75    fn trim_ascii_start_in_place(&mut self) -> &str;
76
77    /// Trims ASCII whitespace from the end of this string without allocating a new string.
78    fn trim_ascii_end_in_place(&mut self) -> &str;
79
80    /// Trims matching text from both ends of this string without allocating a new string.
81    /// For `&str` patterns, this removes repeated prefixes first and then repeated suffixes.
82    fn trim_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str;
83
84    /// Trims matching text from the start of this string without allocating a new string.
85    fn trim_start_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str;
86
87    /// Trims matching text from the end of this string without allocating a new string.
88    fn trim_end_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str;
89}
90
91impl TrimInPlace for String {
92    #[inline]
93    fn trim_in_place(&mut self) -> &str {
94        let (trimmed_str_start_pointer, trimmed_str_length) = {
95            let trimmed_str = self.trim();
96
97            (trimmed_str.as_ptr(), trimmed_str.len())
98        };
99
100        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
101    }
102
103    #[inline]
104    fn trim_start_in_place(&mut self) -> &str {
105        let (trimmed_str_start_pointer, trimmed_str_length) = {
106            let trimmed_str = self.trim_start();
107
108            (trimmed_str.as_ptr(), trimmed_str.len())
109        };
110
111        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
112    }
113
114    #[inline]
115    fn trim_end_in_place(&mut self) -> &str {
116        let trimmed_str_length = self.trim_end().len();
117
118        set_len(self, trimmed_str_length)
119    }
120
121    #[inline]
122    fn trim_ascii_in_place(&mut self) -> &str {
123        let (trimmed_str_start_pointer, trimmed_str_length) = {
124            let trimmed_str = self.trim_ascii();
125
126            (trimmed_str.as_ptr(), trimmed_str.len())
127        };
128
129        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
130    }
131
132    #[inline]
133    fn trim_ascii_start_in_place(&mut self) -> &str {
134        let (trimmed_str_start_pointer, trimmed_str_length) = {
135            let trimmed_str = self.trim_ascii_start();
136
137            (trimmed_str.as_ptr(), trimmed_str.len())
138        };
139
140        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
141    }
142
143    #[inline]
144    fn trim_ascii_end_in_place(&mut self) -> &str {
145        let trimmed_str_length = self.trim_ascii_end().len();
146
147        set_len(self, trimmed_str_length)
148    }
149
150    #[inline]
151    fn trim_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str {
152        let (trimmed_str_start_pointer, trimmed_str_length) = {
153            let trimmed_str = pat.trim_matches_from(self);
154
155            (trimmed_str.as_ptr(), trimmed_str.len())
156        };
157
158        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
159    }
160
161    #[inline]
162    fn trim_start_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str {
163        let (trimmed_str_start_pointer, trimmed_str_length) = {
164            let trimmed_str = pat.trim_start_matches_from(self);
165
166            (trimmed_str.as_ptr(), trimmed_str.len())
167        };
168
169        move_to_front(self, trimmed_str_start_pointer, trimmed_str_length)
170    }
171
172    #[inline]
173    fn trim_end_matches_in_place<P: Pattern>(&mut self, pat: P) -> &str {
174        let trimmed_str_length = pat.trim_end_matches_from(self).len();
175
176        set_len(self, trimmed_str_length)
177    }
178}