Skip to content

Reverting back to JSON serialization #587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,88 +31,6 @@ void NonParamState::reset()
value->reset();
}

void NonParamState::serialize (ChainedArenaAllocator& arena, const NonParamState& state)
{
auto* serialize_num_bytes = arena.allocate<bytes_detail::size_type> (1, 1);
size_t num_bytes = 0;
for (const auto& value : state.values)
{
num_bytes += serialize_string (value->name, arena);
num_bytes += value->serialize (arena);
}
serialize_direct (serialize_num_bytes, num_bytes);
}

void NonParamState::deserialize (nonstd::span<const std::byte>& serial_data, NonParamState& state, ChainedArenaAllocator& arena)
{
if (serial_data.size() < bytes_detail::sizeof_s)
{
state.reset();
return;
}

auto num_bytes = deserialize_direct<bytes_detail::size_type> (serial_data);
if (num_bytes == 0)
{
state.reset();
return;
}

auto data = serial_data.subspan (0, num_bytes);
serial_data = serial_data.subspan (num_bytes);

const auto _ = arena.create_frame();
auto values_copy = arena::make_span<StateValueBase*> (arena, state.values.size());
std::copy (state.values.begin(), state.values.end(), values_copy.begin());
auto values_iter = values_copy.begin();
size_t counter = 0;
const auto get_value_ptr = [&] (std::string_view name) -> StateValueBase*
{
const auto returner = [&] (auto& iter)
{
auto* ptr = *iter;
*iter = nullptr;
++iter;
values_iter = iter;
counter++;
return ptr;
};

for (auto iter = values_iter; iter != values_copy.end(); ++iter)
{
if (*iter != nullptr && (*iter)->name == name)
return returner (iter);
}
for (auto iter = values_copy.begin(); iter != values_iter; ++iter)
{
if (*iter != nullptr && (*iter)->name == name)
return returner (iter);
}
return nullptr;
};

while (! data.empty())
{
const auto value_name = deserialize_string (data);
auto* value = get_value_ptr (value_name);
if (value == nullptr)
{
const auto value_num_bytes = deserialize_direct<bytes_detail::size_type> (data);
data = data.subspan (value_num_bytes);
continue;
}

value->deserialize (data);
}

if (counter < values_copy.size())
{
for (auto* value : values_copy)
if (value != nullptr)
value->reset();
}
}

json NonParamState::serialize_json (const NonParamState& state)
{
auto serial = nlohmann::json::object();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ class NonParamState
/** Resets all the state values to their defaults */
void reset();

/** Custom serializer */
static void serialize (ChainedArenaAllocator& arena, const NonParamState& state);

/** Custom deserializer */
static void deserialize (nonstd::span<const std::byte>& serial_data, NonParamState& state, ChainedArenaAllocator& arena);

/** Custom serializer */
static json serialize_json (const NonParamState& state);

Expand Down
124 changes: 6 additions & 118 deletions modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParamHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ size_t ParamHolder::doForAllParameters (Callable&& callable, size_t index) const
return index;
}

inline void ParamHolder::reset()
{
doForAllParameters ([] (auto& param, size_t)
{ ParameterTypeHelpers::resetParameter (param); });
}

inline void ParamHolder::getParameterPointers (ParamHolder& holder, ParamDeserialList& parameters)
{
for (auto& thing : holder.things)
Expand Down Expand Up @@ -286,124 +292,6 @@ inline void ParamHolder::getParameterPointers (ParamHolder& holder, ParamDeseria
}
}

inline void ParamHolder::serialize (ChainedArenaAllocator& arena, const ParamHolder& paramHolder)
{
auto* serialize_num_bytes = arena.allocate<bytes_detail::size_type> (1, 1);
size_t num_bytes = 0;
paramHolder.doForAllParameters (
[&] (auto& param, size_t)
{
num_bytes += serialize_string (toStringView (param.paramID), arena);
num_bytes += serialize_object (ParameterTypeHelpers::getValue (param), arena);
});
serialize_direct (serialize_num_bytes, num_bytes);
}

