From fb17a1839cfbed2a3f143c65670ec3cda4fe2d02 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Sat, 11 Nov 2023 00:52:01 +0100 Subject: [PATCH] Add `SettingsList` for storing lists of settings (#738) This is a new type that is used to store lists of settings in addition to `SettingsMap`. The difference is that for lists you don't use keys to look up the values, but instead you use indices, or more likely, iteration. --- crates/livesplit-auto-splitting/README.md | 65 ++++ crates/livesplit-auto-splitting/src/lib.rs | 67 +++- .../livesplit-auto-splitting/src/runtime.rs | 292 +++++++++++++++++- .../livesplit-auto-splitting/src/settings.rs | 139 ++++++++- src/auto_splitting/mod.rs | 86 +++++- 5 files changed, 634 insertions(+), 15 deletions(-) diff --git a/crates/livesplit-auto-splitting/README.md b/crates/livesplit-auto-splitting/README.md index 05054e52..0c5f4a70 100644 --- a/crates/livesplit-auto-splitting/README.md +++ b/crates/livesplit-auto-splitting/README.md @@ -42,6 +42,9 @@ pub struct ProcessId(u64); #[repr(transparent)] pub struct SettingsMap(NonZeroU64); +#[repr(transparent)] +pub struct SettingsList(NonZeroU64); + #[repr(transparent)] pub struct SettingValue(NonZeroU64); @@ -295,6 +298,51 @@ extern "C" { key_ptr: *const u8, key_len: usize, ) -> Option; + /// Gets the length of a settings map. + pub fn settings_map_len(map: SettingsMap) -> u64; + /// Gets the key of a setting value from the settings map based on the index + /// by storing it into the buffer provided. Returns `false` if the buffer is + /// too small. After this call, no matter whether it was successful or not, + /// the `buf_len_ptr` will be set to the required buffer size. If `false` is + /// returned and the `buf_len_ptr` got set to 0, the index is out of bounds. + /// The key is guaranteed to be valid UTF-8 and is not nul-terminated. + pub fn settings_map_get_key_by_index( + map: SettingsMap, + idx: u64, + buf_ptr: *mut u8, + buf_len_ptr: *mut usize, + ) -> bool; + /// Gets a copy of the setting value from the settings map based on the + /// index. Returns `None` if the index is out of bounds. Any changes to it + /// are only perceived if it's stored back. You own the setting value and + /// are responsible for freeing it. + pub fn settings_map_get_value_by_index(map: SettingsMap, idx: u64) -> Option; + + /// Creates a new settings list. You own the settings list and are + /// responsible for freeing it. + pub fn settings_list_new() -> SettingsList; + /// Frees a settings list. + pub fn settings_list_free(list: SettingsList); + /// Copies a settings list. No changes inside the copy affect the original + /// settings list. You own the new settings list and are responsible for + /// freeing it. + pub fn settings_list_copy(list: SettingsList) -> SettingsList; + /// Gets the length of a settings list. + pub fn settings_list_len(list: SettingsList) -> u64; + /// Gets a copy of the setting value from the settings list based on the + /// index. Returns `None` if the index is out of bounds. Any changes to it + /// are only perceived if it's stored back. You own the setting value and + /// are responsible for freeing it. + pub fn settings_list_get(list: SettingsList, idx: u64) -> Option; + /// Pushes a copy of the setting value to the end of the settings list. You + /// still retain ownership of the setting value, which means you still need + /// to free it. + pub fn settings_list_push(list: SettingsList, value: SettingValue); + /// Inserts a copy of the setting value into the settings list at the index + /// given. If the index is out of bounds, the setting value is pushed to the + /// end of the settings list. You still retain ownership of the setting + /// value, which means you still need to free it. + pub fn settings_list_insert(list: SettingsList, idx: u64, value: SettingValue); /// Creates a new setting value from a settings map. The value is a copy of /// the settings map. Any changes to the original settings map afterwards @@ -302,6 +350,12 @@ extern "C" { /// value and are responsible for freeing it. You also retain ownership of /// the settings map, which means you still need to free it. pub fn setting_value_new_map(value: SettingsMap) -> SettingValue; + /// Creates a new setting value from a settings list. The value is a copy of + /// the settings list. Any changes to the original settings list afterwards + /// are not going to be perceived by the setting value. You own the setting + /// value and are responsible for freeing it. You also retain ownership of + /// the settings list, which means you still need to free it. + pub fn setting_value_new_list(value: SettingsList) -> SettingValue; /// Creates a new boolean setting value. You own the setting value and are /// responsible for freeing it. pub fn setting_value_new_bool(value: bool) -> SettingValue; @@ -317,6 +371,10 @@ extern "C" { pub fn setting_value_new_string(value_ptr: *const u8, value_len: usize) -> SettingValue; /// Frees a setting value. pub fn setting_value_free(value: SettingValue); + /// Copies a setting value. No changes inside the copy affect the original + /// setting value. You own the new setting value and are responsible for + /// freeing it. + pub fn setting_value_copy(value: SettingValue) -> SettingValue; /// Gets the value of a setting value as a settings map by storing it into /// the pointer provided. Returns `false` if the setting value is not a /// settings map. No value is stored into the pointer in that case. No @@ -324,6 +382,13 @@ extern "C" { /// which means you still need to free it. You own the settings map and are /// responsible for freeing it. pub fn setting_value_get_map(value: SettingValue, value_ptr: *mut SettingsMap) -> bool; + /// Gets the value of a setting value as a settings list by storing it into + /// the pointer provided. Returns `false` if the setting value is not a + /// settings list. No value is stored into the pointer in that case. No + /// matter what happens, you still retain ownership of the setting value, + /// which means you still need to free it. You own the settings list and are + /// responsible for freeing it. + pub fn setting_value_get_list(value: SettingValue, value_ptr: *mut SettingsList) -> bool; /// Gets the value of a boolean setting value by storing it into the pointer /// provided. Returns `false` if the setting value is not a boolean. No /// value is stored into the pointer in that case. No matter what happens, diff --git a/crates/livesplit-auto-splitting/src/lib.rs b/crates/livesplit-auto-splitting/src/lib.rs index 17ba4d67..970e00bf 100644 --- a/crates/livesplit-auto-splitting/src/lib.rs +++ b/crates/livesplit-auto-splitting/src/lib.rs @@ -43,6 +43,9 @@ //! pub struct SettingsMap(NonZeroU64); //! //! #[repr(transparent)] +//! pub struct SettingsList(NonZeroU64); +//! +//! #[repr(transparent)] //! pub struct SettingValue(NonZeroU64); //! //! #[repr(transparent)] @@ -295,6 +298,51 @@ //! key_ptr: *const u8, //! key_len: usize, //! ) -> Option; +//! /// Gets the length of a settings map. +//! pub fn settings_map_len(map: SettingsMap) -> u64; +//! /// Gets the key of a setting value from the settings map based on the index +//! /// by storing it into the buffer provided. Returns `false` if the buffer is +//! /// too small. After this call, no matter whether it was successful or not, +//! /// the `buf_len_ptr` will be set to the required buffer size. If `false` is +//! /// returned and the `buf_len_ptr` got set to 0, the index is out of bounds. +//! /// The key is guaranteed to be valid UTF-8 and is not nul-terminated. +//! pub fn settings_map_get_key_by_index( +//! map: SettingsMap, +//! idx: u64, +//! buf_ptr: *mut u8, +//! buf_len_ptr: *mut usize, +//! ) -> bool; +//! /// Gets a copy of the setting value from the settings map based on the +//! /// index. Returns `None` if the index is out of bounds. Any changes to it +//! /// are only perceived if it's stored back. You own the setting value and +//! /// are responsible for freeing it. +//! pub fn settings_map_get_value_by_index(map: SettingsMap, idx: u64) -> Option; +//! +//! /// Creates a new settings list. You own the settings list and are +//! /// responsible for freeing it. +//! pub fn settings_list_new() -> SettingsList; +//! /// Frees a settings list. +//! pub fn settings_list_free(list: SettingsList); +//! /// Copies a settings list. No changes inside the copy affect the original +//! /// settings list. You own the new settings list and are responsible for +//! /// freeing it. +//! pub fn settings_list_copy(list: SettingsList) -> SettingsList; +//! /// Gets the length of a settings list. +//! pub fn settings_list_len(list: SettingsList) -> u64; +//! /// Gets a copy of the setting value from the settings list based on the +//! /// index. Returns `None` if the index is out of bounds. Any changes to it +//! /// are only perceived if it's stored back. You own the setting value and +//! /// are responsible for freeing it. +//! pub fn settings_list_get(list: SettingsList, idx: u64) -> Option; +//! /// Pushes a copy of the setting value to the end of the settings list. You +//! /// still retain ownership of the setting value, which means you still need +//! /// to free it. +//! pub fn settings_list_push(list: SettingsList, value: SettingValue); +//! /// Inserts a copy of the setting value into the settings list at the index +//! /// given. If the index is out of bounds, the setting value is pushed to the +//! /// end of the settings list. You still retain ownership of the setting +//! /// value, which means you still need to free it. +//! pub fn settings_list_insert(list: SettingsList, idx: u64, value: SettingValue); //! //! /// Creates a new setting value from a settings map. The value is a copy of //! /// the settings map. Any changes to the original settings map afterwards @@ -302,6 +350,12 @@ //! /// value and are responsible for freeing it. You also retain ownership of //! /// the settings map, which means you still need to free it. //! pub fn setting_value_new_map(value: SettingsMap) -> SettingValue; +//! /// Creates a new setting value from a settings list. The value is a copy of +//! /// the settings list. Any changes to the original settings list afterwards +//! /// are not going to be perceived by the setting value. You own the setting +//! /// value and are responsible for freeing it. You also retain ownership of +//! /// the settings list, which means you still need to free it. +//! pub fn setting_value_new_list(value: SettingsList) -> SettingValue; //! /// Creates a new boolean setting value. You own the setting value and are //! /// responsible for freeing it. //! pub fn setting_value_new_bool(value: bool) -> SettingValue; @@ -317,6 +371,10 @@ //! pub fn setting_value_new_string(value_ptr: *const u8, value_len: usize) -> SettingValue; //! /// Frees a setting value. //! pub fn setting_value_free(value: SettingValue); +//! /// Copies a setting value. No changes inside the copy affect the original +//! /// setting value. You own the new setting value and are responsible for +//! /// freeing it. +//! pub fn setting_value_copy(value: SettingValue) -> SettingValue; //! /// Gets the value of a setting value as a settings map by storing it into //! /// the pointer provided. Returns `false` if the setting value is not a //! /// settings map. No value is stored into the pointer in that case. No @@ -324,6 +382,13 @@ //! /// which means you still need to free it. You own the settings map and are //! /// responsible for freeing it. //! pub fn setting_value_get_map(value: SettingValue, value_ptr: *mut SettingsMap) -> bool; +//! /// Gets the value of a setting value as a settings list by storing it into +//! /// the pointer provided. Returns `false` if the setting value is not a +//! /// settings list. No value is stored into the pointer in that case. No +//! /// matter what happens, you still retain ownership of the setting value, +//! /// which means you still need to free it. You own the settings list and are +//! /// responsible for freeing it. +//! pub fn setting_value_get_list(value: SettingValue, value_ptr: *mut SettingsList) -> bool; //! /// Gets the value of a boolean setting value by storing it into the pointer //! /// provided. Returns `false` if the setting value is not a boolean. No //! /// value is stored into the pointer in that case. No matter what happens, @@ -392,6 +457,6 @@ mod timer; pub use process::Process; pub use runtime::{Config, CreationError, InterruptHandle, Runtime, RuntimeGuard}; -pub use settings::{SettingValue, SettingsMap, UserSetting, UserSettingKind}; +pub use settings::{SettingValue, SettingsList, SettingsMap, UserSetting, UserSettingKind}; pub use time; pub use timer::{Timer, TimerState}; diff --git a/crates/livesplit-auto-splitting/src/runtime.rs b/crates/livesplit-auto-splitting/src/runtime.rs index eba99395..5e5507ce 100644 --- a/crates/livesplit-auto-splitting/src/runtime.rs +++ b/crates/livesplit-auto-splitting/src/runtime.rs @@ -2,7 +2,7 @@ use crate::{ process::{build_path, Process}, - settings::UserSetting, + settings::{SettingsList, UserSetting}, timer::Timer, SettingValue, SettingsMap, UserSettingKind, }; @@ -81,12 +81,14 @@ pub enum CreationError { slotmap::new_key_type! { struct ProcessKey; struct SettingsMapKey; + struct SettingsListKey; struct SettingValueKey; } pub struct Context { processes: SlotMap, settings_maps: SlotMap, + settings_lists: SlotMap, setting_values: SlotMap, user_settings: Arc>, shared_data: Arc, @@ -282,6 +284,7 @@ impl RuntimeGuard<'_, T> { let data = self.data.store.data(); data.processes.len() as u64 + data.settings_maps.len() as u64 + + data.settings_lists.len() as u64 + data.setting_values.len() as u64 } } @@ -339,6 +342,7 @@ impl Runtime { Context { processes: SlotMap::with_key(), settings_maps: SlotMap::with_key(), + settings_lists: SlotMap::with_key(), setting_values: SlotMap::with_key(), user_settings: user_settings.clone(), shared_data: shared_data.clone(), @@ -1293,6 +1297,227 @@ fn bind_interface(linker: &mut Linker>) -> Result<(), Creat source, name: "settings_map_get", })? + .func_wrap("env", "settings_map_len", { + |mut caller: Caller<'_, Context>, settings_map: u64| { + let ctx = caller.data_mut(); + + let len = ctx + .settings_maps + .get(SettingsMapKey::from(KeyData::from_ffi(settings_map))) + .ok_or_else(|| format_err!("Invalid settings map handle: {settings_map}"))? + .len() + .try_into() + .unwrap_or(u64::MAX); + + Ok(len) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_map_len", + })? + .func_wrap("env", "settings_map_get_key_by_index", { + |mut caller: Caller<'_, Context>, + settings_map: u64, + index: u64, + buf_ptr: u32, + buf_len_ptr: u32| { + let (memory, context) = memory_and_context(&mut caller); + + let settings_map = context + .settings_maps + .get(SettingsMapKey::from(KeyData::from_ffi(settings_map))) + .ok_or_else(|| format_err!("Invalid settings map handle: {settings_map}"))?; + + let len_bytes = get_arr_mut(memory, buf_len_ptr)?; + + let slot = settings_map.get_by_index(index.try_into().unwrap_or(usize::MAX)); + + if let Some((key, _)) = slot { + *len_bytes = (key.len() as u32).to_le_bytes(); + + let len = u32::from_le_bytes(*len_bytes) as usize; + if len < key.len() { + return Ok(0u32); + } + let buf = get_slice_mut(memory, buf_ptr, key.len() as _)?; + buf.copy_from_slice(key.as_bytes()); + Ok(1u32) + } else { + *len_bytes = 0u32.to_le_bytes(); + Ok(0u32) + } + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_map_get_key_by_index", + })? + .func_wrap("env", "settings_map_get_value_by_index", { + |mut caller: Caller<'_, Context>, settings_map: u64, index: u64| { + let ctx = caller.data_mut(); + + let maybe_slot = if let Ok(index) = index.try_into() { + ctx.settings_maps + .get(SettingsMapKey::from(KeyData::from_ffi(settings_map))) + .ok_or_else(|| format_err!("Invalid settings map handle: {settings_map}"))? + .get_by_index(index) + } else { + None + }; + + Ok(if let Some((_, value)) = maybe_slot { + ctx.setting_values.insert(value.clone()).data().as_ffi() + } else { + 0 + }) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_map_get_value_by_index", + })? + .func_wrap("env", "settings_list_new", { + |mut caller: Caller<'_, Context>| { + let ctx = caller.data_mut(); + ctx.settings_lists + .insert(SettingsList::new()) + .data() + .as_ffi() + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_new", + })? + .func_wrap("env", "settings_list_free", { + |mut caller: Caller<'_, Context>, settings_list: u64| { + caller + .data_mut() + .settings_lists + .remove(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))?; + Ok(()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_free", + })? + .func_wrap("env", "settings_list_copy", { + |mut caller: Caller<'_, Context>, settings_list: u64| { + let ctx = caller.data_mut(); + + let settings_list = ctx + .settings_lists + .get(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))? + .clone(); + + Ok(ctx.settings_lists.insert(settings_list).data().as_ffi()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_copy", + })? + .func_wrap("env", "settings_list_len", { + |caller: Caller<'_, Context>, settings_list: u64| { + let ctx = caller.data(); + + let len = ctx + .settings_lists + .get(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))? + .len() + .try_into() + .unwrap_or(u64::MAX); + + Ok(len) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_len", + })? + .func_wrap("env", "settings_list_get", { + |mut caller: Caller<'_, Context>, settings_list: u64, index: u64| { + let ctx = caller.data_mut(); + + let maybe_value = if let Ok(index) = index.try_into() { + ctx.settings_lists + .get(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| { + format_err!("Invalid settings list handle: {settings_list}") + })? + .get(index) + } else { + None + }; + + Ok(if let Some(value) = maybe_value { + ctx.setting_values.insert(value.clone()).data().as_ffi() + } else { + 0 + }) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_get", + })? + .func_wrap("env", "settings_list_push", { + |mut caller: Caller<'_, Context>, settings_list: u64, setting_value: u64| { + let context = caller.data_mut(); + + let settings_list = context + .settings_lists + .get_mut(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))?; + + let setting_value = context + .setting_values + .get(SettingValueKey::from(KeyData::from_ffi(setting_value))) + .ok_or_else(|| format_err!("Invalid setting value handle: {setting_value}"))?; + + settings_list.push(setting_value.clone()); + + Ok(()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_push", + })? + .func_wrap("env", "settings_list_insert", { + |mut caller: Caller<'_, Context>, + settings_list: u64, + index: u64, + setting_value: u64| { + let context = caller.data_mut(); + + let settings_list = context + .settings_lists + .get_mut(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))?; + + let setting_value = context + .setting_values + .get(SettingValueKey::from(KeyData::from_ffi(setting_value))) + .ok_or_else(|| format_err!("Invalid setting value handle: {setting_value}"))?; + + settings_list.insert( + index.try_into().unwrap_or(usize::MAX), + setting_value.clone(), + ); + + Ok(()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "settings_list_insert", + })? .func_wrap("env", "setting_value_new_map", { |mut caller: Caller<'_, Context>, settings_map: u64| { let context = caller.data_mut(); @@ -1313,6 +1538,26 @@ fn bind_interface(linker: &mut Linker>) -> Result<(), Creat source, name: "setting_value_new_map", })? + .func_wrap("env", "setting_value_new_list", { + |mut caller: Caller<'_, Context>, settings_list: u64| { + let context = caller.data_mut(); + + let settings_list = context + .settings_lists + .get(SettingsListKey::from(KeyData::from_ffi(settings_list))) + .ok_or_else(|| format_err!("Invalid settings list handle: {settings_list}"))?; + + Ok(context + .setting_values + .insert(SettingValue::List(settings_list.clone())) + .data() + .as_ffi()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "setting_value_new_list", + })? .func_wrap("env", "setting_value_new_bool", { |mut caller: Caller<'_, Context>, value: u32| { Ok(caller @@ -1384,6 +1629,23 @@ fn bind_interface(linker: &mut Linker>) -> Result<(), Creat source, name: "setting_value_free", })? + .func_wrap("env", "setting_value_copy", { + |mut caller: Caller<'_, Context>, setting_value: u64| { + let ctx = caller.data_mut(); + + let setting_value = ctx + .setting_values + .get(SettingValueKey::from(KeyData::from_ffi(setting_value))) + .ok_or_else(|| format_err!("Invalid settings value handle: {setting_value}"))? + .clone(); + + Ok(ctx.setting_values.insert(setting_value).data().as_ffi()) + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "setting_value_copy", + })? .func_wrap("env", "setting_value_get_map", { |mut caller: Caller<'_, Context>, setting_value: u64, value_ptr: u32| { let (memory, context) = memory_and_context(&mut caller); @@ -1412,6 +1674,34 @@ fn bind_interface(linker: &mut Linker>) -> Result<(), Creat source, name: "setting_value_get_map", })? + .func_wrap("env", "setting_value_get_list", { + |mut caller: Caller<'_, Context>, setting_value: u64, value_ptr: u32| { + let (memory, context) = memory_and_context(&mut caller); + + let setting_value = context + .setting_values + .get(SettingValueKey::from(KeyData::from_ffi(setting_value))) + .ok_or_else(|| format_err!("Invalid setting value handle: {setting_value}"))?; + + let value_ptr = get_arr_mut(memory, value_ptr)?; + + if let SettingValue::List(value) = setting_value { + *value_ptr = context + .settings_lists + .insert(value.clone()) + .data() + .as_ffi() + .to_le_bytes(); + Ok(1u32) + } else { + Ok(0u32) + } + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "setting_value_get_list", + })? .func_wrap("env", "setting_value_get_bool", { |mut caller: Caller<'_, Context>, setting_value: u64, value_ptr: u32| { let (memory, context) = memory_and_context(&mut caller); diff --git a/crates/livesplit-auto-splitting/src/settings.rs b/crates/livesplit-auto-splitting/src/settings.rs index 0fa89ac8..6400df55 100644 --- a/crates/livesplit-auto-splitting/src/settings.rs +++ b/crates/livesplit-auto-splitting/src/settings.rs @@ -43,8 +43,10 @@ pub enum UserSettingKind { #[non_exhaustive] #[derive(Clone)] pub enum SettingValue { - /// A map of settings that are stored in the [`SettingsMap`]. + /// A map of settings that are stored in a [`SettingsMap`]. Map(SettingsMap), + /// A list of settings that are stored in a [`SettingsList`]. + List(SettingsList), /// A boolean value. Bool(bool), /// A 64-bit signed integer value. @@ -59,6 +61,7 @@ impl fmt::Debug for SettingValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Map(v) => fmt::Debug::fmt(v, f), + Self::List(v) => fmt::Debug::fmt(v, f), Self::Bool(v) => fmt::Debug::fmt(v, f), Self::I64(v) => fmt::Debug::fmt(v, f), Self::F64(v) => fmt::Debug::fmt(v, f), @@ -77,6 +80,7 @@ pub struct SettingsMap { } impl fmt::Debug for SettingsMap { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.values, f) } @@ -84,6 +88,8 @@ impl fmt::Debug for SettingsMap { impl SettingsMap { /// Creates a new empty settings map. + #[must_use] + #[inline] pub fn new() -> Self { Self::default() } @@ -91,6 +97,7 @@ impl SettingsMap { /// Sets a setting to the new value. If the key of the setting doesn't exist /// yet it will be stored as a new value. Otherwise the value will be /// updated. + #[inline] pub fn insert(&mut self, key: Arc, value: SettingValue) { Arc::make_mut(&mut self.values).insert(key, value); } @@ -98,17 +105,145 @@ impl SettingsMap { /// Accesses the value of a setting by its key. While the setting may exist /// as part of the user settings, it may not have been stored into the /// settings map yet, so it may not exist, despite being registered. + #[must_use] + #[inline] pub fn get(&self, key: &str) -> Option<&SettingValue> { self.values.get(key) } + /// Accesses the value of a setting by its index. The index is the position + /// of the setting in the list of all settings. This may be useful for + /// iterating over all settings. Prefer using [`iter`](Self::iter) in most + /// situations though. + #[must_use] + #[inline] + pub fn get_by_index(&self, index: usize) -> Option<(&str, &SettingValue)> { + self.values.get_index(index).map(|(k, v)| (k.as_ref(), v)) + } + /// Iterates over all the setting keys and their values in the map. + #[inline] pub fn iter(&self) -> impl Iterator { self.values.iter().map(|(k, v)| (k.as_ref(), v)) } + /// Returns the number of settings that are stored in the map. + #[must_use] + #[inline] + pub fn len(&self) -> usize { + self.values.len() + } + + /// Returns [`true`] if the map doesn't contain any settings. + #[must_use] + #[inline] + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } + + /// Returns [`true`] if the identity of the map is the same as the identity + /// of the other map. Maps use the copy-on-write principle. This means that + /// cloning a map is cheap because it references all the same data as the + /// original until one of the variables is changed. With this function you + /// can check if two variables internally share the same data and are + /// therefore identical. This is useful to determine if the map has changed + /// since the last time it was checked. You may use this as part of a + /// compare-and-swap loop. + #[must_use] #[inline] - pub(super) fn is_unchanged(&self, other: &Self) -> bool { + pub fn is_unchanged(&self, other: &Self) -> bool { Arc::ptr_eq(&self.values, &other.values) } } + +/// A list of settings that may be used as a [`SettingValue`] as part of a +/// [`SettingsMap`]. It logically resembles a [`Vec`] of [`SettingValue`] and +/// therefore provides similar functionality. +#[derive(Clone, Default)] +pub struct SettingsList { + list: Arc>, +} + +impl fmt::Debug for SettingsList { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.list, f) + } +} + +impl SettingsList { + /// Creates a new empty settings list. + #[must_use] + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Returns the number of settings that are stored in the list. + #[must_use] + #[inline] + pub fn len(&self) -> usize { + self.list.len() + } + + /// Returns [`true`] if the list doesn't contain any settings. + #[must_use] + #[inline] + pub fn is_empty(&self) -> bool { + self.list.is_empty() + } + + /// Accesses the value of a setting by its index. The index is the position + /// of the setting in the list of all settings. This may be useful for + /// iterating over all settings. Prefer using [`iter`](Self::iter) in most + /// situations though. + #[must_use] + #[inline] + pub fn get(&self, index: usize) -> Option<&SettingValue> { + self.list.get(index) + } + + /// Pushes a setting value to the end of the list. + #[inline] + pub fn push(&mut self, value: SettingValue) { + Arc::make_mut(&mut self.list).push(value); + } + + /// Inserts a setting value at the given index. If the index is larger than + /// the length of the list, the value will be appended to the end of the + /// list. + #[inline] + pub fn insert(&mut self, index: usize, value: SettingValue) { + let list = Arc::make_mut(&mut self.list); + list.insert(index.min(list.len()), value); + } + + /// Removes the setting value at the given index and returns it. If the + /// index is larger than the length of the list, [`None`] will be returned. + #[inline] + pub fn remove(&mut self, index: usize) -> Option { + let list = Arc::make_mut(&mut self.list); + if index >= list.len() { + return None; + } + Some(list.remove(index)) + } + + /// Iterates over all the setting values in the list. + #[inline] + pub fn iter(&self) -> std::slice::Iter<'_, SettingValue> { + self.list.iter() + } + + /// Returns [`true`] if the identity of the list is the same as the identity + /// of the other list. Lists use the copy-on-write principle. This means + /// that cloning a list is cheap because it references all the same data as + /// the original until one of the variables is changed. With this function + /// you can check if two variables internally share the same data and are + /// therefore identical. This is useful to determine if the list has changed + /// since the last time it was checked. You may use this as part of a + /// compare-and-swap loop. + pub fn is_unchanged(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.list, &other.list) + } +} diff --git a/src/auto_splitting/mod.rs b/src/auto_splitting/mod.rs index 885a3286..01cf961d 100644 --- a/src/auto_splitting/mod.rs +++ b/src/auto_splitting/mod.rs @@ -43,6 +43,9 @@ //! pub struct SettingsMap(NonZeroU64); //! //! #[repr(transparent)] +//! pub struct SettingsList(NonZeroU64); +//! +//! #[repr(transparent)] //! pub struct SettingValue(NonZeroU64); //! //! #[repr(transparent)] @@ -295,6 +298,51 @@ //! key_ptr: *const u8, //! key_len: usize, //! ) -> Option; +//! /// Gets the length of a settings map. +//! pub fn settings_map_len(map: SettingsMap) -> u64; +//! /// Gets the key of a setting value from the settings map based on the index +//! /// by storing it into the buffer provided. Returns `false` if the buffer is +//! /// too small. After this call, no matter whether it was successful or not, +//! /// the `buf_len_ptr` will be set to the required buffer size. If `false` is +//! /// returned and the `buf_len_ptr` got set to 0, the index is out of bounds. +//! /// The key is guaranteed to be valid UTF-8 and is not nul-terminated. +//! pub fn settings_map_get_key_by_index( +//! map: SettingsMap, +//! idx: u64, +//! buf_ptr: *mut u8, +//! buf_len_ptr: *mut usize, +//! ) -> bool; +//! /// Gets a copy of the setting value from the settings map based on the +//! /// index. Returns `None` if the index is out of bounds. Any changes to it +//! /// are only perceived if it's stored back. You own the setting value and +//! /// are responsible for freeing it. +//! pub fn settings_map_get_value_by_index(map: SettingsMap, idx: u64) -> Option; +//! +//! /// Creates a new settings list. You own the settings list and are +//! /// responsible for freeing it. +//! pub fn settings_list_new() -> SettingsList; +//! /// Frees a settings list. +//! pub fn settings_list_free(list: SettingsList); +//! /// Copies a settings list. No changes inside the copy affect the original +//! /// settings list. You own the new settings list and are responsible for +//! /// freeing it. +//! pub fn settings_list_copy(list: SettingsList) -> SettingsList; +//! /// Gets the length of a settings list. +//! pub fn settings_list_len(list: SettingsList) -> u64; +//! /// Gets a copy of the setting value from the settings list based on the +//! /// index. Returns `None` if the index is out of bounds. Any changes to it +//! /// are only perceived if it's stored back. You own the setting value and +//! /// are responsible for freeing it. +//! pub fn settings_list_get(list: SettingsList, idx: u64) -> Option; +//! /// Pushes a copy of the setting value to the end of the settings list. You +//! /// still retain ownership of the setting value, which means you still need +//! /// to free it. +//! pub fn settings_list_push(list: SettingsList, value: SettingValue); +//! /// Inserts a copy of the setting value into the settings list at the index +//! /// given. If the index is out of bounds, the setting value is pushed to the +//! /// end of the settings list. You still retain ownership of the setting +//! /// value, which means you still need to free it. +//! pub fn settings_list_insert(list: SettingsList, idx: u64, value: SettingValue); //! //! /// Creates a new setting value from a settings map. The value is a copy of //! /// the settings map. Any changes to the original settings map afterwards @@ -302,6 +350,12 @@ //! /// value and are responsible for freeing it. You also retain ownership of //! /// the settings map, which means you still need to free it. //! pub fn setting_value_new_map(value: SettingsMap) -> SettingValue; +//! /// Creates a new setting value from a settings list. The value is a copy of +//! /// the settings list. Any changes to the original settings list afterwards +//! /// are not going to be perceived by the setting value. You own the setting +//! /// value and are responsible for freeing it. You also retain ownership of +//! /// the settings list, which means you still need to free it. +//! pub fn setting_value_new_list(value: SettingsList) -> SettingValue; //! /// Creates a new boolean setting value. You own the setting value and are //! /// responsible for freeing it. //! pub fn setting_value_new_bool(value: bool) -> SettingValue; @@ -317,6 +371,10 @@ //! pub fn setting_value_new_string(value_ptr: *const u8, value_len: usize) -> SettingValue; //! /// Frees a setting value. //! pub fn setting_value_free(value: SettingValue); +//! /// Copies a setting value. No changes inside the copy affect the original +//! /// setting value. You own the new setting value and are responsible for +//! /// freeing it. +//! pub fn setting_value_copy(value: SettingValue) -> SettingValue; //! /// Gets the value of a setting value as a settings map by storing it into //! /// the pointer provided. Returns `false` if the setting value is not a //! /// settings map. No value is stored into the pointer in that case. No @@ -324,6 +382,13 @@ //! /// which means you still need to free it. You own the settings map and are //! /// responsible for freeing it. //! pub fn setting_value_get_map(value: SettingValue, value_ptr: *mut SettingsMap) -> bool; +//! /// Gets the value of a setting value as a settings list by storing it into +//! /// the pointer provided. Returns `false` if the setting value is not a +//! /// settings list. No value is stored into the pointer in that case. No +//! /// matter what happens, you still retain ownership of the setting value, +//! /// which means you still need to free it. You own the settings list and are +//! /// responsible for freeing it. +//! pub fn setting_value_get_list(value: SettingValue, value_ptr: *mut SettingsList) -> bool; //! /// Gets the value of a boolean setting value by storing it into the pointer //! /// provided. Returns `false` if the setting value is not a boolean. No //! /// value is stored into the pointer in that case. No matter what happens, @@ -787,20 +852,19 @@ async fn run( let settings_map = runtime.settings_map(); let setting_value = settings_map.get(key.as_str()); - let user_setting_value = - match runtime.user_settings().iter().find(|x| *x.key == key) { - Some(user_setting) => match user_setting.kind { - UserSettingKind::Bool { default_value } => { - Some(SettingValue::Bool(default_value)) - } - UserSettingKind::Title { heading_level: _ } => None, - }, - None => None, - }; - if setting_value.is_some() { ret.send(setting_value.cloned()).ok(); } else { + let user_setting_value = + match runtime.user_settings().iter().find(|x| *x.key == key) { + Some(user_setting) => match user_setting.kind { + UserSettingKind::Bool { default_value } => { + Some(SettingValue::Bool(default_value)) + } + UserSettingKind::Title { heading_level: _ } => None, + }, + None => None, + }; ret.send(user_setting_value).ok(); }