Skip to content
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

State serialization improvements #570

Merged
merged 25 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
335135b
Re-working plugin state serialization
jatinchowdhury18 Nov 28, 2024
5cd410c
Remove dead comments
jatinchowdhury18 Nov 28, 2024
2660c14
Better backrds compatibility
jatinchowdhury18 Nov 29, 2024
637be67
Merge 2660c1471a84db5d15911160b442d6da2f244193 into e043db1d1f0497922…
jatinchowdhury18 Nov 29, 2024
6d8b920
Apply clang-format
github-actions[bot] Nov 29, 2024
b3f9bc2
Serialization work
jatinchowdhury18 Nov 30, 2024
abf8079
Merge b3f9bc24a037521d156618293c08403abf4fae5a into e043db1d1f0497922…
jatinchowdhury18 Nov 30, 2024
e2e7466
Apply clang-format
github-actions[bot] Nov 30, 2024
5b9e933
More byte serialization work
jatinchowdhury18 Nov 30, 2024
4581c51
Serialization for non-parameter state
jatinchowdhury18 Nov 30, 2024
876a53e
Binary serialization mostly working
jatinchowdhury18 Nov 30, 2024
fb1df93
Merge 876a53ef8267631de6492726bbdd47bdaaa6a44e into e043db1d1f0497922…
jatinchowdhury18 Nov 30, 2024
b10f994
Apply clang-format
github-actions[bot] Nov 30, 2024
e083303
Mossing arena frame
jatinchowdhury18 Nov 30, 2024
47dc588
Tweaking plugin state arena usage
jatinchowdhury18 Dec 1, 2024
e9d7646
Small fixes
jatinchowdhury18 Dec 1, 2024
f53c9e6
Merge e9d76467216e62e334ef91b94eba29f990f88852 into e043db1d1f0497922…
jatinchowdhury18 Dec 1, 2024
38f1427
Apply clang-format
github-actions[bot] Dec 1, 2024
75cc51c
Using ChunkList for parameter deserialization
jatinchowdhury18 Dec 2, 2024
4a4b5f5
Updating non-param state
jatinchowdhury18 Dec 2, 2024
fc0c577
Fixes
jatinchowdhury18 Dec 2, 2024
946ebcd
More fixes
jatinchowdhury18 Dec 2, 2024
76a5633
Adding a couple of tests
jatinchowdhury18 Dec 2, 2024
b24abbd
Merge 76a5633de880eb18120cfc92f3801ace51ae7384 into e043db1d1f0497922…
jatinchowdhury18 Dec 2, 2024
c642319
Apply clang-format
github-actions[bot] Dec 2, 2024
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
4 changes: 2 additions & 2 deletions examples/StatefulPlugin/PluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ PluginEditor::PluginEditor (StatefulPlugin& plug) : juce::AudioProcessorEditor (
const auto setSizeFromState = [this]
{
const auto& stateSize = plugin.getState().nonParams.editorBounds.get();
setSize (stateSize.x, stateSize.y);
setSize (stateSize.first, stateSize.second);
};
setSizeFromState();

Expand Down Expand Up @@ -65,5 +65,5 @@ void PluginEditor::resized()
undoButton.setBounds (bounds.removeFromLeft (80));
redoButton.setBounds (bounds.removeFromLeft (80));

plugin.getState().nonParams.editorBounds = getLocalBounds().getBottomRight();
plugin.getState().nonParams.editorBounds = std::make_pair (getLocalBounds().getRight(), getLocalBounds().getBottom());
}
2 changes: 1 addition & 1 deletion examples/StatefulPlugin/StatefulPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct PluginNonParameterState : chowdsp::NonParamState
addStateValues ({ &editorBounds });
}

chowdsp::StateValue<juce::Point<int>> editorBounds { "editor_bounds", { 300, 500 } };
chowdsp::StateValue<std::pair<int, int>> editorBounds { "editor_bounds", { 300, 500 } };
};

using State = chowdsp::PluginStateImpl<PluginParameterState, PluginNonParameterState>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ class ArenaAllocator
return reinterpret_cast<T*> (raw_data.data() + offset_bytes);
}

