1
0
Fork 0

Store persistent fields as plain JSON strings

On second thought, this is much better. The resulting file size will be
much smaller because only double quotes need to be escaped. It's also
easier to read at a glance.
This commit is contained in:
Robbert van der Helm 2022-01-30 18:23:13 +01:00
parent 3111d75b29
commit 6494d1ed5f
5 changed files with 9 additions and 49 deletions

7
Cargo.lock generated
View file

@ -8,12 +8,6 @@ version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "gain" name = "gain"
version = "0.1.0" version = "0.1.0"
@ -37,7 +31,6 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
name = "nih_plug" name = "nih_plug"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64",
"lazy_static", "lazy_static",
"nih_plug_derive", "nih_plug_derive",
"serde", "serde",

View file

@ -10,7 +10,6 @@ members = ["nih_plug_derive", "plugins/gain", "xtask"]
[dependencies] [dependencies]
nih_plug_derive = { path = "nih_plug_derive" } nih_plug_derive = { path = "nih_plug_derive" }
base64 = "0.13"
lazy_static = "1.4" lazy_static = "1.4"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View file

@ -158,7 +158,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
param_map param_map
} }
fn serialize_fields(&self) -> ::std::collections::HashMap<String, Vec<u8>> { fn serialize_fields(&self) -> ::std::collections::HashMap<String, String> {
let mut serialized = ::std::collections::HashMap::new(); let mut serialized = ::std::collections::HashMap::new();
#(#field_serialize_tokens)* #(#field_serialize_tokens)*
@ -166,7 +166,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
serialized serialized
} }
fn deserialize_fields(&self, serialized: &::std::collections::HashMap<String, Vec<u8>>) { fn deserialize_fields(&self, serialized: &::std::collections::HashMap<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

@ -25,9 +25,9 @@ pub type FloatParam = PlainParam<f32>;
pub type IntParam = PlainParam<i32>; pub type IntParam = PlainParam<i32>;
/// Re-export for use in the [Params] proc-macro. /// Re-export for use in the [Params] proc-macro.
pub use serde_json::from_slice as deserialize_field; pub use serde_json::from_str as deserialize_field;
/// Re-export for use in the [Params] proc-macro. /// Re-export for use in the [Params] proc-macro.
pub use serde_json::to_vec as serialize_field; pub use serde_json::to_string as serialize_field;
/// The functinoality needed for persisting a field to the plugin's state, and for restoring values /// The functinoality needed for persisting a field to the plugin's state, and for restoring values
/// when loading old state. /// when loading old state.
@ -357,13 +357,13 @@ pub trait Params {
/// 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, Vec<u8>>; fn serialize_fields(&self) -> HashMap<String, String>;
/// 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
/// [Self::serialize_fields]. All of thse fields should be wrapped in a [PersistentField] with /// [Self::serialize_fields]. All of thse fields should be wrapped in a [PersistentField] with
/// thread safe interior mutability, like an `RwLock` or a `Mutex`. This gets called when the /// thread safe interior mutability, like an `RwLock` or a `Mutex`. This gets called when the
/// plugin's state is being restored. This uses [deserialize_field] under the hood. /// plugin's state is being restored. This uses [deserialize_field] under the hood.
fn deserialize_fields(&self, serialized: &HashMap<String, Vec<u8>>); fn deserialize_fields(&self, serialized: &HashMap<String, String>);
} }
/// Internal pointers to parameters. This is an implementation detail used by the wrappers. /// Internal pointers to parameters. This is an implementation detail used by the wrappers.

View file

@ -16,7 +16,6 @@
//! Utilities for saving a [crate::plugin::Plugin]'s state. //! Utilities for saving a [crate::plugin::Plugin]'s state.
use serde::de::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@ -40,38 +39,7 @@ pub(crate) struct State {
/// on the [Params] struct that's annotated with `#[persist = "stable_name"]` will be persisted /// on the [Params] struct that's annotated with `#[persist = "stable_name"]` will be persisted
/// this way. /// this way.
/// ///
/// The individual JSON-serialized fields are encoded as base64 strings so they don't take up as /// The individual fields are also serialized as JSON so they can safely be restored
/// much space in the preset. Storing them as a plain JSON string would have also been possible, /// independently of the other fields.
/// but that can get messy with escaping since those will likely also contain double quotes. pub fields: HashMap<String, String>,
#[serde(serialize_with = "encode_fields")]
#[serde(deserialize_with = "decode_fields")]
pub fields: HashMap<String, Vec<u8>>,
}
fn encode_fields<S>(bytes: &HashMap<String, Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_map(
bytes
.into_iter()
.map(|(id, json)| (id, base64::encode(json))),
)
}
fn decode_fields<'de, D>(deserializer: D) -> Result<HashMap<String, Vec<u8>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let base64_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let decoded_map: Result<HashMap<String, Vec<u8>>, D::Error> = base64_map
.into_iter()
.map(|(id, base64)| {
base64::decode(base64)
.map(|decoded| (id, decoded))
.map_err(|err| D::Error::custom(format!("base64 decode failed: {}", err)))
})
.collect();
Ok(decoded_map?)
} }