1use std::borrow::Cow;
24use std::fmt;
25use std::ops::{Deref, Index, RangeFull};
26use std::os::raw::c_char;
27use std::str;
28
29#[derive(Debug)]
31pub struct UiBuffer {
32 pub buffer: Vec<u8>,
33 pub max_len: usize,
34}
35
36impl UiBuffer {
37 pub const fn new(max_len: usize) -> Self {
39 Self {
40 buffer: Vec::new(),
41 max_len,
42 }
43 }
44
45 pub fn scratch_txt(&mut self, txt: impl AsRef<str>) -> *const std::os::raw::c_char {
47 self.refresh_buffer();
48
49 let start_of_substr = self.push(txt);
50 unsafe { self.offset(start_of_substr) }
51 }
52
53 pub fn scratch_txt_opt(&mut self, txt: Option<impl AsRef<str>>) -> *const std::os::raw::c_char {
55 match txt {
56 Some(v) => self.scratch_txt(v),
57 None => std::ptr::null(),
58 }
59 }
60
61 pub fn scratch_txt_two(
63 &mut self,
64 txt_0: impl AsRef<str>,
65 txt_1: impl AsRef<str>,
66 ) -> (*const std::os::raw::c_char, *const std::os::raw::c_char) {
67 self.refresh_buffer();
68
69 let first_offset = self.push(txt_0);
70 let second_offset = self.push(txt_1);
71
72 unsafe { (self.offset(first_offset), self.offset(second_offset)) }
73 }
74
75 pub fn scratch_txt_with_opt(
77 &mut self,
78 txt_0: impl AsRef<str>,
79 txt_1: Option<impl AsRef<str>>,
80 ) -> (*const std::os::raw::c_char, *const std::os::raw::c_char) {
81 match txt_1 {
82 Some(value) => self.scratch_txt_two(txt_0, value),
83 None => (self.scratch_txt(txt_0), std::ptr::null()),
84 }
85 }
86
87 pub fn refresh_buffer(&mut self) {
90 if self.buffer.len() > self.max_len {
91 self.buffer.clear();
92 }
93 }
94
95 pub unsafe fn offset(&self, pos: usize) -> *const std::os::raw::c_char {
101 unsafe { self.buffer.as_ptr().add(pos) as *const _ }
102 }
103
104 pub fn push(&mut self, txt: impl AsRef<str>) -> usize {
107 let len = self.buffer.len();
108 self.buffer.extend(txt.as_ref().as_bytes());
109 self.buffer.push(b'\0');
110
111 len
112 }
113}
114
115#[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)]
117pub struct ImString(pub(crate) Vec<u8>);
118
119impl ImString {
120 pub fn new<T: Into<String>>(value: T) -> ImString {
122 unsafe {
123 let mut s = ImString::from_utf8_unchecked(value.into().into_bytes());
124 s.refresh_len();
125 s
126 }
127 }
128
129 #[inline]
131 pub fn with_capacity(capacity: usize) -> ImString {
132 let mut v = Vec::with_capacity(capacity + 1);
133 v.push(b'\0');
134 ImString(v)
135 }
136
137 #[inline]
144 pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> ImString {
145 v.push(b'\0');
146 ImString(v)
147 }
148
149 #[inline]
156 pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> ImString {
157 ImString(v)
158 }
159
160 #[inline]
162 pub fn clear(&mut self) {
163 self.0.clear();
164 self.0.push(b'\0');
165 }
166
167 #[inline]
169 pub fn push(&mut self, ch: char) {
170 let mut buf = [0; 4];
171 self.push_str(ch.encode_utf8(&mut buf));
172 }
173
174 #[inline]
176 pub fn push_str(&mut self, string: &str) {
177 self.0.pop();
178 self.0.extend(string.bytes());
179 self.0.push(b'\0');
180 unsafe {
181 self.refresh_len();
182 }
183 }
184
185 #[inline]
187 pub fn capacity(&self) -> usize {
188 self.0.capacity() - 1
189 }
190
191 #[inline]
193 pub fn capacity_with_nul(&self) -> usize {
194 self.0.capacity()
195 }
196
197 pub fn reserve(&mut self, additional: usize) {
202 self.0.reserve(additional);
203 }
204
205 pub fn reserve_exact(&mut self, additional: usize) {
208 self.0.reserve_exact(additional);
209 }
210
211 #[inline]
213 pub fn as_ptr(&self) -> *const c_char {
214 self.0.as_ptr() as *const c_char
215 }
216
217 #[inline]
221 pub fn as_mut_ptr(&mut self) -> *mut c_char {
222 self.0.as_mut_ptr() as *mut c_char
223 }
224
225 pub unsafe fn refresh_len(&mut self) {
232 unsafe {
233 let mut len = 0;
236 let ptr = self.as_ptr() as *const u8;
237 while *ptr.add(len) != 0 {
238 len += 1;
239 }
240 self.0.set_len(len + 1);
241 }
242 }
243
244 pub fn len(&self) -> usize {
246 self.0.len().saturating_sub(1)
247 }
248
249 pub fn is_empty(&self) -> bool {
251 self.len() == 0
252 }
253
254 pub fn to_str(&self) -> &str {
256 unsafe { str::from_utf8_unchecked(&self.0[..self.len()]) }
257 }
258}
259
260impl Default for ImString {
261 fn default() -> Self {
262 ImString::with_capacity(0)
263 }
264}
265
266impl fmt::Display for ImString {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 fmt::Display::fmt(self.to_str(), f)
269 }
270}
271
272impl fmt::Debug for ImString {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 fmt::Debug::fmt(self.to_str(), f)
275 }
276}
277
278impl Deref for ImString {
279 type Target = str;
280 fn deref(&self) -> &str {
281 self.to_str()
282 }
283}
284
285impl AsRef<str> for ImString {
286 fn as_ref(&self) -> &str {
287 self.to_str()
288 }
289}
290
291impl From<String> for ImString {
292 fn from(s: String) -> ImString {
293 ImString::new(s)
294 }
295}
296
297impl From<&str> for ImString {
298 fn from(s: &str) -> ImString {
299 ImString::new(s)
300 }
301}
302
303impl Index<RangeFull> for ImString {
304 type Output = str;
305 fn index(&self, _index: RangeFull) -> &str {
306 self.to_str()
307 }
308}
309
310pub type ImStr<'a> = Cow<'a, str>;
312
313#[macro_export]
315macro_rules! im_str {
316 ($e:expr) => {{ $crate::ImString::new($e) }};
317}