/** Returns a pointer to the internal buffer with a given offset in bytes */
template <typename T, typename IntType>
const T* data (IntType offset_bytes) const noexcept
{
return reinterpret_cast<const T*> (raw_data.data() + offset_bytes);
}

/**
* Creates a "frame" for the allocator.
* Once the frame goes out of scope, the allocator will be reset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ class ChainedArenaAllocator
return get_current_arena().template data<T> (offset_bytes);
}

/** Returns the default size for an individual arena */
[[nodiscard]] size_t get_default_arena_size() const noexcept
{
return arena_size_bytes;
}

/** Returns the arena currently being used */
ArenaAllocatorView& get_current_arena()
{
Expand All @@ -133,6 +139,11 @@ class ChainedArenaAllocator
return arena_list.count;
}

[[nodiscard]] auto* get_extra_alloc_list() const noexcept
{
return extra_alloc_list;
}

/**
* Returns the total number of bytes currently being used
* by this allocator.
Expand Down
52 changes: 52 additions & 0 deletions modules/common/chowdsp_json/JSONUtils/chowdsp_JSONAdaptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#if JUCE_MODULE_AVAILABLE_chowdsp_version
#include <chowdsp_version/chowdsp_version.h>
#endif

#ifndef DOXYGEN
namespace nlohmann
{
/** Adapter so that nlohmann::json can serialize juce::String */
template <>
struct adl_serializer<juce::String>
{
static void to_json (json& j, const juce::String& s)
{
j = s.toUTF8();
}

static void from_json (const json& j, juce::String& s)
{
s = j.get<std::string>();
}
};

#if JUCE_MODULE_AVAILABLE_chowdsp_version
/** Adapter so that nlohmann::json can serialize chowdsp::Version */
template <>
struct adl_serializer<::chowdsp::Version>
{
static void to_json (json& j, const ::chowdsp::Version& version)
{
j = version.getVersionHint();
}

static void from_json (const json& j, ::chowdsp::Version& version)
{
if (! j.is_number_integer())
{
version = {};
return;
}

const auto versionHint = j.get<int>();
const auto major = versionHint / 10000;
const auto minor = (versionHint % 10000) / 100;
const auto patch = versionHint % 100;
version = { major, minor, patch };
}
};
#endif
} // namespace nlohmann
#endif
21 changes: 0 additions & 21 deletions modules/common/chowdsp_json/JSONUtils/chowdsp_StringAdapter.h

This file was deleted.

3 changes: 1 addition & 2 deletions modules/common/chowdsp_json/chowdsp_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,5 @@ namespace chowdsp
using json = nlohmann::json;
} // namespace chowdsp

#include "JSONUtils/chowdsp_StringAdapter.h"

