Add safety for printf
This commit is contained in:
parent
ac8ba74a3e
commit
e07eda21e4
2 changed files with 53 additions and 37 deletions
|
@ -1,12 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "unreal-niggery-rs"
|
name = "unreal-niggery-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
widestring = { version = "1.1.0" }
|
thiserror = "2.0.12"
|
||||||
|
widestring = "1.2.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true # Automatically strip symbols from the binary.
|
strip = true # Automatically strip symbols from the binary.
|
||||||
|
|
|
@ -3,16 +3,29 @@ use std::sync::OnceLock;
|
||||||
|
|
||||||
use widestring::{U16CStr, U16CString};
|
use widestring::{U16CStr, U16CString};
|
||||||
|
|
||||||
use crate::{Add, Container};
|
|
||||||
use crate::t_array::TArray;
|
use crate::t_array::TArray;
|
||||||
|
use crate::{Add, Container};
|
||||||
|
|
||||||
type FStringPrintf = fn(handle: usize, fmt: *const u16);
|
type FStringPrintf = fn(handle: usize, fmt: *const u16);
|
||||||
static FSTRING_PRINTF: OnceLock<usize> = OnceLock::new();
|
static FSTRING_PRINTF: OnceLock<usize> = OnceLock::new();
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("ContainsNul<u16> error: {0}")]
|
||||||
|
ContainsNulu16(#[from] widestring::error::ContainsNul<u16>),
|
||||||
|
#[error("New string is too long and no printf ffi binding has been provided")]
|
||||||
|
DataTooLongForUnsafe,
|
||||||
|
#[error("FStringPrintf not set")]
|
||||||
|
FStringPrintfNotSet,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Printf<T: ?Sized> {
|
pub trait Printf<T: ?Sized> {
|
||||||
/// Since rust does not support varargs in functions, it is recommended to call format!
|
/// Since rust does not support varargs in functions, it is recommended to call format!
|
||||||
fn printf<A: AsRef<T>>(handle: usize, fmt: A);
|
fn printf<A: AsRef<T>>(handle: usize, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||||
fn printf_unsafe<A: AsRef<T>>(&mut self, fmt: A);
|
/// Since rust does not support varargs in functions, it is recommended to call format!
|
||||||
|
fn printf_safe<A: AsRef<T>>(&mut self, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||||
|
/// Since rust does not support varargs in functions, it is recommended to call format!
|
||||||
|
fn printf_unsafe<A: AsRef<T>>(&mut self, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FString(pub TArray);
|
pub struct FString(pub TArray);
|
||||||
|
@ -25,35 +38,41 @@ impl FString {
|
||||||
|
|
||||||
impl Printf<str> for FString {
|
impl Printf<str> for FString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn printf<A: AsRef<str>>(handle: usize, fmt: A) {
|
fn printf<A: AsRef<str>>(handle: usize, fmt: A) -> Result<Self, Error> {
|
||||||
let tmp = U16CString::from_str(fmt).unwrap();
|
let tmp = U16CString::from_str(fmt)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
std::mem::transmute::<usize, FStringPrintf>(*fstring_printf())(
|
std::mem::transmute::<usize, FStringPrintf>(
|
||||||
handle,
|
*FSTRING_PRINTF.get().ok_or(Error::FStringPrintfNotSet)?,
|
||||||
tmp.as_ucstr().as_ptr(),
|
)(handle, tmp.as_ucstr().as_ptr());
|
||||||
);
|
}
|
||||||
|
Ok(Self(TArray::read(handle)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printf_safe<A: AsRef<str>>(&mut self, fmt: A) -> Result<Self, Error> {
|
||||||
|
match FSTRING_PRINTF.get() {
|
||||||
|
None => self.printf_unsafe(fmt),
|
||||||
|
Some(_) => Self::printf(self.0.ptr as usize, fmt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn printf_unsafe<A: AsRef<str>>(&mut self, fmt: A) -> Result<Self, Error> {
|
||||||
fn printf_unsafe<A: AsRef<str>>(&mut self, fmt: A) {
|
let tmp = U16CString::from_str(fmt)?;
|
||||||
let tmp = U16CString::from_str(fmt).unwrap();
|
let mut tmp_bytes = tmp.into_vec_with_nul();
|
||||||
let mut tmp_bytes = tmp.into_vec_with_nul();
|
let new_len = tmp_bytes.len();
|
||||||
let new_len = tmp_bytes.len();
|
if new_len > self.0.cap as usize {
|
||||||
if new_len > self.0.cap as usize {
|
return Err(Error::DataTooLongForUnsafe);
|
||||||
// TODO: use Result instead
|
}
|
||||||
panic!("New string is too long and no printf ffi binding has been provided");
|
tmp_bytes.resize(self.0.cap as usize, 0);
|
||||||
}
|
unsafe {
|
||||||
tmp_bytes.resize(self.0.cap as usize, 0);
|
std::ptr::copy_nonoverlapping(
|
||||||
unsafe {
|
tmp_bytes.as_ptr(),
|
||||||
std::ptr::copy_nonoverlapping(
|
self.0.data_ptr as *mut u16,
|
||||||
tmp_bytes.as_ptr(),
|
self.0.cap as usize,
|
||||||
self.0.data_ptr as *mut u16,
|
);
|
||||||
self.0.cap as usize,
|
}
|
||||||
);
|
self.0.len = new_len as u32;
|
||||||
}
|
self.0.write();
|
||||||
self.0.len = new_len as u32;
|
Ok(FString(TArray::read(self.0.ptr as usize)))
|
||||||
self.0.write();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +82,8 @@ impl<'a> Container<&'a U16CStr> for FString {
|
||||||
unsafe {
|
unsafe {
|
||||||
// TODO: Verify this in linux when symphonic is ready
|
// TODO: Verify this in linux when symphonic is ready
|
||||||
U16CStr::from_ptr(self.0.data_ptr as *const u16, self.0.len as usize - 1)
|
U16CStr::from_ptr(self.0.data_ptr as *const u16, self.0.len as usize - 1)
|
||||||
}.unwrap()
|
}
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +108,3 @@ impl Display for FString {
|
||||||
write!(f, "{}", self.read_container().to_string_lossy())
|
write!(f, "{}", self.read_container().to_string_lossy())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn fstring_printf() -> &'static usize {
|
|
||||||
FSTRING_PRINTF.get().unwrap()
|
|
||||||
}
|
|
Loading…
Reference in a new issue