| /* |
| * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. |
| * Copyright (C) 2003 Sun Microsystems, Inc. |
| * |
| * This is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This software is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this software; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| * USA. |
| */ |
| |
| #include "zrleoutstream.h" |
| #include <stdlib.h> |
| |
| #define ZRLE_IN_BUFFER_SIZE 16384 |
| #define ZRLE_OUT_BUFFER_SIZE 1024 |
| #undef ZRLE_DEBUG |
| |
| static rfbBool zrleBufferAlloc(zrleBuffer *buffer, int size) |
| { |
| buffer->ptr = buffer->start = malloc(size); |
| if (buffer->start == NULL) { |
| buffer->end = NULL; |
| return FALSE; |
| } |
| |
| buffer->end = buffer->start + size; |
| |
| return TRUE; |
| } |
| |
| static void zrleBufferFree(zrleBuffer *buffer) |
| { |
| if (buffer->start) |
| free(buffer->start); |
| buffer->start = buffer->ptr = buffer->end = NULL; |
| } |
| |
| static rfbBool zrleBufferGrow(zrleBuffer *buffer, int size) |
| { |
| int offset; |
| |
| size += buffer->end - buffer->start; |
| offset = ZRLE_BUFFER_LENGTH (buffer); |
| |
| buffer->start = realloc(buffer->start, size); |
| if (!buffer->start) { |
| return FALSE; |
| } |
| |
| buffer->end = buffer->start + size; |
| buffer->ptr = buffer->start + offset; |
| |
| return TRUE; |
| } |
| |
| zrleOutStream *zrleOutStreamNew(void) |
| { |
| zrleOutStream *os; |
| |
| os = malloc(sizeof(zrleOutStream)); |
| if (os == NULL) |
| return NULL; |
| |
| if (!zrleBufferAlloc(&os->in, ZRLE_IN_BUFFER_SIZE)) { |
| free(os); |
| return NULL; |
| } |
| |
| if (!zrleBufferAlloc(&os->out, ZRLE_OUT_BUFFER_SIZE)) { |
| zrleBufferFree(&os->in); |
| free(os); |
| return NULL; |
| } |
| |
| os->zs.zalloc = Z_NULL; |
| os->zs.zfree = Z_NULL; |
| os->zs.opaque = Z_NULL; |
| if (deflateInit(&os->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { |
| zrleBufferFree(&os->in); |
| free(os); |
| return NULL; |
| } |
| |
| return os; |
| } |
| |
| void zrleOutStreamFree (zrleOutStream *os) |
| { |
| deflateEnd(&os->zs); |
| zrleBufferFree(&os->in); |
| zrleBufferFree(&os->out); |
| free(os); |
| } |
| |
| rfbBool zrleOutStreamFlush(zrleOutStream *os) |
| { |
| os->zs.next_in = os->in.start; |
| os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); |
| |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamFlush: avail_in %d\n", os->zs.avail_in); |
| #endif |
| |
| while (os->zs.avail_in != 0) { |
| do { |
| int ret; |
| |
| if (os->out.ptr >= os->out.end && |
| !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { |
| rfbLog("zrleOutStreamFlush: failed to grow output buffer\n"); |
| return FALSE; |
| } |
| |
| os->zs.next_out = os->out.ptr; |
| os->zs.avail_out = os->out.end - os->out.ptr; |
| |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamFlush: calling deflate, avail_in %d, avail_out %d\n", |
| os->zs.avail_in, os->zs.avail_out); |
| #endif |
| |
| if ((ret = deflate(&os->zs, Z_SYNC_FLUSH)) != Z_OK) { |
| rfbLog("zrleOutStreamFlush: deflate failed with error code %d\n", ret); |
| return FALSE; |
| } |
| |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamFlush: after deflate: %d bytes\n", |
| os->zs.next_out - os->out.ptr); |
| #endif |
| |
| os->out.ptr = os->zs.next_out; |
| } while (os->zs.avail_out == 0); |
| } |
| |
| os->in.ptr = os->in.start; |
| |
| return TRUE; |
| } |
| |
| static int zrleOutStreamOverrun(zrleOutStream *os, |
| int size) |
| { |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamOverrun\n"); |
| #endif |
| |
| while (os->in.end - os->in.ptr < size && os->in.ptr > os->in.start) { |
| os->zs.next_in = os->in.start; |
| os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); |
| |
| do { |
| int ret; |
| |
| if (os->out.ptr >= os->out.end && |
| !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { |
| rfbLog("zrleOutStreamOverrun: failed to grow output buffer\n"); |
| return FALSE; |
| } |
| |
| os->zs.next_out = os->out.ptr; |
| os->zs.avail_out = os->out.end - os->out.ptr; |
| |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamOverrun: calling deflate, avail_in %d, avail_out %d\n", |
| os->zs.avail_in, os->zs.avail_out); |
| #endif |
| |
| if ((ret = deflate(&os->zs, 0)) != Z_OK) { |
| rfbLog("zrleOutStreamOverrun: deflate failed with error code %d\n", ret); |
| return 0; |
| } |
| |
| #ifdef ZRLE_DEBUG |
| rfbLog("zrleOutStreamOverrun: after deflate: %d bytes\n", |
| os->zs.next_out - os->out.ptr); |
| #endif |
| |
| os->out.ptr = os->zs.next_out; |
| } while (os->zs.avail_out == 0); |
| |
| /* output buffer not full */ |
| |
| if (os->zs.avail_in == 0) { |
| os->in.ptr = os->in.start; |
| } else { |
| /* but didn't consume all the data? try shifting what's left to the |
| * start of the buffer. |
| */ |
| rfbLog("zrleOutStreamOverrun: out buf not full, but in data not consumed\n"); |
| memmove(os->in.start, os->zs.next_in, os->in.ptr - os->zs.next_in); |
| os->in.ptr -= os->zs.next_in - os->in.start; |
| } |
| } |
| |
| if (size > os->in.end - os->in.ptr) |
| size = os->in.end - os->in.ptr; |
| |
| return size; |
| } |
| |
| static int zrleOutStreamCheck(zrleOutStream *os, int size) |
| { |
| if (os->in.ptr + size > os->in.end) { |
| return zrleOutStreamOverrun(os, size); |
| } |
| return size; |
| } |
| |
| void zrleOutStreamWriteBytes(zrleOutStream *os, |
| const zrle_U8 *data, |
| int length) |
| { |
| const zrle_U8* dataEnd = data + length; |
| while (data < dataEnd) { |
| int n = zrleOutStreamCheck(os, dataEnd - data); |
| memcpy(os->in.ptr, data, n); |
| os->in.ptr += n; |
| data += n; |
| } |
| } |
| |
| void zrleOutStreamWriteU8(zrleOutStream *os, zrle_U8 u) |
| { |
| zrleOutStreamCheck(os, 1); |
| *os->in.ptr++ = u; |
| } |
| |
| void zrleOutStreamWriteOpaque8(zrleOutStream *os, zrle_U8 u) |
| { |
| zrleOutStreamCheck(os, 1); |
| *os->in.ptr++ = u; |
| } |
| |
| void zrleOutStreamWriteOpaque16 (zrleOutStream *os, zrle_U16 u) |
| { |
| zrleOutStreamCheck(os, 2); |
| *os->in.ptr++ = ((zrle_U8*)&u)[0]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[1]; |
| } |
| |
| void zrleOutStreamWriteOpaque32 (zrleOutStream *os, zrle_U32 u) |
| { |
| zrleOutStreamCheck(os, 4); |
| *os->in.ptr++ = ((zrle_U8*)&u)[0]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[1]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[2]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[3]; |
| } |
| |
| void zrleOutStreamWriteOpaque24A(zrleOutStream *os, zrle_U32 u) |
| { |
| zrleOutStreamCheck(os, 3); |
| *os->in.ptr++ = ((zrle_U8*)&u)[0]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[1]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[2]; |
| } |
| |
| void zrleOutStreamWriteOpaque24B(zrleOutStream *os, zrle_U32 u) |
| { |
| zrleOutStreamCheck(os, 3); |
| *os->in.ptr++ = ((zrle_U8*)&u)[1]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[2]; |
| *os->in.ptr++ = ((zrle_U8*)&u)[3]; |
| } |