Skip to main content

dear_imgui_rs/string/
im_string.rs

1use std::fmt;
2use std::ops::{Deref, Index, RangeFull};
3use std::os::raw::c_char;
4use std::str;
5
6/// A UTF-8 encoded, growable, implicitly nul-terminated string.
7#[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)]
8pub struct ImString(pub(crate) Vec<u8>);
9
10impl ImString {
11    /// Creates a new `ImString` from an existing string.
12    pub fn new<T: Into<String>>(value: T) -> ImString {
13        let value = value.into();
14        assert!(!value.contains('\0'), "ImString contained null byte");
15        unsafe {
16            let mut s = ImString::from_utf8_unchecked(value.into_bytes());
17            s.refresh_len();
18            s
19        }
20    }
21
22    /// Creates a new empty `ImString` with a particular capacity
23    #[inline]
24    pub fn with_capacity(capacity: usize) -> ImString {
25        let mut v = Vec::with_capacity(capacity + 1);
26        v.push(b'\0');
27        ImString(v)
28    }
29
30    /// Converts a vector of bytes to a `ImString` without checking that the string contains valid
31    /// UTF-8
32    ///
33    /// # Safety
34    ///
35    /// It is up to the caller to guarantee the vector contains valid UTF-8 and no null terminator.
36    #[inline]
37    pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> ImString {
38        v.push(b'\0');
39        ImString(v)
40    }
41
42    /// Converts a vector of bytes to a `ImString` without checking that the string contains valid
43    /// UTF-8
44    ///
45    /// # Safety
46    ///
47    /// It is up to the caller to guarantee the vector contains valid UTF-8 and a null terminator.
48    #[inline]
49    pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> ImString {
50        ImString(v)
51    }
52
53    /// Truncates this `ImString`, removing all contents
54    #[inline]
55    pub fn clear(&mut self) {
56        self.0.clear();
57        self.0.push(b'\0');
58    }
59
60    /// Appends the given character to the end of this `ImString`
61    #[inline]
62    pub fn push(&mut self, ch: char) {
63        let mut buf = [0; 4];
64        self.push_str(ch.encode_utf8(&mut buf));
65    }
66
67    /// Appends a given string slice to the end of this `ImString`
68    #[inline]
69    pub fn push_str(&mut self, string: &str) {
70        assert!(!string.contains('\0'), "ImString contained null byte");
71        self.0.pop();
72        self.0.extend(string.bytes());
73        self.0.push(b'\0');
74        unsafe {
75            self.refresh_len();
76        }
77    }
78
79    /// Returns the capacity of this `ImString` in bytes
80    #[inline]
81    pub fn capacity(&self) -> usize {
82        self.0.capacity() - 1
83    }
84
85    /// Returns the capacity of this `ImString` in bytes, including the implicit null byte
86    #[inline]
87    pub fn capacity_with_nul(&self) -> usize {
88        self.0.capacity()
89    }
90
91    /// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the
92    /// current length.
93    ///
94    /// The capacity may be increased by more than `additional` bytes.
95    pub fn reserve(&mut self, additional: usize) {
96        self.0.reserve(additional);
97    }
98
99    /// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the
100    /// current length
101    pub fn reserve_exact(&mut self, additional: usize) {
102        self.0.reserve_exact(additional);
103    }
104
105    /// Returns a raw pointer to the underlying buffer
106    #[inline]
107    pub fn as_ptr(&self) -> *const c_char {
108        self.0.as_ptr() as *const c_char
109    }
110
111    /// Returns a raw mutable pointer to the underlying buffer.
112    ///
113    /// If the underlying data is modified, `refresh_len` *must* be called afterwards.
114    #[inline]
115    pub fn as_mut_ptr(&mut self) -> *mut c_char {
116        self.0.as_mut_ptr() as *mut c_char
117    }
118
119    /// Ensures the internal buffer length matches the requested size (including the trailing NUL).
120    ///
121    /// This is primarily used to prepare the backing storage for C APIs that write into the buffer
122    /// using an explicit `BufSize` parameter (e.g. `InputText`).
123    pub(crate) fn ensure_buf_size(&mut self, buf_size: usize) {
124        if self.0.len() < buf_size {
125            self.0.resize(buf_size, 0);
126        } else if self.0.len() > buf_size {
127            self.0.truncate(buf_size);
128            if let Some(last) = self.0.last_mut() {
129                *last = 0;
130            } else {
131                self.0.push(0);
132            }
133        } else if let Some(last) = self.0.last_mut() {
134            *last = 0;
135        }
136    }
137
138    /// Refreshes the length of the string by searching for the null terminator
139    ///
140    /// # Safety
141    ///
142    /// This function is unsafe because it assumes the initialized bytes before the null terminator
143    /// contain valid UTF-8.
144    pub unsafe fn refresh_len(&mut self) {
145        if let Some(pos) = self.0.iter().position(|&b| b == 0) {
146            self.0.truncate(pos + 1);
147        } else {
148            self.0.push(0);
149        }
150    }
151
152    /// Returns the length of this `ImString` in bytes, excluding the null terminator
153    pub fn len(&self) -> usize {
154        self.0.len().saturating_sub(1)
155    }
156
157    /// Returns true if this `ImString` is empty
158    pub fn is_empty(&self) -> bool {
159        self.len() == 0
160    }
161
162    /// Converts to a string slice
163    pub fn to_str(&self) -> &str {
164        unsafe { str::from_utf8_unchecked(&self.0[..self.len()]) }
165    }
166}
167
168impl Default for ImString {
169    fn default() -> Self {
170        ImString::with_capacity(0)
171    }
172}
173
174impl fmt::Display for ImString {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        fmt::Display::fmt(self.to_str(), f)
177    }
178}
179
180impl fmt::Debug for ImString {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        fmt::Debug::fmt(self.to_str(), f)
183    }
184}
185
186impl Deref for ImString {
187    type Target = str;
188    fn deref(&self) -> &str {
189        self.to_str()
190    }
191}
192
193impl AsRef<str> for ImString {
194    fn as_ref(&self) -> &str {
195        self.to_str()
196    }
197}
198
199impl From<String> for ImString {
200    fn from(s: String) -> ImString {
201        ImString::new(s)
202    }
203}
204
205impl From<&str> for ImString {
206    fn from(s: &str) -> ImString {
207        ImString::new(s)
208    }
209}
210
211impl Index<RangeFull> for ImString {
212    type Output = str;
213    fn index(&self, _index: RangeFull) -> &str {
214        self.to_str()
215    }
216}