From cd5b2949524c16e01139cb69f5f315c7c4fa8d19 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 26 Feb 2020 22:17:05 +0100 Subject: [PATCH] Replace msgpack with bitsery --- README.md | 12 ++--- meson.build | 5 +- src/common/communication.h | 86 +++++++++++++++++++++++++-------- src/plugin/bridge.cpp | 1 - src/wine-host/native-includes.h | 5 +- 5 files changed, 77 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ac4e165b..27783b27 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ There are a few things that should be done before making this public, including: rewrite some parts of it to make it clearer. - Document what this has been tested on and what does or does not work. - Document wine32 support. -- Swap out msgpack for bitsery and update the architecture section. - Forward audio events. - Forward host callback calls back to the native VST host. - Forward the values from the Windows VST plugin's `AEffect` struct. @@ -31,7 +30,10 @@ the following dependencies: - gcc (tested using GCC 9.2) - A Wine installation with `wiengcc` and the development headers. - Boost -- [msgpack-c](git@github.com:msgpack/msgpack-c.git) + +The following dependencies are included as a Meson wrap: + +- bitsery The project can then be compiled as follows: @@ -104,15 +106,13 @@ follows: - Calls from the native VST host to the plugin's `process()` and `processReplacing()` functions. Both functions get forwarded to the Windows VST plugin through the Wine VST host using a single socket. - - TODO: This is missing updates to the AEffect struct. The first step when passing through any of these function calls over a socket is to serialize the function's parameters as binary data. Both request and the corresponding response objects for all of these function calls can be found in `src/common/communication.h`, along with functions to read and write - these objects over streams and sockets. Right now we're using `msgpack`, but - this should be switched out for [bitsery](https://github.com/fraillt/bitsery) - for lower overhead serialization. + these objects over streams and sockets. The actual binary serialization is + handled using [bitsery](https://github.com/fraillt/bitsery). 6. The Wine VST host loads the Windows VST plugin and starts forwarding messages over the sockets described above. diff --git a/meson.build b/meson.build index afb7e544..4ebb254c 100644 --- a/meson.build +++ b/meson.build @@ -22,7 +22,6 @@ endif boost_dep = dependency('boost', modules : ['filesystem']) bitsery_dep = subproject('bitsery').get_variable('bitsery_dep') -msgpack_dep = dependency('msgpack') threads_dep = dependency('threads') # The built in threads dependency does not know how to handle winegcc wine_threads_dep = declare_dependency(link_args : '-lpthread') @@ -37,7 +36,7 @@ shared_library( ], native : true, include_directories : include_dir, - dependencies : [boost_dep, bitsery_dep, msgpack_dep, threads_dep], + dependencies : [boost_dep, bitsery_dep, threads_dep], link_args : ['-ldl'] ) @@ -48,6 +47,6 @@ executable( ], native : false, include_directories : include_dir, - dependencies : [boost_dep, bitsery_dep, msgpack_dep, wine_threads_dep], + dependencies : [boost_dep, bitsery_dep, wine_threads_dep], link_args : [] ) diff --git a/src/common/communication.h b/src/common/communication.h index 063e1d81..092464e5 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -16,10 +16,15 @@ #pragma once +#include +#include +#include +#include +#include + #include #include #include -#include #include /** @@ -29,6 +34,25 @@ */ constexpr size_t max_string_length = 128; +/** + * The buffer size in bytes used for all buffers for sending and recieving + * messages. + * + * TODO: This should probably depend on the type. 512 bytes is way too much for + * events, and probably not enough for sending audio. + */ +constexpr size_t buffer_size = 512; + +// Types used for serialization and deserialization with bitsery. +template +using Buffer = std::array; + +template +using OutputAdapter = bitsery::OutputBufferAdapter>; + +template +using InputAdapter = bitsery::InputBufferAdapter>; + /** * An event as dispatched by the VST host. These events will get forwarded to * the VST host process running under Wine. The fields here mirror those @@ -49,7 +73,18 @@ struct Event { */ std::optional data; - MSGPACK_DEFINE(opcode, index, value, option, data) + template + void serialize(S& s) { + s.value4b(opcode); + s.value4b(index); + // Hard coding pointer sizes to 8 bytes should be fine, right? Even if + // we're hosting a 32 bit plugin the native VST plugin will still use 64 + // bit large pointers. + s.value8b(value); + s.value4b(option); + s.ext(data, bitsery::ext::StdOptional(), + [](S& s, auto& v) { s.text1b(v, max_string_length); }); + } }; /** @@ -68,15 +103,18 @@ struct EventResult { // TODO: Add missing return value fields; - MSGPACK_DEFINE(return_value, data) + template + void serialize(S& s) { + s.value8b(return_value); + s.ext(data, bitsery::ext::StdOptional(), + [](S& s, auto& v) { s.text1b(v, max_string_length); }); + } }; /** - * Serialize an object and write it to a stream. This function prefixes the - * output with the length of the serialized object in bytes since msgpack - * doesn't handle this on its own. + * Serialize an object using bitsery and write it to a socket. * - * @param stream An ostream that can be written to. + * @param socket The Boost.Asio socket to write to. * @param object The object to write to the stream. * * @relates read_object @@ -84,29 +122,37 @@ struct EventResult { template inline void write_object(Socket& socket, const T& object) { // TODO: Reuse buffers - // TODO: Use boost's buffers directly after switching to bitsery - msgpack::sbuffer buffer; - msgpack::pack(buffer, object); + Buffer buffer; + auto length = + bitsery::quickSerialization>(buffer, object); - socket.send(boost::asio::buffer(buffer.data(), buffer.size())); + socket.send(boost::asio::buffer(buffer, length)); } /** - * Deserialize an object by reading it from a stream. This should be used - * together with `write_object`. This will block until the object is - available. + * Deserialize an object by reading it from a socket. This should be used + * together with `write_object`. This will block until the object is available. * - * @param stream The stream to read from. - * @throw msgpack::type_error If the conversion to an object was not successful. + * @param socket The Boost.Asio socket to read from. + * @throw std::runtime_error If the conversion to an object was not successful. * * @relates write_object */ template inline T read_object(Socket& socket) { - // TODO: Reuse buffers, also this is way too large right now - // TODO: Use boost's buffers directly after switching to bitsery - char buffer[4096]; + // TODO: Reuse buffers + Buffer buffer; auto message_length = socket.receive(boost::asio::buffer(buffer)); - return msgpack::unpack(buffer, message_length).get().convert(); + T object; + auto [_, success] = + bitsery::quickDeserialization>( + {buffer.begin(), message_length}, object); + + if (!success) { + throw std::runtime_error("Deserialization failure in call:" + + std::string(__PRETTY_FUNCTION__)); + } + + return object; } diff --git a/src/plugin/bridge.cpp b/src/plugin/bridge.cpp index 92b065eb..e326311a 100644 --- a/src/plugin/bridge.cpp +++ b/src/plugin/bridge.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "../common/communication.h" diff --git a/src/wine-host/native-includes.h b/src/wine-host/native-includes.h index 326f4934..26ccddb0 100644 --- a/src/wine-host/native-includes.h +++ b/src/wine-host/native-includes.h @@ -16,7 +16,7 @@ #pragma once -// Libraries like Boost and msgpack think we're compiling on Windows or using a +// Libraries like Boost and bitsery think we're compiling on Windows or using a // MSVC toolchain. This will cause them to make assumptions about the way // certain types are defined, which headers are available and which features to // disable (i.e. POSIX specific features). The only way around this I could @@ -34,10 +34,11 @@ #undef __WIN32__ #undef _WIN64 +#include + #include #include #include -#include #pragma pop_macro("WIN32") #pragma pop_macro("_WIN32")