inline void ParamHolder::deserialize (nonstd::span<const std::byte>& serial_data, ParamHolder& paramHolder)
{
using namespace ParameterTypeHelpers;
auto num_bytes = deserialize_direct<bytes_detail::size_type> (serial_data);
if (num_bytes == 0)
{
paramHolder.doForAllParameters (
[&] (auto& param, size_t)
{
ParameterTypeHelpers::resetParameter (param);
});
return;
}

auto data = serial_data.subspan (0, num_bytes);
serial_data = serial_data.subspan (num_bytes);

const auto _ = paramHolder.arena->create_frame();
ParamDeserialList parameters { *paramHolder.arena };
getParameterPointers (paramHolder, parameters);

auto params_iter = parameters.begin();
size_t counter = 0;
const auto get_param_ptr = [&] (std::string_view paramID) -> ThingPtr
{
const auto returner = [&] (auto& iter)
{
(*iter).found = true;
auto ptr = (*iter).ptr;
++iter;
params_iter = iter;
counter++;
return ptr;
};

for (auto iter = params_iter; iter != parameters.end(); ++iter)
{
if ((*iter).id == paramID)
return returner (iter);
}
for (auto iter = parameters.begin(); iter != params_iter; ++iter)
{
if ((*iter).id == paramID)
return returner (iter);
}
return {};
};

while (! data.empty())
{
const auto param_id = deserialize_string (data);
auto param_ptr = get_param_ptr (param_id);
if (param_ptr == nullptr)
{
const auto param_num_bytes = deserialize_direct<bytes_detail::size_type> (data);
data = data.subspan (param_num_bytes);
continue;
}

const auto type = getType (param_ptr);
switch (type)
{
case FloatParam:
setValue (deserialize_object<ParameterElementType<FloatParameter>> (data),
*reinterpret_cast<FloatParameter*> (param_ptr.get_ptr()));
break;
case ChoiceParam:
setValue (deserialize_object<ParameterElementType<ChoiceParameter>> (data),
*reinterpret_cast<ChoiceParameter*> (param_ptr.get_ptr()));
break;
case BoolParam:
setValue (deserialize_object<ParameterElementType<BoolParameter>> (data),
*reinterpret_cast<BoolParameter*> (param_ptr.get_ptr()));
break;
default:
break;
}
}

if (counter < parameters.count())
{
for (auto [param_id, param_ptr, found] : parameters)
{
if (found)
continue;

const auto type = getType (param_ptr);
switch (type)
{
case FloatParam:
resetParameter (*reinterpret_cast<FloatParameter*> (param_ptr.get_ptr()));
break;
case ChoiceParam:
resetParameter (*reinterpret_cast<ChoiceParameter*> (param_ptr.get_ptr()));
break;
case BoolParam:
resetParameter (*reinterpret_cast<BoolParameter*> (param_ptr.get_ptr()));
break;
default:
break;
}
}
}
}

