diff --git a/src/common/bitsery/ext/in-place-variant.h b/src/common/bitsery/ext/in-place-variant.h new file mode 100644 index 00000000..5157aa26 --- /dev/null +++ b/src/common/bitsery/ext/in-place-variant.h @@ -0,0 +1,83 @@ +// 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 + +namespace bitsery { +namespace ext { + +/** + * A temporary replacement for `bitsery::ext::StdVariant` to avoid + * reinitializing the object we're deserializing into if the requested variant + * is currently active. For storing audio buffers we use variants containing + * float and double vectors to have a type safe way to disambiguate between + * single and double precision audio, but as it turns out bitsery's + * `std::variant` extension would always reinitialize those objects, undoing our + * efforts to prevent allocations. + */ +template +class InPlaceVariant : public StdVariant { + public: + template + void deserialize(Des& des, std::variant& obj, Fnc&&) const { + size_t index{}; + details::readSize( + des.adapter(), index, sizeof...(Ts), + std::integral_constant{}); + + this->execIndex(index, obj, [this, &des](auto& data, auto index) { + constexpr size_t Index = decltype(index)::value; + using TElem = + typename std::variant_alternative>::type; + + // Most of this is copied directly from the original implementation. + // We just added the check here to reuse the existing object if + // possible. + if (std::holds_alternative(data)) { + TElem& item = std::get(data); + this->serializeType(des, item); + } else { + TElem item = ::bitsery::Access::create(); + this->serializeType(des, item); + data = std::variant(std::in_place_index_t{}, + std::move(item)); + } + }); + } +}; + +template +InPlaceVariant(Overloads...) -> InPlaceVariant; +} // namespace ext + +namespace traits { + +template +struct ExtensionTraits, Variant> { + static_assert( + bitsery::details::IsSpecializationOf::value, + "InPlaceVariant only works with std::variant"); + using TValue = void; + static constexpr bool SupportValueOverload = false; + static constexpr bool SupportObjectOverload = true; + static constexpr bool SupportLambdaOverload = false; +}; + +} // namespace traits +} // namespace bitsery