[Spice-devel] [PATCH 1/5] Rename dcc-encoders.[ch] to image-encoders.[ch]

Jonathon Jongsma jjongsma at redhat.com
Fri Jun 17 14:15:46 UTC 2016


On Fri, 2016-06-17 at 14:58 +0100, Frediano Ziglio wrote:
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> ---
>  server/Makefile.am      |    4 +-
>  server/dcc-encoders.c   | 1388 ----------------------------------------------
> -
>  server/dcc-encoders.h   |  217 --------
>  server/dcc.h            |    2 +-
>  server/image-encoders.c | 1388
> +++++++++++++++++++++++++++++++++++++++++++++++
>  server/image-encoders.h |  217 ++++++++
>  6 files changed, 1608 insertions(+), 1608 deletions(-)
>  delete mode 100644 server/dcc-encoders.c
>  delete mode 100644 server/dcc-encoders.h
>  create mode 100644 server/image-encoders.c
>  create mode 100644 server/image-encoders.h
> 
> diff --git a/server/Makefile.am b/server/Makefile.am
> index 0af8a1b..921b082 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -148,8 +148,8 @@ libserver_la_SOURCES =				\
>  	dcc-send.c					\
>  	dcc.h					\
>  	display-limits.h			\
> -	dcc-encoders.c					\
> -	dcc-encoders.h					\
> +	image-encoders.c					\
> +	image-encoders.h					\
>  	$(NULL)
>  
>  if HAVE_LZ4
> diff --git a/server/dcc-encoders.c b/server/dcc-encoders.c
> deleted file mode 100644
> index 984f2d4..0000000
> --- a/server/dcc-encoders.c
> +++ /dev/null
> @@ -1,1388 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/
> >.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <glib.h>
> -
> -#include "dcc-encoders.h"
> -#include "spice-bitmap-utils.h"
> -#include "red-worker.h" // red_drawable_unref
> -#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
> -
> -#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
> -
> -#define ENCODER_MESSAGE_SIZE 512
> -
> -#define MAX_GLZ_DRAWABLE_INSTANCES 2
> -
> -typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
> -
> -struct GlzSharedDictionary {
> -    RingItem base;
> -    GlzEncDictContext *dict;
> -    uint32_t refs;
> -    uint8_t id;
> -    pthread_rwlock_t encode_lock;
> -    int migrate_freeze;
> -    RedClient *client; // channel clients of the same client share the dict
> -};
> -
> -/* for each qxl drawable, there may be several instances of lz drawables */
> -/* TODO - reuse this stuff for the top level. I just added a second level of
> multiplicity
> - * at the Drawable by keeping a ring, so:
> - * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
> - * and it should probably (but need to be sure...) be
> - * Drawable -> ring of GlzDrawableInstanceItem.
> - */
> -struct GlzDrawableInstanceItem {
> -    RingItem glz_link;
> -    RingItem free_link;
> -    GlzEncDictImageContext *context;
> -    RedGlzDrawable         *glz_drawable;
> -};
> -
> -struct RedGlzDrawable {
> -    RingItem link;    // ordered by the time it was encoded
> -    RingItem drawable_link;
> -    RedDrawable *red_drawable;
> -    GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
> -    Ring instances;
> -    uint8_t instances_count;
> -    gboolean has_drawable;
> -    ImageEncoders *encoders;
> -};
> -
> -#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
> -                                           drawable_link)
> -#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
> -    SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz,
> LINK_TO_GLZ(link))
> -
> -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance);
> -static void encoder_data_init(EncoderData *data);
> -static void encoder_data_reset(EncoderData *data);
> -static void image_encoders_release_glz(ImageEncoders *enc);
> -
> -
> -static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
> -quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
> -{
> -    EncoderData *usr_data = &(((QuicData *)usr)->data);
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -    spice_critical("%s", message_buf);
> -
> -    longjmp(usr_data->jmp_env, 1);
> -}
> -
> -static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
> -lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
> -{
> -    EncoderData *usr_data = &(((LzData *)usr)->data);
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -    spice_critical("%s", message_buf);
> -
> -    longjmp(usr_data->jmp_env, 1);
> -}
> -
> -static SPICE_GNUC_PRINTF(2, 3) void
> -glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
> -{
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -
> -    spice_critical("%s", message_buf); // if global lz fails in the middle
> -                                        // the consequences are not
> predictable since the window
> -                                        // can turn to be unsynchronized
> between the server and
> -                                        // and the client
> -}
> -
> -static SPICE_GNUC_PRINTF(2, 3) void
> -quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
> -{
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -    spice_warning("%s", message_buf);
> -}
> -
> -static SPICE_GNUC_PRINTF(2, 3) void
> -lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
> -{
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -    spice_warning("%s", message_buf);
> -}
> -
> -static SPICE_GNUC_PRINTF(2, 3) void
> -glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
> -{
> -    va_list ap;
> -    char message_buf[ENCODER_MESSAGE_SIZE];
> -
> -    va_start(ap, fmt);
> -    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> -    va_end(ap);
> -    spice_warning("%s", message_buf);
> -}
> -
> -static void *quic_usr_malloc(QuicUsrContext *usr, int size)
> -{
> -    return spice_malloc(size);
> -}
> -
> -static void *lz_usr_malloc(LzUsrContext *usr, int size)
> -{
> -    return spice_malloc(size);
> -}
> -
> -static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
> -{
> -    return spice_malloc(size);
> -}
> -
> -static void quic_usr_free(QuicUsrContext *usr, void *ptr)
> -{
> -    free(ptr);
> -}
> -
> -static void lz_usr_free(LzUsrContext *usr, void *ptr)
> -{
> -    free(ptr);
> -}
> -
> -static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
> -{
> -    free(ptr);
> -}
> -
> -static void encoder_data_init(EncoderData *data)
> -{
> -    data->bufs_tail = g_new(RedCompressBuf, 1);
> -    data->bufs_head = data->bufs_tail;
> -    data->bufs_head->send_next = NULL;
> -}
> -
> -static void encoder_data_reset(EncoderData *data)
> -{
> -    RedCompressBuf *buf = data->bufs_head;
> -    while (buf) {
> -        RedCompressBuf *next = buf->send_next;
> -        g_free(buf);
> -        buf = next;
> -    }
> -    data->bufs_head = data->bufs_tail = NULL;
> -}
> -
> -/* Allocate more space for compressed buffer.
> - * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
> - */
> -static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
> -{
> -    RedCompressBuf *buf;
> -
> -    buf = g_new(RedCompressBuf, 1);
> -    enc_data->bufs_tail->send_next = buf;
> -    enc_data->bufs_tail = buf;
> -    buf->send_next = NULL;
> -    *io_ptr = buf->buf.bytes;
> -    return sizeof(buf->buf);
> -}
> -
> -static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int
> rows_completed)
> -{
> -    EncoderData *usr_data = &(((QuicData *)usr)->data);
> -    return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) /
> sizeof(uint32_t);
> -}
> -
> -static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
> -{
> -    EncoderData *usr_data = &(((LzData *)usr)->data);
> -    return encoder_usr_more_space(usr_data, io_ptr);
> -}
> -
> -static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
> -{
> -    EncoderData *usr_data = &(((GlzData *)usr)->data);
> -    return encoder_usr_more_space(usr_data, io_ptr);
> -}
> -
> -static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
> -{
> -    EncoderData *usr_data = &(((JpegData *)usr)->data);
> -    return encoder_usr_more_space(usr_data, io_ptr);
> -}
> -
> -#ifdef USE_LZ4
> -static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
> -{
> -    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
> -    return encoder_usr_more_space(usr_data, io_ptr);
> -}
> -#endif
> -
> -static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
> -{
> -    EncoderData *usr_data = &(((ZlibData *)usr)->data);
> -    return encoder_usr_more_space(usr_data, io_ptr);
> -}
> -
> -static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t
> **lines)
> -{
> -    struct SpiceChunk *chunk;
> -
> -    if (enc_data->u.lines_data.reverse) {
> -        if (!(enc_data->u.lines_data.next >= 0)) {
> -            return 0;
> -        }
> -    } else {
> -        if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks-
> >num_chunks)) {
> -            return 0;
> -        }
> -    }
> -
> -    chunk = &enc_data->u.lines_data.chunks->chunk[enc_data-
> >u.lines_data.next];
> -    if (chunk->len % enc_data->u.lines_data.stride) {
> -        return 0;
> -    }
> -
> -    if (enc_data->u.lines_data.reverse) {
> -        enc_data->u.lines_data.next--;
> -        *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride;
> -    } else {
> -        enc_data->u.lines_data.next++;
> -        *lines = chunk->data;
> -    }
> -
> -    return chunk->len / enc_data->u.lines_data.stride;
> -}
> -
> -static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
> -{
> -    EncoderData *usr_data = &(((QuicData *)usr)->data);
> -    return encoder_usr_more_lines(usr_data, lines);
> -}
> -
> -static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
> -{
> -    EncoderData *usr_data = &(((LzData *)usr)->data);
> -    return encoder_usr_more_lines(usr_data, lines);
> -}
> -
> -static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
> -{
> -    EncoderData *usr_data = &(((GlzData *)usr)->data);
> -    return encoder_usr_more_lines(usr_data, lines);
> -}
> -
> -static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
> -{
> -    EncoderData *usr_data = &(((JpegData *)usr)->data);
> -    return encoder_usr_more_lines(usr_data, lines);
> -}
> -
> -#ifdef USE_LZ4
> -static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
> -{
> -    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
> -    return encoder_usr_more_lines(usr_data, lines);
> -}
> -#endif
> -
> -static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
> -{
> -    EncoderData *usr_data = &(((ZlibData *)usr)->data);
> -    int buf_size;
> -
> -    if (!usr_data->u.compressed_data.next) {
> -        spice_assert(usr_data->u.compressed_data.size_left == 0);
> -        return 0;
> -    }
> -
> -    *input = usr_data->u.compressed_data.next->buf.bytes;
> -    buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
> -                   usr_data->u.compressed_data.size_left);
> -
> -    usr_data->u.compressed_data.next = usr_data->u.compressed_data.next-
> >send_next;
> -    usr_data->u.compressed_data.size_left -= buf_size;
> -    return buf_size;
> -}
> -
> -static void image_encoders_init_quic(ImageEncoders *enc)
> -{
> -    enc->quic_data.usr.error = quic_usr_error;
> -    enc->quic_data.usr.warn = quic_usr_warn;
> -    enc->quic_data.usr.info = quic_usr_warn;
> -    enc->quic_data.usr.malloc = quic_usr_malloc;
> -    enc->quic_data.usr.free = quic_usr_free;
> -    enc->quic_data.usr.more_space = quic_usr_more_space;
> -    enc->quic_data.usr.more_lines = quic_usr_more_lines;
> -
> -    enc->quic = quic_create(&enc->quic_data.usr);
> -
> -    if (!enc->quic) {
> -        spice_critical("create quic failed");
> -    }
> -}
> -
> -static void image_encoders_init_lz(ImageEncoders *enc)
> -{
> -    enc->lz_data.usr.error = lz_usr_error;
> -    enc->lz_data.usr.warn = lz_usr_warn;
> -    enc->lz_data.usr.info = lz_usr_warn;
> -    enc->lz_data.usr.malloc = lz_usr_malloc;
> -    enc->lz_data.usr.free = lz_usr_free;
> -    enc->lz_data.usr.more_space = lz_usr_more_space;
> -    enc->lz_data.usr.more_lines = lz_usr_more_lines;
> -
> -    enc->lz = lz_create(&enc->lz_data.usr);
> -
> -    if (!enc->lz) {
> -        spice_critical("create lz failed");
> -    }
> -}
> -
> -static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext
> *image)
> -{
> -    GlzData *lz_data = (GlzData *)usr;
> -    GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem
> *)image;
> -    ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable-
> >encoders;
> -    ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders,
> glz_data);
> -    if (this_enc == drawable_enc) {
> -        glz_drawable_instance_item_free(glz_drawable_instance);
> -    } else {
> -        /* The glz dictionary is shared between all DisplayChannelClient
> -         * instances that belong to the same client, and glz_usr_free_image
> -         * can be called by the dictionary code
> -         * (glz_dictionary_window_remove_head). Thus this function can be
> -         * called from any DisplayChannelClient thread, hence the need for
> -         * this check.
> -         */
> -        pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
> -        ring_add_before(&glz_drawable_instance->free_link,
> -                        &drawable_enc->glz_drawables_inst_to_free);
> -        pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
> -    }
> -}
> -
> -static void image_encoders_init_glz_data(ImageEncoders *enc)
> -{
> -    enc->glz_data.usr.error = glz_usr_error;
> -    enc->glz_data.usr.warn = glz_usr_warn;
> -    enc->glz_data.usr.info = glz_usr_warn;
> -    enc->glz_data.usr.malloc = glz_usr_malloc;
> -    enc->glz_data.usr.free = glz_usr_free;
> -    enc->glz_data.usr.more_space = glz_usr_more_space;
> -    enc->glz_data.usr.more_lines = glz_usr_more_lines;
> -    enc->glz_data.usr.free_image = glz_usr_free_image;
> -}
> -
> -static void image_encoders_init_jpeg(ImageEncoders *enc)
> -{
> -    enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
> -    enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
> -
> -    enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
> -
> -    if (!enc->jpeg) {
> -        spice_critical("create jpeg encoder failed");
> -    }
> -}
> -
> -#ifdef USE_LZ4
> -static inline void image_encoders_init_lz4(ImageEncoders *enc)
> -{
> -    enc->lz4_data.usr.more_space = lz4_usr_more_space;
> -    enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
> -
> -    enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
> -
> -    if (!enc->lz4) {
> -        spice_critical("create lz4 encoder failed");
> -    }
> -}
> -#endif
> -
> -static void image_encoders_init_zlib(ImageEncoders *enc)
> -{
> -    enc->zlib_data.usr.more_space = zlib_usr_more_space;
> -    enc->zlib_data.usr.more_input = zlib_usr_more_input;
> -
> -    enc->zlib = zlib_encoder_create(&enc->zlib_data.usr,
> ZLIB_DEFAULT_COMPRESSION_LEVEL);
> -
> -    if (!enc->zlib) {
> -        spice_critical("create zlib encoder failed");
> -    }
> -}
> -
> -void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData
> *shared_data)
> -{
> -    spice_assert(shared_data);
> -    enc->shared_data = shared_data;
> -
> -    ring_init(&enc->glz_drawables);
> -    ring_init(&enc->glz_drawables_inst_to_free);
> -    pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
> -
> -    image_encoders_init_glz_data(enc);
> -    image_encoders_init_quic(enc);
> -    image_encoders_init_lz(enc);
> -    image_encoders_init_jpeg(enc);
> -#ifdef USE_LZ4
> -    image_encoders_init_lz4(enc);
> -#endif
> -    image_encoders_init_zlib(enc);
> -
> -    // todo: tune level according to bandwidth
> -    enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
> -}
> -
> -void image_encoders_free(ImageEncoders *enc)
> -{
> -    image_encoders_release_glz(enc);
> -    quic_destroy(enc->quic);
> -    enc->quic = NULL;
> -    lz_destroy(enc->lz);
> -    enc->lz = NULL;
> -    jpeg_encoder_destroy(enc->jpeg);
> -    enc->jpeg = NULL;
> -#ifdef USE_LZ4
> -    lz4_encoder_destroy(enc->lz4);
> -    enc->lz4 = NULL;
> -#endif
> -    zlib_encoder_destroy(enc->zlib);
> -    enc->zlib = NULL;
> -}
> -
> -/* Remove from the to_free list and the instances_list.
> -   When no instance is left - the RedGlzDrawable is released too. (and the
> qxl drawable too, if
> -   it is not used by Drawable).
> -   NOTE - 1) can be called only by the display channel that created the
> drawable
> -          2) it is assumed that the instance was already removed from the
> dictionary*/
> -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance)
> -{
> -    RedGlzDrawable *glz_drawable;
> -
> -    spice_assert(instance);
> -    spice_assert(instance->glz_drawable);
> -
> -    glz_drawable = instance->glz_drawable;
> -
> -    spice_assert(glz_drawable->instances_count > 0);
> -
> -    ring_remove(&instance->glz_link);
> -    glz_drawable->instances_count--;
> -
> -    // when the remove callback is performed from the channel that the
> -    // drawable belongs to, the instance is not added to the 'to_free' list
> -    if (ring_item_is_linked(&instance->free_link)) {
> -        ring_remove(&instance->free_link);
> -    }
> -
> -    if (ring_is_empty(&glz_drawable->instances)) {
> -        spice_assert(glz_drawable->instances_count == 0);
> -
> -        if (glz_drawable->has_drawable) {
> -            ring_remove(&glz_drawable->drawable_link);
> -        }
> -        red_drawable_unref(glz_drawable->red_drawable);
> -        glz_drawable->encoders->shared_data->glz_drawable_count--;
> -        if (ring_item_is_linked(&glz_drawable->link)) {
> -            ring_remove(&glz_drawable->link);
> -        }
> -        free(glz_drawable);
> -    }
> -}
> -
> -/*
> - * Releases all the instances of the drawable from the dictionary and the
> display channel client.
> - * The release of the last instance will also release the drawable itself and
> the qxl drawable
> - * if possible.
> - * NOTE - the caller should prevent encoding using the dictionary during this
> operation
> - */
> -static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
> -{
> -    ImageEncoders *enc = glz_drawable->encoders;
> -    RingItem *head_instance = ring_get_head(&glz_drawable->instances);
> -    int cont = (head_instance != NULL);
> -
> -    while (cont) {
> -        if (glz_drawable->instances_count == 1) {
> -            /* Last instance: glz_drawable_instance_item_free will free the
> glz_drawable */
> -            cont = FALSE;
> -        }
> -        GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
> -                                                        GlzDrawableInstanceIt
> em,
> -                                                        glz_link);
> -        if (!ring_item_is_linked(&instance->free_link)) {
> -            // the instance didn't get out from window yet
> -            glz_enc_dictionary_remove_image(enc->glz_dict->dict,
> -                                            instance->context,
> -                                            &enc->glz_data.usr);
> -        }
> -        glz_drawable_instance_item_free(instance);
> -
> -        if (cont) {
> -            head_instance = ring_get_head(&glz_drawable->instances);
> -        }
> -    }
> -}
> -
> -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
> -{
> -    if (enc->glz_dict) {
> -        pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> -        return TRUE;
> -    }
> -    return FALSE;
> -}
> -
> -void image_encoders_glz_encode_unlock(ImageEncoders *enc)
> -{
> -    if (enc->glz_dict) {
> -        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -    }
> -}
> -
> -/*
> - * Remove from the global lz dictionary some glz_drawables that have no
> reference to
> - * Drawable (their qxl drawables are released too).
> - * NOTE - the caller should prevent encoding using the dictionary during the
> operation
> - */
> -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
> -{
> -    RingItem *ring_link;
> -    int n = 0;
> -
> -    if (!enc) {
> -        return 0;
> -    }
> -    ring_link = ring_get_head(&enc->glz_drawables);
> -    while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
> -        RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> -        ring_link = ring_next(&enc->glz_drawables, ring_link);
> -        if (!glz_drawable->has_drawable) {
> -            red_glz_drawable_free(glz_drawable);
> -            n++;
> -        }
> -    }
> -    return n;
> -}
> -
> -void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
> -{
> -    RingItem *ring_link;
> -
> -    if (!enc->glz_dict) {
> -        return;
> -    }
> -    pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
> -    while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
> -        GlzDrawableInstanceItem *drawable_instance =
> SPICE_CONTAINEROF(ring_link,
> -                                                                 GlzDrawableI
> nstanceItem,
> -                                                                 free_link);
> -        glz_drawable_instance_item_free(drawable_instance);
> -    }
> -    pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
> -}
> -
> -/* Clear all lz drawables - enforce their removal from the global dictionary.
> -   NOTE - prevents encoding using the dictionary during the operation*/
> -void image_encoders_free_glz_drawables(ImageEncoders *enc)
> -{
> -    RingItem *ring_link;
> -    GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
> -
> -    if (!glz_dict) {
> -        return;
> -    }
> -
> -    // assure no display channel is during global lz encoding
> -    pthread_rwlock_wrlock(&glz_dict->encode_lock);
> -    while ((ring_link = ring_get_head(&enc->glz_drawables))) {
> -        RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> -        // no need to lock the to_free list, since we assured no other thread
> is encoding and
> -        // thus not other thread access the to_free list of the channel
> -        red_glz_drawable_free(drawable);
> -    }
> -    pthread_rwlock_unlock(&glz_dict->encode_lock);
> -}
> -
> -void glz_retention_free_drawables(GlzImageRetention *ret)
> -{
> -    RingItem *glz_item, *next_item;
> -    RedGlzDrawable *glz;
> -    SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz,
> LINK_TO_GLZ(glz_item)) {
> -        red_glz_drawable_free(glz);
> -    }
> -}
> -
> -void glz_retention_detach_drawables(GlzImageRetention *ret)
> -{
> -    RingItem *item, *next;
> -
> -    RING_FOREACH_SAFE(item, next, &ret->ring) {
> -        SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable
> = FALSE;
> -        ring_remove(item);
> -    }
> -}
> -
> -static void image_encoders_freeze_glz(ImageEncoders *enc)
> -{
> -    pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> -    enc->glz_dict->migrate_freeze = TRUE;
> -    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -}
> -
> -void image_encoders_glz_get_restore_data(ImageEncoders *enc,
> -                                         uint8_t *out_id,
> GlzEncDictRestoreData *out_data)
> -{
> -    spice_assert(enc->glz_dict);
> -    image_encoders_freeze_glz(enc);
> -    *out_id = enc->glz_dict->id;
> -    glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
> -                                        &enc->glz_data.usr);
> -}
> -
> -static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client,
> uint8_t id,
> -                                                      GlzEncDictContext
> *dict)
> -{
> -    spice_return_val_if_fail(dict != NULL, NULL);
> -
> -    GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
> -
> -    shared_dict->dict = dict;
> -    shared_dict->id = id;
> -    shared_dict->refs = 1;
> -    shared_dict->migrate_freeze = FALSE;
> -    shared_dict->client = client;
> -    ring_item_init(&shared_dict->base);
> -    pthread_rwlock_init(&shared_dict->encode_lock, NULL);
> -
> -    return shared_dict;
> -}
> -
> -static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
> -static Ring glz_dictionary_list = {&glz_dictionary_list,
> &glz_dictionary_list};
> -
> -static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t
> dict_id)
> -{
> -    RingItem *now;
> -    GlzSharedDictionary *ret = NULL;
> -
> -    now = &glz_dictionary_list;
> -    while ((now = ring_next(&glz_dictionary_list, now))) {
> -        GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
> -        if ((dict->client == client) && (dict->id == dict_id)) {
> -            ret = dict;
> -            break;
> -        }
> -    }
> -
> -    return ret;
> -}
> -
> -#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
> -
> -static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
> -                                                  RedClient *client,
> -                                                  uint8_t id, int
> window_size)
> -{
> -    spice_info("Lz Window %d Size=%d", id, window_size);
> -
> -    GlzEncDictContext *glz_dict =
> -        glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc-
> >glz_data.usr);
> -
> -    return glz_shared_dictionary_new(client, id, glz_dict);
> -}
> -
> -gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
> -                                           RedClient *client,
> -                                           uint8_t id, int window_size)
> -{
> -    GlzSharedDictionary *shared_dict;
> -
> -    spice_return_val_if_fail(!enc->glz_dict, FALSE);
> -
> -    pthread_mutex_lock(&glz_dictionary_list_lock);
> -
> -    shared_dict = find_glz_dictionary(client, id);
> -    if (shared_dict) {
> -        shared_dict->refs++;
> -    } else {
> -        shared_dict = create_glz_dictionary(enc, client, id, window_size);
> -        ring_add(&glz_dictionary_list, &shared_dict->base);
> -    }
> -
> -    pthread_mutex_unlock(&glz_dictionary_list_lock);
> -    enc->glz_dict = shared_dict;
> -    return shared_dict != NULL;
> -}
> -
> -static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
> -                                                   RedClient *client,
> -                                                   uint8_t id,
> -                                                   GlzEncDictRestoreData
> *restore_data)
> -{
> -    GlzEncDictContext *glz_dict =
> -        glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
> -
> -    return glz_shared_dictionary_new(client, id, glz_dict);
> -}
> -
> -gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
> -                                               RedClient *client,
> -                                               uint8_t id,
> -                                               GlzEncDictRestoreData
> *restore_data)
> -{
> -    GlzSharedDictionary *shared_dict = NULL;
> -
> -    spice_return_val_if_fail(!enc->glz_dict, FALSE);
> -
> -    pthread_mutex_lock(&glz_dictionary_list_lock);
> -
> -    shared_dict = find_glz_dictionary(client, id);
> -
> -    if (shared_dict) {
> -        shared_dict->refs++;
> -    } else {
> -        shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
> -        ring_add(&glz_dictionary_list, &shared_dict->base);
> -    }
> -
> -    pthread_mutex_unlock(&glz_dictionary_list_lock);
> -    enc->glz_dict = shared_dict;
> -    return shared_dict != NULL;
> -}
> -
> -gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
> -{
> -    enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc-
> >glz_data.usr);
> -    return enc->glz != NULL;
> -}
> -
> -/* destroy encoder, and dictionary if no one uses it*/
> -static void image_encoders_release_glz(ImageEncoders *enc)
> -{
> -    GlzSharedDictionary *shared_dict;
> -
> -    image_encoders_free_glz_drawables(enc);
> -
> -    glz_encoder_destroy(enc->glz);
> -    enc->glz = NULL;
> -
> -    if (!(shared_dict = enc->glz_dict)) {
> -        return;
> -    }
> -
> -    enc->glz_dict = NULL;
> -    pthread_mutex_lock(&glz_dictionary_list_lock);
> -    if (--shared_dict->refs != 0) {
> -        pthread_mutex_unlock(&glz_dictionary_list_lock);
> -        return;
> -    }
> -    ring_remove(&shared_dict->base);
> -    pthread_mutex_unlock(&glz_dictionary_list_lock);
> -    glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
> -    free(shared_dict);
> -}
> -
> -int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
> -                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> -{
> -    QuicData *quic_data = &enc->quic_data;
> -    QuicContext *quic = enc->quic;
> -    volatile QuicImageType type;
> -    int size, stride;
> -    stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
> -
> -#ifdef COMPRESS_DEBUG
> -    spice_info("QUIC compress");
> -#endif
> -
> -    switch (src->format) {
> -    case SPICE_BITMAP_FMT_32BIT:
> -        type = QUIC_IMAGE_TYPE_RGB32;
> -        break;
> -    case SPICE_BITMAP_FMT_RGBA:
> -        type = QUIC_IMAGE_TYPE_RGBA;
> -        break;
> -    case SPICE_BITMAP_FMT_16BIT:
> -        type = QUIC_IMAGE_TYPE_RGB16;
> -        break;
> -    case SPICE_BITMAP_FMT_24BIT:
> -        type = QUIC_IMAGE_TYPE_RGB24;
> -        break;
> -    default:
> -        return FALSE;
> -    }
> -
> -    encoder_data_init(&quic_data->data);
> -
> -    if (setjmp(quic_data->data.jmp_env)) {
> -        encoder_data_reset(&quic_data->data);
> -        return FALSE;
> -    }
> -
> -    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> -        spice_chunks_linearize(src->data);
> -    }
> -
> -    quic_data->data.u.lines_data.chunks = src->data;
> -    quic_data->data.u.lines_data.stride = src->stride;
> -    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> -        quic_data->data.u.lines_data.next = 0;
> -        quic_data->data.u.lines_data.reverse = 0;
> -        stride = src->stride;
> -    } else {
> -        quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
> -        quic_data->data.u.lines_data.reverse = 1;
> -        stride = -src->stride;
> -    }
> -    size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
> -                       quic_data->data.bufs_head->buf.words,
> -                       G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
> -
> -    // the compressed buffer is bigger than the original data
> -    if ((size << 2) > (src->y * src->stride)) {
> -        longjmp(quic_data->data.jmp_env, 1);
> -    }
> -
> -    dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
> -    dest->u.quic.data_size = size << 2;
> -
> -    o_comp_data->comp_buf = quic_data->data.bufs_head;
> -    o_comp_data->comp_buf_size = size << 2;
> -
> -    stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride *
> src->y,
> -                      o_comp_data->comp_buf_size);
> -    return TRUE;
> -}
> -
> -static const LzImageType bitmap_fmt_to_lz_image_type[] = {
> -    LZ_IMAGE_TYPE_INVALID,
> -    LZ_IMAGE_TYPE_PLT1_LE,
> -    LZ_IMAGE_TYPE_PLT1_BE,
> -    LZ_IMAGE_TYPE_PLT4_LE,
> -    LZ_IMAGE_TYPE_PLT4_BE,
> -    LZ_IMAGE_TYPE_PLT8,
> -    LZ_IMAGE_TYPE_RGB16,
> -    LZ_IMAGE_TYPE_RGB24,
> -    LZ_IMAGE_TYPE_RGB32,
> -    LZ_IMAGE_TYPE_RGBA,
> -    LZ_IMAGE_TYPE_A8
> -};
> -
> -int image_encoders_compress_lz(ImageEncoders *enc,
> -                               SpiceImage *dest, SpiceBitmap *src,
> -                               compress_send_data_t* o_comp_data)
> -{
> -    LzData *lz_data = &enc->lz_data;
> -    LzContext *lz = enc->lz;
> -    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
> -    int size;            // size of the compressed data
> -
> -    stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
> -
> -#ifdef COMPRESS_DEBUG
> -    spice_info("LZ LOCAL compress");
> -#endif
> -
> -    encoder_data_init(&lz_data->data);
> -
> -    if (setjmp(lz_data->data.jmp_env)) {
> -        encoder_data_reset(&lz_data->data);
> -        return FALSE;
> -    }
> -
> -    lz_data->data.u.lines_data.chunks = src->data;
> -    lz_data->data.u.lines_data.stride = src->stride;
> -    lz_data->data.u.lines_data.next = 0;
> -    lz_data->data.u.lines_data.reverse = 0;
> -
> -    size = lz_encode(lz, type, src->x, src->y,
> -                     !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
> -                     NULL, 0, src->stride,
> -                     lz_data->data.bufs_head->buf.bytes,
> -                     sizeof(lz_data->data.bufs_head->buf));
> -
> -    // the compressed buffer is bigger than the original data
> -    if (size > (src->y * src->stride)) {
> -        longjmp(lz_data->data.jmp_env, 1);
> -    }
> -
> -    if (bitmap_fmt_is_rgb(src->format)) {
> -        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
> -        dest->u.lz_rgb.data_size = size;
> -
> -        o_comp_data->comp_buf = lz_data->data.bufs_head;
> -        o_comp_data->comp_buf_size = size;
> -    } else {
> -        /* masks are 1BIT bitmaps without palettes, but they are not
> compressed
> -         * (see fill_mask) */
> -        spice_assert(src->palette);
> -        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
> -        dest->u.lz_plt.data_size = size;
> -        dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
> -        dest->u.lz_plt.palette = src->palette;
> -        dest->u.lz_plt.palette_id = src->palette->unique;
> -        o_comp_data->comp_buf = lz_data->data.bufs_head;
> -        o_comp_data->comp_buf_size = size;
> -
> -        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
> -    }
> -
> -    stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride *
> src->y,
> -                      o_comp_data->comp_buf_size);
> -    return TRUE;
> -}
> -
> -int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
> -                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> -{
> -    JpegData *jpeg_data = &enc->jpeg_data;
> -    LzData *lz_data = &enc->lz_data;
> -    JpegEncoderContext *jpeg = enc->jpeg;
> -    LzContext *lz = enc->lz;
> -    volatile JpegEncoderImageType jpeg_in_type;
> -    int jpeg_size = 0;
> -    volatile int has_alpha = FALSE;
> -    int alpha_lz_size = 0;
> -    int comp_head_filled;
> -    int comp_head_left;
> -    int stride;
> -    uint8_t *lz_out_start_byte;
> -    stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
> -
> -#ifdef COMPRESS_DEBUG
> -    spice_info("JPEG compress");
> -#endif
> -
> -    switch (src->format) {
> -    case SPICE_BITMAP_FMT_16BIT:
> -        jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
> -        break;
> -    case SPICE_BITMAP_FMT_24BIT:
> -        jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
> -        break;
> -    case SPICE_BITMAP_FMT_32BIT:
> -        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
> -        break;
> -    case SPICE_BITMAP_FMT_RGBA:
> -        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
> -        has_alpha = TRUE;
> -        break;
> -    default:
> -        return FALSE;
> -    }
> -
> -    encoder_data_init(&jpeg_data->data);
> -
> -    if (setjmp(jpeg_data->data.jmp_env)) {
> -        encoder_data_reset(&jpeg_data->data);
> -        return FALSE;
> -    }
> -
> -    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> -        spice_chunks_linearize(src->data);
> -    }
> -
> -    jpeg_data->data.u.lines_data.chunks = src->data;
> -    jpeg_data->data.u.lines_data.stride = src->stride;
> -    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> -        jpeg_data->data.u.lines_data.next = 0;
> -        jpeg_data->data.u.lines_data.reverse = 0;
> -        stride = src->stride;
> -    } else {
> -        jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
> -        jpeg_data->data.u.lines_data.reverse = 1;
> -        stride = -src->stride;
> -    }
> -    jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type,
> -                            src->x, src->y, NULL,
> -                            0, stride, jpeg_data->data.bufs_head->buf.bytes,
> -                            sizeof(jpeg_data->data.bufs_head->buf));
> -
> -    // the compressed buffer is bigger than the original data
> -    if (jpeg_size > (src->y * src->stride)) {
> -        longjmp(jpeg_data->data.jmp_env, 1);
> -    }
> -
> -    if (!has_alpha) {
> -        dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
> -        dest->u.jpeg.data_size = jpeg_size;
> -
> -        o_comp_data->comp_buf = jpeg_data->data.bufs_head;
> -        o_comp_data->comp_buf_size = jpeg_size;
> -        o_comp_data->is_lossy = TRUE;
> -
> -        stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src-
> >stride * src->y,
> -                          o_comp_data->comp_buf_size);
> -        return TRUE;
> -    }
> -
> -    lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
> -    lz_data->data.bufs_tail = lz_data->data.bufs_head;
> -
> -    comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
> -    comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
> -    lz_out_start_byte = lz_data->data.bufs_head->buf.bytes +
> comp_head_filled;
> -
> -    lz_data->data.u.lines_data.chunks = src->data;
> -    lz_data->data.u.lines_data.stride = src->stride;
> -    lz_data->data.u.lines_data.next = 0;
> -    lz_data->data.u.lines_data.reverse = 0;
> -
> -    alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
> -                               !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
> -                               NULL, 0, src->stride,
> -                               lz_out_start_byte,
> -                               comp_head_left);
> -
> -    // the compressed buffer is bigger than the original data
> -    if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
> -        longjmp(jpeg_data->data.jmp_env, 1);
> -    }
> -
> -    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
> -    dest->u.jpeg_alpha.flags = 0;
> -    if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
> -        dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
> -    }
> -
> -    dest->u.jpeg_alpha.jpeg_size = jpeg_size;
> -    dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
> -
> -    o_comp_data->comp_buf = jpeg_data->data.bufs_head;
> -    o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
> -    o_comp_data->is_lossy = TRUE;
> -    stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src-
> >stride * src->y,
> -                      o_comp_data->comp_buf_size);
> -    return TRUE;
> -}
> -
> -#ifdef USE_LZ4
> -int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
> -                                SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> -{
> -    Lz4Data *lz4_data = &enc->lz4_data;
> -    Lz4EncoderContext *lz4 = enc->lz4;
> -    int lz4_size = 0;
> -    stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
> -
> -#ifdef COMPRESS_DEBUG
> -    spice_info("LZ4 compress");
> -#endif
> -
> -    encoder_data_init(&lz4_data->data);
> -
> -    if (setjmp(lz4_data->data.jmp_env)) {
> -        encoder_data_reset(&lz4_data->data);
> -        return FALSE;
> -    }
> -
> -    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> -        spice_chunks_linearize(src->data);
> -    }
> -
> -    lz4_data->data.u.lines_data.chunks = src->data;
> -    lz4_data->data.u.lines_data.stride = src->stride;
> -    lz4_data->data.u.lines_data.next = 0;
> -    lz4_data->data.u.lines_data.reverse = 0;
> -
> -    lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head-
> >buf.bytes,
> -                          sizeof(lz4_data->data.bufs_head->buf),
> -                          src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src-
> >format);
> -
> -    // the compressed buffer is bigger than the original data
> -    if (lz4_size > (src->y * src->stride)) {
> -        longjmp(lz4_data->data.jmp_env, 1);
> -    }
> -
> -    dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
> -    dest->u.lz4.data_size = lz4_size;
> -
> -    o_comp_data->comp_buf = lz4_data->data.bufs_head;
> -    o_comp_data->comp_buf_size = lz4_size;
> -
> -    stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride *
> src->y,
> -                      o_comp_data->comp_buf_size);
> -    return TRUE;
> -}
> -#endif
> -
> -/* if already exists, returns it. Otherwise allocates and adds it (1) to the
> ring tail
> -   in the channel (2) to the Drawable*/
> -static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable
> *red_drawable,
> -                                        GlzImageRetention *glz_retention)
> -{
> -    RedGlzDrawable *ret;
> -    RingItem *item, *next;
> -
> -    // TODO - I don't really understand what's going on here, so doing the
> technical equivalent
> -    // now that we have multiple glz_dicts, so the only way to go from dcc to
> drawable glz is to go
> -    // over the glz_ring (unless adding some better data structure then a
> ring)
> -    SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret,
> LINK_TO_GLZ(item)) {
> -        if (ret->encoders == enc) {
> -            return ret;
> -        }
> -    }
> -
> -    ret = spice_new(RedGlzDrawable, 1);
> -
> -    ret->encoders = enc;
> -    ret->red_drawable = red_drawable_ref(red_drawable);
> -    ret->has_drawable = TRUE;
> -    ret->instances_count = 0;
> -    ring_init(&ret->instances);
> -
> -    ring_item_init(&ret->link);
> -    ring_item_init(&ret->drawable_link);
> -    ring_add_before(&ret->link, &enc->glz_drawables);
> -    ring_add(&glz_retention->ring, &ret->drawable_link);
> -    enc->shared_data->glz_drawable_count++;
> -    return ret;
> -}
> -
> -/* allocates new instance and adds it to instances in the given drawable.
> -   NOTE - the caller should set the glz_instance returned by the encoder by
> itself.*/
> -static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable
> *glz_drawable)
> -{
> -    spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
> -    // NOTE: We assume the additions are performed consecutively, without
> removals in the middle
> -    GlzDrawableInstanceItem *ret = glz_drawable->instances_pool +
> glz_drawable->instances_count;
> -    glz_drawable->instances_count++;
> -
> -    ring_item_init(&ret->free_link);
> -    ring_item_init(&ret->glz_link);
> -    ring_add(&glz_drawable->instances, &ret->glz_link);
> -    ret->context = NULL;
> -    ret->glz_drawable = glz_drawable;
> -
> -    return ret;
> -}
> -
> -#define MIN_GLZ_SIZE_FOR_ZLIB 100
> -
> -int image_encoders_compress_glz(ImageEncoders *enc,
> -                                SpiceImage *dest, SpiceBitmap *src,
> -                                RedDrawable *red_drawable,
> -                                GlzImageRetention *glz_retention,
> -                                compress_send_data_t* o_comp_data,
> -                                gboolean enable_zlib_glz_wrap)
> -{
> -    stat_start_time_t start_time;
> -    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
> -    spice_assert(bitmap_fmt_is_rgb(src->format));
> -    GlzData *glz_data = &enc->glz_data;
> -    ZlibData *zlib_data;
> -    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
> -    RedGlzDrawable *glz_drawable;
> -    GlzDrawableInstanceItem *glz_drawable_instance;
> -    int glz_size;
> -    int zlib_size;
> -
> -#ifdef COMPRESS_DEBUG
> -    spice_info("LZ global compress fmt=%d", src->format);
> -#endif
> -
> -    if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict-
> >dict)) {
> -        return FALSE;
> -    }
> -
> -    pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
> -    /* using the global dictionary only if it is not frozen */
> -    if (enc->glz_dict->migrate_freeze) {
> -        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -        return FALSE;
> -    }
> -
> -    encoder_data_init(&glz_data->data);
> -
> -    glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
> -    glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
> -
> -    glz_data->data.u.lines_data.chunks = src->data;
> -    glz_data->data.u.lines_data.stride = src->stride;
> -    glz_data->data.u.lines_data.next = 0;
> -    glz_data->data.u.lines_data.reverse = 0;
> -
> -    glz_size = glz_encode(enc->glz, type, src->x, src->y,
> -                          (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL,
> 0,
> -                          src->stride, glz_data->data.bufs_head->buf.bytes,
> -                          sizeof(glz_data->data.bufs_head->buf),
> -                          glz_drawable_instance,
> -                          &glz_drawable_instance->context);
> -
> -    stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride *
> src->y, glz_size);
> -
> -    if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
> -        goto glz;
> -    }
> -    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
> -    zlib_data = &enc->zlib_data;
> -
> -    encoder_data_init(&zlib_data->data);
> -
> -    zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
> -    zlib_data->data.u.compressed_data.size_left = glz_size;
> -
> -    zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
> -                            glz_size, zlib_data->data.bufs_head->buf.bytes,
> -                            sizeof(zlib_data->data.bufs_head->buf));
> -
> -    // the compressed buffer is bigger than the original data
> -    if (zlib_size >= glz_size) {
> -        encoder_data_reset(&zlib_data->data);
> -        goto glz;
> -    } else {
> -        encoder_data_reset(&glz_data->data);
> -    }
> -
> -    dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
> -    dest->u.zlib_glz.glz_data_size = glz_size;
> -    dest->u.zlib_glz.data_size = zlib_size;
> -
> -    o_comp_data->comp_buf = zlib_data->data.bufs_head;
> -    o_comp_data->comp_buf_size = zlib_size;
> -
> -    stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size,
> zlib_size);
> -    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -    return TRUE;
> -
> -glz:
> -    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -
> -    dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
> -    dest->u.lz_rgb.data_size = glz_size;
> -
> -    o_comp_data->comp_buf = glz_data->data.bufs_head;
> -    o_comp_data->comp_buf_size = glz_size;
> -
> -    return TRUE;
> -}
> -
> -void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
> -{
> -    clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
> -
> -    stat_compress_init(&shared_data->off_stat, "off", stat_clock);
> -    stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
> -    stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
> -    stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
> -    stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
> -    stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
> -    stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha",
> stat_clock);
> -    stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
> -}
> -
> -void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
> -{
> -    stat_reset(&shared_data->off_stat);
> -    stat_reset(&shared_data->quic_stat);
> -    stat_reset(&shared_data->lz_stat);
> -    stat_reset(&shared_data->glz_stat);
> -    stat_reset(&shared_data->jpeg_stat);
> -    stat_reset(&shared_data->zlib_glz_stat);
> -    stat_reset(&shared_data->jpeg_alpha_stat);
> -    stat_reset(&shared_data->lz4_stat);
> -}
> -
> -#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
> -
> -#ifdef COMPRESS_STAT
> -static void stat_print_one(const char *name, const stat_info_t *stat)
> -{
> -    spice_info(STAT_FMT, name, stat->count,
> -               stat_byte_to_mega(stat->orig_size),
> -               stat_byte_to_mega(stat->comp_size),
> -               stat_cpu_time_to_sec(stat->total));
> -}
> -
> -static void stat_sum(stat_info_t *total, const stat_info_t *stat)
> -{
> -    total->count += stat->count;
> -    total->orig_size += stat->orig_size;
> -    total->comp_size += stat->comp_size;
> -    total->total += stat->total;
> -}
> -#endif
> -
> -void image_encoder_shared_stat_print(const ImageEncoderSharedData
> *shared_data)
> -{
> -#ifdef COMPRESS_STAT
> -    /* sum all statistics */
> -    stat_info_t total = {
> -        .count = 0,
> -        .orig_size = 0,
> -        .comp_size = 0,
> -        .total = 0
> -    };
> -    stat_sum(&total, &shared_data->off_stat);
> -    stat_sum(&total, &shared_data->quic_stat);
> -    stat_sum(&total, &shared_data->glz_stat);
> -    stat_sum(&total, &shared_data->lz_stat);
> -    stat_sum(&total, &shared_data->jpeg_stat);
> -    stat_sum(&total, &shared_data->jpeg_alpha_stat);
> -    stat_sum(&total, &shared_data->lz4_stat);
> -
> -    /* fix for zlib glz */
> -    total.total += shared_data->zlib_glz_stat.total;
> -    if (shared_data->zlib_glz_stat.count) {
> -        total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
> -                          shared_data->zlib_glz_stat.comp_size;
> -    }
> -
> -    spice_info("Method   \t  count  \torig_size(MB)\tenc_size(MB)\tenc_time(s
> )");
> -    stat_print_one("OFF      ", &shared_data->off_stat);
> -    stat_print_one("QUIC     ", &shared_data->quic_stat);
> -    stat_print_one("GLZ      ", &shared_data->glz_stat);
> -    stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
> -    stat_print_one("LZ       ", &shared_data->lz_stat);
> -    stat_print_one("JPEG     ", &shared_data->jpeg_stat);
> -    stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
> -    stat_print_one("LZ4      ", &shared_data->lz4_stat);
> -    spice_info("-------------------------------------------------------------
> ------");
> -    stat_print_one("Total    ", &total);
> -#endif
> -}
> diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
> deleted file mode 100644
> index 9286970..0000000
> --- a/server/dcc-encoders.h
> +++ /dev/null
> @@ -1,217 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/
> >.
> -*/
> -#ifndef DCC_ENCODERS_H_
> -#define DCC_ENCODERS_H_
> -
> -#include <setjmp.h>
> -#include <common/quic.h>
> -#include <common/lz.h>
> -
> -#include "stat.h"
> -#include "red-parse-qxl.h"
> -#include "glz-encoder.h"
> -#include "jpeg-encoder.h"
> -#ifdef USE_LZ4
> -#include "lz4-encoder.h"
> -#endif
> -#include "zlib-encoder.h"
> -
> -struct RedClient;
> -
> -typedef struct RedCompressBuf RedCompressBuf;
> -typedef struct RedGlzDrawable RedGlzDrawable;
> -typedef struct ImageEncoders ImageEncoders;
> -typedef struct ImageEncoderSharedData ImageEncoderSharedData;
> -typedef struct GlzSharedDictionary GlzSharedDictionary;
> -typedef struct GlzImageRetention GlzImageRetention;
> -
> -void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
> -void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
> -void image_encoder_shared_stat_print(const ImageEncoderSharedData
> *shared_data);
> -
> -void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData
> *shared_data);
> -void image_encoders_free(ImageEncoders *enc);
> -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
> -void image_encoders_free_glz_drawables(ImageEncoders *enc);
> -void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
> -gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
> -void image_encoders_glz_get_restore_data(ImageEncoders *enc,
> -                                         uint8_t *out_id,
> GlzEncDictRestoreData *out_data);
> -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
> -void image_encoders_glz_encode_unlock(ImageEncoders *enc);
> -void glz_retention_free_drawables(GlzImageRetention *ret);
> -void glz_retention_detach_drawables(GlzImageRetention *ret);
> -
> -#define RED_COMPRESS_BUF_SIZE (1024 * 64)
> -struct RedCompressBuf {
> -    /* This buffer provide space for compression algorithms.
> -     * Some algorithms access the buffer as an array of 32 bit words
> -     * so is defined to make sure is always aligned that way.
> -     */
> -    union {
> -        uint8_t  bytes[RED_COMPRESS_BUF_SIZE];
> -        uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
> -    } buf;
> -    RedCompressBuf *send_next;
> -};
> -
> -static inline void compress_buf_free(RedCompressBuf *buf)
> -{
> -    g_free(buf);
> -}
> -
> -gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
> -                                           struct RedClient *client,
> -                                           uint8_t id, int window_size);
> -gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
> -                                               struct RedClient *client,
> -                                               uint8_t id,
> -                                               GlzEncDictRestoreData
> *restore_data);
> -
> -typedef struct  {
> -    RedCompressBuf *bufs_head;
> -    RedCompressBuf *bufs_tail;
> -    jmp_buf jmp_env;
> -    union {
> -        struct {
> -            SpiceChunks *chunks;
> -            int next;
> -            int stride;
> -            int reverse;
> -        } lines_data;
> -        struct {
> -            RedCompressBuf* next;
> -            int size_left;
> -        } compressed_data; // for encoding data that was already compressed
> by another method
> -    } u;
> -} EncoderData;
> -
> -typedef struct {
> -    QuicUsrContext usr;
> -    EncoderData data;
> -} QuicData;
> -
> -typedef struct {
> -    LzUsrContext usr;
> -    EncoderData data;
> -} LzData;
> -
> -typedef struct {
> -    JpegEncoderUsrContext usr;
> -    EncoderData data;
> -} JpegData;
> -
> -#ifdef USE_LZ4
> -typedef struct {
> -    Lz4EncoderUsrContext usr;
> -    EncoderData data;
> -} Lz4Data;
> -#endif
> -
> -typedef struct {
> -    ZlibEncoderUsrContext usr;
> -    EncoderData data;
> -} ZlibData;
> -
> -typedef struct {
> -    GlzEncoderUsrContext usr;
> -    EncoderData data;
> -} GlzData;
> -
> -struct GlzImageRetention {
> -    Ring ring;
> -};
> -
> -static inline void glz_retention_init(GlzImageRetention *ret)
> -{
> -    ring_init(&ret->ring);
> -}
> -
> -struct ImageEncoderSharedData {
> -    uint32_t glz_drawable_count;
> -
> -    stat_info_t off_stat;
> -    stat_info_t lz_stat;
> -    stat_info_t glz_stat;
> -    stat_info_t quic_stat;
> -    stat_info_t jpeg_stat;
> -    stat_info_t zlib_glz_stat;
> -    stat_info_t jpeg_alpha_stat;
> -    stat_info_t lz4_stat;
> -};
> -
> -struct ImageEncoders {
> -    ImageEncoderSharedData *shared_data;
> -
> -    QuicData quic_data;
> -    QuicContext *quic;
> -
> -    LzData lz_data;
> -    LzContext  *lz;
> -
> -    int jpeg_quality;
> -
> -    JpegData jpeg_data;
> -    JpegEncoderContext *jpeg;
> -
> -#ifdef USE_LZ4
> -    Lz4Data lz4_data;
> -    Lz4EncoderContext *lz4;
> -#endif
> -
> -    int zlib_level;
> -
> -    ZlibData zlib_data;
> -    ZlibEncoder *zlib;
> -
> -    /* global lz encoding entities */
> -    GlzSharedDictionary *glz_dict;
> -    GlzEncoderContext *glz;
> -    GlzData glz_data;
> -
> -    Ring glz_drawables;               // all the living lz drawable, ordered
> by encoding time
> -    Ring glz_drawables_inst_to_free;               // list of instances to be
> freed
> -    pthread_mutex_t glz_drawables_inst_to_free_lock;
> -};
> -
> -typedef struct compress_send_data_t {
> -    void* comp_buf;
> -    uint32_t comp_buf_size;
> -    SpicePalette *lzplt_palette;
> -    int is_lossy;
> -} compress_send_data_t;
> -
> -int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
> -                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> -int image_encoders_compress_lz(ImageEncoders *enc,
> -                               SpiceImage *dest, SpiceBitmap *src,
> -                               compress_send_data_t* o_comp_data);
> -int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
> -                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> -int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
> -                                SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> -int image_encoders_compress_glz(ImageEncoders *enc,
> -                                SpiceImage *dest, SpiceBitmap *src,
> -                                RedDrawable *red_drawable,
> -                                GlzImageRetention *glz_retention,
> -                                compress_send_data_t* o_comp_data,
> -                                gboolean enable_zlib_glz_wrap);
> -
> -#define RED_RELEASE_BUNCH_SIZE 64
> -
> -#endif /* DCC_ENCODERS_H_ */
> diff --git a/server/dcc.h b/server/dcc.h
> index 0c19c02..5f7b16f 100644
> --- a/server/dcc.h
> +++ b/server/dcc.h
> @@ -21,7 +21,7 @@
>  #include "red-worker.h"
>  #include "pixmap-cache.h"
>  #include "cache-item.h"
> -#include "dcc-encoders.h"
> +#include "image-encoders.h"
>  #include "stream.h"
>  #include "display-limits.h"
>  
> diff --git a/server/image-encoders.c b/server/image-encoders.c
> new file mode 100644
> index 0000000..b47ae35
> --- /dev/null
> +++ b/server/image-encoders.c
> @@ -0,0 +1,1388 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/
> >.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <glib.h>
> +
> +#include "image-encoders.h"
> +#include "spice-bitmap-utils.h"
> +#include "red-worker.h" // red_drawable_unref
> +#include "pixmap-cache.h" // MAX_CACHE_CLIENTS
> +
> +#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
> +
> +#define ENCODER_MESSAGE_SIZE 512
> +
> +#define MAX_GLZ_DRAWABLE_INSTANCES 2
> +
> +typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
> +
> +struct GlzSharedDictionary {
> +    RingItem base;
> +    GlzEncDictContext *dict;
> +    uint32_t refs;
> +    uint8_t id;
> +    pthread_rwlock_t encode_lock;
> +    int migrate_freeze;
> +    RedClient *client; // channel clients of the same client share the dict
> +};
> +
> +/* for each qxl drawable, there may be several instances of lz drawables */
> +/* TODO - reuse this stuff for the top level. I just added a second level of
> multiplicity
> + * at the Drawable by keeping a ring, so:
> + * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
> + * and it should probably (but need to be sure...) be
> + * Drawable -> ring of GlzDrawableInstanceItem.
> + */
> +struct GlzDrawableInstanceItem {
> +    RingItem glz_link;
> +    RingItem free_link;
> +    GlzEncDictImageContext *context;
> +    RedGlzDrawable         *glz_drawable;
> +};
> +
> +struct RedGlzDrawable {
> +    RingItem link;    // ordered by the time it was encoded
> +    RingItem drawable_link;
> +    RedDrawable *red_drawable;
> +    GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
> +    Ring instances;
> +    uint8_t instances_count;
> +    gboolean has_drawable;
> +    ImageEncoders *encoders;
> +};
> +
> +#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
> +                                           drawable_link)
> +#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
> +    SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz,
> LINK_TO_GLZ(link))
> +
> +static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance);
> +static void encoder_data_init(EncoderData *data);
> +static void encoder_data_reset(EncoderData *data);
> +static void image_encoders_release_glz(ImageEncoders *enc);
> +
> +
> +static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
> +quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
> +{
> +    EncoderData *usr_data = &(((QuicData *)usr)->data);
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +    spice_critical("%s", message_buf);
> +
> +    longjmp(usr_data->jmp_env, 1);
> +}
> +
> +static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
> +lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
> +{
> +    EncoderData *usr_data = &(((LzData *)usr)->data);
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +    spice_critical("%s", message_buf);
> +
> +    longjmp(usr_data->jmp_env, 1);
> +}
> +
> +static SPICE_GNUC_PRINTF(2, 3) void
> +glz_usr_error(GlzEncoderUsrContext *usr, const char *fmt, ...)
> +{
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +
> +    spice_critical("%s", message_buf); // if global lz fails in the middle
> +                                        // the consequences are not
> predictable since the window
> +                                        // can turn to be unsynchronized
> between the server and
> +                                        // and the client
> +}
> +
> +static SPICE_GNUC_PRINTF(2, 3) void
> +quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
> +{
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +    spice_warning("%s", message_buf);
> +}
> +
> +static SPICE_GNUC_PRINTF(2, 3) void
> +lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
> +{
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +    spice_warning("%s", message_buf);
> +}
> +
> +static SPICE_GNUC_PRINTF(2, 3) void
> +glz_usr_warn(GlzEncoderUsrContext *usr, const char *fmt, ...)
> +{
> +    va_list ap;
> +    char message_buf[ENCODER_MESSAGE_SIZE];
> +
> +    va_start(ap, fmt);
> +    vsnprintf(message_buf, sizeof(message_buf), fmt, ap);
> +    va_end(ap);
> +    spice_warning("%s", message_buf);
> +}
> +
> +static void *quic_usr_malloc(QuicUsrContext *usr, int size)
> +{
> +    return spice_malloc(size);
> +}
> +
> +static void *lz_usr_malloc(LzUsrContext *usr, int size)
> +{
> +    return spice_malloc(size);
> +}
> +
> +static void *glz_usr_malloc(GlzEncoderUsrContext *usr, int size)
> +{
> +    return spice_malloc(size);
> +}
> +
> +static void quic_usr_free(QuicUsrContext *usr, void *ptr)
> +{
> +    free(ptr);
> +}
> +
> +static void lz_usr_free(LzUsrContext *usr, void *ptr)
> +{
> +    free(ptr);
> +}
> +
> +static void glz_usr_free(GlzEncoderUsrContext *usr, void *ptr)
> +{
> +    free(ptr);
> +}
> +
> +static void encoder_data_init(EncoderData *data)
> +{
> +    data->bufs_tail = g_new(RedCompressBuf, 1);
> +    data->bufs_head = data->bufs_tail;
> +    data->bufs_head->send_next = NULL;
> +}
> +
> +static void encoder_data_reset(EncoderData *data)
> +{
> +    RedCompressBuf *buf = data->bufs_head;
> +    while (buf) {
> +        RedCompressBuf *next = buf->send_next;
> +        g_free(buf);
> +        buf = next;
> +    }
> +    data->bufs_head = data->bufs_tail = NULL;
> +}
> +
> +/* Allocate more space for compressed buffer.
> + * The pointer returned in io_ptr is garanteed to be aligned to 4 bytes.
> + */
> +static int encoder_usr_more_space(EncoderData *enc_data, uint8_t **io_ptr)
> +{
> +    RedCompressBuf *buf;
> +
> +    buf = g_new(RedCompressBuf, 1);
> +    enc_data->bufs_tail->send_next = buf;
> +    enc_data->bufs_tail = buf;
> +    buf->send_next = NULL;
> +    *io_ptr = buf->buf.bytes;
> +    return sizeof(buf->buf);
> +}
> +
> +static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int
> rows_completed)
> +{
> +    EncoderData *usr_data = &(((QuicData *)usr)->data);
> +    return encoder_usr_more_space(usr_data, (uint8_t **)io_ptr) /
> sizeof(uint32_t);
> +}
> +
> +static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
> +{
> +    EncoderData *usr_data = &(((LzData *)usr)->data);
> +    return encoder_usr_more_space(usr_data, io_ptr);
> +}
> +
> +static int glz_usr_more_space(GlzEncoderUsrContext *usr, uint8_t **io_ptr)
> +{
> +    EncoderData *usr_data = &(((GlzData *)usr)->data);
> +    return encoder_usr_more_space(usr_data, io_ptr);
> +}
> +
> +static int jpeg_usr_more_space(JpegEncoderUsrContext *usr, uint8_t **io_ptr)
> +{
> +    EncoderData *usr_data = &(((JpegData *)usr)->data);
> +    return encoder_usr_more_space(usr_data, io_ptr);
> +}
> +
> +#ifdef USE_LZ4
> +static int lz4_usr_more_space(Lz4EncoderUsrContext *usr, uint8_t **io_ptr)
> +{
> +    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
> +    return encoder_usr_more_space(usr_data, io_ptr);
> +}
> +#endif
> +
> +static int zlib_usr_more_space(ZlibEncoderUsrContext *usr, uint8_t **io_ptr)
> +{
> +    EncoderData *usr_data = &(((ZlibData *)usr)->data);
> +    return encoder_usr_more_space(usr_data, io_ptr);
> +}
> +
> +static inline int encoder_usr_more_lines(EncoderData *enc_data, uint8_t
> **lines)
> +{
> +    struct SpiceChunk *chunk;
> +
> +    if (enc_data->u.lines_data.reverse) {
> +        if (!(enc_data->u.lines_data.next >= 0)) {
> +            return 0;
> +        }
> +    } else {
> +        if (!(enc_data->u.lines_data.next < enc_data->u.lines_data.chunks-
> >num_chunks)) {
> +            return 0;
> +        }
> +    }
> +
> +    chunk = &enc_data->u.lines_data.chunks->chunk[enc_data-
> >u.lines_data.next];
> +    if (chunk->len % enc_data->u.lines_data.stride) {
> +        return 0;
> +    }
> +
> +    if (enc_data->u.lines_data.reverse) {
> +        enc_data->u.lines_data.next--;
> +        *lines = chunk->data + chunk->len - enc_data->u.lines_data.stride;
> +    } else {
> +        enc_data->u.lines_data.next++;
> +        *lines = chunk->data;
> +    }
> +
> +    return chunk->len / enc_data->u.lines_data.stride;
> +}
> +
> +static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
> +{
> +    EncoderData *usr_data = &(((QuicData *)usr)->data);
> +    return encoder_usr_more_lines(usr_data, lines);
> +}
> +
> +static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
> +{
> +    EncoderData *usr_data = &(((LzData *)usr)->data);
> +    return encoder_usr_more_lines(usr_data, lines);
> +}
> +
> +static int glz_usr_more_lines(GlzEncoderUsrContext *usr, uint8_t **lines)
> +{
> +    EncoderData *usr_data = &(((GlzData *)usr)->data);
> +    return encoder_usr_more_lines(usr_data, lines);
> +}
> +
> +static int jpeg_usr_more_lines(JpegEncoderUsrContext *usr, uint8_t **lines)
> +{
> +    EncoderData *usr_data = &(((JpegData *)usr)->data);
> +    return encoder_usr_more_lines(usr_data, lines);
> +}
> +
> +#ifdef USE_LZ4
> +static int lz4_usr_more_lines(Lz4EncoderUsrContext *usr, uint8_t **lines)
> +{
> +    EncoderData *usr_data = &(((Lz4Data *)usr)->data);
> +    return encoder_usr_more_lines(usr_data, lines);
> +}
> +#endif
> +
> +static int zlib_usr_more_input(ZlibEncoderUsrContext *usr, uint8_t** input)
> +{
> +    EncoderData *usr_data = &(((ZlibData *)usr)->data);
> +    int buf_size;
> +
> +    if (!usr_data->u.compressed_data.next) {
> +        spice_assert(usr_data->u.compressed_data.size_left == 0);
> +        return 0;
> +    }
> +
> +    *input = usr_data->u.compressed_data.next->buf.bytes;
> +    buf_size = MIN(sizeof(usr_data->u.compressed_data.next->buf),
> +                   usr_data->u.compressed_data.size_left);
> +
> +    usr_data->u.compressed_data.next = usr_data->u.compressed_data.next-
> >send_next;
> +    usr_data->u.compressed_data.size_left -= buf_size;
> +    return buf_size;
> +}
> +
> +static void image_encoders_init_quic(ImageEncoders *enc)
> +{
> +    enc->quic_data.usr.error = quic_usr_error;
> +    enc->quic_data.usr.warn = quic_usr_warn;
> +    enc->quic_data.usr.info = quic_usr_warn;
> +    enc->quic_data.usr.malloc = quic_usr_malloc;
> +    enc->quic_data.usr.free = quic_usr_free;
> +    enc->quic_data.usr.more_space = quic_usr_more_space;
> +    enc->quic_data.usr.more_lines = quic_usr_more_lines;
> +
> +    enc->quic = quic_create(&enc->quic_data.usr);
> +
> +    if (!enc->quic) {
> +        spice_critical("create quic failed");
> +    }
> +}
> +
> +static void image_encoders_init_lz(ImageEncoders *enc)
> +{
> +    enc->lz_data.usr.error = lz_usr_error;
> +    enc->lz_data.usr.warn = lz_usr_warn;
> +    enc->lz_data.usr.info = lz_usr_warn;
> +    enc->lz_data.usr.malloc = lz_usr_malloc;
> +    enc->lz_data.usr.free = lz_usr_free;
> +    enc->lz_data.usr.more_space = lz_usr_more_space;
> +    enc->lz_data.usr.more_lines = lz_usr_more_lines;
> +
> +    enc->lz = lz_create(&enc->lz_data.usr);
> +
> +    if (!enc->lz) {
> +        spice_critical("create lz failed");
> +    }
> +}
> +
> +static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext
> *image)
> +{
> +    GlzData *lz_data = (GlzData *)usr;
> +    GlzDrawableInstanceItem *glz_drawable_instance = (GlzDrawableInstanceItem
> *)image;
> +    ImageEncoders *drawable_enc = glz_drawable_instance->glz_drawable-
> >encoders;
> +    ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders,
> glz_data);
> +    if (this_enc == drawable_enc) {
> +        glz_drawable_instance_item_free(glz_drawable_instance);
> +    } else {
> +        /* The glz dictionary is shared between all DisplayChannelClient
> +         * instances that belong to the same client, and glz_usr_free_image
> +         * can be called by the dictionary code
> +         * (glz_dictionary_window_remove_head). Thus this function can be
> +         * called from any DisplayChannelClient thread, hence the need for
> +         * this check.
> +         */
> +        pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
> +        ring_add_before(&glz_drawable_instance->free_link,
> +                        &drawable_enc->glz_drawables_inst_to_free);
> +        pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
> +    }
> +}
> +
> +static void image_encoders_init_glz_data(ImageEncoders *enc)
> +{
> +    enc->glz_data.usr.error = glz_usr_error;
> +    enc->glz_data.usr.warn = glz_usr_warn;
> +    enc->glz_data.usr.info = glz_usr_warn;
> +    enc->glz_data.usr.malloc = glz_usr_malloc;
> +    enc->glz_data.usr.free = glz_usr_free;
> +    enc->glz_data.usr.more_space = glz_usr_more_space;
> +    enc->glz_data.usr.more_lines = glz_usr_more_lines;
> +    enc->glz_data.usr.free_image = glz_usr_free_image;
> +}
> +
> +static void image_encoders_init_jpeg(ImageEncoders *enc)
> +{
> +    enc->jpeg_data.usr.more_space = jpeg_usr_more_space;
> +    enc->jpeg_data.usr.more_lines = jpeg_usr_more_lines;
> +
> +    enc->jpeg = jpeg_encoder_create(&enc->jpeg_data.usr);
> +
> +    if (!enc->jpeg) {
> +        spice_critical("create jpeg encoder failed");
> +    }
> +}
> +
> +#ifdef USE_LZ4
> +static inline void image_encoders_init_lz4(ImageEncoders *enc)
> +{
> +    enc->lz4_data.usr.more_space = lz4_usr_more_space;
> +    enc->lz4_data.usr.more_lines = lz4_usr_more_lines;
> +
> +    enc->lz4 = lz4_encoder_create(&enc->lz4_data.usr);
> +
> +    if (!enc->lz4) {
> +        spice_critical("create lz4 encoder failed");
> +    }
> +}
> +#endif
> +
> +static void image_encoders_init_zlib(ImageEncoders *enc)
> +{
> +    enc->zlib_data.usr.more_space = zlib_usr_more_space;
> +    enc->zlib_data.usr.more_input = zlib_usr_more_input;
> +
> +    enc->zlib = zlib_encoder_create(&enc->zlib_data.usr,
> ZLIB_DEFAULT_COMPRESSION_LEVEL);
> +
> +    if (!enc->zlib) {
> +        spice_critical("create zlib encoder failed");
> +    }
> +}
> +
> +void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData
> *shared_data)
> +{
> +    spice_assert(shared_data);
> +    enc->shared_data = shared_data;
> +
> +    ring_init(&enc->glz_drawables);
> +    ring_init(&enc->glz_drawables_inst_to_free);
> +    pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
> +
> +    image_encoders_init_glz_data(enc);
> +    image_encoders_init_quic(enc);
> +    image_encoders_init_lz(enc);
> +    image_encoders_init_jpeg(enc);
> +#ifdef USE_LZ4
> +    image_encoders_init_lz4(enc);
> +#endif
> +    image_encoders_init_zlib(enc);
> +
> +    // todo: tune level according to bandwidth
> +    enc->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
> +}
> +
> +void image_encoders_free(ImageEncoders *enc)
> +{
> +    image_encoders_release_glz(enc);
> +    quic_destroy(enc->quic);
> +    enc->quic = NULL;
> +    lz_destroy(enc->lz);
> +    enc->lz = NULL;
> +    jpeg_encoder_destroy(enc->jpeg);
> +    enc->jpeg = NULL;
> +#ifdef USE_LZ4
> +    lz4_encoder_destroy(enc->lz4);
> +    enc->lz4 = NULL;
> +#endif
> +    zlib_encoder_destroy(enc->zlib);
> +    enc->zlib = NULL;
> +}
> +
> +/* Remove from the to_free list and the instances_list.
> +   When no instance is left - the RedGlzDrawable is released too. (and the
> qxl drawable too, if
> +   it is not used by Drawable).
> +   NOTE - 1) can be called only by the display channel that created the
> drawable
> +          2) it is assumed that the instance was already removed from the
> dictionary*/
> +static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance)
> +{
> +    RedGlzDrawable *glz_drawable;
> +
> +    spice_assert(instance);
> +    spice_assert(instance->glz_drawable);
> +
> +    glz_drawable = instance->glz_drawable;
> +
> +    spice_assert(glz_drawable->instances_count > 0);
> +
> +    ring_remove(&instance->glz_link);
> +    glz_drawable->instances_count--;
> +
> +    // when the remove callback is performed from the channel that the
> +    // drawable belongs to, the instance is not added to the 'to_free' list
> +    if (ring_item_is_linked(&instance->free_link)) {
> +        ring_remove(&instance->free_link);
> +    }
> +
> +    if (ring_is_empty(&glz_drawable->instances)) {
> +        spice_assert(glz_drawable->instances_count == 0);
> +
> +        if (glz_drawable->has_drawable) {
> +            ring_remove(&glz_drawable->drawable_link);
> +        }
> +        red_drawable_unref(glz_drawable->red_drawable);
> +        glz_drawable->encoders->shared_data->glz_drawable_count--;
> +        if (ring_item_is_linked(&glz_drawable->link)) {
> +            ring_remove(&glz_drawable->link);
> +        }
> +        free(glz_drawable);
> +    }
> +}
> +
> +/*
> + * Releases all the instances of the drawable from the dictionary and the
> display channel client.
> + * The release of the last instance will also release the drawable itself and
> the qxl drawable
> + * if possible.
> + * NOTE - the caller should prevent encoding using the dictionary during this
> operation
> + */
> +static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
> +{
> +    ImageEncoders *enc = glz_drawable->encoders;
> +    RingItem *head_instance = ring_get_head(&glz_drawable->instances);
> +    int cont = (head_instance != NULL);
> +
> +    while (cont) {
> +        if (glz_drawable->instances_count == 1) {
> +            /* Last instance: glz_drawable_instance_item_free will free the
> glz_drawable */
> +            cont = FALSE;
> +        }
> +        GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
> +                                                        GlzDrawableInstanceIt
> em,
> +                                                        glz_link);
> +        if (!ring_item_is_linked(&instance->free_link)) {
> +            // the instance didn't get out from window yet
> +            glz_enc_dictionary_remove_image(enc->glz_dict->dict,
> +                                            instance->context,
> +                                            &enc->glz_data.usr);
> +        }
> +        glz_drawable_instance_item_free(instance);
> +
> +        if (cont) {
> +            head_instance = ring_get_head(&glz_drawable->instances);
> +        }
> +    }
> +}
> +
> +gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
> +{
> +    if (enc->glz_dict) {
> +        pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> +        return TRUE;
> +    }
> +    return FALSE;
> +}
> +
> +void image_encoders_glz_encode_unlock(ImageEncoders *enc)
> +{
> +    if (enc->glz_dict) {
> +        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> +    }
> +}
> +
> +/*
> + * Remove from the global lz dictionary some glz_drawables that have no
> reference to
> + * Drawable (their qxl drawables are released too).
> + * NOTE - the caller should prevent encoding using the dictionary during the
> operation
> + */
> +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
> +{
> +    RingItem *ring_link;
> +    int n = 0;
> +
> +    if (!enc) {
> +        return 0;
> +    }
> +    ring_link = ring_get_head(&enc->glz_drawables);
> +    while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
> +        RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> +        ring_link = ring_next(&enc->glz_drawables, ring_link);
> +        if (!glz_drawable->has_drawable) {
> +            red_glz_drawable_free(glz_drawable);
> +            n++;
> +        }
> +    }
> +    return n;
> +}
> +
> +void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
> +{
> +    RingItem *ring_link;
> +
> +    if (!enc->glz_dict) {
> +        return;
> +    }
> +    pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
> +    while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
> +        GlzDrawableInstanceItem *drawable_instance =
> SPICE_CONTAINEROF(ring_link,
> +                                                                 GlzDrawableI
> nstanceItem,
> +                                                                 free_link);
> +        glz_drawable_instance_item_free(drawable_instance);
> +    }
> +    pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
> +}
> +
> +/* Clear all lz drawables - enforce their removal from the global dictionary.
> +   NOTE - prevents encoding using the dictionary during the operation*/
> +void image_encoders_free_glz_drawables(ImageEncoders *enc)
> +{
> +    RingItem *ring_link;
> +    GlzSharedDictionary *glz_dict = enc ? enc->glz_dict : NULL;
> +
> +    if (!glz_dict) {
> +        return;
> +    }
> +
> +    // assure no display channel is during global lz encoding
> +    pthread_rwlock_wrlock(&glz_dict->encode_lock);
> +    while ((ring_link = ring_get_head(&enc->glz_drawables))) {
> +        RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> +        // no need to lock the to_free list, since we assured no other thread
> is encoding and
> +        // thus not other thread access the to_free list of the channel
> +        red_glz_drawable_free(drawable);
> +    }
> +    pthread_rwlock_unlock(&glz_dict->encode_lock);
> +}
> +
> +void glz_retention_free_drawables(GlzImageRetention *ret)
> +{
> +    RingItem *glz_item, *next_item;
> +    RedGlzDrawable *glz;
> +    SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz,
> LINK_TO_GLZ(glz_item)) {
> +        red_glz_drawable_free(glz);
> +    }
> +}
> +
> +void glz_retention_detach_drawables(GlzImageRetention *ret)
> +{
> +    RingItem *item, *next;
> +
> +    RING_FOREACH_SAFE(item, next, &ret->ring) {
> +        SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable
> = FALSE;
> +        ring_remove(item);
> +    }
> +}
> +
> +static void image_encoders_freeze_glz(ImageEncoders *enc)
> +{
> +    pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> +    enc->glz_dict->migrate_freeze = TRUE;
> +    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> +}
> +
> +void image_encoders_glz_get_restore_data(ImageEncoders *enc,
> +                                         uint8_t *out_id,
> GlzEncDictRestoreData *out_data)
> +{
> +    spice_assert(enc->glz_dict);
> +    image_encoders_freeze_glz(enc);
> +    *out_id = enc->glz_dict->id;
> +    glz_enc_dictionary_get_restore_data(enc->glz_dict->dict, out_data,
> +                                        &enc->glz_data.usr);
> +}
> +
> +static GlzSharedDictionary *glz_shared_dictionary_new(RedClient *client,
> uint8_t id,
> +                                                      GlzEncDictContext
> *dict)
> +{
> +    spice_return_val_if_fail(dict != NULL, NULL);
> +
> +    GlzSharedDictionary *shared_dict = spice_new0(GlzSharedDictionary, 1);
> +
> +    shared_dict->dict = dict;
> +    shared_dict->id = id;
> +    shared_dict->refs = 1;
> +    shared_dict->migrate_freeze = FALSE;
> +    shared_dict->client = client;
> +    ring_item_init(&shared_dict->base);
> +    pthread_rwlock_init(&shared_dict->encode_lock, NULL);
> +
> +    return shared_dict;
> +}
> +
> +static pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
> +static Ring glz_dictionary_list = {&glz_dictionary_list,
> &glz_dictionary_list};
> +
> +static GlzSharedDictionary *find_glz_dictionary(RedClient *client, uint8_t
> dict_id)
> +{
> +    RingItem *now;
> +    GlzSharedDictionary *ret = NULL;
> +
> +    now = &glz_dictionary_list;
> +    while ((now = ring_next(&glz_dictionary_list, now))) {
> +        GlzSharedDictionary *dict = SPICE_UPCAST(GlzSharedDictionary, now);
> +        if ((dict->client == client) && (dict->id == dict_id)) {
> +            ret = dict;
> +            break;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
> +
> +static GlzSharedDictionary *create_glz_dictionary(ImageEncoders *enc,
> +                                                  RedClient *client,
> +                                                  uint8_t id, int
> window_size)
> +{
> +    spice_info("Lz Window %d Size=%d", id, window_size);
> +
> +    GlzEncDictContext *glz_dict =
> +        glz_enc_dictionary_create(window_size, MAX_LZ_ENCODERS, &enc-
> >glz_data.usr);
> +
> +    return glz_shared_dictionary_new(client, id, glz_dict);
> +}
> +
> +gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
> +                                           RedClient *client,
> +                                           uint8_t id, int window_size)
> +{
> +    GlzSharedDictionary *shared_dict;
> +
> +    spice_return_val_if_fail(!enc->glz_dict, FALSE);
> +
> +    pthread_mutex_lock(&glz_dictionary_list_lock);
> +
> +    shared_dict = find_glz_dictionary(client, id);
> +    if (shared_dict) {
> +        shared_dict->refs++;
> +    } else {
> +        shared_dict = create_glz_dictionary(enc, client, id, window_size);
> +        ring_add(&glz_dictionary_list, &shared_dict->base);
> +    }
> +
> +    pthread_mutex_unlock(&glz_dictionary_list_lock);
> +    enc->glz_dict = shared_dict;
> +    return shared_dict != NULL;
> +}
> +
> +static GlzSharedDictionary *restore_glz_dictionary(ImageEncoders *enc,
> +                                                   RedClient *client,
> +                                                   uint8_t id,
> +                                                   GlzEncDictRestoreData
> *restore_data)
> +{
> +    GlzEncDictContext *glz_dict =
> +        glz_enc_dictionary_restore(restore_data, &enc->glz_data.usr);
> +
> +    return glz_shared_dictionary_new(client, id, glz_dict);
> +}
> +
> +gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
> +                                               RedClient *client,
> +                                               uint8_t id,
> +                                               GlzEncDictRestoreData
> *restore_data)
> +{
> +    GlzSharedDictionary *shared_dict = NULL;
> +
> +    spice_return_val_if_fail(!enc->glz_dict, FALSE);
> +
> +    pthread_mutex_lock(&glz_dictionary_list_lock);
> +
> +    shared_dict = find_glz_dictionary(client, id);
> +
> +    if (shared_dict) {
> +        shared_dict->refs++;
> +    } else {
> +        shared_dict = restore_glz_dictionary(enc, client, id, restore_data);
> +        ring_add(&glz_dictionary_list, &shared_dict->base);
> +    }
> +
> +    pthread_mutex_unlock(&glz_dictionary_list_lock);
> +    enc->glz_dict = shared_dict;
> +    return shared_dict != NULL;
> +}
> +
> +gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id)
> +{
> +    enc->glz = glz_encoder_create(id, enc->glz_dict->dict, &enc-
> >glz_data.usr);
> +    return enc->glz != NULL;
> +}
> +
> +/* destroy encoder, and dictionary if no one uses it*/
> +static void image_encoders_release_glz(ImageEncoders *enc)
> +{
> +    GlzSharedDictionary *shared_dict;
> +
> +    image_encoders_free_glz_drawables(enc);
> +
> +    glz_encoder_destroy(enc->glz);
> +    enc->glz = NULL;
> +
> +    if (!(shared_dict = enc->glz_dict)) {
> +        return;
> +    }
> +
> +    enc->glz_dict = NULL;
> +    pthread_mutex_lock(&glz_dictionary_list_lock);
> +    if (--shared_dict->refs != 0) {
> +        pthread_mutex_unlock(&glz_dictionary_list_lock);
> +        return;
> +    }
> +    ring_remove(&shared_dict->base);
> +    pthread_mutex_unlock(&glz_dictionary_list_lock);
> +    glz_enc_dictionary_destroy(shared_dict->dict, &enc->glz_data.usr);
> +    free(shared_dict);
> +}
> +
> +int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
> +                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> +{
> +    QuicData *quic_data = &enc->quic_data;
> +    QuicContext *quic = enc->quic;
> +    volatile QuicImageType type;
> +    int size, stride;
> +    stat_start_time_t start_time;
> +    stat_start_time_init(&start_time, &enc->shared_data->quic_stat);
> +
> +#ifdef COMPRESS_DEBUG
> +    spice_info("QUIC compress");
> +#endif
> +
> +    switch (src->format) {
> +    case SPICE_BITMAP_FMT_32BIT:
> +        type = QUIC_IMAGE_TYPE_RGB32;
> +        break;
> +    case SPICE_BITMAP_FMT_RGBA:
> +        type = QUIC_IMAGE_TYPE_RGBA;
> +        break;
> +    case SPICE_BITMAP_FMT_16BIT:
> +        type = QUIC_IMAGE_TYPE_RGB16;
> +        break;
> +    case SPICE_BITMAP_FMT_24BIT:
> +        type = QUIC_IMAGE_TYPE_RGB24;
> +        break;
> +    default:
> +        return FALSE;
> +    }
> +
> +    encoder_data_init(&quic_data->data);
> +
> +    if (setjmp(quic_data->data.jmp_env)) {
> +        encoder_data_reset(&quic_data->data);
> +        return FALSE;
> +    }
> +
> +    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> +        spice_chunks_linearize(src->data);
> +    }
> +
> +    quic_data->data.u.lines_data.chunks = src->data;
> +    quic_data->data.u.lines_data.stride = src->stride;
> +    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> +        quic_data->data.u.lines_data.next = 0;
> +        quic_data->data.u.lines_data.reverse = 0;
> +        stride = src->stride;
> +    } else {
> +        quic_data->data.u.lines_data.next = src->data->num_chunks - 1;
> +        quic_data->data.u.lines_data.reverse = 1;
> +        stride = -src->stride;
> +    }
> +    size = quic_encode(quic, type, src->x, src->y, NULL, 0, stride,
> +                       quic_data->data.bufs_head->buf.words,
> +                       G_N_ELEMENTS(quic_data->data.bufs_head->buf.words));
> +
> +    // the compressed buffer is bigger than the original data
> +    if ((size << 2) > (src->y * src->stride)) {
> +        longjmp(quic_data->data.jmp_env, 1);
> +    }
> +
> +    dest->descriptor.type = SPICE_IMAGE_TYPE_QUIC;
> +    dest->u.quic.data_size = size << 2;
> +
> +    o_comp_data->comp_buf = quic_data->data.bufs_head;
> +    o_comp_data->comp_buf_size = size << 2;
> +
> +    stat_compress_add(&enc->shared_data->quic_stat, start_time, src->stride *
> src->y,
> +                      o_comp_data->comp_buf_size);
> +    return TRUE;
> +}
> +
> +static const LzImageType bitmap_fmt_to_lz_image_type[] = {
> +    LZ_IMAGE_TYPE_INVALID,
> +    LZ_IMAGE_TYPE_PLT1_LE,
> +    LZ_IMAGE_TYPE_PLT1_BE,
> +    LZ_IMAGE_TYPE_PLT4_LE,
> +    LZ_IMAGE_TYPE_PLT4_BE,
> +    LZ_IMAGE_TYPE_PLT8,
> +    LZ_IMAGE_TYPE_RGB16,
> +    LZ_IMAGE_TYPE_RGB24,
> +    LZ_IMAGE_TYPE_RGB32,
> +    LZ_IMAGE_TYPE_RGBA,
> +    LZ_IMAGE_TYPE_A8
> +};
> +
> +int image_encoders_compress_lz(ImageEncoders *enc,
> +                               SpiceImage *dest, SpiceBitmap *src,
> +                               compress_send_data_t* o_comp_data)
> +{
> +    LzData *lz_data = &enc->lz_data;
> +    LzContext *lz = enc->lz;
> +    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
> +    int size;            // size of the compressed data
> +
> +    stat_start_time_t start_time;
> +    stat_start_time_init(&start_time, &enc->shared_data->lz_stat);
> +
> +#ifdef COMPRESS_DEBUG
> +    spice_info("LZ LOCAL compress");
> +#endif
> +
> +    encoder_data_init(&lz_data->data);
> +
> +    if (setjmp(lz_data->data.jmp_env)) {
> +        encoder_data_reset(&lz_data->data);
> +        return FALSE;
> +    }
> +
> +    lz_data->data.u.lines_data.chunks = src->data;
> +    lz_data->data.u.lines_data.stride = src->stride;
> +    lz_data->data.u.lines_data.next = 0;
> +    lz_data->data.u.lines_data.reverse = 0;
> +
> +    size = lz_encode(lz, type, src->x, src->y,
> +                     !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
> +                     NULL, 0, src->stride,
> +                     lz_data->data.bufs_head->buf.bytes,
> +                     sizeof(lz_data->data.bufs_head->buf));
> +
> +    // the compressed buffer is bigger than the original data
> +    if (size > (src->y * src->stride)) {
> +        longjmp(lz_data->data.jmp_env, 1);
> +    }
> +
> +    if (bitmap_fmt_is_rgb(src->format)) {
> +        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_RGB;
> +        dest->u.lz_rgb.data_size = size;
> +
> +        o_comp_data->comp_buf = lz_data->data.bufs_head;
> +        o_comp_data->comp_buf_size = size;
> +    } else {
> +        /* masks are 1BIT bitmaps without palettes, but they are not
> compressed
> +         * (see fill_mask) */
> +        spice_assert(src->palette);
> +        dest->descriptor.type = SPICE_IMAGE_TYPE_LZ_PLT;
> +        dest->u.lz_plt.data_size = size;
> +        dest->u.lz_plt.flags = src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN;
> +        dest->u.lz_plt.palette = src->palette;
> +        dest->u.lz_plt.palette_id = src->palette->unique;
> +        o_comp_data->comp_buf = lz_data->data.bufs_head;
> +        o_comp_data->comp_buf_size = size;
> +
> +        o_comp_data->lzplt_palette = dest->u.lz_plt.palette;
> +    }
> +
> +    stat_compress_add(&enc->shared_data->lz_stat, start_time, src->stride *
> src->y,
> +                      o_comp_data->comp_buf_size);
> +    return TRUE;
> +}
> +
> +int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
> +                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> +{
> +    JpegData *jpeg_data = &enc->jpeg_data;
> +    LzData *lz_data = &enc->lz_data;
> +    JpegEncoderContext *jpeg = enc->jpeg;
> +    LzContext *lz = enc->lz;
> +    volatile JpegEncoderImageType jpeg_in_type;
> +    int jpeg_size = 0;
> +    volatile int has_alpha = FALSE;
> +    int alpha_lz_size = 0;
> +    int comp_head_filled;
> +    int comp_head_left;
> +    int stride;
> +    uint8_t *lz_out_start_byte;
> +    stat_start_time_t start_time;
> +    stat_start_time_init(&start_time, &enc->shared_data->jpeg_alpha_stat);
> +
> +#ifdef COMPRESS_DEBUG
> +    spice_info("JPEG compress");
> +#endif
> +
> +    switch (src->format) {
> +    case SPICE_BITMAP_FMT_16BIT:
> +        jpeg_in_type = JPEG_IMAGE_TYPE_RGB16;
> +        break;
> +    case SPICE_BITMAP_FMT_24BIT:
> +        jpeg_in_type = JPEG_IMAGE_TYPE_BGR24;
> +        break;
> +    case SPICE_BITMAP_FMT_32BIT:
> +        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
> +        break;
> +    case SPICE_BITMAP_FMT_RGBA:
> +        jpeg_in_type = JPEG_IMAGE_TYPE_BGRX32;
> +        has_alpha = TRUE;
> +        break;
> +    default:
> +        return FALSE;
> +    }
> +
> +    encoder_data_init(&jpeg_data->data);
> +
> +    if (setjmp(jpeg_data->data.jmp_env)) {
> +        encoder_data_reset(&jpeg_data->data);
> +        return FALSE;
> +    }
> +
> +    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> +        spice_chunks_linearize(src->data);
> +    }
> +
> +    jpeg_data->data.u.lines_data.chunks = src->data;
> +    jpeg_data->data.u.lines_data.stride = src->stride;
> +    if ((src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
> +        jpeg_data->data.u.lines_data.next = 0;
> +        jpeg_data->data.u.lines_data.reverse = 0;
> +        stride = src->stride;
> +    } else {
> +        jpeg_data->data.u.lines_data.next = src->data->num_chunks - 1;
> +        jpeg_data->data.u.lines_data.reverse = 1;
> +        stride = -src->stride;
> +    }
> +    jpeg_size = jpeg_encode(jpeg, enc->jpeg_quality, jpeg_in_type,
> +                            src->x, src->y, NULL,
> +                            0, stride, jpeg_data->data.bufs_head->buf.bytes,
> +                            sizeof(jpeg_data->data.bufs_head->buf));
> +
> +    // the compressed buffer is bigger than the original data
> +    if (jpeg_size > (src->y * src->stride)) {
> +        longjmp(jpeg_data->data.jmp_env, 1);
> +    }
> +
> +    if (!has_alpha) {
> +        dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG;
> +        dest->u.jpeg.data_size = jpeg_size;
> +
> +        o_comp_data->comp_buf = jpeg_data->data.bufs_head;
> +        o_comp_data->comp_buf_size = jpeg_size;
> +        o_comp_data->is_lossy = TRUE;
> +
> +        stat_compress_add(&enc->shared_data->jpeg_stat, start_time, src-
> >stride * src->y,
> +                          o_comp_data->comp_buf_size);
> +        return TRUE;
> +    }
> +
> +    lz_data->data.bufs_head = jpeg_data->data.bufs_tail;
> +    lz_data->data.bufs_tail = lz_data->data.bufs_head;
> +
> +    comp_head_filled = jpeg_size % sizeof(lz_data->data.bufs_head->buf);
> +    comp_head_left = sizeof(lz_data->data.bufs_head->buf) - comp_head_filled;
> +    lz_out_start_byte = lz_data->data.bufs_head->buf.bytes +
> comp_head_filled;
> +
> +    lz_data->data.u.lines_data.chunks = src->data;
> +    lz_data->data.u.lines_data.stride = src->stride;
> +    lz_data->data.u.lines_data.next = 0;
> +    lz_data->data.u.lines_data.reverse = 0;
> +
> +    alpha_lz_size = lz_encode(lz, LZ_IMAGE_TYPE_XXXA, src->x, src->y,
> +                               !!(src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN),
> +                               NULL, 0, src->stride,
> +                               lz_out_start_byte,
> +                               comp_head_left);
> +
> +    // the compressed buffer is bigger than the original data
> +    if ((jpeg_size + alpha_lz_size) > (src->y * src->stride)) {
> +        longjmp(jpeg_data->data.jmp_env, 1);
> +    }
> +
> +    dest->descriptor.type = SPICE_IMAGE_TYPE_JPEG_ALPHA;
> +    dest->u.jpeg_alpha.flags = 0;
> +    if (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN) {
> +        dest->u.jpeg_alpha.flags |= SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN;
> +    }
> +
> +    dest->u.jpeg_alpha.jpeg_size = jpeg_size;
> +    dest->u.jpeg_alpha.data_size = jpeg_size + alpha_lz_size;
> +
> +    o_comp_data->comp_buf = jpeg_data->data.bufs_head;
> +    o_comp_data->comp_buf_size = jpeg_size + alpha_lz_size;
> +    o_comp_data->is_lossy = TRUE;
> +    stat_compress_add(&enc->shared_data->jpeg_alpha_stat, start_time, src-
> >stride * src->y,
> +                      o_comp_data->comp_buf_size);
> +    return TRUE;
> +}
> +
> +#ifdef USE_LZ4
> +int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
> +                                SpiceBitmap *src, compress_send_data_t*
> o_comp_data)
> +{
> +    Lz4Data *lz4_data = &enc->lz4_data;
> +    Lz4EncoderContext *lz4 = enc->lz4;
> +    int lz4_size = 0;
> +    stat_start_time_t start_time;
> +    stat_start_time_init(&start_time, &enc->shared_data->lz4_stat);
> +
> +#ifdef COMPRESS_DEBUG
> +    spice_info("LZ4 compress");
> +#endif
> +
> +    encoder_data_init(&lz4_data->data);
> +
> +    if (setjmp(lz4_data->data.jmp_env)) {
> +        encoder_data_reset(&lz4_data->data);
> +        return FALSE;
> +    }
> +
> +    if (src->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE) {
> +        spice_chunks_linearize(src->data);
> +    }
> +
> +    lz4_data->data.u.lines_data.chunks = src->data;
> +    lz4_data->data.u.lines_data.stride = src->stride;
> +    lz4_data->data.u.lines_data.next = 0;
> +    lz4_data->data.u.lines_data.reverse = 0;
> +
> +    lz4_size = lz4_encode(lz4, src->y, src->stride, lz4_data->data.bufs_head-
> >buf.bytes,
> +                          sizeof(lz4_data->data.bufs_head->buf),
> +                          src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN, src-
> >format);
> +
> +    // the compressed buffer is bigger than the original data
> +    if (lz4_size > (src->y * src->stride)) {
> +        longjmp(lz4_data->data.jmp_env, 1);
> +    }
> +
> +    dest->descriptor.type = SPICE_IMAGE_TYPE_LZ4;
> +    dest->u.lz4.data_size = lz4_size;
> +
> +    o_comp_data->comp_buf = lz4_data->data.bufs_head;
> +    o_comp_data->comp_buf_size = lz4_size;
> +
> +    stat_compress_add(&enc->shared_data->lz4_stat, start_time, src->stride *
> src->y,
> +                      o_comp_data->comp_buf_size);
> +    return TRUE;
> +}
> +#endif
> +
> +/* if already exists, returns it. Otherwise allocates and adds it (1) to the
> ring tail
> +   in the channel (2) to the Drawable*/
> +static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable
> *red_drawable,
> +                                        GlzImageRetention *glz_retention)
> +{
> +    RedGlzDrawable *ret;
> +    RingItem *item, *next;
> +
> +    // TODO - I don't really understand what's going on here, so doing the
> technical equivalent
> +    // now that we have multiple glz_dicts, so the only way to go from dcc to
> drawable glz is to go
> +    // over the glz_ring (unless adding some better data structure then a
> ring)
> +    SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret,
> LINK_TO_GLZ(item)) {
> +        if (ret->encoders == enc) {
> +            return ret;
> +        }
> +    }
> +
> +    ret = spice_new(RedGlzDrawable, 1);
> +
> +    ret->encoders = enc;
> +    ret->red_drawable = red_drawable_ref(red_drawable);
> +    ret->has_drawable = TRUE;
> +    ret->instances_count = 0;
> +    ring_init(&ret->instances);
> +
> +    ring_item_init(&ret->link);
> +    ring_item_init(&ret->drawable_link);
> +    ring_add_before(&ret->link, &enc->glz_drawables);
> +    ring_add(&glz_retention->ring, &ret->drawable_link);
> +    enc->shared_data->glz_drawable_count++;
> +    return ret;
> +}
> +
> +/* allocates new instance and adds it to instances in the given drawable.
> +   NOTE - the caller should set the glz_instance returned by the encoder by
> itself.*/
> +static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable
> *glz_drawable)
> +{
> +    spice_assert(glz_drawable->instances_count < MAX_GLZ_DRAWABLE_INSTANCES);
> +    // NOTE: We assume the additions are performed consecutively, without
> removals in the middle
> +    GlzDrawableInstanceItem *ret = glz_drawable->instances_pool +
> glz_drawable->instances_count;
> +    glz_drawable->instances_count++;
> +
> +    ring_item_init(&ret->free_link);
> +    ring_item_init(&ret->glz_link);
> +    ring_add(&glz_drawable->instances, &ret->glz_link);
> +    ret->context = NULL;
> +    ret->glz_drawable = glz_drawable;
> +
> +    return ret;
> +}
> +
> +#define MIN_GLZ_SIZE_FOR_ZLIB 100
> +
> +int image_encoders_compress_glz(ImageEncoders *enc,
> +                                SpiceImage *dest, SpiceBitmap *src,
> +                                RedDrawable *red_drawable,
> +                                GlzImageRetention *glz_retention,
> +                                compress_send_data_t* o_comp_data,
> +                                gboolean enable_zlib_glz_wrap)
> +{
> +    stat_start_time_t start_time;
> +    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
> +    spice_assert(bitmap_fmt_is_rgb(src->format));
> +    GlzData *glz_data = &enc->glz_data;
> +    ZlibData *zlib_data;
> +    LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
> +    RedGlzDrawable *glz_drawable;
> +    GlzDrawableInstanceItem *glz_drawable_instance;
> +    int glz_size;
> +    int zlib_size;
> +
> +#ifdef COMPRESS_DEBUG
> +    spice_info("LZ global compress fmt=%d", src->format);
> +#endif
> +
> +    if ((src->x * src->y) >= glz_enc_dictionary_get_size(enc->glz_dict-
> >dict)) {
> +        return FALSE;
> +    }
> +
> +    pthread_rwlock_rdlock(&enc->glz_dict->encode_lock);
> +    /* using the global dictionary only if it is not frozen */
> +    if (enc->glz_dict->migrate_freeze) {
> +        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> +        return FALSE;
> +    }
> +
> +    encoder_data_init(&glz_data->data);
> +
> +    glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
> +    glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
> +
> +    glz_data->data.u.lines_data.chunks = src->data;
> +    glz_data->data.u.lines_data.stride = src->stride;
> +    glz_data->data.u.lines_data.next = 0;
> +    glz_data->data.u.lines_data.reverse = 0;
> +
> +    glz_size = glz_encode(enc->glz, type, src->x, src->y,
> +                          (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL,
> 0,
> +                          src->stride, glz_data->data.bufs_head->buf.bytes,
> +                          sizeof(glz_data->data.bufs_head->buf),
> +                          glz_drawable_instance,
> +                          &glz_drawable_instance->context);
> +
> +    stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride *
> src->y, glz_size);
> +
> +    if (!enable_zlib_glz_wrap || (glz_size < MIN_GLZ_SIZE_FOR_ZLIB)) {
> +        goto glz;
> +    }
> +    stat_start_time_init(&start_time, &enc->shared_data->zlib_glz_stat);
> +    zlib_data = &enc->zlib_data;
> +
> +    encoder_data_init(&zlib_data->data);
> +
> +    zlib_data->data.u.compressed_data.next = glz_data->data.bufs_head;
> +    zlib_data->data.u.compressed_data.size_left = glz_size;
> +
> +    zlib_size = zlib_encode(enc->zlib, enc->zlib_level,
> +                            glz_size, zlib_data->data.bufs_head->buf.bytes,
> +                            sizeof(zlib_data->data.bufs_head->buf));
> +
> +    // the compressed buffer is bigger than the original data
> +    if (zlib_size >= glz_size) {
> +        encoder_data_reset(&zlib_data->data);
> +        goto glz;
> +    } else {
> +        encoder_data_reset(&glz_data->data);
> +    }
> +
> +    dest->descriptor.type = SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB;
> +    dest->u.zlib_glz.glz_data_size = glz_size;
> +    dest->u.zlib_glz.data_size = zlib_size;
> +
> +    o_comp_data->comp_buf = zlib_data->data.bufs_head;
> +    o_comp_data->comp_buf_size = zlib_size;
> +
> +    stat_compress_add(&enc->shared_data->zlib_glz_stat, start_time, glz_size,
> zlib_size);
> +    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> +    return TRUE;
> +
> +glz:
> +    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> +
> +    dest->descriptor.type = SPICE_IMAGE_TYPE_GLZ_RGB;
> +    dest->u.lz_rgb.data_size = glz_size;
> +
> +    o_comp_data->comp_buf = glz_data->data.bufs_head;
> +    o_comp_data->comp_buf_size = glz_size;
> +
> +    return TRUE;
> +}
> +
> +void image_encoder_shared_init(ImageEncoderSharedData *shared_data)
> +{
> +    clockid_t stat_clock = CLOCK_THREAD_CPUTIME_ID;
> +
> +    stat_compress_init(&shared_data->off_stat, "off", stat_clock);
> +    stat_compress_init(&shared_data->lz_stat, "lz", stat_clock);
> +    stat_compress_init(&shared_data->glz_stat, "glz", stat_clock);
> +    stat_compress_init(&shared_data->quic_stat, "quic", stat_clock);
> +    stat_compress_init(&shared_data->jpeg_stat, "jpeg", stat_clock);
> +    stat_compress_init(&shared_data->zlib_glz_stat, "zlib", stat_clock);
> +    stat_compress_init(&shared_data->jpeg_alpha_stat, "jpeg_alpha",
> stat_clock);
> +    stat_compress_init(&shared_data->lz4_stat, "lz4", stat_clock);
> +}
> +
> +void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data)
> +{
> +    stat_reset(&shared_data->off_stat);
> +    stat_reset(&shared_data->quic_stat);
> +    stat_reset(&shared_data->lz_stat);
> +    stat_reset(&shared_data->glz_stat);
> +    stat_reset(&shared_data->jpeg_stat);
> +    stat_reset(&shared_data->zlib_glz_stat);
> +    stat_reset(&shared_data->jpeg_alpha_stat);
> +    stat_reset(&shared_data->lz4_stat);
> +}
> +
> +#define STAT_FMT "%s\t%8u\t%13.8g\t%12.8g\t%12.8g"
> +
> +#ifdef COMPRESS_STAT
> +static void stat_print_one(const char *name, const stat_info_t *stat)
> +{
> +    spice_info(STAT_FMT, name, stat->count,
> +               stat_byte_to_mega(stat->orig_size),
> +               stat_byte_to_mega(stat->comp_size),
> +               stat_cpu_time_to_sec(stat->total));
> +}
> +
> +static void stat_sum(stat_info_t *total, const stat_info_t *stat)
> +{
> +    total->count += stat->count;
> +    total->orig_size += stat->orig_size;
> +    total->comp_size += stat->comp_size;
> +    total->total += stat->total;
> +}
> +#endif
> +
> +void image_encoder_shared_stat_print(const ImageEncoderSharedData
> *shared_data)
> +{
> +#ifdef COMPRESS_STAT
> +    /* sum all statistics */
> +    stat_info_t total = {
> +        .count = 0,
> +        .orig_size = 0,
> +        .comp_size = 0,
> +        .total = 0
> +    };
> +    stat_sum(&total, &shared_data->off_stat);
> +    stat_sum(&total, &shared_data->quic_stat);
> +    stat_sum(&total, &shared_data->glz_stat);
> +    stat_sum(&total, &shared_data->lz_stat);
> +    stat_sum(&total, &shared_data->jpeg_stat);
> +    stat_sum(&total, &shared_data->jpeg_alpha_stat);
> +    stat_sum(&total, &shared_data->lz4_stat);
> +
> +    /* fix for zlib glz */
> +    total.total += shared_data->zlib_glz_stat.total;
> +    if (shared_data->zlib_glz_stat.count) {
> +        total.comp_size = total.comp_size - shared_data->glz_stat.comp_size +
> +                          shared_data->zlib_glz_stat.comp_size;
> +    }
> +
> +    spice_info("Method   \t  count  \torig_size(MB)\tenc_size(MB)\tenc_time(s
> )");
> +    stat_print_one("OFF      ", &shared_data->off_stat);
> +    stat_print_one("QUIC     ", &shared_data->quic_stat);
> +    stat_print_one("GLZ      ", &shared_data->glz_stat);
> +    stat_print_one("ZLIB GLZ ", &shared_data->zlib_glz_stat);
> +    stat_print_one("LZ       ", &shared_data->lz_stat);
> +    stat_print_one("JPEG     ", &shared_data->jpeg_stat);
> +    stat_print_one("JPEG-RGBA", &shared_data->jpeg_alpha_stat);
> +    stat_print_one("LZ4      ", &shared_data->lz4_stat);
> +    spice_info("-------------------------------------------------------------
> ------");
> +    stat_print_one("Total    ", &total);
> +#endif
> +}
> diff --git a/server/image-encoders.h b/server/image-encoders.h
> new file mode 100644
> index 0000000..9286970
> --- /dev/null
> +++ b/server/image-encoders.h
> @@ -0,0 +1,217 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/
> >.
> +*/
> +#ifndef DCC_ENCODERS_H_
> +#define DCC_ENCODERS_H_


Change this to IMAGE_ENCODERS_H_?


> +
> +#include <setjmp.h>
> +#include <common/quic.h>
> +#include <common/lz.h>
> +
> +#include "stat.h"
> +#include "red-parse-qxl.h"
> +#include "glz-encoder.h"
> +#include "jpeg-encoder.h"
> +#ifdef USE_LZ4
> +#include "lz4-encoder.h"
> +#endif
> +#include "zlib-encoder.h"
> +
> +struct RedClient;
> +
> +typedef struct RedCompressBuf RedCompressBuf;
> +typedef struct RedGlzDrawable RedGlzDrawable;
> +typedef struct ImageEncoders ImageEncoders;
> +typedef struct ImageEncoderSharedData ImageEncoderSharedData;
> +typedef struct GlzSharedDictionary GlzSharedDictionary;
> +typedef struct GlzImageRetention GlzImageRetention;
> +
> +void image_encoder_shared_init(ImageEncoderSharedData *shared_data);
> +void image_encoder_shared_stat_reset(ImageEncoderSharedData *shared_data);
> +void image_encoder_shared_stat_print(const ImageEncoderSharedData
> *shared_data);
> +
> +void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData
> *shared_data);
> +void image_encoders_free(ImageEncoders *enc);
> +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
> +void image_encoders_free_glz_drawables(ImageEncoders *enc);
> +void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
> +gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
> +void image_encoders_glz_get_restore_data(ImageEncoders *enc,
> +                                         uint8_t *out_id,
> GlzEncDictRestoreData *out_data);
> +gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
> +void image_encoders_glz_encode_unlock(ImageEncoders *enc);
> +void glz_retention_free_drawables(GlzImageRetention *ret);
> +void glz_retention_detach_drawables(GlzImageRetention *ret);
> +
> +#define RED_COMPRESS_BUF_SIZE (1024 * 64)
> +struct RedCompressBuf {
> +    /* This buffer provide space for compression algorithms.
> +     * Some algorithms access the buffer as an array of 32 bit words
> +     * so is defined to make sure is always aligned that way.
> +     */
> +    union {
> +        uint8_t  bytes[RED_COMPRESS_BUF_SIZE];
> +        uint32_t words[RED_COMPRESS_BUF_SIZE / 4];
> +    } buf;
> +    RedCompressBuf *send_next;
> +};
> +
> +static inline void compress_buf_free(RedCompressBuf *buf)
> +{
> +    g_free(buf);
> +}
> +
> +gboolean image_encoders_get_glz_dictionary(ImageEncoders *enc,
> +                                           struct RedClient *client,
> +                                           uint8_t id, int window_size);
> +gboolean image_encoders_restore_glz_dictionary(ImageEncoders *enc,
> +                                               struct RedClient *client,
> +                                               uint8_t id,
> +                                               GlzEncDictRestoreData
> *restore_data);
> +
> +typedef struct  {
> +    RedCompressBuf *bufs_head;
> +    RedCompressBuf *bufs_tail;
> +    jmp_buf jmp_env;
> +    union {
> +        struct {
> +            SpiceChunks *chunks;
> +            int next;
> +            int stride;
> +            int reverse;
> +        } lines_data;
> +        struct {
> +            RedCompressBuf* next;
> +            int size_left;
> +        } compressed_data; // for encoding data that was already compressed
> by another method
> +    } u;
> +} EncoderData;
> +
> +typedef struct {
> +    QuicUsrContext usr;
> +    EncoderData data;
> +} QuicData;
> +
> +typedef struct {
> +    LzUsrContext usr;
> +    EncoderData data;
> +} LzData;
> +
> +typedef struct {
> +    JpegEncoderUsrContext usr;
> +    EncoderData data;
> +} JpegData;
> +
> +#ifdef USE_LZ4
> +typedef struct {
> +    Lz4EncoderUsrContext usr;
> +    EncoderData data;
> +} Lz4Data;
> +#endif
> +
> +typedef struct {
> +    ZlibEncoderUsrContext usr;
> +    EncoderData data;
> +} ZlibData;
> +
> +typedef struct {
> +    GlzEncoderUsrContext usr;
> +    EncoderData data;
> +} GlzData;
> +
> +struct GlzImageRetention {
> +    Ring ring;
> +};
> +
> +static inline void glz_retention_init(GlzImageRetention *ret)
> +{
> +    ring_init(&ret->ring);
> +}
> +
> +struct ImageEncoderSharedData {
> +    uint32_t glz_drawable_count;
> +
> +    stat_info_t off_stat;
> +    stat_info_t lz_stat;
> +    stat_info_t glz_stat;
> +    stat_info_t quic_stat;
> +    stat_info_t jpeg_stat;
> +    stat_info_t zlib_glz_stat;
> +    stat_info_t jpeg_alpha_stat;
> +    stat_info_t lz4_stat;
> +};
> +
> +struct ImageEncoders {
> +    ImageEncoderSharedData *shared_data;
> +
> +    QuicData quic_data;
> +    QuicContext *quic;
> +
> +    LzData lz_data;
> +    LzContext  *lz;
> +
> +    int jpeg_quality;
> +
> +    JpegData jpeg_data;
> +    JpegEncoderContext *jpeg;
> +
> +#ifdef USE_LZ4
> +    Lz4Data lz4_data;
> +    Lz4EncoderContext *lz4;
> +#endif
> +
> +    int zlib_level;
> +
> +    ZlibData zlib_data;
> +    ZlibEncoder *zlib;
> +
> +    /* global lz encoding entities */
> +    GlzSharedDictionary *glz_dict;
> +    GlzEncoderContext *glz;
> +    GlzData glz_data;
> +
> +    Ring glz_drawables;               // all the living lz drawable, ordered
> by encoding time
> +    Ring glz_drawables_inst_to_free;               // list of instances to be
> freed
> +    pthread_mutex_t glz_drawables_inst_to_free_lock;
> +};
> +
> +typedef struct compress_send_data_t {
> +    void* comp_buf;
> +    uint32_t comp_buf_size;
> +    SpicePalette *lzplt_palette;
> +    int is_lossy;
> +} compress_send_data_t;
> +
> +int image_encoders_compress_quic(ImageEncoders *enc, SpiceImage *dest,
> +                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> +int image_encoders_compress_lz(ImageEncoders *enc,
> +                               SpiceImage *dest, SpiceBitmap *src,
> +                               compress_send_data_t* o_comp_data);
> +int image_encoders_compress_jpeg(ImageEncoders *enc, SpiceImage *dest,
> +                                 SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> +int image_encoders_compress_lz4(ImageEncoders *enc, SpiceImage *dest,
> +                                SpiceBitmap *src, compress_send_data_t*
> o_comp_data);
> +int image_encoders_compress_glz(ImageEncoders *enc,
> +                                SpiceImage *dest, SpiceBitmap *src,
> +                                RedDrawable *red_drawable,
> +                                GlzImageRetention *glz_retention,
> +                                compress_send_data_t* o_comp_data,
> +                                gboolean enable_zlib_glz_wrap);
> +
> +#define RED_RELEASE_BUNCH_SIZE 64
> +
> +#endif /* DCC_ENCODERS_H_ */


Aside from the comment about the header guards, looks good.

Acked-by: Jonathon Jongsma <jjongsma at redhat.com>


More information about the Spice-devel mailing list