diff --git a/native/src/base/misc.rs b/native/src/base/misc.rs index 19802db7e..37450f0c3 100644 --- a/native/src/base/misc.rs +++ b/native/src/base/misc.rs @@ -97,8 +97,8 @@ macro_rules! cstr { #[macro_export] macro_rules! raw_cstr { - ($s:literal) => {{ - cstr!($s).as_ptr() + ($($str:tt)*) => {{ + cstr!($($str)*).as_ptr() }}; } diff --git a/native/src/core/resetprop/persist.rs b/native/src/core/resetprop/persist.rs index 2ecb276d5..5cb99a14d 100644 --- a/native/src/core/resetprop/persist.rs +++ b/native/src/core/resetprop/persist.rs @@ -1,17 +1,18 @@ use core::ffi::c_char; -use std::pin::Pin; use std::{ fs::{read_to_string, remove_file, rename, File}, io::{BufWriter, Write}, + ops::{Deref, DerefMut}, os::fd::FromRawFd, path::{Path, PathBuf}, + pin::Pin, }; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use base::{ - cstr, debug, libc::mkstemp, Directory, LoggedResult, MappedFile, StringExt, Utf8CStr, - WalkResult, + cstr, debug, libc::mkstemp, raw_cstr, Directory, LoggedError, LoggedResult, MappedFile, + StringExt, Utf8CStr, WalkResult, }; use crate::ffi::{clone_attr, prop_cb_exec, PropCb}; @@ -31,17 +32,6 @@ macro_rules! PERSIST_PROP { }; } -struct MatchNameCb<'a> { - cb: Pin<&'a mut PropCb>, - name: &'a Utf8CStr, -} - -struct PropCollectCb<'a> { - props: &'a mut PersistentProperties, - replace_name: Option<&'a Utf8CStr>, - replace_value: Option<&'a Utf8CStr>, -} - trait PropCbExec { fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr); } @@ -52,54 +42,43 @@ impl PropCbExec for Pin<&mut PropCb> { } } -impl PropCbExec for MatchNameCb<'_> { - fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) { - if name == self.name { - self.cb.exec(name, value); - debug!("resetprop: found prop [{}] = [{}]", name, value); +impl Deref for PersistentProperties { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.properties + } +} + +impl DerefMut for PersistentProperties { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.properties + } +} + +trait PropExt { + fn find_index(&self, name: &Utf8CStr) -> Result; + fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord>; +} + +impl PropExt for PersistentProperties { + fn find_index(&self, name: &Utf8CStr) -> Result { + self.binary_search_by(|p| p.name.as_deref().cmp(&Some(name.deref()))) + } + + fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord> { + if let Ok(idx) = self.find_index(name) { + Ok(&mut self[idx]) + } else { + Err(LoggedError::default()) } } } -impl PropCbExec for PropCollectCb<'_> { - fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) { - let replace_value = self.replace_value.unwrap_or(value); - let value = self.replace_name.map_or(value, |replace_name| { - if name.as_bytes() == replace_name.as_bytes() { - replace_value - } else { - value - } - }); - self.props.properties.push(PersistentPropertyRecord { - name: Some(name.to_string()), - value: Some(value.to_string()), - }) - } -} - -fn check_pb() -> bool { +fn check_proto() -> bool { Path::new(PERSIST_PROP!()).exists() } -fn pb_get_prop(cb: &mut dyn PropCbExec) -> LoggedResult<()> { - debug!("resetprop: decode with protobuf [{}]", PERSIST_PROP!()); - let m = MappedFile::open(cstr!(PERSIST_PROP!()))?; - let m = m.as_ref(); - let mut r = BytesReader::from_bytes(m); - let mut pp = PersistentProperties::from_reader(&mut r, m)?; - pp.properties.iter_mut().for_each(|p| { - if let PersistentPropertyRecord { - name: Some(ref mut n), - value: Some(ref mut v), - } = p - { - cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v)); - } - }); - Ok(()) -} - fn file_get_prop(name: &Utf8CStr) -> LoggedResult { let path = PathBuf::new().join(PERSIST_PROP_DIR!()).join(name); let path = path.as_path(); @@ -107,27 +86,6 @@ fn file_get_prop(name: &Utf8CStr) -> LoggedResult { Ok(read_to_string(path)?) } -fn pb_write_props(props: &PersistentProperties) -> LoggedResult<()> { - let mut tmp = String::from(concat!(PERSIST_PROP!(), ".XXXXXX")); - tmp.nul_terminate(); - { - let f = unsafe { - let fd = mkstemp(tmp.as_mut_ptr().cast()); - if fd < 0 { - return Err(Default::default()); - } - File::from_raw_fd(fd) - }; - debug!("resetprop: encode with protobuf [{}]", tmp); - props.write_message(&mut Writer::new(BufWriter::new(f)))?; - } - unsafe { - clone_attr(cstr!(PERSIST_PROP!()).as_ptr(), tmp.as_ptr().cast()); - } - rename(tmp, PERSIST_PROP!())?; - Ok(()) -} - fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()> { let path = PathBuf::new().join(PERSIST_PROP_DIR!()).join(name); let path = path.as_path(); @@ -152,25 +110,73 @@ fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()> Ok(()) } +fn proto_read_props() -> LoggedResult { + debug!("resetprop: decode with protobuf [{}]", PERSIST_PROP!()); + let m = MappedFile::open(cstr!(PERSIST_PROP!()))?; + let m = m.as_ref(); + let mut r = BytesReader::from_bytes(m); + let mut props = PersistentProperties::from_reader(&mut r, m)?; + // Keep the list sorted for binary search + props.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + Ok(props) +} + +fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> { + let mut tmp = String::from(concat!(PERSIST_PROP!(), ".XXXXXX")); + tmp.nul_terminate(); + { + let f = unsafe { + let fd = mkstemp(tmp.as_mut_ptr().cast()); + if fd < 0 { + return Err(Default::default()); + } + File::from_raw_fd(fd) + }; + debug!("resetprop: encode with protobuf [{}]", tmp); + props.write_message(&mut Writer::new(BufWriter::new(f)))?; + } + unsafe { + clone_attr(raw_cstr!(PERSIST_PROP!()), tmp.as_ptr().cast()); + } + rename(tmp, PERSIST_PROP!())?; + Ok(()) +} + pub unsafe fn persist_get_prop(name: *const c_char, prop_cb: Pin<&mut PropCb>) { - unsafe fn inner(name: *const c_char, mut prop_cb: Pin<&mut PropCb>) -> LoggedResult<()> { - let name = Utf8CStr::from_ptr(name)?; - if check_pb() { - pb_get_prop(&mut MatchNameCb { cb: prop_cb, name }) + fn inner(name: *const c_char, mut prop_cb: Pin<&mut PropCb>) -> LoggedResult<()> { + let name = unsafe { Utf8CStr::from_ptr(name)? }; + if check_proto() { + let mut props = proto_read_props()?; + if let Ok(PersistentPropertyRecord { + name: Some(ref mut n), + value: Some(ref mut v), + }) = props.find(name) + { + prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v)); + } } else { let mut value = file_get_prop(name)?; prop_cb.exec(name, Utf8CStr::from_string(&mut value)); debug!("resetprop: found prop [{}] = [{}]", name, value); - Ok(()) } + Ok(()) } inner(name, prop_cb).ok(); } pub unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>) { fn inner(mut prop_cb: Pin<&mut PropCb>) -> LoggedResult<()> { - if check_pb() { - pb_get_prop(&mut prop_cb) + if check_proto() { + let mut props = proto_read_props()?; + props.iter_mut().for_each(|p| { + if let PersistentPropertyRecord { + name: Some(ref mut n), + value: Some(ref mut v), + } = p + { + prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v)); + } + }); } else { let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR!()))?; dir.for_all_file(|f| { @@ -181,23 +187,23 @@ pub unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>) { } Ok(WalkResult::Continue) })?; - Ok(()) } + Ok(()) } inner(prop_cb).ok(); } pub unsafe fn persist_delete_prop(name: *const c_char) -> bool { - unsafe fn inner(name: *const c_char) -> LoggedResult<()> { - let name = Utf8CStr::from_ptr(name)?; - if check_pb() { - let mut pp = PersistentProperties { properties: vec![] }; - pb_get_prop(&mut PropCollectCb { - props: &mut pp, - replace_name: Some(name), - replace_value: None, - })?; - pb_write_props(&pp) + fn inner(name: *const c_char) -> LoggedResult<()> { + let name = unsafe { Utf8CStr::from_ptr(name)? }; + if check_proto() { + let mut props = proto_read_props()?; + if let Ok(idx) = props.find_index(name) { + props.remove(idx); + proto_write_props(&props) + } else { + Err(LoggedError::default()) + } } else { file_set_prop(name, None) } @@ -208,14 +214,19 @@ pub unsafe fn persist_set_prop(name: *const c_char, value: *const c_char) -> boo unsafe fn inner(name: *const c_char, value: *const c_char) -> LoggedResult<()> { let name = Utf8CStr::from_ptr(name)?; let value = Utf8CStr::from_ptr(value)?; - if check_pb() { - let mut pp = PersistentProperties { properties: vec![] }; - pb_get_prop(&mut PropCollectCb { - props: &mut pp, - replace_name: Some(name), - replace_value: Some(value), - })?; - pb_write_props(&pp) + if check_proto() { + let mut props = proto_read_props()?; + match props.find_index(name) { + Ok(idx) => props[idx].value = Some(value.to_string()), + Err(idx) => props.insert( + idx, + PersistentPropertyRecord { + name: Some(name.to_string()), + value: Some(value.to_string()), + }, + ), + } + proto_write_props(&props) } else { file_set_prop(name, Some(value)) }