Program Listing for File deserialization.cc#
↰ Return to documentation for file (src/deserialization.cc)
#include "nodes-graph/deserialization.h"
namespace ng::deserialization {
namespace {
// Verifies that a length-prefixed element consumed exactly the number of bytes announced.
void check_consumed(const ReadHead& head, std::size_t start, sr::len_t len, const char* what) {
if (head.pos - start != len)
throw std::runtime_error(std::string(what) + " length does not match the expected size");
}
} // namespace
// --- Base types
sr::len_t deserialize_len(ReadHead& head) {
sr::len_t len;
head.read(&len, sizeof(len));
return len;
}
sr::bound_t deserialize_bound(ReadHead& head) {
sr::bound_t bound;
head.read(&bound, sizeof(bound));
return bound;
}
bool deserialize_bool(ReadHead& head) {
uint8_t raw;
head.read(&raw, sizeof(raw));
if (raw > 1)
throw std::runtime_error("Invalid boolean value");
return raw == 1;
}
socket_t deserialize_socket_type(ReadHead& head) {
socket_t socket_type;
head.read(&socket_type, sizeof(socket_type));
return socket_type;
}
// --- Strings and identifiers
std::string deserialize_string(ReadHead& head) {
sr::len_t len = deserialize_len(head);
if (len == 0)
throw std::runtime_error("String length must include the null terminator");
head.require(len);
if (head.c_str()[len - 1] != '\0')
throw std::runtime_error("String not null-terminated");
std::string result(head.c_str(), len - 1);
head.pos += len;
return result;
}
qualifier_t deserialize_qualifier(ReadHead& head) {
std::size_t start = head.pos;
sr::len_t len = deserialize_len(head);
qualifier_t qualifier;
for (std::size_t i = 0 ; i < qualifier_t::N_CHUNKS ; i++) {
qualifier.chunks[i] = deserialize_array<QUALIFIER_CHUNK_SIZE>(head);
}
check_consumed(head, start, len, "Qualifier");
return qualifier;
}
socket_uuid_t deserialize_socket_uuid(ReadHead& head) {
std::size_t start = head.pos;
sr::len_t len = deserialize_len(head);
node_id_t node_id = deserialize_array<NODE_ID_SIZE>(head);
socket_id_t socket_id = deserialize_array<SOCKET_ID_SIZE>(head);
check_consumed(head, start, len, "Socket UUID");
return as_socket_uuid(node_id, socket_id);
}
link_t deserialize_link(ReadHead& head) {
std::size_t start = head.pos;
sr::len_t len = deserialize_len(head);
socket_uuid_t to = deserialize_socket_uuid(head);
socket_uuid_t from = deserialize_socket_uuid(head);
check_consumed(head, start, len, "Link");
return link_t{to, from};
}
// --- Graph components
SocketAttributes deserialize_socket_base(ReadHead& head) {
SocketAttributes attrs;
attrs.start = head.pos;
attrs.len = deserialize_len(head);
attrs.name = deserialize_string(head);
attrs.id = deserialize_array<SOCKET_ID_SIZE>(head);
attrs.type = deserialize_socket_type(head);
attrs.active = deserialize_bool(head);
attrs.on_added_link_callbacks = deserialize_set(head, deserialize_qualifier, sr::Bounds::callbacks);
attrs.on_broken_link_callbacks = deserialize_set(head, deserialize_qualifier, sr::Bounds::callbacks);
return attrs;
}
InputSocket deserialize_input_socket(ReadHead& head) {
SocketAttributes attrs = deserialize_socket_base(head);
std::string value = deserialize_string(head);
std::set<std::string> allowed_values = deserialize_set(head, deserialize_string, sr::Bounds::whitelist);
bool linkable = deserialize_bool(head);
std::set<qualifier_t> on_value_changed_callbacks = deserialize_set(head, deserialize_qualifier, sr::Bounds::callbacks);
check_consumed(head, attrs.start, attrs.len, "InputSocket");
return InputSocket(
attrs.name,
attrs.id,
attrs.type,
attrs.active,
value,
linkable,
allowed_values,
attrs.on_added_link_callbacks,
attrs.on_broken_link_callbacks,
on_value_changed_callbacks
);
}
OutputSocket deserialize_output_socket(ReadHead& head) {
SocketAttributes attrs = deserialize_socket_base(head);
check_consumed(head, attrs.start, attrs.len, "OutputSocket");
return OutputSocket(
attrs.name,
attrs.id,
attrs.type,
attrs.active,
attrs.on_added_link_callbacks,
attrs.on_broken_link_callbacks
);
}
Node deserialize_node(ReadHead& head) {
std::size_t start = head.pos;
sr::len_t len = deserialize_len(head);
node_id_t id = deserialize_array<NODE_ID_SIZE>(head);
qualifier_t qualifier = deserialize_qualifier(head);
std::string name = deserialize_string(head);
std::set<socket_id_t> input_sockets = deserialize_set(head, deserialize_array<SOCKET_ID_SIZE>, sr::Bounds::sockets);
std::set<socket_id_t> output_sockets = deserialize_set(head, deserialize_array<SOCKET_ID_SIZE>, sr::Bounds::sockets);
bool proxy = deserialize_bool(head);
check_consumed(head, start, len, "Node");
return Node(name, id, qualifier, input_sockets, output_sockets, proxy);
}
Graph::InterfaceProperties deserialize_interface_properties(ReadHead& head) {
std::size_t start = head.pos;
sr::len_t len = deserialize_len(head);
socket_id_t socket_id = deserialize_array<SOCKET_ID_SIZE>(head);
std::string show_as = deserialize_string(head);
check_consumed(head, start, len, "InterfaceProperties");
return Graph::InterfaceProperties{show_as, socket_id};
}
// ---
void Deserializer::deserialize_graph() {
ReadHead head(buffer_);
sr::len_t len = deserialize_len(head);
if (len != buffer_.size())
throw std::runtime_error("Graph length does not match the size of the buffer");
if (deserialize_bound(head) != sr::Bounds::graph)
throw std::runtime_error("Expected graph bound at the beginning of the graph deserialization");
qualifier_t graph_qualifier = deserialize_qualifier(head);
// Files written by a more recent version of the parser can't be read.
sr::version_t version = deserialize_array<3>(head);
if (!sr::is_sup_or_equal(sr::parser_version(), version))
throw std::runtime_error("Parser version is not supported");
if (deserialize_string(head) != sr::app_name())
throw std::runtime_error("App name does not match the expected value");
std::string graph_name = deserialize_string(head);
Graph::links_t links = deserialize_set(head, deserialize_link, sr::Bounds::links);
Graph::nodes_t nodes = deserialize_map(head, deserialize_array<NODE_ID_SIZE>, deserialize_node, sr::Bounds::nodes);
Graph::input_sockets_t input_sockets = deserialize_map(head, deserialize_socket_uuid, deserialize_input_socket, sr::Bounds::sockets);
Graph::output_sockets_t output_sockets = deserialize_map(head, deserialize_socket_uuid, deserialize_output_socket, sr::Bounds::sockets);
Graph::interface_t parameters = deserialize_map(head, deserialize_socket_uuid, deserialize_interface_properties, sr::Bounds::interface);
Graph::interface_t results = deserialize_map(head, deserialize_socket_uuid, deserialize_interface_properties, sr::Bounds::interface);
if (deserialize_bound(head) != sr::Bounds::graph)
throw std::runtime_error("Expected graph bound at the end of the graph deserialization");
if (head.pos != len)
throw std::runtime_error("Graph deserialization did not consume the expected number of bytes");
graph_ = Graph(
std::move(graph_name),
std::move(nodes),
std::move(links),
std::move(input_sockets),
std::move(output_sockets),
std::move(parameters),
std::move(results)
);
graph_.set_qualifier(graph_qualifier);
}
} // namespace ng::deserialization