#include "JSONUtils/chowdsp_JSONAdaptors.h"
#include "JSONUtils/chowdsp_JSONUtils.h"
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace chowdsp
{
nonstd::span<const std::byte> get_bytes_for_deserialization (nonstd::span<const std::byte>& bytes)
{
using namespace bytes_detail;

size_type bytes_count;
std::memcpy (&bytes_count, bytes.data(), sizeof_s);
jassert (sizeof_s + bytes_count <= bytes.size());

const auto serial_bytes = bytes.subspan (sizeof_s, bytes_count);
bytes = bytes.subspan (sizeof_s + bytes_count);

return serial_bytes;
}

size_t get_serial_num_bytes (ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame)
{
size_t num_bytes = 0;
auto* start_arena = frame == nullptr ? arena.get_arenas().head : frame->arena_at_start;
const auto add_bytes_count = [&num_bytes, start_arena, frame] (const ArenaAllocatorView& arena_node)
{
size_t bytes_offset = 0;
if (start_arena == &arena_node && frame != nullptr)
bytes_offset = frame->arena_frame.bytes_used_at_start;
num_bytes += arena_node.get_bytes_used() - bytes_offset;
};

for (auto* arena_node = start_arena; arena_node != &arena.get_current_arena(); arena_node = arena_node->next)
add_bytes_count (*arena_node);
add_bytes_count (arena.get_current_arena());

return num_bytes;
}

void dump_serialized_bytes (nonstd::span<std::byte> serial,
ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame)
{
const auto num_bytes = serial.size();
jassert (num_bytes == get_serial_num_bytes (arena, frame));

auto* start_arena = frame == nullptr ? arena.get_arenas().head : frame->arena_at_start;
size_t bytes_counter = 0;
const auto copy_bytes = [num_bytes, start_arena, frame, &serial, &bytes_counter] (const ArenaAllocatorView& arena_node)
{
size_t bytes_offset = 0;
if (start_arena == &arena_node && frame != nullptr)
bytes_offset = frame->arena_frame.bytes_used_at_start;

const auto bytes_to_copy = std::min (arena_node.get_bytes_used() - bytes_offset,
num_bytes - bytes_counter);
std::memcpy (serial.data() + bytes_counter, arena_node.data<std::byte> (bytes_offset), bytes_to_copy);

bytes_counter += bytes_to_copy;
};
for (auto* arena_node = start_arena; arena_node != &arena.get_current_arena(); arena_node = arena_node->next)
copy_bytes (*arena_node);
copy_bytes (arena.get_current_arena());
}

void dump_serialized_bytes (juce::MemoryBlock& data,
ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame)
{
const auto initial_size = data.getSize();
const auto num_bytes = get_serial_num_bytes (arena, frame);
data.setSize (initial_size + num_bytes);
dump_serialized_bytes ({ static_cast<std::byte*> (data.getData()) + initial_size, num_bytes }, arena, frame);
}

std::string_view deserialize_string (nonstd::span<const std::byte>& bytes)
{
const auto serial_bytes = get_bytes_for_deserialization (bytes);
return { reinterpret_cast<const char*> (serial_bytes.data()), serial_bytes.size() };
}
} // namespace chowdsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#pragma once

