Skip to main content

dear_imgui_rs/string/
im_string.rs

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