paulo@0: /* paulo@0: * $Id: tx_deflate.c,v 1.15 2004/05/02 08:55:00 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2004 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "gt_packet.h" /* packet manipulation macros */ paulo@0: paulo@0: #include "io/tx_stack.h" paulo@0: #include "io/tx_layer.h" paulo@0: #include "io/io_buf.h" paulo@0: paulo@0: #include paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #define DEFLATE_DEBUG 0 paulo@0: paulo@0: #if DEFLATE_DEBUG paulo@0: #define DEFLATE_TRACEFN(tx) \ paulo@0: GT->DBGSOCK (GT, tx->stack->c, "entered") paulo@0: paulo@0: #define DEFLATE_DUMP(tx_deflate) \ paulo@0: { \ paulo@0: if (DEFLATE_DEBUG) \ paulo@0: { \ paulo@0: float percent = ((float)tx_deflate->nbytes_in - \ paulo@0: tx_deflate->nbytes_out - tx_deflate->nbytes_unflushed) / \ paulo@0: (float)tx_deflate->nbytes_in; \ paulo@0: \ paulo@0: GT->DBGSOCK (GT, tx->stack->c, "in %lu out %lu flushed %lu unflushed %lu (flushing %d) " \ paulo@0: "ratio %.2f%% avg %.2f", \ paulo@0: (long)tx_deflate->nbytes_in, (long)tx_deflate->nbytes_out, \ paulo@0: (long)tx_deflate->nbytes_flushed, \ paulo@0: (long)tx_deflate->nbytes_unflushed, \ paulo@0: (long)tx_deflate->flushing, percent * 100.0, \ paulo@0: (double)tx_deflate->nbytes_out / \ paulo@0: difftime (time (NULL), tx->stack->start_time)); \ paulo@0: } \ paulo@0: } paulo@0: #else /* !DEFLATE_DEBUG */ paulo@0: #define DEFLATE_TRACEFN(tx) paulo@0: #define DEFLATE_DUMP(tx_deflate) paulo@0: #endif /* DEFLATE_DEBUG */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #define TX_DEFLATE_BUFSIZE (1024 - 1) /* -1 for auto-nullification */ paulo@0: paulo@0: #define FLUSH_AFTER (4096) /* flush after this many bytes */ paulo@0: paulo@0: #define NAGLE_TIMEOUT (200 * MSEC) /* 200 milliseconds */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: struct tx_deflate paulo@0: { paulo@0: /* zlib data */ paulo@0: z_stream z; paulo@0: paulo@0: /* compressed buffer */ paulo@0: struct io_buf *buf; paulo@0: paulo@0: /* Nagle timer that sends stored data after NAGLE_TIMEOUT milliseconds */ paulo@0: timer_id nagle_timer; paulo@0: paulo@0: size_t nbytes_in; /* total uncompressed bytes */ paulo@0: size_t nbytes_out; /* total compressed bytes */ paulo@0: size_t nbytes_flushed; /* total bytes written to lower layer */ paulo@0: size_t nbytes_unflushed; /* bytes currently waiting in z_stream */ paulo@0: paulo@0: /* paulo@0: * Whether the zstream is currently being flushed, and so whether deflate paulo@0: * must receive a Z_SYNC_FLUSH parameter to continue flushing. The flush paulo@0: * ends when deflate returns with avail_out > 0. paulo@0: */ paulo@0: BOOL flushing; paulo@0: paulo@0: /* paulo@0: * When doing a flush, it's possible that there will be a partially paulo@0: * filled buffer leftover. If there's no new data that comes in, the data paulo@0: * will be delayed again until more data comes from the upper layer. This paulo@0: * flag is set when this happens, so we know that we should flush the paulo@0: * buffer to the lower layer as soon as possible, even if it isn't paulo@0: * completely full. paulo@0: */ paulo@0: BOOL delayed; paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void start_nagle_timer (struct tx_layer *tx, struct tx_deflate *deflate); paulo@0: static void stop_nagle_timer (struct tx_layer *tx, struct tx_deflate *deflate); paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void tx_deflate_enable (struct tx_layer *tx) paulo@0: { paulo@0: /* TODO */ paulo@0: } paulo@0: paulo@0: static void tx_deflate_disable (struct tx_layer *tx) paulo@0: { paulo@0: /* TODO */ paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void tx_deflate_toggle (struct tx_layer *tx, BOOL stop) paulo@0: { paulo@0: /* nothing, we do not consume packets, only pass along */ paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static BOOL alloc_buffer (struct tx_deflate *tx_deflate) paulo@0: { paulo@0: if (tx_deflate->buf) paulo@0: return TRUE; paulo@0: paulo@0: if (!(tx_deflate->buf = io_buf_new (TX_DEFLATE_BUFSIZE))) paulo@0: return FALSE; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static void finish_flush (struct tx_deflate *tx_deflate) paulo@0: { paulo@0: tx_deflate->nbytes_unflushed = 0; paulo@0: tx_deflate->flushing = FALSE; paulo@0: } paulo@0: paulo@0: static tx_status_t flush_buffer (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: tx_status_t ret; paulo@0: size_t n; paulo@0: paulo@0: DEFLATE_TRACEFN(tx); paulo@0: paulo@0: n = io_buf_read_avail (tx_deflate->buf); paulo@0: paulo@0: /* paulo@0: * The buffer filled up. Try to send again until the lower paulo@0: * layer is saturated. paulo@0: */ paulo@0: ret = gt_tx_layer_queue (tx, tx_deflate->buf); paulo@0: assert (ret != TX_EMPTY); paulo@0: paulo@0: if (ret == TX_ERROR || ret == TX_FULL) paulo@0: return ret; paulo@0: paulo@0: tx_deflate->nbytes_flushed += n; paulo@0: assert (ret == TX_OK); paulo@0: paulo@0: stop_nagle_timer (tx, tx_deflate); paulo@0: paulo@0: tx_deflate->buf = NULL; paulo@0: tx_deflate->delayed = FALSE; paulo@0: paulo@0: return TX_OK; paulo@0: } paulo@0: paulo@0: /* paulo@0: * Try to flush the data inside the z_stream and send it to the layer beneath paulo@0: * this one. paulo@0: */ paulo@0: static tx_status_t flush_stream (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: z_stream *z = &tx_deflate->z; paulo@0: tx_status_t ret; paulo@0: int zret; paulo@0: size_t wlen, old_avail; paulo@0: paulo@0: DEFLATE_TRACEFN(tx); paulo@0: paulo@0: if (!alloc_buffer (tx_deflate)) paulo@0: return TX_ERROR; paulo@0: paulo@0: old_avail = io_buf_write_avail (tx_deflate->buf); paulo@0: paulo@0: z->avail_in = 0; paulo@0: z->next_in = NULL; /* don't disrupt anything else */ paulo@0: z->next_out = io_buf_write_ptr (tx_deflate->buf); paulo@0: z->avail_out = old_avail; paulo@0: paulo@0: zret = deflate (z, Z_SYNC_FLUSH); paulo@0: paulo@0: /* paulo@0: * If this is true we've already flushed all possible data. paulo@0: */ paulo@0: if (zret == Z_BUF_ERROR) paulo@0: { paulo@0: tx_deflate->flushing = FALSE; paulo@0: paulo@0: /* send the stored data */ paulo@0: if (io_buf_read_avail (tx_deflate->buf) > 0) paulo@0: return flush_buffer (tx, tx_deflate); paulo@0: paulo@0: return TX_EMPTY; paulo@0: } paulo@0: paulo@0: if (zret != Z_OK) paulo@0: return TX_ERROR; paulo@0: paulo@0: wlen = old_avail - z->avail_out; paulo@0: paulo@0: io_buf_push (tx_deflate->buf, wlen); paulo@0: tx_deflate->nbytes_out += wlen; paulo@0: paulo@0: tx_deflate->flushing = TRUE; paulo@0: paulo@0: /* if there is space, the flush completed successfully */ paulo@0: if (z->avail_out > 0) paulo@0: finish_flush (tx_deflate); paulo@0: paulo@0: if ((ret = flush_buffer (tx, tx_deflate) != TX_OK)) paulo@0: return ret; paulo@0: paulo@0: /* stop when the flush completes */ paulo@0: if (!tx_deflate->flushing) paulo@0: return TX_OK; paulo@0: paulo@0: /* tail recurse until the flush completes */ paulo@0: return flush_stream (tx, tx_deflate); paulo@0: } paulo@0: paulo@0: static BOOL deflate_nagle_timeout (struct tx_layer *tx) paulo@0: { paulo@0: struct tx_deflate *tx_deflate = tx->udata; paulo@0: tx_status_t ret; paulo@0: paulo@0: DEFLATE_TRACEFN(tx); paulo@0: paulo@0: /* this assertion means we have to disarm the timer when sending the paulo@0: * buffer */ paulo@0: assert (tx_deflate->buf != NULL); paulo@0: paulo@0: ret = flush_stream (tx, tx_deflate); paulo@0: paulo@0: /* no matter what, we disable the Nagle timer after this */ paulo@0: stop_nagle_timer (tx, tx_deflate); paulo@0: paulo@0: if (ret == TX_ERROR) paulo@0: { paulo@0: gt_tx_stack_abort (tx->stack); paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: if (DEFLATE_DEBUG) paulo@0: GT->DBGSOCK (GT, tx->stack->c, "buffer delayed?: %d", tx_deflate->delayed); paulo@0: paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: static void start_nagle_timer (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: if (DEFLATE_DEBUG) paulo@0: GT->DBGSOCK (GT, tx->stack->c, "nagle timer=%d", tx_deflate->nagle_timer); paulo@0: paulo@0: if (tx_deflate->nagle_timer != 0) paulo@0: return; paulo@0: paulo@0: tx_deflate->nagle_timer = timer_add (NAGLE_TIMEOUT, paulo@0: (TimerCallback)deflate_nagle_timeout, paulo@0: tx); paulo@0: } paulo@0: paulo@0: static void stop_nagle_timer (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: if (DEFLATE_DEBUG) paulo@0: GT->DBGSOCK (GT, tx->stack->c, "nagle timer=%d", tx_deflate->nagle_timer); paulo@0: paulo@0: timer_remove_zero (&tx_deflate->nagle_timer); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * The upper layer has sent us a buffer to process. paulo@0: */ paulo@0: static tx_status_t tx_deflate_queue (struct tx_layer *tx, struct io_buf *msg) paulo@0: { paulo@0: struct tx_deflate *tx_deflate = tx->udata; paulo@0: z_stream *z = &tx_deflate->z; paulo@0: BOOL flush_completed = FALSE; paulo@0: int ret; paulo@0: paulo@0: DEFLATE_TRACEFN(tx); paulo@0: paulo@0: /* paulo@0: * Deflate the incoming message, adding it to the buffer. paulo@0: * paulo@0: * If our buffer is currently full, return TX_FULL. paulo@0: */ paulo@0: paulo@0: if (!alloc_buffer (tx_deflate)) paulo@0: { paulo@0: io_buf_free (msg); paulo@0: return TX_ERROR; paulo@0: } paulo@0: paulo@0: z->next_in = io_buf_read_ptr (msg); paulo@0: z->avail_in = io_buf_read_avail (msg); paulo@0: z->next_out = io_buf_write_ptr (tx_deflate->buf); paulo@0: z->avail_out = io_buf_write_avail (tx_deflate->buf); paulo@0: paulo@0: if (z->avail_out == 0) paulo@0: return TX_FULL; paulo@0: paulo@0: while (io_buf_read_avail (msg) > 0 && z->avail_out > 0) paulo@0: { paulo@0: size_t rlen, wlen; paulo@0: paulo@0: assert (z->next_in == io_buf_read_ptr (msg)); paulo@0: assert (z->next_out == io_buf_write_ptr (tx_deflate->buf)); paulo@0: paulo@0: /* begin flushing after a certain amount */ paulo@0: if (tx_deflate->nbytes_unflushed >= FLUSH_AFTER) paulo@0: tx_deflate->flushing = TRUE; paulo@0: paulo@0: ret = deflate (z, tx_deflate->flushing ? Z_SYNC_FLUSH : 0); paulo@0: paulo@0: if (ret != Z_OK) paulo@0: { paulo@0: GT->DBGFN (GT, "deflate: error %d", ret); paulo@0: io_buf_free (msg); paulo@0: return TX_ERROR; paulo@0: } paulo@0: paulo@0: rlen = io_buf_read_avail (msg) - z->avail_in; paulo@0: wlen = io_buf_write_avail (tx_deflate->buf) - z->avail_out; paulo@0: assert (rlen > 0 || wlen > 0); /* hmm, is this true when flushing? */ paulo@0: #if 0 paulo@0: assert (wlen > 0); paulo@0: #endif paulo@0: paulo@0: tx_deflate->nbytes_in += rlen; paulo@0: tx_deflate->nbytes_unflushed += rlen; paulo@0: tx_deflate->nbytes_out += wlen; paulo@0: paulo@0: DEFLATE_DUMP(tx_deflate); paulo@0: paulo@0: /* update the buffer lengths */ paulo@0: io_buf_push (tx_deflate->buf, wlen); paulo@0: io_buf_pop (msg, rlen); paulo@0: paulo@0: if (z->avail_out == 0) paulo@0: break; paulo@0: paulo@0: /* paulo@0: * If we have available output space and no more input space, paulo@0: * we know the flush completed, so unset flush mode. paulo@0: * paulo@0: * NOTE: there might be a bug here. The flush may fit exactly paulo@0: * everytime, causing us to never leave flush mode. I think zlib may paulo@0: * try to prevent this itself, though. paulo@0: */ paulo@0: if (tx_deflate->flushing && z->avail_in == 0) paulo@0: { paulo@0: flush_completed = TRUE; paulo@0: finish_flush (tx_deflate); paulo@0: } paulo@0: } paulo@0: paulo@0: /* paulo@0: * If we completed a flush, and the buffer isn't full, set the delayed paulo@0: * flag so that service_deflate() will write the buffer immediately to paulo@0: * reduce latency, as it has already endured a Nagle timeout period. paulo@0: */ paulo@0: if (flush_completed && paulo@0: io_buf_read_avail (tx_deflate->buf) < TX_DEFLATE_BUFSIZE) paulo@0: { paulo@0: if (DEFLATE_DEBUG) paulo@0: { paulo@0: GT->DBGSOCK (GT, tx->stack->c, "setting ->delayed flag on buf(%d)", paulo@0: io_buf_read_avail (tx_deflate->buf)); paulo@0: } paulo@0: paulo@0: tx_deflate->delayed = TRUE; paulo@0: } paulo@0: paulo@0: /* paulo@0: * If the message buffer was only partially emptied, don't free paulo@0: * it and let tx_layer.c know to handle it specially. paulo@0: */ paulo@0: if (io_buf_read_avail (msg) > 0) paulo@0: return TX_PARTIAL; paulo@0: paulo@0: io_buf_free (msg); paulo@0: paulo@0: return TX_OK; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Get more data to write. paulo@0: */ paulo@0: static tx_status_t get_buffers (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: if (tx_deflate->buf && io_buf_write_avail (tx_deflate->buf) == 0) paulo@0: return TX_OK; paulo@0: paulo@0: return gt_tx_layer_ready (tx); paulo@0: } paulo@0: paulo@0: /* paulo@0: * This is the most complicated part of the whole stack: paulo@0: * paulo@0: * [1] Call upper layer's ready routine to grab a buffer (gt_tx_layer_ready). paulo@0: * paulo@0: * [2] That function will call tx_deflate_queue, which compresses the data to paulo@0: * a buffer, as many times as it can while there's more data to process. paulo@0: * paulo@0: * [3] If we didn't fill the buffer, or there was no data, return TX_EMPTY paulo@0: * telling the lower layer there is no data. paulo@0: * paulo@0: * [4] If there's no data in the upper layer, but we're in flush mode, call paulo@0: * flush_stream() to send whatever data is stored inside the z_stream, paulo@0: * and stop. paulo@0: * paulo@0: * [5] If we filled the buffer, or if we have a paritally filled buffer that paulo@0: * was delayed in deflate_nagle_timeout(), send it to the lower layer with paulo@0: * flush_buffer(). If the lower layer returns TX_FULL, stop and return paulo@0: * TX_OK. Otherwise, continue by calling this function recursively. paulo@0: * paulo@0: * NOTE: The buffer is filled in tx_deflate_queue but sent in this paulo@0: * function (or from the Nagle timer if the buffer isn't full). paulo@0: * paulo@0: * The caller of this function has to setup a Nagle timer if any data was paulo@0: * written and TX_FULL was not encountered. paulo@0: */ paulo@0: static tx_status_t service_deflate (struct tx_layer *tx, paulo@0: struct tx_deflate *tx_deflate) paulo@0: { paulo@0: tx_status_t ret; paulo@0: paulo@0: DEFLATE_TRACEFN(tx); paulo@0: paulo@0: /* [1] + [2] */ paulo@0: ret = get_buffers (tx, tx_deflate); paulo@0: paulo@0: if (ret == TX_ERROR) paulo@0: return TX_ERROR; paulo@0: paulo@0: /* [3] */ paulo@0: if (ret == TX_EMPTY) paulo@0: { paulo@0: assert (ret == TX_EMPTY); paulo@0: paulo@0: /* [4]: continue flush even if no data avail */ paulo@0: if (tx_deflate->flushing) paulo@0: ret = flush_stream (tx, tx_deflate); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: assert (tx_deflate->buf != NULL); paulo@0: paulo@0: if (DEFLATE_DEBUG) paulo@0: { paulo@0: if (tx_deflate->delayed) paulo@0: { paulo@0: GT->DBGSOCK (GT, tx->stack->c, "flushing delayed buf(%d)", paulo@0: io_buf_read_avail (tx_deflate->buf)); paulo@0: } paulo@0: } paulo@0: paulo@0: assert (ret == TX_OK); paulo@0: paulo@0: /* paulo@0: * [5] paulo@0: * paulo@0: * flush_buffer will stop the Nagle timer if the buffer was paulo@0: * successfully sent. paulo@0: * paulo@0: * We must also flush the buffer if it contains partial data from a paulo@0: * previous flush that was delayed in the Nagle timer due to having no paulo@0: * space. paulo@0: */ paulo@0: if (tx_deflate->delayed || io_buf_write_avail (tx_deflate->buf) == 0) paulo@0: ret = flush_buffer (tx, tx_deflate); paulo@0: paulo@0: if (ret != TX_OK) paulo@0: return ret; paulo@0: paulo@0: /* tail recurse until the lower layer is saturated */ paulo@0: return service_deflate (tx, tx_deflate); paulo@0: } paulo@0: paulo@0: /* paulo@0: * The lower layer is ready to write. paulo@0: */ paulo@0: static tx_status_t tx_deflate_ready (struct tx_layer *tx) paulo@0: { paulo@0: struct tx_deflate *tx_deflate = tx->udata; paulo@0: size_t old_flushed; paulo@0: tx_status_t ret; paulo@0: paulo@0: /* keep track of how much was previously flushed */ paulo@0: old_flushed = tx_deflate->nbytes_flushed; paulo@0: paulo@0: ret = service_deflate (tx, tx_deflate); paulo@0: paulo@0: if (ret == TX_ERROR || ret == TX_FULL) paulo@0: { paulo@0: if (ret == TX_FULL) paulo@0: { paulo@0: /* flush buffer shouldve deactivated the Nagle timer */ paulo@0: assert (tx_deflate->nagle_timer == 0); paulo@0: paulo@0: /* we wrote something -- let caller know it's ok */ paulo@0: ret = TX_OK; paulo@0: } paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: assert (ret == TX_OK || ret == TX_EMPTY); paulo@0: paulo@0: /* paulo@0: * If the lower layer was not saturated (evidenced by _not_ returning paulo@0: * TX_FULL), and there is a partially completed buffer, the Nagle paulo@0: * timer must be armed. This ensures the data waiting in this layer will paulo@0: * go out in a timely manner. If the lower layer was saturated, we don't paulo@0: * need to arm the timer because there is no buffer space to flush to paulo@0: * anyway, and when the lower layer unsaturates it will reinvoke this paulo@0: * layer to write more data. paulo@0: * paulo@0: * TODO: Still need to flush if there is some urgent data waiting. So, paulo@0: * should add a ->flush callback. paulo@0: * paulo@0: * XXX: Using tx_deflate->buf != NULL as a hacky way to recognize that paulo@0: * some data was written to the z_stream. paulo@0: */ paulo@0: if (tx_deflate->buf != NULL) paulo@0: start_nagle_timer (tx, tx_deflate); paulo@0: paulo@0: if (DEFLATE_DEBUG) paulo@0: { paulo@0: GT->DBGSOCK (GT, tx->stack->c, "buf waiting=[%d] ret=%s", paulo@0: tx_deflate->buf ? io_buf_read_avail (tx_deflate->buf) : 0, paulo@0: ret == TX_EMPTY ? "TX_EMPTY" : "TX_OK"); paulo@0: } paulo@0: paulo@0: DEFLATE_DUMP(tx_deflate); paulo@0: paulo@0: /* paulo@0: * For the return value from this function, decipher whether paulo@0: * service_deflate() wrote some data. paulo@0: * paulo@0: * If nothing was written, then we should stop sending now, by returning paulo@0: * TX_EMPTY. That will remove the input in tx_link.c that's calling this paulo@0: * layer, which kind of sucks, because this could be the case a lot of the paulo@0: * time when the whole buffer hasn't been filled up, leading to a removing paulo@0: * and adding the input a lot. paulo@0: * paulo@0: * Otherwise, return TX_OK if something was sent to the lower layer. paulo@0: */ paulo@0: if (old_flushed == tx_deflate->nbytes_flushed) paulo@0: return TX_EMPTY; paulo@0: paulo@0: return TX_OK; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static BOOL tx_deflate_init (struct tx_layer *tx) paulo@0: { paulo@0: struct tx_deflate *tx_deflate; paulo@0: paulo@0: if (!(tx_deflate = malloc (sizeof(*tx_deflate)))) paulo@0: return FALSE; paulo@0: paulo@0: /* zlib documents these variables as needing initialization before paulo@0: * deflateInit() */ paulo@0: tx_deflate->z.zalloc = Z_NULL; paulo@0: tx_deflate->z.zfree = Z_NULL; paulo@0: tx_deflate->z.opaque = Z_NULL; paulo@0: paulo@0: if (deflateInit (&tx_deflate->z, Z_DEFAULT_COMPRESSION) != Z_OK) paulo@0: { paulo@0: FREE (tx_deflate); paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: tx_deflate->buf = NULL; paulo@0: tx_deflate->nagle_timer = 0; paulo@0: tx_deflate->nbytes_in = 0; paulo@0: tx_deflate->nbytes_out = 0; paulo@0: tx_deflate->nbytes_flushed = 0; paulo@0: tx_deflate->nbytes_unflushed = 0; paulo@0: tx_deflate->flushing = FALSE; paulo@0: tx_deflate->delayed = FALSE; paulo@0: paulo@0: tx->udata = tx_deflate; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static void tx_deflate_destroy (struct tx_layer *tx) paulo@0: { paulo@0: struct tx_deflate *tx_deflate = tx->udata; paulo@0: paulo@0: io_buf_free (tx_deflate->buf); paulo@0: timer_remove (tx_deflate->nagle_timer); paulo@0: paulo@0: deflateEnd (&tx_deflate->z); paulo@0: FREE (tx_deflate); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: struct tx_layer_ops gt_tx_deflate_ops = paulo@0: { paulo@0: tx_deflate_init, paulo@0: tx_deflate_destroy, paulo@0: tx_deflate_toggle, paulo@0: tx_deflate_queue, paulo@0: tx_deflate_ready, paulo@0: tx_deflate_enable, paulo@0: tx_deflate_disable, paulo@0: };