From 12930739ec993709e5e06c1ecc236e44e6c0f0a4 Mon Sep 17 00:00:00 2001 From: Stu Black Date: Fri, 17 Oct 2025 11:47:22 -0400 Subject: [PATCH] Allocate PropList description with `memory::alloc_bytes`. All memory allocations passed back from FFI functions should be allocated with `memory::alloc_bytes`, so that C code can call `memory::free_bytes` when it's done with them. --- wutil-rs/src/memory.rs | 22 +++++++++++++++++++--- wutil-rs/src/prop_list.rs | 23 +++++++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/wutil-rs/src/memory.rs b/wutil-rs/src/memory.rs index 7ced619b..8f06450a 100644 --- a/wutil-rs/src/memory.rs +++ b/wutil-rs/src/memory.rs @@ -18,7 +18,7 @@ //! memory than the baseline Window Maker code, it isn't really necessary in //! this day and age. -use std::{alloc, mem, ptr::{self, NonNull}}; +use std::{alloc, ffi::{c_char, CStr}, mem, ptr::{self, NonNull}}; /// Tracks the layout and reference count of an allocated chunk of memory. #[derive(Clone, Copy)] @@ -84,6 +84,14 @@ pub fn alloc_bytes(size: usize) -> *mut u8 { result } +/// Allocates a segment with [`alloc_bytes`] and fills it with the contents of +/// `s`. The resulting string should be free'd by passing it to [`free_bytes`]. +pub fn alloc_string(s: &CStr) -> *mut c_char { + let result = alloc_bytes(s.count_bytes() + 1).cast::(); + unsafe { ptr::copy(s.as_ptr().cast::(), result, s.count_bytes() + 1); } + result.cast::() +} + /// Frees the bytes pointed to by `b`. /// /// ## Safety @@ -170,9 +178,9 @@ pub mod ffi { #[cfg(test)] mod test { - use super::{alloc_bytes, free_bytes, ffi::wrealloc, Header}; + use super::{alloc_bytes, alloc_string, ffi::wrealloc, free_bytes, Header}; - use std::{mem, os::raw::c_void, ptr}; + use std::{ffi::CStr, mem, os::raw::c_void, ptr}; #[test] fn recover_header() { @@ -219,4 +227,12 @@ mod test { assert_eq!(unsafe { *y }, 17); unsafe { free_bytes(y.cast::()); } } + + #[test] + fn alloc_free_string() { + let s = alloc_string(c"hello"); + assert!(!s.is_null()); + assert_eq!(unsafe { CStr::from_ptr(s) }, c"hello"); + unsafe { free_bytes(s.cast::()); } + } } diff --git a/wutil-rs/src/prop_list.rs b/wutil-rs/src/prop_list.rs index 4a2376f0..3b98f022 100644 --- a/wutil-rs/src/prop_list.rs +++ b/wutil-rs/src/prop_list.rs @@ -357,7 +357,7 @@ impl PropList { } pub mod ffi { - use crate::{data::Data, find_file::path_from_cstr}; + use crate::{data::Data, find_file::path_from_cstr, memory}; use super::{ merge_deep, merge_shallow, parser, subtract_deep, subtract_shallow, Node, PropList, @@ -784,7 +784,7 @@ pub mod ffi { } } match CString::new(buf) { - Ok(s) => s.into_raw(), + Ok(s) => memory::alloc_string(s.as_c_str()), Err(_) => ptr::null_mut(), } } @@ -869,3 +869,22 @@ pub mod ffi { } } } + +#[cfg(test)] +mod test { + use std::ffi::CString; + + use crate::memory; + + use super::{Node, PropList, ffi}; + + #[test] + fn free_proplist_description() { + let mut plist = PropList::new(Node::Array(vec![PropList::new(Node::String(CString::from(c"hello"))), + PropList::new(Node::String(CString::from(c"world!")))])); + let desc = unsafe { ffi::WMGetPropListDescription(&mut plist, 1) }; + assert!(!desc.is_null()); + + unsafe { memory::ffi::wfree(desc.cast()); } + } +}