diff --git a/src/common/bitsery/ext/message-reference.h b/src/common/bitsery/ext/message-reference.h new file mode 100644 index 00000000..17e47368 --- /dev/null +++ b/src/common/bitsery/ext/message-reference.h @@ -0,0 +1,92 @@ + +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +#include "../../serialization/common.h" + +namespace bitsery { +namespace ext { + +/** + * An adapter for serializing zero-copy references to objects using + * `MessageHandler`. The idea is that when serializing, we just read data + * from the object pointed at by the reference. Then when deserializing, we'll + * write the data to some `std::option` (so we don't have to initialize an + * unused object on the serializing side), and we'll then change our reference + * to point to the value contained within that option. + * + * This lets us serialize 'references' to objects that can be backed by actual + * persistent objects. That way we can avoid allocations during the processing + * loop. + */ +template +class MessageReference { + public: + /** + * @param deseerialization_store The object we'll deserialize into, so we + * can point the `MessageReference` to this object. On the serializing + * side this won't be touched. + */ + MessageReference(std::optional& deseerialization_store) + : deseerialization_store(deseerialization_store){}; + + template + void serialize(Ser& ser, + const ::MessageReference& object_ref, + Fnc&&) const { + ser.object(object_ref.get()); + } + + template + void deserialize(Des& des, ::MessageReference& object_ref, Fnc&&) const { + if (!deseerialization_store) { + deseerialization_store.emplace(); + } + + // Since we cannot directly deserialize into a reference, we'll + // deserialize into this (persistent) backing object and then point the + // reference to this object. + des.object(*deseerialization_store); + object_ref = *deseerialization_store; + } + + private: + /** + * The actual `T` we'll deserialize into so we can point the reference to + * that object after deserializing. + */ + std::optional& deseerialization_store; +}; + +} // namespace ext + +namespace traits { + +template +struct ExtensionTraits, ::MessageReference> { + using TValue = void; + static constexpr bool SupportValueOverload = false; + static constexpr bool SupportObjectOverload = true; + static constexpr bool SupportLambdaOverload = false; +}; + +} // namespace traits +} // namespace bitsery