#[cfg(feature = "link")]
use crate::errors::Result;
use crate::labview_layout;
#[cfg(feature = "link")]
use crate::memory::OwnedUHandle;
use crate::memory::{LVCopy, UHandle, UPtr};
use encoding_rs::Encoding;
use std::borrow::Cow;
use std::sync::LazyLock;
#[cfg(target_os = "windows")]
fn get_encoding() -> &'static Encoding {
#[link(name = "kernel32")]
extern "stdcall" {
fn GetACP() -> u32;
}
let code_page = unsafe { GetACP() };
codepage::to_encoding(code_page as u16).expect("Unknown code page.")
}
#[cfg(target_os = "linux")]
fn get_encoding() -> &'static Encoding {
encoding_rs::WINDOWS_1252
}
#[cfg(target_os = "macos")]
fn get_encoding() -> &'static Encoding {
encoding_rs::UTF_8
}
pub(crate) static LV_ENCODING: LazyLock<&'static Encoding> = LazyLock::new(get_encoding);
labview_layout!(
pub struct LStr {
size: i32,
data: [u8],
}
);
impl LVCopy for LStr {}
pub type LStrHandle<'a> = UHandle<'a, LStr>;
pub type LStrPtr = UPtr<LStr>;
#[cfg(feature = "link")]
pub type LStrOwned = OwnedUHandle<LStr>;
impl LStr {
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.size as usize) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.data.as_mut_ptr(), self.size as usize) }
}
pub fn size(&self) -> usize {
std::mem::size_of::<i32>() + self.data.len()
}
pub fn size_with_data(data: &[u8]) -> usize {
std::mem::size_of::<i32>() + data.len()
}
pub fn to_rust_string_with_encoding(&self, encoding: &'static Encoding) -> Cow<str> {
let (result, _, _) = encoding.decode(self.as_slice());
result
}
pub fn to_rust_string(&self) -> Cow<str> {
self.to_rust_string_with_encoding(&LV_ENCODING)
}
}
impl std::fmt::Display for LStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_rust_string())
}
}
impl std::fmt::Debug for LStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}\"", self.to_rust_string())
}
}
impl PartialEq for LStr {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
#[cfg(feature = "link")]
impl LStrHandle<'_> {
pub fn set(&mut self, value: &[u8]) -> Result<()> {
let input_length = value.len();
let struct_size = LStr::size_with_data(value);
unsafe {
self.resize(struct_size)?;
let l_str = self.as_ref_mut()?;
l_str.size = input_length as i32;
for (value, output) in value.iter().zip(l_str.data.iter_mut()) {
*output = *value;
}
}
Ok(())
}
pub fn set_str(&mut self, value: &str) -> Result<()> {
self.set_str_with_encoding(&LV_ENCODING, value)
}
pub fn set_str_with_encoding(&mut self, encoder: &'static Encoding, value: &str) -> Result<()> {
let (buffer, _, _) = encoder.encode(value);
self.set(&buffer)
}
}
#[cfg(feature = "link")]
impl LStrOwned {
pub fn empty_string() -> Result<Self> {
unsafe { OwnedUHandle::<LStr>::new_unsized(|handle| handle.set(&[])) }
}
pub fn from_data(data: &[u8]) -> Result<Self> {
unsafe { OwnedUHandle::<LStr>::new_unsized(|handle| handle.set(data)) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::alloc::{alloc, Layout, LayoutError};
impl LStr {
pub(crate) fn layout_of(n: usize) -> std::result::Result<Layout, LayoutError> {
let (layout, _) = Layout::new::<i32>().extend(Layout::array::<u8>(n)?)?;
let layout = layout.pad_to_align();
Ok(layout)
}
pub(crate) unsafe fn boxed_uninit(n: usize) -> Box<Self> {
let layout = Self::layout_of(n).unwrap();
let ptr = alloc(layout);
let ptr = core::slice::from_raw_parts(ptr, n);
let ptr = ptr as *const [u8] as *mut LStr;
let b = Box::from_raw(ptr);
debug_assert_eq!(std::mem::size_of_val(&*ptr), layout.size());
b
}
pub fn boxed_from_str(value: &str) -> Box<LStr> {
let length = value.len();
let bytes = value.as_bytes();
let mut boxed = unsafe { Self::boxed_uninit(length) };
boxed.size = length as i32;
for (i, byte) in bytes.iter().enumerate() {
boxed.data[i] = *byte;
}
boxed
}
}
#[test]
fn test_lstr_handle_debug() {
let string = LStr::boxed_from_str("Hello World");
let mut pointer = Box::into_raw(string);
let raw_handle = std::ptr::addr_of_mut!(pointer);
let handle = LStrHandle::from_raw(raw_handle);
let debug = format!("{:?}", handle);
assert!(debug.contains("Hello World"));
}
}