1
0
Fork 0

Use BTreeMaps in the state

This ensures that the order is consistent when saving the same state
file multiple times.
This commit is contained in:
Robbert van der Helm 2022-07-02 19:10:56 +02:00
parent 71efe779a5
commit 5e6e920418
4 changed files with 21 additions and 11 deletions

View file

@ -6,6 +6,14 @@ new and what's changed, this document lists all breaking changes in reverse
chronological order. If a new feature did not require any changes to existing chronological order. If a new feature did not require any changes to existing
code then it will not be listed here. code then it will not be listed here.
## [2022-07-02]
- The `Params::serialize_fields()` and `Params::deserialize_fields()` methods
and the `State` struct now use `BTreeMap`s instead of `HashMap`s so the order
is consistent the plugin's state to JSON multiple times. These things are part
of NIH-plug's internals, so unless you're implementing the `Params` trait by
hand you will not notice any changes.
## [2022-06-01] ## [2022-06-01]
- The `ClapPlugin::CLAP_FEATURES` field now uses an array of `ClapFeature` - The `ClapPlugin::CLAP_FEATURES` field now uses an array of `ClapFeature`

View file

@ -255,8 +255,8 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
param_map param_map
} }
fn serialize_fields(&self) -> ::std::collections::HashMap<String, String> { fn serialize_fields(&self) -> ::std::collections::BTreeMap<String, String> {
let mut serialized = ::std::collections::HashMap::new(); let mut serialized = ::std::collections::BTreeMap::new();
#(#field_serialize_tokens)* #(#field_serialize_tokens)*
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*]; let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
@ -267,7 +267,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
serialized serialized
} }
fn deserialize_fields(&self, serialized: &::std::collections::HashMap<String, String>) { fn deserialize_fields(&self, serialized: &::std::collections::BTreeMap<String, String>) {
for (field_name, data) in serialized { for (field_name, data) in serialized {
match field_name.as_str() { match field_name.as_str() {
#(#field_deserialize_tokens)* #(#field_deserialize_tokens)*

View file

@ -1,6 +1,6 @@
//! Implementation details for the parameter management. //! Implementation details for the parameter management.
use std::collections::HashMap; use std::collections::BTreeMap;
use super::{Param, ParamFlags, ParamMut}; use super::{Param, ParamFlags, ParamMut};
@ -59,8 +59,8 @@ pub unsafe trait Params: 'static + Send + Sync {
/// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing /// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing
/// JSON-representations of those fields so they can be written to the plugin's state and /// JSON-representations of those fields so they can be written to the plugin's state and
/// recalled later. This uses [`serialize_field()`] under the hood. /// recalled later. This uses [`serialize_field()`] under the hood.
fn serialize_fields(&self) -> HashMap<String, String> { fn serialize_fields(&self) -> BTreeMap<String, String> {
HashMap::new() BTreeMap::new()
} }
/// Restore all fields marked with `#[persist = "stable_name"]` from a hashmap created by /// Restore all fields marked with `#[persist = "stable_name"]` from a hashmap created by
@ -69,7 +69,7 @@ pub unsafe trait Params: 'static + Send + Sync {
/// This gets called when the plugin's state is being restored. This uses [deserialize_field()] /// This gets called when the plugin's state is being restored. This uses [deserialize_field()]
/// under the hood. /// under the hood.
#[allow(unused_variables)] #[allow(unused_variables)]
fn deserialize_fields(&self, serialized: &HashMap<String, String>) {} fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {}
} }
/// Internal pointers to parameters. This is an implementation detail used by the wrappers for type /// Internal pointers to parameters. This is an implementation detail used by the wrappers for type

View file

@ -2,7 +2,7 @@
//! to plugins through the [`GuiContext`][crate::prelude::GuiContext]. //! to plugins through the [`GuiContext`][crate::prelude::GuiContext].
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use std::sync::Arc; use std::sync::Arc;
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::{ParamPtr, Params};
@ -25,19 +25,21 @@ pub enum ParamValue {
/// A plugin's state so it can be restored at a later point. This object can be serialized and /// A plugin's state so it can be restored at a later point. This object can be serialized and
/// deserialized using serde. /// deserialized using serde.
///
/// The fields are stored as `BTreeMap`s so the order in the serialized file is consistent.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginState { pub struct PluginState {
/// The plugin's parameter values. These are stored unnormalized. This mean sthe old values will /// The plugin's parameter values. These are stored unnormalized. This mean sthe old values will
/// be recalled when when the parameter's range gets increased. Doing so may still mess with /// be recalled when when the parameter's range gets increased. Doing so may still mess with
/// parameter automation though, depending on how the host impelments that. /// parameter automation though, depending on how the host impelments that.
pub params: HashMap<String, ParamValue>, pub params: BTreeMap<String, ParamValue>,
/// Arbitrary fields that should be persisted together with the plugin's parameters. Any field /// Arbitrary fields that should be persisted together with the plugin's parameters. Any field
/// on the [`Params`][crate::param::internals::Params] struct that's annotated with `#[persist = /// on the [`Params`][crate::param::internals::Params] struct that's annotated with `#[persist =
/// "stable_name"]` will be persisted this way. /// "stable_name"]` will be persisted this way.
/// ///
/// The individual fields are also serialized as JSON so they can safely be restored /// The individual fields are also serialized as JSON so they can safely be restored
/// independently of the other fields. /// independently of the other fields.
pub fields: HashMap<String, String>, pub fields: BTreeMap<String, String>,
} }
/// Create a parameters iterator from the hashtables stored in the plugin wrappers. This avoids /// Create a parameters iterator from the hashtables stored in the plugin wrappers. This avoids
@ -77,7 +79,7 @@ pub(crate) unsafe fn serialize_object<'a>(
// We'll serialize parameter values as a simple `string_param_id: display_value` map. // We'll serialize parameter values as a simple `string_param_id: display_value` map.
// NOTE: If the plugin is being modulated (and the plugin is a CLAP plugin in Bitwig Studio), // NOTE: If the plugin is being modulated (and the plugin is a CLAP plugin in Bitwig Studio),
// then this should save the values without any modulation applied to it // then this should save the values without any modulation applied to it
let params: HashMap<_, _> = params_iter let params: BTreeMap<_, _> = params_iter
.into_iter() .into_iter()
.map(|(param_id_str, param_ptr)| match param_ptr { .map(|(param_id_str, param_ptr)| match param_ptr {
ParamPtr::FloatParam(p) => ( ParamPtr::FloatParam(p) => (