diff options
Diffstat (limited to 'protocols/Gadu-Gadu/src/libgadu/deflate.cpp')
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/deflate.cpp | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/src/libgadu/deflate.cpp b/protocols/Gadu-Gadu/src/libgadu/deflate.cpp new file mode 100644 index 0000000000..46f36f1e69 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/deflate.cpp @@ -0,0 +1,226 @@ +/* $Id$ */ + +/* + * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file deflate.c + * + * \brief Funkcje kompresji Deflate + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ + +#include <stdlib.h> +#include <string.h> + +#include "libgadu.h" +#include "deflate.h" + +#ifdef GG_CONFIG_HAVE_ZLIB +#include <zlib.h> +#endif + +/** + * \internal Kompresuje dane wejściowe algorytmem Deflate z najwyższym + * stopniem kompresji, tak samo jak oryginalny klient. + * + * Wynik funkcji należy zwolnić za pomocą \c free. + * + * \param in Ciąg znaków do skompresowania, zakończony \c \\0 + * \param out_lenp Wskaźnik na zmienną, do której zostanie zapisana + * długość bufora wynikowego + * + * \return Skompresowany ciąg znaków lub \c NULL w przypadku niepowodzenia. + */ +unsigned char *gg_deflate(const char *in, size_t *out_lenp) +{ +#ifdef GG_CONFIG_HAVE_ZLIB + int ret; + z_stream strm; + unsigned char *out, *out2; + size_t out_len; + + if (in == NULL || out_lenp == NULL) + return NULL; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = strlen(in); + strm.next_in = (unsigned char*) in; + + ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflateInit() failed (%d)\n", ret); + return NULL; + } + + out_len = deflateBound(&strm, strm.avail_in); + out = (unsigned char*)malloc(out_len); + + if (out == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + strm.avail_out = out_len; + strm.next_out = out; + + for (;;) { + ret = deflate(&strm, Z_FINISH); + + if (ret == Z_STREAM_END) + break; + + /* raczej nie powinno się zdarzyć przy Z_FINISH i out_len == deflateBound(), + * ale dokumentacja zlib nie wyklucza takiej możliwości */ + if (ret == Z_OK) { + out_len *= 2; + out2 = (unsigned char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + + strm.avail_out = out_len / 2; + strm.next_out = out + out_len / 2; + } else { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided"); + goto fail; + } + } + + out_len = strm.total_out; + out2 = (unsigned char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + *out_lenp = out_len; + deflateEnd(&strm); + + return out2; + +fail: + *out_lenp = 0; + deflateEnd(&strm); + free(out); +#endif + return NULL; +} + +/** + * \internal Dekompresuje dane wejściowe w formacie Deflate. + * + * Wynik funkcji należy zwolnić za pomocą \c free. + * + * \param in Bufor danych skompresowanych algorytmem Deflate + * \param length Długość bufora wejściowego + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. + * + * \return Zdekompresowany ciąg znaków, zakończony \c \\0, + * lub \c NULL w przypadku niepowodzenia. + */ +char *gg_inflate(const unsigned char *in, size_t length) +{ +#ifdef GG_CONFIG_HAVE_ZLIB + int ret; + z_stream strm; + char *out = NULL, *out2; + size_t out_len = 1024; + int first = 1; + + if (in == NULL) + return NULL; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = length; + strm.next_in = (unsigned char*) in; + + ret = inflateInit(&strm); + if (ret != Z_OK) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflateInit() failed (%d)\n", ret); + return NULL; + } + + do { + out_len *= 2; + out2 = (char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + + if (first) { + strm.avail_out = out_len; + strm.next_out = (unsigned char*) out; + } else { + strm.avail_out = out_len / 2; + strm.next_out = (unsigned char*) out + out_len / 2; + } + + ret = inflate(&strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided"); + goto fail; + } + + first = 0; + } while (ret != Z_STREAM_END); + + /* rezerwujemy ostatni znak na NULL-a */ + out_len = strm.total_out + 1; + out2 = (char*)realloc(out, out_len); + + if (out2 == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len); + goto fail; + } + + out = out2; + out[out_len - 1] = '\0'; + + inflateEnd(&strm); + + return out; + +fail: + inflateEnd(&strm); + free(out); +#endif + return NULL; +} |