namespace chowdsp
{
#ifndef DOXYGEN
namespace bytes_detail
{
using size_type = uint16_t;
static constexpr auto sizeof_s = sizeof (size_type);
} // namespace bytes_detail
#endif

template <typename ArenaType>
static std::byte* get_bytes_for_serialization (size_t bytes_count, ArenaType& arena)
{
using namespace bytes_detail;
jassert (bytes_count <= std::numeric_limits<size_type>::max());

if constexpr (std::is_same_v<ArenaType, ChainedArenaAllocator>)
jassert (sizeof_s + bytes_count <= arena.get_default_arena_size());

auto* bytes = static_cast<std::byte*> (arena.allocate_bytes (sizeof_s + bytes_count, 1));
jassert (bytes != nullptr);

const auto bytes_count_cast = static_cast<size_type> (bytes_count);
std::memcpy (bytes, &bytes_count_cast, sizeof_s);

return bytes + sizeof_s; // NOLINT
}

nonstd::span<const std::byte> get_bytes_for_deserialization (nonstd::span<const std::byte>& bytes);

template <typename TDest, typename TSource>
void serialize_direct (TDest* ptr, const TSource& source)
{
const auto source_cast = static_cast<TDest> (source);
std::memcpy (ptr, &source_cast, sizeof (TDest));
}

template <typename T, typename ArenaType>
static size_t serialize_object (const T& object, ArenaType& arena)
{
auto* bytes = get_bytes_for_serialization (sizeof (T), arena);
std::memcpy (bytes, &object, sizeof (T)); // NOLINT
return bytes_detail::sizeof_s + sizeof (T);
}

template <typename T, typename ArenaType>
static size_t serialize_span (nonstd::span<const T> data, ArenaType& arena)
{
const auto num_bytes = sizeof (T) * data.size();
auto* bytes = get_bytes_for_serialization (num_bytes, arena);
std::memcpy (bytes, data.data(), num_bytes); // NOLINT
return bytes_detail::sizeof_s + num_bytes;
}

template <typename ArenaType>
static size_t serialize_string (std::string_view str, ArenaType& arena)
{
const auto num_bytes = sizeof (char) * str.size();
auto* bytes = get_bytes_for_serialization (num_bytes, arena);
std::memcpy (bytes, str.data(), num_bytes); // NOLINT
return bytes_detail::sizeof_s + num_bytes;
}

template <typename MemoryResourceType>
static nonstd::span<const std::byte> dump_serialized_bytes (const ArenaAllocator<MemoryResourceType>& arena,
const typename ArenaAllocator<MemoryResourceType>::Frame* frame = nullptr)
{
const auto bytes_offset = frame == nullptr ? 0 : frame->bytes_used_at_start;
const auto bytes_count = arena.get_bytes_used() - bytes_offset;
return { arena.template data<std::byte> (bytes_offset), bytes_count };
}

size_t get_serial_num_bytes (ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame = nullptr);

void dump_serialized_bytes (nonstd::span<std::byte> serial,
ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame = nullptr);

void dump_serialized_bytes (juce::MemoryBlock& data,
ChainedArenaAllocator& arena,
const ChainedArenaAllocator::Frame* frame = nullptr);

template <typename T>
T deserialize_direct (nonstd::span<const std::byte>& bytes)
{
T x;
std::memcpy (&x, bytes.data(), sizeof (T));
bytes = bytes.subspan (sizeof (T));
return x;
}

template <typename T>
static T deserialize_object (nonstd::span<const std::byte>& bytes)
{
const auto serial_bytes = get_bytes_for_deserialization (bytes);
jassert (serial_bytes.size() == sizeof (T));

JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wclass-memaccess")
T object;
std::memcpy (&object, serial_bytes.data(), serial_bytes.size());
return object;
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}

template <typename T>
static size_t get_span_length (const nonstd::span<const std::byte>& bytes)
{
using namespace bytes_detail;

size_type sizeof_span_bytes;
std::memcpy (&sizeof_span_bytes, bytes.data(), sizeof_s);

jassert (sizeof_span_bytes % sizeof (T) == 0);
return static_cast<size_t> (sizeof_span_bytes) / sizeof (T);
}

template <typename T>
static void deserialize_span (nonstd::span<T> span, nonstd::span<const std::byte>& bytes)
{
const auto serial_bytes = get_bytes_for_deserialization (bytes);

jassert (serial_bytes.size() % sizeof (T) == 0);
jassert (serial_bytes.size() / sizeof (T) == span.size());

std::memcpy (span.data(), serial_bytes.data(), serial_bytes.size());
}

std::string_view deserialize_string (nonstd::span<const std::byte>& bytes);
} // namespace chowdsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "chowdsp_serialization.h"

#include "Serialization/chowdsp_ByteSerializer.cpp"
4 changes: 3 additions & 1 deletion modules/common/chowdsp_serialization/chowdsp_serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ BEGIN_JUCE_MODULE_DECLARATION
version: 2.3.0
name: ChowDSP Serialization Utilities
description: Utility methods for serializing data structures into XML, JSON, or some other format
dependencies: juce_core, chowdsp_core, chowdsp_json, chowdsp_reflection
dependencies: juce_core, chowdsp_core, chowdsp_json, chowdsp_reflection, chowdsp_data_structures

website: https://ccrma.stanford.edu/~jatin/chowdsp
license: BSD 3-Clause
Expand All @@ -25,8 +25,10 @@ BEGIN_JUCE_MODULE_DECLARATION
#include <chowdsp_core/chowdsp_core.h>
#include <chowdsp_json/chowdsp_json.h>
#include <chowdsp_reflection/chowdsp_reflection.h>
#include <chowdsp_data_structures/chowdsp_data_structures.h>

#include "Serialization/chowdsp_BaseSerializer.h"
#include "Serialization/chowdsp_Serialization.h"
#include "Serialization/chowdsp_JSONSerializer.h"
#include "Serialization/chowdsp_XMLSerializer.h"
#include "Serialization/chowdsp_ByteSerializer.h"
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ FloatParameter::FloatParameter (const ParameterID& parameterID,
{ return valueToTextFunction (v); })
.withValueFromStringFunction (std::move (textToValueFunction))),
#endif
unsnappedDefault (valueRange.convertTo0to1 (defaultFloatValue)),
defaultValueInRange (defaultFloatValue),
normalisableRange (valueRange)
{
}
Expand Down
Loading