use std::ops::Range;
use std::os::raw::c_char;
#[derive(Debug)]
pub struct UiBuffer {
pub buffer: Vec<u8>,
pub max_len: usize,
}
impl UiBuffer {
pub const fn new(max_len: usize) -> Self {
Self {
buffer: Vec::new(),
max_len,
}
}
pub fn scratch_txt(&mut self, txt: impl AsRef<str>) -> *const c_char {
self.refresh_buffer();
let start_of_substr = self.push(txt);
unsafe { self.offset(start_of_substr) }
}
pub(crate) fn scratch_txt_concat(&mut self, parts: &[&str]) -> *const c_char {
self.refresh_buffer();
let start = self.buffer.len();
for part in parts {
self.extend_c_string_bytes(part.as_bytes());
}
self.buffer.push(0);
unsafe { self.offset(start) }
}
pub fn scratch_txt_range(&mut self, txt: impl AsRef<str>) -> Range<*const c_char> {
self.refresh_buffer();
let start = self.buffer.len();
self.buffer.extend_from_slice(txt.as_ref().as_bytes());
let end = self.buffer.len();
self.buffer.push(0);
unsafe { self.offset(start)..self.offset(end) }
}
pub fn scratch_txt_opt(&mut self, txt: Option<impl AsRef<str>>) -> *const c_char {
match txt {
Some(v) => self.scratch_txt(v),
None => std::ptr::null(),
}
}
pub fn scratch_txt_two(
&mut self,
txt_0: impl AsRef<str>,
txt_1: impl AsRef<str>,
) -> (*const c_char, *const c_char) {
self.refresh_buffer();
let first_offset = self.push(txt_0);
let second_offset = self.push(txt_1);
unsafe { (self.offset(first_offset), self.offset(second_offset)) }
}
pub fn scratch_txt_with_opt(
&mut self,
txt_0: impl AsRef<str>,
txt_1: Option<impl AsRef<str>>,
) -> (*const c_char, *const c_char) {
match txt_1 {
Some(value) => self.scratch_txt_two(txt_0, value),
None => (self.scratch_txt(txt_0), std::ptr::null()),
}
}
pub fn refresh_buffer(&mut self) {
if self.buffer.len() > self.max_len {
self.buffer.clear();
}
}
pub unsafe fn offset(&self, pos: usize) -> *const c_char {
unsafe { self.buffer.as_ptr().add(pos) as *const _ }
}
pub fn push(&mut self, txt: impl AsRef<str>) -> usize {
let txt = txt.as_ref();
let len = self.buffer.len();
self.extend_c_string_bytes(txt.as_bytes());
self.buffer.push(b'\0');
len
}
fn extend_c_string_bytes(&mut self, bytes: &[u8]) {
if bytes.contains(&0) {
self.buffer.extend(
bytes
.iter()
.map(|&byte| if byte == 0 { b'?' } else { byte }),
);
} else {
self.buffer.extend(bytes);
}
}
}