inline json ParamHolder::serialize_json (const ParamHolder& paramHolder)
{
auto serial = nlohmann::json::object();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,8 @@ class ParamHolder
template <typename Callable>
size_t doForAllParameters (Callable&& callable, size_t index = 0) const;

/** Custom serializer */
static void serialize (ChainedArenaAllocator& arena, const ParamHolder& paramHolder);

/** Custom deserializer */
static void deserialize (nonstd::span<const std::byte>& serial_data, ParamHolder& paramHolder);
/** Resets all parameters to their default values */
void reset();

/** Custom serializer */
static json serialize_json (const ParamHolder& paramHolder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,7 @@ PluginStateImpl<ParameterState, NonParameterState>::~PluginStateImpl()
template <typename ParameterState, typename NonParameterState>
void PluginStateImpl<ParameterState, NonParameterState>::serialize (juce::MemoryBlock& data) const
{
auto& arena = const_cast<ChainedArenaAllocator&> (*params.arena);
const auto frame = arena.create_frame();

#if defined JucePlugin_VersionString
serialize_object (currentPluginVersion.getVersionHint(), arena);
#else
serialize_object (int {}, arena);
#endif

NonParamState::serialize (arena, nonParams);
ParamHolder::serialize (arena, params);

dump_serialized_bytes (data, arena, &frame);
JSONUtils::toMemoryBlock (serialize (*this), data);
}

template <typename ParameterState, typename NonParameterState>
Expand All @@ -43,26 +31,23 @@ void PluginStateImpl<ParameterState, NonParameterState>::deserialize (juce::Memo
callOnMainThread (
[this, data = std::move (dataBlock)]
{
nonstd::span serial_data { static_cast<const std::byte*> (data.getData()), data.getSize() };
if (serial_data.size() > 8
&& (static_cast<char> (serial_data[2]) == '['
|| static_cast<char> (serial_data[2]) == '{'))
try
{
deserialize (JSONUtils::fromMemoryBlock (data), *this);

params.applyVersionStreaming (pluginStateVersion);
if (nonParams.versionStreamingCallback != nullptr)
nonParams.versionStreamingCallback (pluginStateVersion);
}
else
catch (const std::exception& e)
{
pluginStateVersion = Version::fromVersionHint (deserialize_object<int> (serial_data));
NonParamState::deserialize (serial_data, nonParams, *params.arena);
ParamHolder::deserialize (serial_data, params);
juce::Logger::writeToLog (juce::String { "Encountered exception while deserializing plugin state: " } + e.what());
juce::Logger::writeToLog ("Resetting plugin state...");
params.reset();
nonParams.reset();
}

params.applyVersionStreaming (pluginStateVersion);
if (nonParams.versionStreamingCallback != nullptr)
nonParams.versionStreamingCallback (pluginStateVersion);

getParameterListeners().updateBroadcastersFromMessageThread();

if (undoManager != nullptr)
undoManager->clearUndoHistory();
});
Expand Down
68 changes: 0 additions & 68 deletions modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ struct StateValueBase
[[nodiscard]] virtual nlohmann::json serialize_json() const { return {}; }
virtual void deserialize_json (const nlohmann::json&) {}

[[nodiscard]] virtual size_t serialize (ChainedArenaAllocator&) const { return 0; }
virtual void deserialize (nonstd::span<const std::byte>&) {}

const std::string_view name {};
Broadcaster<void()> changeBroadcaster {};
};
Expand Down Expand Up @@ -85,71 +82,6 @@ struct StateValue : StateValueBase
set (deserial.get<element_type>());
}

/** Binary serializer */
[[nodiscard]] size_t serialize (ChainedArenaAllocator& arena) const override
{
static constexpr auto is_span = TypeTraits::IsIterable<T> && ! TypeTraits::IsMapLike<T>;

// Values need to track how many bytes they're serializing so the parent can know also.
auto* serialize_num_bytes = arena.allocate<bytes_detail::size_type> (1, 1);

size_t num_bytes = 0;
if constexpr (std::is_same_v<T, json>)
{
num_bytes = serialize_string (get().dump(), arena);
}
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
{
num_bytes = serialize_string (currentValue, arena);
}
else if constexpr (std::is_same_v<T, juce::String>)
{
num_bytes = serialize_string (toStringView (currentValue), arena);
}
else if constexpr (is_span)
{
num_bytes = serialize_span<typename T::value_type> (currentValue, arena);
}
else
{
num_bytes = serialize_object (get(), arena);
}

serialize_direct (serialize_num_bytes, num_bytes);
return bytes_detail::sizeof_s + num_bytes;
}

void deserialize (nonstd::span<const std::byte>& data) override
{
static constexpr auto is_span = TypeTraits::IsIterable<T> && ! TypeTraits::IsMapLike<T>;

[[maybe_unused]] const auto num_bytes = deserialize_direct<bytes_detail::size_type> (data);
if constexpr (std::is_same_v<T, json>)
{
set (json::parse (deserialize_string (data)));
}
else if constexpr (std::is_same_v<T, std::string>)
{
set (std::string { deserialize_string (data) });
}
else if constexpr (std::is_same_v<T, std::string_view>)
{
set (deserialize_string (data));
}
else if constexpr (std::is_same_v<T, juce::String>)
{
set (toString (deserialize_string (data)));
}
else if constexpr (is_span)
{
deserialize_span<typename T::value_type> (currentValue, data);
}
else
{
set (deserialize_object<element_type> (data));
}
}

const element_type defaultValue;

private:
Expand Down
Loading