From 3111d75b291d43c920571c42987fbcad388892e6 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 30 Jan 2022 18:15:01 +0100 Subject: [PATCH] Serialize custom persistent fields as base64 This still takes up more space than plain JSON would, but I don't know if serde_json would allow inlining that JSON. Presumably not. It would get hairy quickly as a regular embedded JSON string due to the encoding. --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/wrapper/state.rs | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 522d533d..a6a26820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ 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" @@ -31,6 +37,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "nih_plug" version = "0.1.0" dependencies = [ + "base64", "lazy_static", "nih_plug_derive", "serde", diff --git a/Cargo.toml b/Cargo.toml index 26400503..e52e8dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ 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" diff --git a/src/wrapper/state.rs b/src/wrapper/state.rs index 0317f4ee..bf40085c 100644 --- a/src/wrapper/state.rs +++ b/src/wrapper/state.rs @@ -16,6 +16,7 @@ //! Utilities for saving a [crate::plugin::Plugin]'s state. +use serde::de::Error; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -39,7 +40,38 @@ pub(crate) struct State { /// on the [Params] struct that's annotated with `#[persist = "stable_name"]` will be persisted /// this way. /// - /// TODO: Serialize this as base64 or some other more densely packed representation. Currently - /// every bute takes up 2-4 ASCII characters + /// 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>, } + +fn encode_fields(bytes: &HashMap>, serializer: S) -> Result +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>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let base64_map: HashMap = HashMap::deserialize(deserializer)?; + let decoded_map: Result>, 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?) +}