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

View file

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

View file

@ -158,7 +158,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
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();
#(#field_serialize_tokens)*
@ -166,7 +166,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
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 {
match field_name.as_str() {
#(#field_deserialize_tokens)*

View file

@ -25,9 +25,9 @@ pub type FloatParam = PlainParam<f32>;
pub type IntParam = PlainParam<i32>;
/// 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.
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
/// when loading old state.
@ -357,13 +357,13 @@ pub trait Params {
/// 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
/// 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
/// [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
/// 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.

View file

@ -16,7 +16,6 @@
//! Utilities for saving a [crate::plugin::Plugin]'s state.
use serde::de::Error;
use serde::{Deserialize, Serialize};
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
/// this way.
///
/// The individual JSON-serialized fields are encoded as base64 strings so they don't take up as
/// much space in the preset. Storing them as a plain JSON string would have also been possible,
/// but that can get messy with escaping since those will likely also contain double quotes.
#[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?)
/// The individual fields are also serialized as JSON so they can safely be restored
/// independently of the other fields.
pub fields: HashMap<String, String>,
}