#include <zlib.h>
#include "private/svn_subr_private.h"
#include "private/svn_error_private.h"
#include "svn_private_config.h"
const char *
svn_zlib__compiled_version(void)
{
static const char zlib_version_str[] = ZLIB_VERSION;
return zlib_version_str;
}
const char *
svn_zlib__runtime_version(void)
{
return zlibVersion();
}
#if ZLIB_VERNUM >= 0x1200
#define svnCompressBound(LEN) compressBound(LEN)
#else
#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
#endif
#define MIN_COMPRESS_SIZE 512
static svn_error_t *
zlib_encode(const char *data,
apr_size_t len,
svn_stringbuf_t *out,
int compression_level)
{
unsigned long endlen;
apr_size_t intlen;
unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p;
svn_stringbuf_setempty(out);
p = svn__encode_uint(buf, (apr_uint64_t)len);
svn_stringbuf_appendbytes(out, (const char *)buf, p - buf);
intlen = out->len;
if (len < MIN_COMPRESS_SIZE || compression_level == SVN__COMPRESSION_NONE)
{
svn_stringbuf_appendbytes(out, data, len);
}
else
{
int zerr;
svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
endlen = out->blocksize;
zerr = compress2((unsigned char *)out->data + intlen, &endlen,
(const unsigned char *)data, len,
compression_level);
if (zerr != Z_OK)
return svn_error_trace(svn_error__wrap_zlib(
zerr, "compress2",
_("Compression of svndiff data failed")));
if (endlen >= len)
{
svn_stringbuf_appendbytes(out, data, len);
return SVN_NO_ERROR;
}
out->len = endlen + intlen;
out->data[out->len] = 0;
}
return SVN_NO_ERROR;
}
static svn_error_t *
zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
apr_size_t limit)
{
apr_size_t len;
apr_uint64_t size;
const unsigned char *oldplace = in;
in = svn__decode_uint(&size, in, in + inLen);
len = (apr_size_t)size;
if (in == NULL || len != size)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
_("Decompression of zlib compressed data failed: no size"));
if (len > limit)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
_("Decompression of zlib compressed data failed: "
"size too large"));
inLen -= (in - oldplace);
if (inLen == len)
{
svn_stringbuf_ensure(out, len);
memcpy(out->data, in, len);
out->data[len] = 0;
out->len = len;
return SVN_NO_ERROR;
}
else
{
unsigned long zlen = len;
int zerr;
svn_stringbuf_ensure(out, len);
zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
if (zerr != Z_OK)
return svn_error_trace(svn_error__wrap_zlib(
zerr, "uncompress",
_("Decompression of svndiff data failed")));
if (zlen != len)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
NULL,
_("Size of uncompressed data "
"does not match stored original length"));
out->data[zlen] = 0;
out->len = zlen;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn__compress_zlib(const void *data, apr_size_t len,
svn_stringbuf_t *out,
int compression_method)
{
if ( compression_method < SVN__COMPRESSION_NONE
|| compression_method > SVN__COMPRESSION_ZLIB_MAX)
return svn_error_createf(SVN_ERR_BAD_COMPRESSION_METHOD, NULL,
_("Unsupported compression method %d"),
compression_method);
return zlib_encode(data, len, out, compression_method);
}
svn_error_t *
svn__decompress_zlib(const void *data, apr_size_t len,
svn_stringbuf_t *out,
apr_size_t limit)
{
return zlib_decode(data, len, out, limit);
}