1use std::borrow::{Borrow, Cow};
2use std::ffi::CStr;
3use std::ops::{Deref, Index, RangeFull};
4use std::os::raw::c_char;
5use std::str;
6use std::{fmt, ptr};
7
8#[derive(Debug)]
10pub(crate) struct UiBuffer {
11 buffer: Vec<u8>,
12 max_len: usize,
13}
14
15impl UiBuffer {
16 pub fn new(max_len: usize) -> Self {
18 Self {
19 buffer: Vec::with_capacity(max_len),
20 max_len,
21 }
22 }
23
24 pub fn scratch_txt(&mut self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
26 self.refresh_buffer();
27 self.push(txt)
28 }
29
30 pub fn scratch_txt_opt(&mut self, txt: Option<impl AsRef<str>>) -> *const sys::cty::c_char {
32 match txt {
33 Some(v) => self.scratch_txt(v),
34 None => ptr::null(),
35 }
36 }
37
38 pub fn scratch_txt_two(
39 &mut self,
40 txt_0: impl AsRef<str>,
41 txt_1: impl AsRef<str>,
42 ) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
43 self.refresh_buffer();
44 (self.push(txt_0), self.push(txt_1))
45 }
46
47 pub fn scratch_txt_with_opt(
48 &mut self,
49 txt_0: impl AsRef<str>,
50 txt_1: Option<impl AsRef<str>>,
51 ) -> (*const sys::cty::c_char, *const sys::cty::c_char) {
52 match txt_1 {
53 Some(value) => self.scratch_txt_two(txt_0, value),
54 None => (self.scratch_txt(txt_0), ptr::null()),
55 }
56 }
57
58 pub fn refresh_buffer(&mut self) {
60 if self.buffer.len() > self.max_len {
61 self.buffer.clear();
62 }
63 }
64
65 pub fn push(&mut self, txt: impl AsRef<str>) -> *const sys::cty::c_char {
67 unsafe {
68 let len = self.buffer.len();
69 self.buffer.extend(txt.as_ref().as_bytes());
70 self.buffer.push(b'\0');
71
72 self.buffer.as_ptr().add(len) as *const _
73 }
74 }
75}
76
77#[macro_export]
78#[deprecated = "all functions take AsRef<str> now -- use inline strings or `format` instead"]
79macro_rules! im_str {
80 ($e:literal $(,)?) => {{
81 const __INPUT: &str = concat!($e, "\0");
82 {
83 const _CHECK_NUL: [(); 0] = [(); {
85 let bytes = __INPUT.as_bytes();
86 let mut i = 0;
87 let mut found_nul = 0;
88 while i < bytes.len() - 1 && found_nul == 0 {
89 if bytes[i] == 0 {
90 found_nul = 1;
91 }
92 i += 1;
93 }
94 found_nul
95 }];
96 const RESULT: &'static $crate::ImStr = unsafe {
97 $crate::__core::mem::transmute::<&'static [u8], &'static $crate::ImStr>(__INPUT.as_bytes())
98 };
99 RESULT
100 }
101 }};
102 ($e:literal, $($arg:tt)+) => ({
103 $crate::ImString::new(format!($e, $($arg)*))
104 });
105}
106
107#[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)]
109pub struct ImString(pub(crate) Vec<u8>);
110
111impl ImString {
112 pub fn new<T: Into<String>>(value: T) -> ImString {
114 unsafe {
115 let mut s = ImString::from_utf8_unchecked(value.into().into_bytes());
116 s.refresh_len();
117 s
118 }
119 }
120
121 #[inline]
123 pub fn with_capacity(capacity: usize) -> ImString {
124 let mut v = Vec::with_capacity(capacity + 1);
125 v.push(b'\0');
126 ImString(v)
127 }
128
129 #[inline]
136 pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> ImString {
137 v.push(b'\0');
138 ImString(v)
139 }
140
141 #[inline]
148 pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> ImString {
149 ImString(v)
150 }
151
152 #[inline]
154 pub fn clear(&mut self) {
155 self.0.clear();
156 self.0.push(b'\0');
157 }
158
159 #[inline]
161 pub fn push(&mut self, ch: char) {
162 let mut buf = [0; 4];
163 self.push_str(ch.encode_utf8(&mut buf));
164 }
165
166 #[inline]
168 pub fn push_str(&mut self, string: &str) {
169 self.0.pop();
170 self.0.extend(string.bytes());
171 self.0.push(b'\0');
172 unsafe {
173 self.refresh_len();
174 }
175 }
176
177 #[inline]
179 pub fn capacity(&self) -> usize {
180 self.0.capacity() - 1
181 }
182
183 #[inline]
185 pub fn capacity_with_nul(&self) -> usize {
186 self.0.capacity()
187 }
188
189 pub fn reserve(&mut self, additional: usize) {
194 self.0.reserve(additional);
195 }
196
197 pub fn reserve_exact(&mut self, additional: usize) {
200 self.0.reserve_exact(additional);
201 }
202
203 #[inline]
205 pub fn as_ptr(&self) -> *const c_char {
206 self.0.as_ptr() as *const c_char
207 }
208
209 #[inline]
213 pub fn as_mut_ptr(&mut self) -> *mut c_char {
214 self.0.as_mut_ptr() as *mut c_char
215 }
216
217 #[inline]
227 pub unsafe fn refresh_len(&mut self) {
228 let len = CStr::from_ptr(self.0.as_ptr() as *const c_char)
229 .to_bytes_with_nul()
230 .len();
231 self.0.set_len(len);
232 }
233}
234
235impl<'a> Default for ImString {
236 #[inline]
237 fn default() -> ImString {
238 ImString(vec![b'\0'])
239 }
240}
241
242impl From<String> for ImString {
243 #[inline]
244 fn from(s: String) -> ImString {
245 ImString::new(s)
246 }
247}
248
249impl<'a> From<ImString> for Cow<'a, ImStr> {
250 #[inline]
251 fn from(s: ImString) -> Cow<'a, ImStr> {
252 Cow::Owned(s)
253 }
254}
255
256impl<'a> From<&'a ImString> for Cow<'a, ImStr> {
257 #[inline]
258 fn from(s: &'a ImString) -> Cow<'a, ImStr> {
259 Cow::Borrowed(s)
260 }
261}
262
263impl<'a, T: ?Sized + AsRef<ImStr>> From<&'a T> for ImString {
264 #[inline]
265 fn from(s: &'a T) -> ImString {
266 s.as_ref().to_owned()
267 }
268}
269
270impl AsRef<ImStr> for ImString {
271 #[inline]
272 fn as_ref(&self) -> &ImStr {
273 self
274 }
275}
276
277impl Borrow<ImStr> for ImString {
278 #[inline]
279 fn borrow(&self) -> &ImStr {
280 self
281 }
282}
283
284impl AsRef<str> for ImString {
285 #[inline]
286 fn as_ref(&self) -> &str {
287 self.to_str()
288 }
289}
290
291impl Borrow<str> for ImString {
292 #[inline]
293 fn borrow(&self) -> &str {
294 self.to_str()
295 }
296}
297
298impl Index<RangeFull> for ImString {
299 type Output = ImStr;
300 #[inline]
301 fn index(&self, _index: RangeFull) -> &ImStr {
302 self
303 }
304}
305
306impl fmt::Debug for ImString {
307 #[inline]
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 fmt::Debug::fmt(self.to_str(), f)
310 }
311}
312
313impl fmt::Display for ImString {
314 #[inline]
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 fmt::Display::fmt(self.to_str(), f)
317 }
318}
319
320impl Deref for ImString {
321 type Target = ImStr;
322 #[inline]
323 fn deref(&self) -> &ImStr {
324 unsafe {
328 &*(CStr::from_ptr(self.0.as_ptr() as *const c_char) as *const CStr as *const ImStr)
329 }
330 }
331}
332
333impl fmt::Write for ImString {
334 #[inline]
335 fn write_str(&mut self, s: &str) -> fmt::Result {
336 self.push_str(s);
337 Ok(())
338 }
339
340 #[inline]
341 fn write_char(&mut self, c: char) -> fmt::Result {
342 self.push(c);
343 Ok(())
344 }
345}
346
347#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
349#[repr(transparent)]
350pub struct ImStr([u8]);
351
352impl<'a> Default for &'a ImStr {
353 #[inline]
354 fn default() -> &'a ImStr {
355 static SLICE: &[u8] = &[0];
356 unsafe { ImStr::from_utf8_with_nul_unchecked(SLICE) }
357 }
358}
359
360impl fmt::Debug for ImStr {
361 #[inline]
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 fmt::Debug::fmt(&self.0, f)
364 }
365}
366
367impl fmt::Display for ImStr {
368 #[inline]
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 fmt::Display::fmt(self.to_str(), f)
371 }
372}
373
374impl ImStr {
375 #[inline]
382 pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a ImStr {
383 ImStr::from_cstr_unchecked(CStr::from_ptr(ptr))
384 }
385
386 #[inline]
393 pub unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &ImStr {
394 &*(bytes as *const [u8] as *const ImStr)
395 }
396
397 #[inline]
403 pub unsafe fn from_cstr_unchecked(value: &CStr) -> &ImStr {
404 &*(value.to_bytes_with_nul() as *const [u8] as *const ImStr)
405 }
406
407 #[inline]
409 pub fn as_ptr(&self) -> *const c_char {
410 self.0.as_ptr() as *const c_char
411 }
412
413 #[inline]
415 pub fn to_str(&self) -> &str {
416 self.sanity_check();
417 unsafe { str::from_utf8_unchecked(&self.0[..(self.0.len() - 1)]) }
418 }
419
420 #[inline]
422 pub fn is_empty(&self) -> bool {
423 debug_assert!(self.0.len() != 0);
424 self.0.len() == 1
425 }
426
427 #[inline]
430 fn sanity_check(&self) {
431 debug_assert!(
432 str::from_utf8(&self.0).is_ok()
433 && !self.0.is_empty()
434 && !self.0[..(self.0.len() - 1)].contains(&0u8)
435 && self.0[self.0.len() - 1] == 0,
436 "bad ImStr: {:?}",
437 &self.0
438 );
439 }
440}
441
442impl AsRef<CStr> for ImStr {
443 #[inline]
444 fn as_ref(&self) -> &CStr {
445 unsafe { CStr::from_bytes_with_nul_unchecked(&self.0) }
447 }
448}
449
450impl AsRef<ImStr> for ImStr {
451 #[inline]
452 fn as_ref(&self) -> &ImStr {
453 self
454 }
455}
456
457impl AsRef<str> for ImStr {
458 #[inline]
459 fn as_ref(&self) -> &str {
460 self.to_str()
461 }
462}
463
464impl<'a> From<&'a ImStr> for Cow<'a, ImStr> {
465 #[inline]
466 fn from(s: &'a ImStr) -> Cow<'a, ImStr> {
467 Cow::Borrowed(s)
468 }
469}
470
471impl ToOwned for ImStr {
472 type Owned = ImString;
473 #[inline]
474 fn to_owned(&self) -> ImString {
475 self.sanity_check();
476 ImString(self.0.to_owned())
477 }
478}
479
480#[test]
481fn test_imstring_constructors() {
482 let s = ImString::new("test");
483 assert_eq!(s.0, b"test\0");
484
485 let s = ImString::with_capacity(100);
486 assert_eq!(s.0, b"\0");
487
488 let s = unsafe { ImString::from_utf8_unchecked(vec![b't', b'e', b's', b't']) };
489 assert_eq!(s.0, b"test\0");
490
491 let s = unsafe { ImString::from_utf8_with_nul_unchecked(vec![b't', b'e', b's', b't', b'\0']) };
492 assert_eq!(s.0, b"test\0");
493}
494
495#[test]
496fn test_imstring_operations() {
497 let mut s = ImString::new("test");
498 s.clear();
499 assert_eq!(s.0, b"\0");
500 s.push('z');
501 assert_eq!(s.0, b"z\0");
502 s.push('ä');
503 assert_eq!(s.0, b"z\xc3\xa4\0");
504 s.clear();
505 s.push_str("imgui-rs");
506 assert_eq!(s.0, b"imgui-rs\0");
507 s.push_str("öä");
508 assert_eq!(s.0, b"imgui-rs\xc3\xb6\xc3\xa4\0");
509}
510
511#[test]
512fn test_imstring_fmt_write() {
513 use std::fmt::Write;
514 let mut s = ImString::default();
515 let _ = write!(s, "format {:02x}", 0x42);
516 assert_eq!(s.0, b"format 42\0");
517}
518
519#[test]
520fn test_imstring_refresh_len() {
521 let mut s = ImString::new("testing");
522 unsafe {
523 let mut ptr = s.as_mut_ptr() as *mut u8;
524 ptr = ptr.wrapping_add(2);
525 *ptr = b'z';
526 ptr = ptr.wrapping_add(1);
527 *ptr = b'\0';
528 }
529 assert_eq!(s.0, b"tez\0ing\0");
530 unsafe { s.refresh_len() };
531 assert_eq!(s.0, b"tez\0");
532}
533
534#[test]
535fn test_imstring_interior_nul() {
536 let s = ImString::new("test\0ohno");
537 assert_eq!(s.0, b"test\0");
538 assert_eq!(s.to_str(), "test");
539 assert!(!s.is_empty());
540
541 let s = ImString::new("\0ohno");
542 assert_eq!(s.to_str(), "");
543 assert!(s.is_empty());
544}