Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
graph_serializer.hpp
Go to the documentation of this file.
1#pragma once
2
3// GraphSerializer — encode/decode SignalGraph to .pulpgraph JSON.
4//
5// Captures (topology + per-node plugin state + editor layout) so a graph
6// can be saved to disk, transferred between machines, and rebuilt later.
7//
8// Plugin re-resolution: scanner-identity-first. The on-disk format stores
9// (format, unique_id, manufacturer, name, version, last_path, optional
10// SHA256 hash). On load, the resolver tries the identity tuple first, then
11// the path with hash verification, then surfaces a clear missing-plugin
12// error. Plugin binaries are NEVER embedded — that breaks signing,
13// notarization, and AU/AUv3 component-manager registration.
14//
15// Schema changes must go through registered migrations so older saved graphs
16// either upgrade deterministically or fail with an explicit load error.
17
20#include <filesystem>
21#include <cstdint>
22#include <functional>
23#include <string>
24#include <unordered_map>
25#include <vector>
26
27namespace pulp::host {
28
29class HostScanCache;
30class ScanBlacklist;
31
32// How from_json turns a plugin node's stored identity into a loaded slot.
33//
34// The file names each plugin node's `last_path`, and loading that path as
35// given would run whatever library it points at. So, in order:
36// 1. Audio Units resolve through the component registry by identity; the
37// stored path is never opened.
38// 2. `last_path` is loaded only when its real path (symlinks resolved) lies
39// inside a known plugin root (PluginScanner::default_paths plus
40// `plugin_roots`) and `blacklist` does not record it. After the load the
41// slot's identity must match the node's; a bundle that holds a different
42// plugin is refused.
43// 3. Otherwise the node is listed in LoadResult::missing_plugins with the
44// reason and is NOT loaded. Scanning the roots to re-find the plugin by
45// (format, unique_id) runs every installed bundle's entry point, so it is
46// an explicit opt-in through `scan_mode`, never the default.
48 // Never enumerate the roots: an unrooted node is missing.
50 // Enumerate through the pulp-scan-worker child only; when no worker can
51 // be found the node is missing and nothing runs in this process.
53 // Enumerate through the worker, and run CLAP entry points in this
54 // process when no worker can be found. Only for tools that already run
55 // arbitrary plugin code in-process by design.
57};
58
60 // Optional exact-version registry used to restore v4 Processor records.
61 // When absent, Processor records are reported as unsupported.
63 std::vector<ProcessorTypeKey> processor_allowlist;
64 // Extra directories whose bundles may be loaded, e.g. a project's own
65 // plugin folder. Each is matched after symlink resolution.
66 std::vector<std::string> plugin_roots;
67 // Whether the standard per-format install directories count as known
68 // roots. Hermetic tools and tests turn this off so a load never touches
69 // the machine's installed plugin collection.
71 // Bundles recorded here (a prior scan crash) are reported missing rather
72 // than loaded, and a scan run for `scan_mode` records its own crash and
73 // timeout verdicts into it, so the caller can persist them. Caller-owned;
74 // must outlive from_json.
76 // Earlier scan results, consulted by a `scan_mode` scan so an unchanged
77 // bundle is never handed to a worker again: without it a stale last_path
78 // costs one worker per installed bundle on every load. Caller-owned; must
79 // outlive from_json.
81 // When false no plugin node is loaded at all: each stays an unresolved
82 // placeholder with its identity, which is what a caller that cannot use
83 // plugin slots anyway (pulp bake) wants, since loading would run code
84 // for nothing.
85 bool load_plugins = true;
86 // Whether an unrooted node may be re-found by (format, unique_id) in the
87 // known roots. See GraphPluginScanMode.
89 // Worker binary for a scan; empty resolves it like ScanOptions does.
90 std::string scan_worker_path;
91 // Load a `last_path` that lies outside every known root, for a caller
92 // whose operator named that path on purpose. Trusts every path in the
93 // file; a library caller should prefer registering `plugin_roots`.
95};
96
98 public:
99 // File loads are bounded before parsing so a caller can safely hand a
100 // user-selected .pulpgraph path to the serializer. The limit covers the
101 // encoded JSON envelope (including base64 state), not decoded payloads.
102 static constexpr std::size_t max_file_bytes = 64u * 1024u * 1024u;
103
105 std::function<bool(const std::string& source_json,
106 std::string& destination_json)>;
107
109 static bool register_migration(int from_version,
110 int to_version,
111 MigrationFn migration);
112
113 // Encode the entire graph as JSON. The `editor_layout` map (NodeId →
114 // (x,y)) is optional — pass an empty map if the graph has no UI.
115 static std::string to_json(const SignalGraph& graph,
116 const std::unordered_map<NodeId, std::pair<float,float>>& editor_layout = {});
117
134
135 static const char* save_code_name(SaveResult::Code code) noexcept;
136
137 // Serialize one bounded graph file. The destination is replaced only
138 // after the complete canonical JSON payload has been written.
139 static SaveResult
140 to_file(const SignalGraph& graph, const std::filesystem::path& path,
141 const std::unordered_map<NodeId, std::pair<float, float>>& editor_layout = {});
142
143 // Result of a load attempt. Plugins that couldn't be re-resolved show
144 // up in `missing_plugins` with their original identity tuple — the
145 // graph still loads but those nodes have null PluginSlots.
146 struct LoadResult {
147 bool ok = false;
148 std::string error; // populated when ok == false
149 std::vector<std::string> missing_plugins; // identity strings
150 // Plugins that resolved and loaded, but rejected their persisted state.
151 // The graph remains loaded, but state is not guaranteed to have been
152 // restored; callers decide whether recovery is required without
153 // confusing this with a missing plugin.
154 std::vector<std::string> state_restore_failures; // identity strings
155 std::vector<std::string> missing_custom_node_types; // type_id@version
156 std::unordered_map<NodeId, std::pair<float,float>> editor_layout;
157 };
158
159 // Decode JSON into a freshly cleared graph. Returns LoadResult; the
160 // graph is mutated in-place. Two-pass: instantiate all nodes (resolve
161 // plugins per GraphLoadOptions, then PluginSlot::load), then walk
162 // connections and replay them. The overload without options uses the
163 // defaults: standard roots only, no blacklist, no scanning, unrooted
164 // paths refused.
165 static LoadResult from_json(SignalGraph& graph, const std::string& json);
166 static LoadResult from_json(SignalGraph& graph, const std::string& json,
167 const GraphLoadOptions& options);
168
169 // Read one bounded graph file, then apply the same migration and restore
170 // rules as from_json. A read or size failure leaves graph untouched.
171 static LoadResult from_file(SignalGraph& graph, const std::filesystem::path& path,
172 const GraphLoadOptions& options = {});
173};
174
175} // namespace pulp::host
Definition graph_serializer.hpp:97
static constexpr std::size_t max_file_bytes
Definition graph_serializer.hpp:102
static bool register_migration(int from_version, int to_version, MigrationFn migration)
static const char * save_code_name(SaveResult::Code code) noexcept
static int current_format_version()
static std::string to_json(const SignalGraph &graph, const std::unordered_map< NodeId, std::pair< float, float > > &editor_layout={})
static LoadResult from_file(SignalGraph &graph, const std::filesystem::path &path, const GraphLoadOptions &options={})
static LoadResult from_json(SignalGraph &graph, const std::string &json, const GraphLoadOptions &options)
static LoadResult from_json(SignalGraph &graph, const std::string &json)
std::function< bool(const std::string &source_json, std::string &destination_json)> MigrationFn
Definition graph_serializer.hpp:104
static SaveResult to_file(const SignalGraph &graph, const std::filesystem::path &path, const std::unordered_map< NodeId, std::pair< float, float > > &editor_layout={})
Definition scan_cache.hpp:32
Control-thread building block for hosts restoring a set of Processor nodes.
Definition processor_restore.hpp:99
Definition scan_blacklist.hpp:28
Definition signal_graph_runtime.hpp:83
Definition graph_runtime_buffer_assignment.hpp:8
std::uint32_t NodeId
Definition graph_types.hpp:7
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
GraphPluginScanMode
Definition graph_serializer.hpp:47
@ allow_in_process
Definition graph_serializer.hpp:56
@ none
Definition graph_serializer.hpp:49
@ isolated_only
Definition graph_serializer.hpp:52
Definition graph_serializer.hpp:59
bool load_plugins
Definition graph_serializer.hpp:85
bool include_default_plugin_roots
Definition graph_serializer.hpp:70
bool allow_unrooted_plugin_paths
Definition graph_serializer.hpp:94
std::string scan_worker_path
Definition graph_serializer.hpp:90
HostScanCache * scan_cache
Definition graph_serializer.hpp:80
std::vector< std::string > plugin_roots
Definition graph_serializer.hpp:66
ProcessorRestoreRegistry * processor_registry
Definition graph_serializer.hpp:62
GraphPluginScanMode scan_mode
Definition graph_serializer.hpp:88
ScanBlacklist * blacklist
Definition graph_serializer.hpp:75
std::vector< ProcessorTypeKey > processor_allowlist
Definition graph_serializer.hpp:63
Definition graph_serializer.hpp:146
std::vector< std::string > missing_custom_node_types
Definition graph_serializer.hpp:155
std::string error
Definition graph_serializer.hpp:148
std::vector< std::string > missing_plugins
Definition graph_serializer.hpp:149
bool ok
Definition graph_serializer.hpp:147
std::vector< std::string > state_restore_failures
Definition graph_serializer.hpp:154
std::unordered_map< NodeId, std::pair< float, float > > editor_layout
Definition graph_serializer.hpp:156
Definition graph_serializer.hpp:118
bool ok
Definition graph_serializer.hpp:130
Code
Definition graph_serializer.hpp:119
@ directory_sync_failed
Definition graph_serializer.hpp:127
@ temporary_unavailable
Definition graph_serializer.hpp:124
@ parent_unavailable
Definition graph_serializer.hpp:123
@ serialization_refused
Definition graph_serializer.hpp:121
@ write_failed
Definition graph_serializer.hpp:125
@ file_too_large
Definition graph_serializer.hpp:122
@ replace_failed
Definition graph_serializer.hpp:126
std::string error
Definition graph_serializer.hpp:132
Code code
Definition graph_serializer.hpp:131