[Spice-devel] [RFC] Adding compression to the webdav folder sharing
Snir Sheriber
ssheribe at redhat.com
Sun Aug 21 15:16:16 UTC 2016
Adding lz4 compression same as in spicevmc channel.
Compression will be enabled only if LZ4 is in use && socket
is not AF_LOCAL type (local) && data size is under threshold
&& host has lz4 compression capability.
-handling compressed msgs is implemented in src/channel-webdav.c
-compression of msgs is implemented in src/vmcstream.c
---
src/channel-webdav.c | 57 ++++++++++++++++++++++++++++++++++++++++++
src/vmcstream.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+)
diff --git a/src/channel-webdav.c b/src/channel-webdav.c
index 4ecc769..39598a5 100644
--- a/src/channel-webdav.c
+++ b/src/channel-webdav.c
@@ -25,6 +25,10 @@
#include "vmcstream.h"
#include "giopipe.h"
+#ifdef USE_LZ4
+#include <lz4.h>
+#endif
+
/**
* SECTION:channel-webdav
* @short_description: exports a directory
@@ -531,6 +535,9 @@ static void spice_webdav_channel_init(SpiceWebdavChannel *channel)
GOutputStream *ostream = g_io_stream_get_output_stream(G_IO_STREAM(c->stream));
c->queue = output_queue_new(ostream);
+#ifdef USE_LZ4
+ spice_channel_set_capability(channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
+#endif
}
static void spice_webdav_channel_finalize(GObject *object)
@@ -593,6 +600,50 @@ static void webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *in)
buf, size);
}
+/* coroutine context */
+static int webdav_try_handle_compressed_msg(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceWebdavChannel *self = SPICE_WEBDAV_CHANNEL(channel);
+ SpiceWebdavChannelPrivate *c = self->priv;
+ int size;
+ int decompressed_size = 0;
+ char *decompressed = NULL;
+
+ SpiceMsgCompressedData *compressed_data_msg = spice_msg_in_parsed(in);
+
+ if (compressed_data_msg->uncompressed_size == 0) {
+ spice_warning("Invalid uncompressed_size");
+ return FALSE;
+ }
+
+ switch (compressed_data_msg->type) {
+#ifdef USE_LZ4
+ case SPICE_DATA_COMPRESSION_TYPE_LZ4:
+ decompressed = g_malloc(compressed_data_msg->uncompressed_size);
+ decompressed_size = LZ4_decompress_safe ((char*)compressed_data_msg->compressed_data,
+ decompressed,
+ compressed_data_msg->compressed_size,
+ compressed_data_msg->uncompressed_size);
+ break;
+#endif
+ default:
+ spice_warning("Unknown Compression Type");
+ return FALSE;
+ }
+ if (decompressed_size != compressed_data_msg->uncompressed_size) {
+ spice_warning("Decompress Error decompressed_size=%d expected=%u",
+ decompressed_size, compressed_data_msg->uncompressed_size);
+ g_free(decompressed);
+ return FALSE;
+ }
+
+ size = decompressed_size;
+ spice_vmc_input_stream_co_data(
+ SPICE_VMC_INPUT_STREAM(g_io_stream_get_input_stream(G_IO_STREAM(c->stream))),
+ (uint8_t*)decompressed, size);
+ g_free(decompressed);
+ return TRUE;
+}
/* coroutine context */
static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
@@ -604,6 +655,12 @@ static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
if (type == SPICE_MSG_SPICEVMC_DATA)
webdav_handle_msg(channel, msg);
+ else if (type == SPICE_MSG_SPICEVMC_COMPRESSED_DATA) {
+ if(!webdav_try_handle_compressed_msg(channel, msg)) {
+ spice_warning("handling compressed message failed");
+ g_return_if_reached();
+ }
+ }
else if (parent_class->handle_msg)
parent_class->handle_msg(channel, msg);
else
diff --git a/src/vmcstream.c b/src/vmcstream.c
index 0634bce..57f3758 100644
--- a/src/vmcstream.c
+++ b/src/vmcstream.c
@@ -23,6 +23,11 @@
#include "spice-channel-priv.h"
#include "gio-coroutine.h"
+#ifdef USE_LZ4
+#include <lz4.h>
+#define COMPRESS_THRESHOLD 1000
+#endif
+
struct _SpiceVmcInputStream
{
GInputStream parent_instance;
@@ -43,6 +48,9 @@ struct _SpiceVmcInputStreamClass
GInputStreamClass parent_class;
};
+#ifdef USE_LZ4
+static int try_write_compress_LZ4(SpiceChannel *channel, const uint8_t *data, int count);
+#endif
static gssize spice_vmc_input_stream_read (GInputStream *stream,
void *buffer,
gsize count,
@@ -373,6 +381,63 @@ spice_vmc_output_stream_new(SpiceChannel *channel)
return self;
}
+#ifdef USE_LZ4
+static int try_write_compress_LZ4(SpiceChannel *channel, const uint8_t *data, int count)
+{
+ SpiceChannelPrivate *c;
+ SpiceMsgOut *msg_out_compressed;
+ int bound, compressed_data_count;
+ uint8_t *compressed_buf;
+ SpiceMsgCompressedData compressed_data_msg = {
+ .type = SPICE_DATA_COMPRESSION_TYPE_LZ4,
+ .uncompressed_size = count
+ };
+
+ c = SPICE_CHANNEL(channel)->priv;
+ if (g_socket_get_family(c->sock) == G_SOCKET_FAMILY_UNIX) {
+ /* AF_LOCAL socket - data will not be compressed */
+ return FALSE;
+ }
+ if (count <= COMPRESS_THRESHOLD) {
+ /* Not enough data to compress */
+ return FALSE;
+ }
+ if (!spice_channel_test_capability(SPICE_CHANNEL(channel),
+ SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4)) {
+ /* No server compression capability - data will not be compressed */
+ return FALSE;
+ }
+ bound = LZ4_compressBound(count);
+ if (bound == 0) {
+ /* Invalid bound - data will not be compressed */
+ return FALSE;
+ }
+
+ compressed_buf = g_malloc(bound);
+ compressed_data_count = LZ4_compress_default((char*)data,
+ (char*)compressed_buf,
+ count,
+ bound);
+ if (compressed_data_count > 0 && compressed_data_count < count) {
+ compressed_data_msg.compressed_data = compressed_buf;
+ msg_out_compressed = spice_msg_out_new(SPICE_CHANNEL(channel),
+ SPICE_MSGC_SPICEVMC_COMPRESSED_DATA);
+ msg_out_compressed->marshallers->msg_SpiceMsgCompressedData(msg_out_compressed->marshaller,
+ &compressed_data_msg);
+ spice_marshaller_add_ref_full(msg_out_compressed->marshaller,
+ compressed_data_msg.compressed_data,
+ compressed_data_count,
+ (spice_marshaller_item_free_func)g_free,
+ channel);
+ spice_msg_out_send(msg_out_compressed);
+ return TRUE;
+ }
+ /* if not - free & fallback to sending the message uncompressed */
+ g_free(compressed_buf);
+ return FALSE;
+}
+#endif
+
static gssize
spice_vmc_output_stream_write_fn(GOutputStream *stream,
const void *buffer,
@@ -383,6 +448,11 @@ spice_vmc_output_stream_write_fn(GOutputStream *stream,
SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream);
SpiceMsgOut *msg_out;
+#ifdef USE_LZ4
+ if (try_write_compress_LZ4(SPICE_CHANNEL(self->channel), buffer, count)) {
+ return count;
+ }
+#endif
msg_out = spice_msg_out_new(SPICE_CHANNEL(self->channel),
SPICE_MSGC_SPICEVMC_DATA);
spice_marshaller_add(msg_out->marshaller, buffer, count);
--
2.5.5
More information about the Spice-devel
mailing list